tubone

tubone

Boyaki makes a new world


 Recent posts  6 / 52

SepOctNovDecJanオムロン HV-F312で腰の痛みをなくすGithub Actionを使って、簡単CIを作ってみるChromeDriverがGoogleChrome v76 に対応していないらしい。Googleデータポータルを触ってみるHyper-vにMetasploitableの仮想マシンを立ててみるNightwatch.jsでE2Eテストを回したときにうまく動かないたった一つの理由particles.jsをVue.jsで使ってかっこいいページを作るGitPitchを使ってMarkdownからプレゼンテーションを作ってBadgeをレポジトリに貼るSentryを使ってフロントエンドのエラーを確認するJSON Resume + API With GitHubを使って、さくっと職務経歴書チックなもののAPIなど作ってみるGitHubに30日間草を生やし続けた感想Netlify Formを使って、簡易Contact Formを作ってみる10/1は天下一品の日Ansible + Serverspecを使ってMacの環境構築を自動でする (Ansible編)Ansible + Serverspecを使ってMacの環境構築を自動でする (Serverspec編)今日のラーメン台風の時の我が家のセンサー(netatmo)の値をZabbixで見る究極の謝罪はSlackのスタンプを活用しよう! ~明日から使えるSlackスタンプスニペット集~ハロウィーンはえちえちでチンパンジーなイベントじゃない。GitHubと向き合うイベントだ昔ながらのラーメンたべたい珠玉の一杯。たくさん残業した日はこのラーメンを食べろ!スープの衝撃!ここまでうまいスープはあるのか!?なラーメンGitHubに草を生やし続け90日が経ったので感想を書くGoでAWS Lambdaを動かして、GitHubAPIv4(GraphQL)を叩いてみた感想Hadoopゾウさんについて本気出して考えてみたNuxt.jsでparticles-bg-vueを使うNuxt.jsのmodulesをCompositionAPIで使ってみる(@nuxtjs/toast編)くろおびらーめん with チャーシュー飯Nuxt.jsのmodulesをCompositionAPIで使ってみる(@nuxtjs/toast Global Option編)Nuxt.js + Composition APIでVuexのStateをReactiveに使う方法【初学者】Juliaを使って円周率を求める初めてプログラミングをした時のゴミソース見つけた面倒なことはPythonにやらせよう@GitHub API v4を使ったリリース実績取得Gatsby.jsで作ったブログに読み終わるまで○○分を追加した話Google広告設定でみる属性情報であなたをもっと知ろう!Blog用に新しいLogo作った話今年1年を振り返ってGoのEchoでJaegerを使ってボトルネックを調査するGatsby.jsで作ったBlogの投稿をGitHubの草にして表示させるWeb Developer Roadmap 2020を眺めながら今年の目標(Frontend)をだらだら考えるの会AWS X-RayでLambdaのトレースをしつつ、Datadog APMに連携するMonWedFri
AWS X-RayでLambdaのトレースをしつつ、Datadog APMに連携するWeb Developer Roadmap 2020を眺めながら今年の目標(Frontend)をだらだら考えるの会Gatsby.jsで作ったBlogの投稿をGitHubの草にして表示させるGoのEchoでJaegerを使ってボトルネックを調査する今年1年を振り返ってBlog用に新しいLogo作った話


 SearchBox

Search your interesting by Algolia in this blog.


 All 143 Tags

ぼやき 19JavaScript 12GitHub 7ラーメン 6Vue.js 6TypeScript 6デブ活 5Nuxt.js 4React 3Gatsby.js 3Ansible 3Serverspec 3Mac 3自動テスト 3AWS 3Python 3Auto Provisioning 3GitHub Action 2Netlify 2Lambda 2Go 2GraphQL 2ChromeDriver 2Chainer 2Write Code Every Day 2CompositionAPI 2toast 2test 1particle.js 1GitPitch 1GitHub Badges 1Azusa Colors 1GitHubAction 1Sentry 1監視 1JSON Resume 1GitHub Pages 1Resume 1CV 1 1weed 1CI 1Netlify Form 1健康 1Contact Form 1Travis 1天下一品 1腰痛 1Gatsby 1E2E Test 1Selenium 1GoogleChrome 1Azure Devops Build Pipeline 1netatmo 1台風 1IoT 1センシング 1Google Apps Script 1API FLASH 1SlackAPI 1Clasp 1Headless CMS 1Jest 1Unit Test 1Slack 1Stamp 1ハロウィーン 1Nim 1docopt 1CLI 1昆布 1Googleデータポータル 1BI 1Google Analytics 1仮想化 1AWS認定ソリューションアーキテクトプロフェッショナル 1資格 1勉強法 1Hyper-v 1metasploitable 1RNN 1LSTM 1Chat BOT 1アイマス 1デレマス 1Hadoop 1ゾウ 1ひなこのーと 1Deep Learing 1OpenCV 1機械学習 1CNN 1分類学習 1顔認識 1powerShell 1particles-bg-vue 1particle 1Proton 1particles.js 1E2Eテスト 1Nightwatch.js 1チャーシュー飯 1Composition API 1Vuex 1ストアパターン 1ギター 1DTM 1エフェクター 1ATELIERZ 1Caparison 1VOCALOID 1Julia 1円周率 1初心者 1The Gauss–Legendre algorithm 1Leibniz formula for π 1かわいい 1恐竜時代 1ことり隊 1早稲田 1GitHub API v4 1リリース実績 1Estimated Reading Time 1あと何分 1Google広告設定 1Google 1属性情報 1Persolal Data 1Server 1Seti@Home 1Logo 1SVG 1振り返り 1Echo 1Jaeger 1ボトルネック調査 1React Calendar Heatmap 1Frontend 1Web Developer Roadmap 2020 1X-Ray 1Datadog 1APM 1modules 1
この記事は618文字3.1で読めます

やらねば。(風立ちぬ)

案件でGoを使った開発にシフトしつつあるので必死こいて勉強してるわけですが、AWS LambdaGoで実装したことがなかったのでちょっと触ってみました、というお話。

Table of Contents

AWS LambdaがGoで動くことを知ってますか?

img

知っている人も多いと思いますが、2017年のre:Invent 2017(AWSのカンファレンスイベント)でLambdaに関するアップデートの中でGoで動くようになったよ~というのがありました。[速報]AWS Lambdaが機能強化。.NETとGo言語をサポート、サーバレスアプリケーションのリポジトリも登場。AWS re:Invent 2017

img

Lambda自体は、裏側の基盤にAWS Firecrackerを導入したことがきっかけで、集約化と安全性がめちゃんこあがったので、タイムアウトが長くなったり、カスタムランタイムに対応したりと一気に進化したイメージがありましたが、いまだにPythonか、Node.jsで書くかしかして無かったです。(怠け)

どこかでFirecrackerをいじりたいですね。

仕事上使う機会に恵まれたので今回Go Lambdaを初めて触ることにしました。

今回の開発スコープ

今回はお勉強というか、感触をつかむためにやるだけなのでちゃんとしたサービスは作りません。

別件でGitHub API(GraphQL)を触る必要もあったのでまとめてやってしまいます。

やること

  • main.goのみ

    • RepositoryとかModelとかUsecaseとかそういうのは作らないよ
  • GitHub APIv4 GraphQLを使うよ

    • とりあえず自分の公開されてるレポジトリの使用言語を一覧取るよ
  • 手でビルドし、手でデプロイするよ

とりあえずコード

これが作ったショボコードです。追って解説します。

package main

import (
	"context"
	"fmt"
	"github.com/shurcooL/githubv4"
	"golang.org/x/oauth2"
	"github.com/deckarep/golang-set"
	"github.com/aws/aws-lambda-go/lambda"
)

type Language struct {
	Name  string
	Color string
}

type Repository struct {
	Name string
	Languages struct {
		Nodes []struct {
			Language `graphql:"... on Language"`
		}
	} `graphql:"languages(first: 100)"`
}

var query struct {
	Search struct {
		Nodes []struct {
			Repository `graphql:"... on Repository"`
		}
	} `graphql:"search(first: 100, query: $q, type: $searchType)"`
}


func getLangList () (mapset.Set){
	src := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: "7xxxxxxxxxxxxxxxxxxxxxxxx"},
	)
	httpClient := oauth2.NewClient(context.Background(), src)

	client := githubv4.NewClient(httpClient)

	langlist := mapset.NewSet()

	variables := map[string]interface{}{
		"q": githubv4.String("user:tubone24"), //検索するuser名
		"searchType":  githubv4.SearchTypeRepository,
	}
	
	err := client.Query(context.Background(), &query, variables)
	if err != nil {
		// Handle error.
		fmt.Println(err)
	}

	for _, repo := range query.Search.Nodes {
		fmt.Println("---------")
		fmt.Println(repo.Name)
		for _, lang := range repo.Languages.Nodes {
			fmt.Println(lang.Name)
			langlist.Add(lang.Name)
		}
	}
	return langlist

}

func LambdaHandler () (string, error){
	result := getLangList()
	return fmt.Sprint(result), nil
}

func main() {
	lambda.Start(LambdaHandler)
}

GraphQLをGoで叩く

Goはまぎれもなくサーバーサイドな言語なのでどちらかというと関心事がGraphQLのサーバサイド実装でBFFたくさん作るのめんどくさいからまとめてGraphQLで返したい!という感じのモチベーションの記事が多めではありますが、ちゃんとGraphQLクライアントみつけました。

shurcooL/graphql

さらに、GitHubAPIv4専用のクライアントも見つけましたのでこっちを使うことにします。

shurcooL/githubv4

話は逸れますがGitHubAPIv4はGitHubのPersonal access tokensAccess Tokenを発行する必要があります。

New personal access tokenから発行できます。発行しておきましょう。shurcooL/githubv4でも使います。

img

shurcooL/githubv4自体の使い方はそこまで難しくなく、HttpClientやAuthをすませた後、GraphQLのクエリGoの構造体として定義して投げつければよいです。

このようなGraphQLなら・・

{
  search(query: "user:tubone24", type: REPOSITORY, first: 100) {
    edges {
      node {
        ... on Repository {
          name
          languages(first: 100) {
            edges {
              node {
                name
                color
              }
            }
          }
        }
      }
    }
  }
}

下記のようにすれば取得できます。

//main.go

import (
	"context"
	"fmt"
        "golang.org/x/oauth2"
	"github.com/shurcooL/githubv4"
)

// 構造体でGraphQL定義

type Language struct {
	Name  string
	Color string
}

type Repository struct {
	Name string
	Languages struct {
		Nodes []struct {
			Language `graphql:"... on Language"`
		}
	} `graphql:"languages(first: 100)"`
}

var query struct {
	Search struct {
		Nodes []struct {
			Repository `graphql:"... on Repository"`
		}
	} `graphql:"search(first: 100, query: $q, type: $searchType)"`
}


func hoge () {
	src := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: "7xxxxxxxxxxxxxxxxxxxxxxxx"},
	) //AccessTokenを設定

	httpClient := oauth2.NewClient(context.Background(), src) //AccessTokenをhttpClientに設定

	client := githubv4.NewClient(httpClient) //先ほど作ったhttpClient使ってclientを作成

	variables := map[string]interface{}{
		"q": githubv4.String("user:tubone24"), //検索するuser名
		"searchType":  githubv4.SearchTypeRepository,
	}
	
	err := client.Query(context.Background(), &query, variables) //client.Queryで実行。エラーのみが戻りで実行結果は咲くほど定義した構造体に格納
	if err != nil {
		// Handle error.
		fmt.Println(err)
	}

	for _, repo := range query.Search.Nodes {
		fmt.Println("---------")
		fmt.Println(repo.Name)
		for _, lang := range repo.Languages.Nodes {
			fmt.Println(lang.Name)
                        fmt.Println(lang.Color)
		}
	}

}

あらまぁ、簡単!と思ったものの、上記のようにNodesは取得できたのですが、Edgesに定義された値がどうやってもとれない。。

例えば・・

{
  search(query: "user:tubone24", type: REPOSITORY, first: 100) {
    edges {
      node {
        ... on Repository {
          name
          languages(first: 100) {
            edges {
              size              node {
                name
                color
              }
            }
          }
        }
      }
    }
  }
}

のようにedgesに項目がありnodeを取りたい場合

type Language struct {
	Name  string
	Color string
}

type Repository struct {
	Name string
	Languages struct {
            Edges []struct {		Node struct {			Language `graphql:"... on Language"`		}            }	} `graphql:"languages(first: 100)"`
}

var query struct {
	Search struct {
		Nodes []struct {
			Repository `graphql:"... on Repository"`
		}
	} `graphql:"search(first: 100, query: $q, type: $searchType)"`
}

とやってもエラーになってしまう・・。

こちらのPRの通りEdgesは構造体のスライスで宣言してね~というのも試したのですがうまくいかず。。

使用言語のサイズが取りたいだけなんですよ…。

取りたい項目

今回は趣旨から反するので一旦塩漬け。。

LambdaでGoを使うとき

main関数には AWSが用意している github.com/aws/aws-lambda-go/lambda からロジックを Invokeさせないと問答無用でLambdaでエラーになってしまいます。

そこで github.com/aws/aws-lambda-go/lambdalambda.Start を使って動かします。

func LambdaHandler () (string, error){
	result := hoge() //login
	return fmt.Sprint(result), nil
}

func main() {
	lambda.Start(LambdaHandler)}

AWS Lambdaにデプロイする

GoをLambdaにデプロイするときは、実行ファイルにBuildしたものをZIPで固めてあげます。

Lambda画面のCloud9から編集できないんですね・・・

実行ファイル、ということはビルドするプラットフォーム(OSとか)に依存してしまうのでは?と思ったのですが、 ベストプラクティスとして GOOS=linux をgo build時につけることでLinux互換な実行ファイルになるみたいです。

$ GOOS=linux go build main.go

あとは実行ファイルをZIPで固めて、Lambda作ってアップロードして保存すれば終わりです。

実行

Lambdaのテスト実行をしてみます。

img

無事、GitHubの私のレポジトリ群の言語一覧が取れました。

printしているものはCloudwatchにも出てきていました。(goのlogを使ってもきちんとCWにログ出るそうです。)

img

ひとまず完成っぽいです。

おまけ

上記のコードでは、レポジトリ群に重複した言語があった場合は重複を避ける形で出力しています。

PythonではSetという便利なものがあるのですが、Goではあるのでしょうか・・・。

ありました。

deckarep/golang-set

import (
	"fmt"
	"github.com/deckarep/golang-set")

// 中略・・

func main () {
	langlist := mapset.NewSet() // setを作る        // 中略
	for _, repo := range query.Search.Nodes {
		fmt.Println("---------")
		fmt.Println(repo.Name)
		for _, lang := range repo.Languages.Nodes {
			fmt.Println(lang.Name)
			langlist.Add(lang.Name) //setにAddする		}
	}
	return langlist // set{hoge, fuga} 重複がないsetが返る
}

便利!

こういう便利なもの、もっと作っていきたいですね。

使ってみての感想

GoでLambdaを組んでみての感想は

  • Cloud9で直接Lambda編集したいなぁ…
  • lambda.startにラッピングする必要があるのでローカルで確認しにくいなぁ

    • こちら解決する方法は次回考えます
  • 果たして早くなったのか?

    • わからん。調べたい
˚