- 投稿日:2020-01-21T23:04:50+09:00
Go for文の"_"の使い方
golangのfor文を勉強していて
"_"の意味を理解したのでまとめておきます。
結論から言いますと
インデックス番号を表示する必要がない場合に
"_"を使います。main.gopackage main import "fmt" func main(){ //スライス作成 l := []string{"jave", "python", "go"} //インデックス番号とスライスの要素を表示 for i:=0; i < len(l); i++{ fmt.Println(i, l[i]) } //スライスの要素だけを表示 for _, v := range l{ fmt.Println(v) } //"_"を使わずに要素を表示させようとすると for v := range l{ fmt.Println(v) } //インデックス番号が表示される //"_"でインデックス番号を埋める必要がある //map作成 m := map[string]int{"apple": 100, "banana":200} //mapのキーと値を表示 for k, v := range m{ fmt.Println(k, v) } //mapのキーを表示 for k := range m{ fmt.Println(k) } //mapの値を表示 for _, v := range m{ fmt.Println(v) } }
- 投稿日:2020-01-21T20:58:16+09:00
golangで標準入力
- 投稿日:2020-01-21T17:19:15+09:00
k8sとAPI Gateway(Ambassador)を用いたコンテナ管理
こんにちは。kwashiです。SPAによるフロントエンドと、APIサーバの組み合わせで構築したシステムを実装したので紹介します。前職で、モノリシックなシステムに、大規模な機能を追加して苦労したため機能や構成の分離を意識して実装しました。もし、Ambassadorやk8sのマニフェストを参考にしたい場合は、記事の後半に書いています。
※ 実装したシステムは、何らかのサービスを提供する仕様ではなく、技術的な検証・ポートフォリオを目的として作成しました。
SPAは、サーバーサイド側でのHTMLレンダリングを行わず、サーバーサイド側の役割をAPIサーバーに徹底させるために用いています。各APIはサービスごとにコンテナ化することで分離し、コンテナ管理ツールで各サービスを管理することで、スケーリングやデプロイが容易にできるようにしています。また、各APIへのトラフィックをルーティングするためAPI Gatewayを使用しています。
上記のような構成にした場合、認証機能をAPIサーバーとフロントエンド両方でセキュアに実装する必要があり面倒なので、Firebase Authenticationという認証基盤を用いて認証させます。
使用技術の実装に関しては、他の記事との重複も多いため、参考も含め別のQiita記事を紹介することとし、本記事では、システムの概要、k8sでの管理やAPI Gateway Ambassadorの設定ファイルに関して、記述していきます。
※ 今回の実装では、データベースにアクセスする部分を隔離して実装し、DBに関する実装は省いています。
git: (k-washi/example-k8s-ambassador)[https://github.com/k-washi/example-k8s-ambassador]
システム概要
図のように、クライアント側は、Vueを用いており、Nginxから発行されます。APIサーバーは、k8sで管理しており、認証が必要ないREST-API1とJWTの検証が必要なREST-API2、そして、JWTの検証を行うJWT-AutoOのDockerImageを実行しています。
また、ルーティング制御などを行うAPI GatewayとしてAmbassadorを用いています。クライアント側では、Firebaseと連携し、ログイン、ユーサー登録、ユーザ情報の取得、更新を可能としています。
また、REST-API1では、簡易なJSONによるGET, POSTの機能を提供しています。
REST-API2は、ユーザー情報に基づいたサービス提供の例として、JWT依存の認証、認可を伴うユーザー情報を提供しています。REST-API2の認証、認可は、gRPCを用いてJWTをJWT-AuthOに送信し、JWT-AuthOでFirebaseと連携してJWTの検証を行っています。
また、ユーザidに紐付けてJWTに証明部分を保存しておき、認証タイミングで新たに発行されたJWTであるかどうか確認も行っています。※基本的にJWTを用いる場合は、重要なデータは、管理すべきではないという意見もあります。そこで、クライアント側がFirebaseと連携し認証することで取得した新たなJWTを添えて、サーバーにアクセスした場合に、JWT-AuthOでは認証の状態であると認識し、最新のJWTであるかどうかに関わらず、JWTが送られてきた場合は認可の状態であると認識するような仕組みにしています。
つまり、重要なデータにアクセスするときは、常にクライアント側がFirebaseと連携した認証を行うということになります。
(このJWT周りに関しては議論すべきであると思います、、、)API Gatewayは以下のルーティングを制御しています。また、各APIに対するDocker ImageとGitも以下の通りです。
※API Gatewayのportは30000に設定している。- path: "/:80" - image: kwashizaki/example-vue-cli - git: https://github.com/k-washi/example-vue-cli.git - msg:vueにより構築したフロントエンド - paths: ["/api/ex-golang/rest-api/", "/api/ex-golang/health/"] - image: kwashizaki/example-golang-rest-api - git: https://github.com/k-washi/example-golang-rest-api.git - msg: REST-API1が提供するAPI(GET, POSTで文を提供、保存, & healthでstatus 200を返答) - paths: ["/api/ex-jwt/jwt/ex-jwt-auth", "/api/ex-jwt/auth/ex-authentication"] - images: kwashizaki/example-golang-jwt-auth-client:v1.0.0 - git: https://github.com/k-washi/example-golang-jwt-auth/tree/master/testApp - msg: REST-API2が提供するJWT-AuthOによるJWT検証を伴うユーザー情報の提供 - paths: ["/ex-jwt-sr/"] #gRPC - image: kwashizaki/example-golang-jwt-auth-server:v1.0.0 - git: https://github.com/k-washi/example-golang-jwt-auth - msg: JWT-AuthOにおけるJWT検証( gRPCサーバー)使用技術の概要
次に、使用した技術を紹介します。私が以前書いた記事にリンクを飛ばしているので、参考にしてみてください。
個人的には、golangの記事が気に入っているのでぜひ!!Vue.js
現在人気のJavaScriptフレームワークです。個人的に、Reactの非同期処理に使用するRedux-sagaの学習コストが高いと感じ、また、使用感もVue.jsの方が良かったのでVue.jsを選択しました。
フロントエンドにおいてSPAを作成するために使用。ルーティングにvue-router, ユーザー名など全体で使用する状態の管理にVuex, UIコンポーネントフレームワークとしてVuetifyを用いました。# vue --version 3.10.0
基本的には、以下の私の過去記事を拡張した実装にしている。(Git: https://github.com/k-washi/example-vue-cli.git )
Qiita: Vue.js (Vuex, vue-router, vuetify) とFirebaseで始めるユーザー管理Golang
Pythonを使い続けていたのですが、静的型付け言語も使用してみたかったので最近流行っているGolangをバックエンドの言語として選択。
並列処理が書きやすく、標準パッケージのサポートが強力。
本システムでは、各サービスをコンテナ化しており、コンテナを軽量化できるという利点がある。例えば、今回Golangで作成したサービスは、以下のように20MB程度でDockerコンテナ化できる。
REPOSITORY TAG IMAGE ID CREATED SIZE kwashizaki/example-golang-jwt-auth-server v1.0.0 4cf9b4595b01 7 hours ago 23.9MB kwashizaki/example-golang-jwt-auth-client v1.0.0 baa4bce6c5fe 44 hours ago 24.5MB kwashizaki/example-golang-rest-api v1.0.0 8d92d819d8ad 8 days ago 22.6MBまた、Webフレームワークとして、軽量かつシンプルなginを使用した。
本システムのAPIは、基本的には、私が以前書いたはじめてのGolang Webアプリケーション ~ テスト, Dockerコンテナ化までと同様の構成で実装している。
Firebase
Firebaseは、Googleが提供している、MBaas(Mobile Backend as a service)の一つです。機能として、データーベースや認証があり、本システムでは、認証機能であるFirebase Authenticationを使用した。趣味程度の範囲では無料で使用できます。
Firebase Authenticationは、フロントエンドにおいて認証の結果、JWT((Json Web Token))を送り返す。
サーバー側は、フロントエンドから送られてきたJWTを用いてFirebaseに検証してもらい、有効なJWTかどうか判断することが可能である。
つまり、サーバー側で、ログイン済のユーザーかどうか、JWTを用いて検証できる。※ サーバー側では、検証に必要なパラメータを定義したex-firebase-auth-firebase-adminsdk-xxxx.jsonファイルが必要である。
firebase SDKにて提供されている。また、Golangによる実装は、「Vue.js + Go言語 + Firebase 」で始める! Frontend & Backend API 両方で認証するセキュアなSPA開発ハンズオン!を参考にした。
JWT
以下のフォーマットに従って構成された文字列である。
{base64エンコードしたhead1er}.{base64エンコードしたclaims}.{署名}発行者(今回は、Firebase)が鍵を使用して、JSONに署名することでトークンとして扱うことができる。また、鍵を使用して検証することで改善を検知できる。
claims部には、ユーザー名などの任意の情報を含めることができる。JWTを分解してclaims部分を抽出、そして、Firebaseにおける情報をデコードするライブラリを、k-washi/jwt-decodeに作成した。
Qiita: Golang によるfirebase AuthenticationにおけるJWT解析
nginx
OSとアプリケーションソフトウェアとアプリケーションとの仲立ちをするミドルウェアの一つで、HTTPリクエストなどを送ったときに、レスポンスを返すWebサーバーソフトウェア。リバースプロキシやロードバランサ機能があり、Apacheと比較して、早くて高付加に強い。
SPAを配布するためのWebサーバーとして用いた。
Qiita: Vue.jsプロジェクトにおけるnginxの設定とDockerによるコンテナ化の例
gRPC
gRPCは、RPC(Remoto Procedure Call)を実現するためにGoogleが開発したプロトコルの一つです。Protocol Buffersを使ってデータをシリアライズし、高速な通信を実現できる点が特徴です。gRPCに関しては、gRPCって何?が参考になりました。
本システムでは、バックエンドにおいてサービス間の通信に使用しました。
Qiita: Golangで始めるgRPC
Docker
ホストマシンのカーネルを利用しプロセスやユーザなどを隔離することで、あたかも別のマシンが動いているかのように動かします。そのため、軽量で高速に仮想環境を起動、停止などが可能です。
私が以前書いたはじめてのGolang Webアプリケーション ~ テスト, Dockerコンテナ化までという記事にGolangのDockerコンテナ化の方法を載せています。
今回、システムに用いたDockerImageは以下の通りです。
REPOSITORY TAG IMAGE ID CREATED SIZE kwashizaki/example-vue-cli v1.0.0 7d1f0394bec3 2 hours ago 25.3MB kwashizaki/example-golang-jwt-auth-server v1.0.0 4cf9b4595b01 7 hours ago 23.9MB kwashizaki/example-golang-jwt-auth-client v1.0.0 baa4bce6c5fe 44 hours ago 24.5MB kwashizaki/example-golang-rest-api v1.0.0 8d92d819d8ad 8 days ago 22.6MB quay.io/datawire/ambassador 0.83.0 d8caf63d933c 3 weeks ago 691MBKubernetes(k8s)
自動デプロイ、スケーリング、アプリ・コンテナの運用自動化のために設計されたオープンソースのプラットフォームです。
本記事では、本システムのk8s設定方法に関して、説明していきます。Ambassador
公式より
Ambassadorはマイクロサービス用のAPIGatewayです。
機能
- AWS APIGatewayのようなAPIGatewayのホスティング機能
- Kongのような伝統的なAPIGateway機能
- Nginxや, Envoy, k8sのIngressのようなProxy機能
具体的な機能
- 細かなルーティング制御、正規表現ベースのルーティング、ホストルーティングなどが可能
- 認証
- gRPC, HTTP/2をサポート
- カナリアリリース
- シャドートラッキング機能
- 特定サービスへのL7トラフィックの透過的な監視
運用者(Ops)の観点
- ルーティングとスケーリングをEnvoyとKubernetesに依存しているので、展開と操作が簡単
- TLSTerminationとリダイレクトを広範囲に運用可能
- トラブルシューティング時に統合的に診断する事が可能
- 複数の異なるバージョンのAmbassadorを運用出来て、簡単にテスト、更新する事が可能
- Istioと連携してサービスメッシュ化が可能
内部実装
- k8sを最大限に利用し、信頼性(reliability), 可用性(availability), スケーラビリティ(scalability)を担保
- 状態管理の為に、データベースのようなストレージを必要とせず、Kubernetes内に全ての状態管理を維持
- スケール時は、k8sのReplicasetでレプリカ数を変更するか、horizontal pod autoscalerを利用する事で簡単にスケール事が可能
- EnvoyProxyを使用して、全てのトラフィックのルーティングとプロキシーを実行
プログラム等のVersion
# go version go version go1.12.7 darwin/amd64 # docker -v Docker version 19.03.2, build 6a30dfc # kubectl version >lient Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.6", GitCommit:"96fac5cd13a5dc064f7d9f4f23030a6aeface6cc", GitTreeState:"clean", BuildDate:"2019-08-19T11:13:49Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"darwin/amd64"} Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.6", GitCommit:"96fac5cd13a5dc064f7d9f4f23030a6aeface6cc", GitTreeState:"clean", BuildDate:"2019-08-19T11:05:16Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"} # vue --version 3.10.0k8s マニフェスト
本章では、以下のようなpod, service, deploymentsを立ち上げる。
サービスを見て分かるように、localhost:30000としてExternal ipが設定されている。
一方で、kubectl get pods NAME READY STATUS RESTARTS AGE ambassador-55d75bc95b-29ckv 1/1 Running 0 3m59s ambassador-55d75bc95b-2lxzv 1/1 Running 0 3m27s ambassador-55d75bc95b-9vjnz 1/1 Running 0 2m18s ex-go-5c5747dbdb-ddkpx 1/1 Running 1 5d21h ex-jwt-cl-576556588d-7sdgc 1/1 Running 1 5d21h ex-jwt-sr-78bdf9f4d4-6dmft 1/1 Running 1 5d21h kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ambassador LoadBalancer 10.97.238.197 localhost 30000:30685/TCP 5d21h ambassador-admin NodePort 10.103.162.214 <none> 8877:31915/TCP 5d21h ex-go ClusterIP 10.101.114.104 <none> 8080/TCP 5d21h ex-jwt-cl ClusterIP 10.106.203.56 <none> 8080/TCP 5d21h ex-jwt-sr ClusterIP 10.99.209.88 <none> 8080/TCP 5d21h kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 92d kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE ambassador 3/3 3 3 5d22h ex-go 1/1 1 1 5d22h ex-jwt-cl 1/1 1 1 5d22h ex-jwt-sr 1/1 1 1 5d22hSecret機能
k8sのSecret機能を用いて、Firebaseの設定ファイルなど漏らしたくない秘密情報を設定する。
env/env-secret.txtgoogle_app_creds=/tmp/ex-firebase-auth-firebase-adminsdk-xxxxxxx.jsonのファイルを作成し、
kubectl create secret generic --save-config firebase-secret --from-env-file ./env/env-secret.txtのコマンドを用いて以下の、ファイルの設定を読み込む。
確認は、kubectl get secret #NAME TYPE DATA AGE #firebase-secret Opaque 1 5d21hここで設定したファイルのパスへ、DockerImageをデプロイするときに、Firebase SDKの設定ファイルをコピーする。
API Gateway Ambassadorの設定
k8sはRBACという、各種リソースへのアクセス権限を管理する仕組みです。それが有効であるとして、公式で提供されている、manifestをインストールします。
kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-rbac.yaml
一応Git:example-k8s-ambassador/ambassador/ambassador-rbac.yamlに、実際に使用したプログラムをおいています。
基本的には、
1. ambassadorというサービスアカウントとサービス等に権限を与えるClusterRoleをClusterRoleBindingで紐付け
2. ambassadorコンテナを使用するport番号(http:80, admin:8877)で作成し、レプリカ数3でDeploymentを作成。
3. NodoPortとして、port(admin:8877)を紐付けてサービスを作成
を行っている。NodePortととしてServiceを作成したので、以下のようにLoodBalancerを設定している。
httpのportは30000に上書きし、コンテナで受け付けるポートを8080に設定している。kubectl apply -f ambassador/ambassador-service.yaml
ambassador/ambassador-service.yaml--- apiVersion: v1 kind: Service metadata: name: ambassador spec: type: LoadBalancer #externalTrafficPolicy: Local ports: - name: http port: 30000 targetPort: 8080 selector: service: ambassadorConfig Map
k8s内で使用する変数を設定している。
このあと、各サービスをデプロイするので、その際の変数として使用する。kubectl apply -f example-golang-vue/example-jwt-config.yaml
example-golang-vue/example-jwt-config.yamlapiVersion: v1 kind: ConfigMap metadata: name: ex-jwt-map data: origin.host: localhost #数字は, ""で囲む origin.port: "80" ambassador.host: ex-jwt-sr ambassador.port: "8080" jwtserver.host: localhost jwtserver.port: "50051"コンテナのデプロイ
getambassador.io/configにルーティングの設定を行っている。
そこでは、Ambassadorを経由し、http://ex-go:8080としてリクエストするように設定している。
このex-goは、Deploymentに合わせている。kubectl apply -f example-golang-vue/example-golang.yaml
example-golang-vue/example-golang.yamlapiVersion: v1 kind: Service metadata: name: ex-go annotations: getambassador.io/config: | --- apiVersion: ambassador/v0 kind: Mapping name: ex-health-map prefix: /api/ex-golang service: http://ex-go:8080 spec: type: ClusterIP ports: - name: cl-ip-ex-go port: 8080 targetPort: 8080 selector: app: ex-go --- apiVersion: apps/v1 kind: Deployment metadata: name: ex-go spec: replicas: 1 selector: matchLabels: app: ex-go strategy: type: RollingUpdate template: metadata: labels: app: ex-go spec: containers: - name: ex-go-ui image: kwashizaki/example-golang-rest-api:v1.0.0 ports: - name: ex-go containerPort: 8080 resources: limits: cpu: "0.1" memory: 100Mi他のサービス、デプロイメントのマニフェストもデプロイする。他のサービスも、上のマニフェストと同様に設定している。
コンテナ内で使用する環境変数として、env配下に設定している。また、Firebaseの設定に関してはデプロイ時に、Volume配下のパスのファイルをVolumeMountのパスへコピーしている。
これによって、DockerImage内に秘匿すべき設定ファイルを含む必要がなくなる。gRPCに関しては、ルーティングの制御が特殊で、getambassador.io/confiのserviceを ex-jwt-srとしスキームを書く必要がない。
また、/jwtauth.JwtService/は、gRPCのプロトコルをgolang用に変換したファイルに記載されている。kubectl apply -f example-golang-vue/example-jwt-server.yaml kubectl apply -f example-golang-vue/example-jwt-client.yamlexample-golang-vue/example-jwt-client.yamlapiVersion: v1 kind: Service metadata: name: ex-jwt-cl annotations: getambassador.io/config: | --- apiVersion: ambassador/v0 kind: Mapping name: ex-jwt-cl prefix: /api/ex-jwt service: http://ex-jwt-cl:8080 spec: type: ClusterIP ports: - name: cl-ip-ex-jwt port: 8080 targetPort: 8080 selector: app: ex-jwt-cl --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: ex-jwt-cl spec: replicas: 1 strategy: type: RollingUpdate template: metadata: labels: app: ex-jwt-cl spec: containers: - name: ex-jwt-ui image: kwashizaki/example-golang-jwt-auth-client:v1.0.0 ports: - name: ex-jwt-cl containerPort: 8080 resources: limits: cpu: "0.1" memory: 100Mi env: - name: ORIGIN_HOST valueFrom: configMapKeyRef: name: ex-jwt-map key: origin.host - name: ORIGIN_PORT valueFrom: configMapKeyRef: name: ex-jwt-map key: origin.port - name: AMBASSADORHOST valueFrom: configMapKeyRef: name: ex-jwt-map key: ambassador.host - name: PORT valueFrom: configMapKeyRef: name: ex-jwt-map key: ambassador.portexample-golang-vue/example-jwt-server.yamlapiVersion: v1 kind: Service metadata: name: ex-jwt-sr annotations: getambassador.io/config: | --- apiVersion: ambassador/v0 kind: Mapping name: ex-jwt grpc: True prefix: /jwtauth.JwtService/ rewrite: /jwtauth.JwtService/ service: ex-jwt-sr spec: type: ClusterIP ports: - name: cl-ip-ex-jwt-sr port: 8080 targetPort: 50051 selector: app: ex-jwt-sr --- apiVersion: apps/v1 kind: Deployment metadata: name: ex-jwt-sr spec: replicas: 1 selector: matchLabels: app: ex-jwt-sr strategy: type: RollingUpdate template: metadata: labels: app: ex-jwt-sr spec: containers: - name: ex-jwt-sr image: kwashizaki/example-golang-jwt-auth-server:v1.0.0 ports: - name: ex-jwt-sr-api containerPort: 50051 resources: limits: cpu: "0.1" memory: 100Mi env: - name: AMBASSADORHOST valueFrom: configMapKeyRef: name: ex-jwt-map key: jwtserver.host - name: PORT valueFrom: configMapKeyRef: name: ex-jwt-map key: jwtserver.port - name: GOOGLE_APPLICATION_CREDENTIALS valueFrom: secretKeyRef: name: firebase-secret key: google_app_creds volumeMounts: - name: firebase-creds mountPath: /tmp #firebase-auth-credファイルを置く場所 readOnly: true volumes: - name: firebase-creds hostPath: path: /Users/washizakikai/DevLocal/git/kwashi/example-k8s-ambassador/envまとめ
ここでは、k8sによるコンテナ管理とAPI Gateway であるambassadorの設定をしめし、コンテナ間で連携したマイクロサービスの例を示した。
他のQiita記事と重複をさけ、特にk8sのマニフェストに関して説明している。もし、VueやGolangの設定が気になる方は、記事の途中に参照した記事をぜひ見てみてください。
- 投稿日:2020-01-21T16:42:43+09:00
AtCoderのコンテストをスクレイピングして自動的にGoogleCalendarに追加する
はじめに
僕も9月に始めたばかりの競技プログラミングサイト,AtCoderですが,「公式のGoogle Calendarの反映が遅い!!」と前から思っていたので,だったら自分で作ってしまおうということで作ってみました.
一見難しそうに見えますが割と簡単なのでサクッと作成した流れを書いていこうと思います!カレンダー:AtCoder Google Calendar
ソースコード:https://github.com/champon1020/sachiカレンダーは勝手に追加してもらってかまいません!
システムフロー
一番はじめに思いつくオーソドックスなやり方かと思います.
スクレイピング
今回はGoを使おうと思ったので、goqueryというパッケージを使用してみました.
こちらの記事とGoDocを参考にしました.
とてもシンプルで扱いやすいパッケージでした.Google Calendar Apiの設定
コード書くよりもここが一番大変でした.
とりあえず,設定の手順は以下の通りになります.
- Google Api Console でプロジェクト作成
- Google Calendar Api を有効化
- OAuth 2.0の同意画面の設定
- OAuth 2.0のアカウントを作成 & JSONファイルをダウンロード
- Google Calendar Apiを使ってコードを書く
- アクセストークンをJSONファイルで作成
とりあえずよくわからんのでドキュメントに沿っていきました.
Google Calendar Api Sample : https://developers.google.com/calendar/quickstart/js
Goのサンプルコード : https://developers.google.com/calendar/quickstart/goいろんな記事にもっと詳しいやり方が書いてあると思うので,ここでは簡単に説明します.
Consoleに入ったら左上の方に「プロジェクト作成」蘭があるか,ドロップダウンで「新しいプロジェクト作成」とかがあると思うので,そこから流れに任せてプロジェクトを作成します.
上の検索欄で「Google Calendar」と検索するか,もしくは直接ググってみるとGoogle Calendar Api用の画面にくると思います.あとは「APIを有効化」を押せばOKです.
普通のGoogle Api Consoleに戻って「OAuth 2.0同意画面」の設定を行います.左ボックスにあるとおもいます.
「OAuth 2.0同意画面」とは,単にアプリケーションがアカウントのGoogleサービスにアクセスするときの認証を行う画面のことです.
名前とかは適当でいいですが,スコープだけ注意してください!(後ほど記載)Google Consoleの左ボックスの「認証情報」から,「OAuth2.0」のアカウントを作成します.作成したら右の方にあるダウンロードボタンでJSONファイルをダウンロードします.
※このファイルは機密情報なので必ずgitignoreするか,もしくはリポジトリ内に入れないでください!Google Calendar Apiのドキュメントにサンプルコードが載ってるので,とりあえずそれを写経しました.
サンプルコードを実行してみました.すると,実行したターミナルにURLが出てくるのでそこをクリックすると,認証画面に飛びます.しかし,進んでいくと以下の画面が出ました.
デベロッパーなので気にせず「安全ではないページに移動」から移動してみましょう.
進んでいくとトークンのようなキーが生成されるので,それをコピーして実行ターミナルにペーストします.
すると,token.json
というファイルが作成されます.
これでアプリからGoogle Calendarにアクセスできるようになりました.スコープについて
スコープとは,簡単に言うと「OAuth2.0でどこまでを許可対象に入れるか」みたいな感じだと思います.今回はEventを扱うだけなので以下のスコープを追加しました.
https://www.googleapis.com/auth/calendar.eventsまた,サンプルコードをそのまま用いてclientを作成し,そのまま追加したいイベントをGoogle Calendarにプッシュすると以下のエラーが出ます.
Error 403: Insufficient Permission: Request had insufficient authentication scopes.OAuth2.0の同意画面側ではスコープの設定が完了していますが,肝心のサンプルコード内ではスコープを設定できていない状態でした.
そこで,サンプルコードを以下のように変更します.func main() { ... // config, err := google.ConfigFromJSON(b, calendar.CalendarReadonlyScope) config, err := google.ConfigFromJSON(b, calendar.CalendarEventsScope) ... }これでもう一度
token.json
を作成するところから始めれば問題ないはずです!デプロイ
僕はVPSを使用しているので,scpでサーバに送ってビルドして実行しただけです.
一応コマンドを載せて起きます.// build go build -o hoge // run ./hoge &常駐アプリなので
&
をつけて実行しました.まとめ
速攻で作ったので出来の良いものではないかもしれませんが,楽しかったのでよかったです.
また,テストコードも一応作成したのですが,コンテストが毎回更新されるため,エラーが起きないかどうかくらいしかテストすることができませんでした...
こういう場合,どのようにテストを行えば良いのかわかりません...笑
勉強しなければいけませんね.参考文献
Google Calendar Api Document : https://developers.google.com/calendar
goquery GoDoc : https://godoc.org/github.com/PuerkitoBio/goquery
Goとgoqueryでスクレイピング : https://qiita.com/Yaruki00/items/b50e346551690b158a79
- 投稿日:2020-01-21T01:27:35+09:00
Go歴0日だけどGo/beegoでSDK使わずにLINEBot作ってみる
はじめに
こんにちは、今回はGoの入門がてらLINEBotを作成したいと思います。
Goにはこれっしょ!となるようなWebフレームワークは存在しないようですが、筆者の浅い調べによるとGinというフレームワークが人気があるようです。
また、同様の浅い調べによってbeegoというWebフレームワークも割と人気があるとのことで。どっちにしようか迷ったのですが、なんとなく情報が少ないbeegoの方を使うことにしました。
LINEBotの基本のキであるおうむ返しBotを作成します。
Goの書き方で何か変な部分やこっちの方がええんちゃうってのがありましたら、コメ欄にてそっと教えてください。
beego公式
https://beego.me/今回Goの書き方は以下の記事を参考に頑張ります。
他言語プログラマがgolangの基本を押さえる為のまとめLINE Developerへの登録/チャネルの登録
↓ここから登録
https://developers.line.biz/ja/色々現在の状況と異なりますが以下の記事を参考にチャネルの登録を行います
https://qiita.com/nkjm/items/38808bbc97d6927837cdGoのインストール
僕はMacユーザーなので以下で行うインストール手順は全てMacユーザー向けです。
そもそもGoがインストールされていないのでインストールします。
$ brew install go $ go version #これでversionが出ればインストールOKですbeegoのインストール
go get -u github.com/astaxie/beego go get -u github.com/beego/bee
以下を参考にパスを通します。
Beego(Golang Framework)使い方メモ$ export GO15VENDOREXPERIMENT=1 $ export GOPATH=$HOME/go $ export PATH=$PATH:$GOPATH/bin $ export PATH=$PATH:$GOROOT/bin $ source ~/.bashrcbeegoプロジェクト作成
https://beego.me/docs/quickstart/new.md
$ bee new linebot以下のような構成になります
linebot ├── conf │ └── app.conf ├── controllers │ └── default.go ├── main.go ├── models ├── routers │ └── router.go ├── static │ ├── css │ ├── img │ └── js ├── tests │ └── default_test.go └── views └── index.tpl以下でサーバーを起動できます
$ bee runhttp://localhost:8080/
にアクセスして以下の画面が出れば成功ですルーティング
https://beego.me/docs/quickstart/router.md
LINEBot用に新たに以下を追加します。
router/router.gopackage routers import ( "linebot/controllers" "github.com/astaxie/beego" ) func init() { beego.Router("/", &controllers.MainController{}) beego.Router("/line", &controllers.LineController{}) //追加 }jsonの扱い
jsonの扱いについては以下の記事が参考になりました
https://qiita.com/nayuneko/items/2ec20ba69804e8bf7ca3Goってこんなにjsonが扱いずらいものなんですね。。
PHPなら5行で書けそうな処理だね。いや本当に。
とりあえず構造体で諸々定義していきます。
LINEAPIで使用するjsonはネストしているので、以下の記事内のパターン1で行きます。
https://qiita.com/msh5/items/dc524e38073ed8e3831bline_controller.gotype LineMessageBodyJson struct { ReplyToken string `json:"replyToken"` Messages LineMessageMessageJsons `json:"messages"` } type LineMessageMessageJson struct{ Type string `json:"type"` Text string `json:"text"` } type LineMessageMessageJsons []LineMessageMessageJson type LineMessageFromJson struct { Events []struct{ ReplyToken string `json:"replyToken"` Message struct{ Text string `json:"text"` } `json:"message"` } `json:"events"` }こんな感じで構造体を定義しました。
コントローラーの作成
先ほどの構造体の定義も含めてコントローラーを作成します。
https://beego.me/docs/quickstart/controller.md
https://beego.me/docs/mvc/controller/controller.mdパラメータの受け取り方については以下のページが参考になります。
https://beego.me/docs/mvc/controller/params.mdline_controller.gopackage controllers import ( "github.com/astaxie/beego" "encoding/json" "net/http" //"net/url" "bytes" "log" "io/ioutil" ) type LineController struct { beego.Controller } type LineMessageBodyJson struct { ReplyToken string `json:"replyToken"` Messages LineMessageMessageJsons `json:"messages"` } type LineMessageMessageJson struct{ Type string `json:"type"` Text string `json:"text"` } type LineMessageMessageJsons []LineMessageMessageJson type LineMessageFromJson struct { Events []struct{ ReplyToken string `json:"replyToken"` Message struct{ Text string `json:"text"` } `json:"message"` } `json:"events"` } func (this *LineController) Post() { // request内容の受け取り処理 var received_message LineMessageFromJson body, err := ioutil.ReadAll(this.Ctx.Request.Body) if err != nil { log.Fatal(err) } if err := json.Unmarshal(body, &received_message); err != nil { log.Fatal(err) } // eventごとに処理(今回は1つだけ) for _, event := range received_message.Events{ message := event.Message //source := event.Source reply_token := event.ReplyToken text := message.Text //replyするデータの用意 data := new(LineMessageBodyJson) data.ReplyToken = reply_token to_send_message := LineMessageMessageJson { Type: "text", Text: text, } var to_send_messages LineMessageMessageJsons to_send_messages = append(to_send_messages, to_send_message) data.Messages = to_send_messages json_data, _ := json.Marshal(data) //request作成 endpoint_uri := "https://api.line.me/v2/bot/message/reply" req, err := http.NewRequest( "POST", endpoint_uri, bytes.NewBuffer(json_data), ) if err != nil { log.Fatal(err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer hogehoge") //自分のアクセストークン client := &http.Client{} resp, err_in_req := client.Do(req) if err_in_req != nil { log.Fatal(err_in_req) } log.Print(resp) defer resp.Body.Close() } }僕が慣れていないからかもしれませんが、GoってPostリクエストの受け取りだったり、先ほど出てきたjsonの扱いだったり、色々大変ですね。。
実装はこれで終了です!
ngrokを用いてテストする
ngrokの設定
実際に試してみます。
WebhookURLにlocalhostは指定できないのでngrokを使います。以下の記事を参考にngrokをインストールしてください。
ngrokが便利すぎるインストールできたら
$ ngrok http 8080とすると
このように起動できます。
これより後に出てくるngrokのURLは適宜自分のものと置き換えてくださいWebhookURLの設定
MessagingAPI設定|>Webhook設定|>WebhookURL
から
https://5fb80f51.ngrok.io/line
を入力します。テスト
これで準備は整いました!
サーバーを起動して以下のようにおうむ返しされれば成功です!
終わりに
僕はいろんな言語に入門する際に試しにLINEBotを作ってみるのですが、Goは一番大変でした。
GoにはSDKが用意されているので大人しくSDKを使用するべきだと感じました。
(SDK使わずにFlexメッセージとか作成しようもんなら一大苦労ですね多分。。)Web用途に限らずこれから頑張ってGo勉強したいと思います!
- 投稿日:2020-01-21T00:15:21+09:00
Goでduを高速化してみた
背景
後輩: ホームディレクトリのディスク使用率が100%になったみたいです!新規でログインできなくなりやした!
ワタシ: 犯人をさがせー
後輩: dfしたけど100%ってことしかわからんとです....
ワタシ: duで調べるんやで! -sk?オプションはググって!
後輩: ググって実行したんですけど、めっちゃ時間かかって返ってこないっす....
ワタシ: 待つしかないなー\(^o^)/ワタシだけかもしれませんが、
du
ってオプション覚えられなくないですか?
あと、遅くないですか?こんな出力にしたいな
ファイルパス ファイル数 容量
grepableにしたいよね〜実装
Goにはゴルーチンという必殺の武器があるので、並列実行で早くなりますな!
https://github.com/kuritayu/infra-tools/blob/master/cmd/rapidu/main.goベンチマーク
環境はMac、timeの出力結果。ファイル数
598909
、容量129755.2 MB
。du -sk /Users 0.58s user 11.63s system 57% cpu 21.387 total/Users 598909 files 129755.2 MB # 実際の出力結果 rapidu /Users 5.44s user 27.37s system 232% cpu 14.114 total7秒早くなった。出力結果をシンプルにできた!