Drone v0.8 の .drone.yml を v1 の .drone.jsonnet に変換するツールを作った

    Drone v0.8 の .drone.yml を v1 の .drone.jsonnet に変換するツールを作ったので紹介します。 https://github.com/suzuki-shunsuke/drone-jsonnet-generator 背景 https://docs.drone.io/user-guide/pipeline/migrating/ Drone は v0.8 から v1 で .drone.yml のフォーマットが大きく変わっています。 Drone v1 ではビルド実行時に自動で変換しているため、v0.8 の .drone.yml でもそのまま動きます(matrix builds も動きます)。 そのため、Drone v0.8 から v1 に移行する際、すぐに .drone.yml を修正しなくても問題ないのですが、 v1 独自の機能が出てきた場合 v0.8 のフォーマットの場合利用できないかもしれませんし、 いつまでも古いままだと気持ち悪いので出来るならフォーマットを変換したいです。 drone-cli ではフォーマットを変換する drone convert というコマンドが提供されています。 ただし、 drone convert は matrix build を multiple pipeline に変換するのですが、 非常に冗長になります。 そのため、jsonnet を利用することが推奨されています。 https://docs.drone.io/user-guide/pipeline/migrating/ The above syntax can be quite verbose if you are testing a large number of variations.

    .drone.jsonnet と .drone.yml を比較する Drone plugin を作った

    久しぶりに Drone plugin を作ったので紹介します。 https://www.github.com/suzuki-shunsuke/drone-plugin-jsonnet-check .drone.jsonnet から .drone.yml を生成していて、両方を Git で管理している場合に、 .drone.jsonnet と .drone.yml の状態が一致しているかテストするための plugin です。 Drone v1 では matrix builds が廃止され、multiple pipeline が導入されました。 matrix builds を drone convert コマンドで multiple pipeline に変換すると、pipeline の数が多いほど冗長でメンテナンス性が悪くなります。 そこで公式では jsonnet で記述して .drone.yml に変換する方法が推奨されています。 https://docs.drone.io/user-guide/pipeline/migrating/ To simplify your configuration we recommend using jsonnet. $ drone jsonnet --format --stream jsonnet から yaml への変換は Jsonnet extension を使うと Drone がビルド実行時に自動で変換してくれるので .drone.yml を管理する必要はなくなりますが、 使っていない場合、 .drone.jsonnet と .

    go-jsoneq - 2つの値がJSONとして等しいか比較するGoライブラリ

    https://github.com/suzuki-shunsuke/go-jsoneq 2つの値がJSONとして等しいか比較するGoライブラリを開発したので紹介します。 「2つの値がJSONとして等しい」とは、2つの値をそれぞれJSON文字列に変換したら、2つが表現するデータがおなじになるという意味です。 struct { Foo string `json:"foo"` }{ Foo: "bar", } と map[string]interface{}{"foo": "bar"} を JSON に変換したらともに {"foo": "bar"} になりますね。 json.Marshaler のテストや、 実際の JSON 文字列から構造体を定義したときにちゃんと定義できているかチェックするのに使えると思います。 jsoneq.Equal でやっていることは単純です。 json.Marshal で []byte に変換 json.Unmarshal で []byte を map, array と primitive な型からなるオブジェクト(?)に変換 reflect.DeepEqual で比較 引数が []byte の場合は 1 は飛ばします。 GoDoc やサンプルを見れば使い方は簡単にわかると思います。 以上、簡単ですが、自作ライブラリの紹介でした。

    durl - 壊れたURLを検知するCLIツール

    結構前に開発したツールですが、まだ記事にしてなかったので紹介します。 https://github.com/suzuki-shunsuke/durl ファイル中の URL が壊れていないかチェックするツールです。 ファイル中の URL を抽出し、HTTPリクエストを投げてステータスコードが 2xx でないものがあった場合、異常終了します。 なお、ページ内リンク(アンカー)が壊れているものについては検知できません。 インストール Go製で、バイナリを GitHub Releases で公開しています。 https://github.com/suzuki-shunsuke/durl/releases Docker イメージ https://quay.io/repository/suzuki_shunsuke/durl busybox ベースの Docker イメージも提供しています。 CI で使うのに便利です。 使い方 durl init で設定ファイル .durl.yml を生成します。 $ durl init durl check に対象ファイルパスのリストを標準入力として渡してください。 find コマンドなどと組み合わせると良いです。 https://github.com/suzuki-shunsuke/go-errlog/blob/v0.9.0/scripts/durl.sh#L9 find . \ -type d -name node_modules -prune -o \ -type d -name .git -prune -o \ -type d -name vendor -prune -o \ -type f -print | \ grep -v package-lock.

    毎週30分の技術共有会

    自分が最近職場で行っている技術共有の取り組みについて紹介したいと思います。 背景 これまで自分は積極的に自分にとって新しい技術を取り入れてサービスの品質の向上に繋げてきました。 ただし、それらの技術に関して周りに十分に共有できていなかった側面がありました。 やっていること 毎週30分決まった時間にスライドを使って発表しています。 対象は同じ部署の希望者です。 枠は30分ですが、実質話しているのは20分くらいな気がします。 k8sのハンズオン的なこともやりました(そのときは30分で終わらないので2回に分けてやりました)。 話したいことはたくさんあるのですが、とりあえず大きなトピックとして以下の3つに絞っています。 k8s(Rancher): オーケストレーション (いまここ) Drone: CI/CD Graylog: ログ収集 これまで話したこと・話す予定のこと k8s の初心者が k8s を本番運用を視野に入れつつ検証環境で使ってみるところまでを目指して話しています。 なぜ k8s を使うのか(部署のコンテキストに合わせて導入意義を説明) k8s のリソース(Pod, Service, Deployment, etc) について k8s, Rancher ハンズオン(2回) 簡単なアプリケーションをデプロイしてみたり Logging (いまここ) モニタリング IP制限のかかった外部サービスへアクセスする方法 毎週30分というペース感について 以下のようなことを配慮しました。 集中力が続くこと 60分は長すぎる 持続可能であること 1, 2 回やっただけでは意味がない 30分だけなら参加しやすい 準備のコストも現実的な範囲 30分と短めなので毎週やる。隔週とかだと頻度が少なすぎるし、1回飛ぶと1ヶ月空いてしまう これまでの結果 特に大きな成果があるわけではないですが、 k8sに興味を持ちk8sを検証環境で使ってくれる人が出てきました。 共有会がk8s を触るきっかけになったのだとしたらそれだけでもやってよかったと思います。 また、自分自身学ぶこともありました。 Logging に関して自分は今まで Sidecar pattern を使っていたのですが、Cluster Level Logging への移行を検討するきっかけになりました。

    Rancherでusername が重複してログインできなくなった場合の解消方法

    先日起こった Rancher のトラブルの解消方法について紹介したいと思います。 Rancher のバージョンは v2.1.6 です。 admin ユーザーでログインしようとしたところ、エラーが起こりました。 最初パスワードが間違っているのかと思い、パスワードリセットしたものの、解消しませんでした。 https://rancher.com/docs/rancher/v2.x/en/faq/technical/#how-can-i-reset-the-admin-password エラーメッセージをよく見ると 500 エラーでした。そこで rancher のコンテナのログを見ました。 [ERROR] API error response 500 for POST /v3-public/localProviders/local?action=login. Cause: found more than one users with username admin username が admin のユーザーが複数人いるからログインに失敗しているようです。 であれば、ユーザーを rename ないし delete すれば解消しそうです。 しかし Admin 権限を持っているのが admin しかいないため、ユーザーを rename したり delete するのが難しいです。 どうすればよいかと思って調べてたところ rancher のコンテナ内で kubectl コマンドを使うことで Rancher の Custom Resource を操作できそうなことを知りました。 https://qiita.com/yamamoto-febc/items/498b911611dd25351ad7 そこで 2 人いる admin の片方を rename することで解消しました。

    Goの設定管理で viper の代わりに confita を使う

    Golang の設定管理のライブラリといえば viper が有名ですが、 confita も良さそうだったので紹介したいと思います。 confita の機能としては以下のようなものがあります。 構造体に設定をマッピング flag や環境変数、設定ファイルに対応 複数の設定ファイルに対応 構造体に設定をマッピングすることで、https://github.com/go-playground/validator のようなライブラリを使って設定のバリデーションが出来ます。 また viper は v1.3.1 の時点で複数の設定ファイルを扱いにくいです。 Viper can search multiple paths, but currently a single Viper instance only supports a single configuration file. k8s で ConfigMap と Secret を設定ファイルとして扱う場合、複数のファイルを扱えないと不便です。 その点 confita は複数の設定ファイルを問題なく扱えます。 以下はフラグで指定した複数の設定ファイルから設定を読み込む簡単なサンプルです。 import ( "context" "gopkg.in/go-playground/validator.v9" "github.com/heetch/confita" "github.com/heetch/confita/backend" "github.com/heetch/confita/backend/file" flag "github.com/spf13/pflag" ) func loadConfig(ctx context.Context) (Config, error) { cps := flag.StringSliceP("config", "c", nil, "configuration file path") flag.

    JS以外でのnpmの活用

    npm は Node.js のパッケージマネージャーですが、自分はJS以外のプロジェクトでも使えると思っています。 実際、Goのアプリケーション、OSS、ansible role, playbook など種類を問わず、自分が管理している多くのリポジトリで使っています。 ただ、GoのOSSで npm 使っているのは自分以外で見たことはないですし、 正直あまり賛同はされないかなと思いますが、こういう考え方もあると思っていただけたらと思います。 npm を使う理由は Node製のツールを使うため npm scripts を使うため (今回書きたいのはこっち) の2つあります。 Node製のツール husky: Git Hookを設定 commitlint: commit メッセージのlint standard-version: コミットログによって Change Log を生成 などを使っています。 Nodeはバージョンの変化が速く、互換性が壊れたりとかも多い印象ですが、 グローバルにインストールしなくてもリポジトリごとに install 出来る(package.jsonで管理できる)のでその点は(特にチーム開発では)良いと思います。 npm scripts npm scripts によってそのリポジトリの開発に使うコマンド群を管理するということを自分はしています。 https://github.com/suzuki-shunsuke/gomic/blob/v0.5.7/package.json なにもツールを使わない場合に比べ、こうすることでチーム全体でコマンドを統一できますし、一連のコマンドをスクリプト化して npm scripts で実行できるようにするなど、自動化も促進されます。 ごく簡単な自動化の例ですが、tag を打つと同時にソースコード中のバージョン番号を更新するのを npm run tag v1.1.0 といったコマンドで出来るようにしています。 こうすることで tag とversionコマンドで出力されるバージョンが違うなんてことを防ぐことが出来ます。 https://github.com/suzuki-shunsuke/gomic/blob/v0.5.7/scripts/tag.sh また、オプションによって動作が変わるようなコマンドは npm scripts によって実行することでオプションを統一できます。 例えば gofmt は -s オプションの有無で結果が変わります。

    Golang の好きなところ

    自分は 2017/8頃(曖昧)からメインで書く言語をPythonからGolangに変更しました。 Goを書き始めて割と早い段階でGoが一番好きになりました。 そこでなんで Go が好きなのかということを頑張って言語化しようと思います。 若干他の言語と比較する部分もありますが、決して他の言語をディスったり、 他の言語より優れているということが言いたいわけではないのでご了承ください。 依存するものが小さく、バイナリ1つインストールするだけで良い Prometheus の exporter とかインストールするの簡単 Docker Imageも最小限になる 静的型付け ビルド出来ている時点で一定の信頼性が担保されている よく知らないコードを読んだり修正するときとかだいぶ有り難い GoDocが素晴らしい 何もしなくてもライブラリのドキュメントが出来上がっている ライブラリの公開が容易 GitHubに公開するだけ npm や pypi のようなレジストリがないので楽 go test とか go vet, gofmt みたいに標準ツールが揃っている コーディング規約で悩む必要がない lintツールが充実している gometalinter とか使っておけば OK lintできる環境を構築するのにそこまで頑張らなくて良い エラーハンドリングが暗黙的に省略できないので信頼性が高い Goのエラーハンドリング嫌いって人もいるし、v2で改善されるって話も聞くけど、自分はむしろ好き(面倒なのは理解できるけど) 言語仕様がシンプル(客観的な根拠はないし、難しい部分もあるけど、そんな気がする) メタプログラミング使った、魔術的なコードになりにくい interface 使ってコードを疎結合にするのが書いてて気持ちいい 並列処理が書きやすい

    go-error-handling-logging-practice v0.2

    以前 Golang のロギング・エラーハンドリングについて書きました。 https://suzuki-shunsuke.github.io/golang-logging-error-handling-practice/ https://github.com/suzuki-shunsuke/go-error-handling-logging-practice それを少し v0.1 から v0.2 に互換性を壊す形でアップデートしようかと思います。 本記事ではその変更点について書きます。 変更点 関数のエラーに情報を付与する責務を関数に割り当てていたものを、呼び出し元に割り当てるようにします。 具体的には元々 func createUser(name string, age int) error { return errlog.Wrap(checkName(name), logrus.Fields{"age": age}, "failed to create a user") } だったものが func createUser(name string, age int) error { return errlog.Wrap(checkName(name), nil, "user name is invalid") } になります。 変更理由 メタ情報のフィールド名はコンテキストに依存します。 上記の例だとユーザー名というメタ情報のフィールド名は name より user_name や admin_name, owner_name としたほうが適切かもしれません。それは関数内部では分からず、呼び出し元でないと分かりません。呼び出し元でないとフィールド名の衝突が避けられないこともあるでしょう。 メッセージに関しても同様のことが言えます。 また、元々 v0.1 ではユーザーが定義した関数と 標準関数やサードパーティのライブラリなど、プロジェクト外部で定義された関数 interface の関数やメソッド を区別し、前者では関数側でエラーに情報を付与させる一方、後者では呼び出し元で情報を付与させるというふうにしていました。