クラウドワークス エンジニアブログ

日本最大級のクラウドソーシング「クラウドワークス」の開発の裏側をお届けするエンジニアブログ

Terraform AWSプロバイダv4アップグレードツールを作ろう

はじめに

SREチームの @minamijoyo です。趣味のTerraformで遊んでいたら、先日HashiCorpさんから「Core Contributor to HashiCorp Terraform for 2022」という名の、がんばったで賞をもらいました。対戦よろしくおねがいします。

crowdworks.jp のインフラはAWSで運用されており、インフラの管理にTerraformを使っています。先日ようやくTerraform AWSプロバイダv4アップグレードが完了しました。既にご存知の人も多いかもですが、v4ではS3バケット関連で大きな破壊的変更が入っており、アップグレード作業はなかなか大変です。もう皆さんアップグレードは終わりましたか? crowdworks.jp ではもうかれこれ6年以上Terraformを利用しており、Terraformの設定はおよそ7万行、tfstateの数は280個ぐらいの規模感です。さすがに数万行規模になってくると、手で大きな破壊的変更をやる気が起きなくて、最近Terraformのリファクタリング用のライブラリを作り始めました。そして最終的にいいかんじのアップグレードツールを書きました。

github.com

とりあえずツールの使い方だけ知りたいという人はリポジトリのREADMEを読んで下さい。(※現状aws_s3_bucketリファクタリングにしか対応してないことに注意)

この記事ではツールの使い方ではなく、仕組みの解説をします。一体誰得なんだと思いつつ、Terraform設定をいいかんじにプログラムで書き換えたいなという人向けです。最低限のTerraformの使い方とGoプログラミングは前提知識とします。逆にAWSに特化した知識はほとんどなくても読むのには支障はないと思うので、他のクラウドプロバイダをお使いの人も参考になるはず。

本稿執筆時の各種ソフトウェアのバージョンは以下のとおりです。

特にtfeditはまだ作りたてなので、インターフェースはしばらく不安定かもしれません。最新の情報は各READMEやCHANGELOGなどを参考にして下さい。

続きを読む

エンジニアだけどPOやってみる!

エンジニアだけどPOやってみる!

はじめに

こんにちは、ビズアシスタントオンライン(以下、ビズアシ)というサービスのエンジニアをしている山田です。

みなさんこちらの記事はきっともう読んでくれてますよね?

そうです。ビズアシはPO(プロダクトオーナー)と愉快なエンジニアたちのパーティーでプロダクト開発に携わっています。

少し前に山あり谷ありの冒険をひとつ終え、ついにタイムカードアプリをリリースした私たちですが、なんと7月に現POが産休に入ります!

その間わたくし山田がPO代理を務めさせていただくこととなりました。 5月半ばから引き継ぎが始まり、まだ一ヶ月程のぴよっこPOです。右も左も分かりません、、、

当記事はそんなぴよっこPOの奮闘記、、、ではなく、頭の中の話になります。

続きを読む

既存プロダクトへのドメイン駆動設計の導入検討について

こんにちは!

はじめまして、クラウドログ開発チームで主にバックエンドを担当しているエンジニアの武田です。

クラウドワークスへの入社は2022年の2月で、現時点で約4ヶ月程経ちました。 常に新しい事へのチャレンジしながら毎日を過ごしているため、「あれ、まだ4ヶ月…」という感覚です。
今回はそんな日々のチャレンジを欠かさないクラウドログ開発チームから今後のチャレンジの一部をご紹介したいと思います。

現在、クラウドログ開発チームでは既存プロダクトへのドメイン駆動設計の導入検討をしています。 その中で私が考えている「課題」や「検討しているアプローチ」について、想いのままに書いてみたいと思います。 お付き合い頂ければ幸いです。

続きを読む

GAAD Japan 2022 でスポンサー協賛と LT をしてきました

アイキャッチ:GAAD Japan 2022 でスポンサー協賛とLTをしてきました

こんにちは。@okuto_oyamaです。

今回は 5 月 19 日に開催された GAAD Japan 2022 にて株式会社クラウドワークス(以下、弊社)としてスポンサー協賛させて頂くとともに LT という形で発表してきましたので、その振り返り記事になります。

続きを読む

新版チェリー本を読んで今更ながら知ったこと〜主にmap(&:upcase)の話〜

新版チェリー本を読んで今更ながら知ったこと〜主にmap(&:upcase)の話〜

こんにちは、エンジニアの神山です。

Rubyを書いてる人であれば一度は聞いたことあるチェリー本こと プロを目指す人のためのRuby入門 。その新版が昨年12月に出ました。

先日読んでたところ、今まで知らなかったことや忘れていたことがいくつか発掘されたので、記事にしてみました。ちなみに僕はRubyを触って6年になります。

先にチェリー本のリンクを貼っておきます。とても素晴らしい本なので、ぜひ読んでみてください。

プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発デバッグ技法まで (Software Design plus)

www.amazon.co.jp

map(&:upcase) の仕組み

これを知れた喜びからこの記事を書いたと言っても過言ではありません。(わりとみんな知っているのだろうか。。)

皆さんは、①の書き方が②のようにも書ける理由をご存知でしょうか。

# ①
['abc', 'def'].map { |string| string.upcase }

# ②
['abc', 'def'].map(&:upcase)

これには Procオブジェクトが関わっています。ただ単純な話ではないので細かく説明していきます。 ※ブロックやProcオブジェクトに関してはWEB上にたくさん情報があるので簡単な解説にとどめます。

ブロックとは

ブロックとは処理(のかたまり)です。先程のコードで言うと、 { |string| string.upcase } です。言語化すると 文字列を大文字にする処理 になります。 Rubyを書いてる人であれば eachmap などのメソッドでよく使いますね。

ちなみにブロックを扱うメソッドを定義するにはこのように書きます。(いろんな書き方があるので下は一例です。)

# 文字列に何らかの処理をするメソッド
def string_ex(string, &block)
  puts '文字列に処理を施します'
  block.call(string)
end

> string_ex('hoge') { |s| s.upcase }
文字列に処理を施します
=> "HOGE"

> string_ex('hoge') { |s| "!!!#{s}!!!" }
文字列に処理を施します
=> "!!!hoge!!!" 

# 組み合わせて使うこともできます。
> string_ex(string_ex('hoge') { |s| "!!!#{s}!!!" }) { |s| s.upcase }
文字列に処理を施します
文字列に処理を施します
=> "!!!HOGE!!!"

このようにブロックを扱うメソッドを定義すると、状況に応じて好きな処理を渡すことできます。 ※ブロックを引数として受け取るには & が必要です。

Procオブジェクト とは

Procオブジェクトとはブロックのような処理をオブジェクト化したものです。先程の例をProcオブジェクトを使って書き直してみます。

# 小文字を大文字にする処理
upcase_proc = Proc.new { |s| s.upcase }

# 文字列を強調させる処理
emphasis_proc = Proc.new { |s| "!!!#{s}!!!" }

> string_ex('hoge', &upcase_proc)
文字列に処理を施します
=> "HOGE"

> string_ex('hoge', &emphasis_proc)
文字列に処理を施します
=> "!!!hoge!!!"

> string_ex(string_ex('hoge', &upcase_proc), &emphasis_proc)
文字列に処理を施します
文字列に処理を施します
=> "!!!HOGE!!!"

ブロックで渡したときとの違いとして、Procオブジェクトを先程のメソッドに渡すには & をつける必要があります。

mapにProcオブジェクトを渡す

今までの流れからわかるように、 &proc_object のように書けば map にProcオブジェクトを渡すことができます。

upcase_proc = Proc.new { |s| s.upcase }

> ['abc', 'def'].map(&upcase_proc)
=> ["ABC", "DEF"]

ここで補足ですが & はメソッドにProcオブジェクトを渡す前に to_proc メソッドを利用してProcオブジェクトに変換を行っております。 今回で言うと、 map に渡す前に実は upcase_proc.to_proc を行っております。ただProcオブジェクトに to_proc しても変わらないので今回は処理に変化はありません。

シンボルをProcオブジェクトに変換

「そんなことできるの?」って思う人もいるかも知れません。僕も初めて知りました。 たとえば今まで例に使っていた upcase_proc は以下のように定義できます。

> upcase_proc = :upcase.to_proc
=> #<Proc:0x000055b7ae696728(&:upcase) (lambda)>

> string_ex('hoge', &upcase_proc)
文字列に処理を施します
=> "HOGE"

まとめ

改めて大事なことを列挙します。

  • ブロックはProcオブジェクトとしてオブジェクト化できる
  • Procオブジェクトはブロックを扱うメソッドに対して、 & を使うことでブロックのように渡せる
  • & は引数をメソッドに渡す前に引数に to_proc を行う
  • シンボルは to_proc でProcオブジェクト化される

つまり map(&:upcase)map { |string| string.upcase } と同義になるのは、以下のような説明になります。

  • :upcase& によってProcオブジェクトに変換(内部で .to_proc される)
  • また & によってProcオブジェクトをブロックと同じように map に渡される

正規表現のキャプチャに名前をつけられる

正規表現のキャプチャは度々使ってましたが、名前をつけられることは初めて知りました。

正規表現のキャプチャとは

() を使うことでマッチした値を利用することができます。

> text = '2022年5月10日'
=> "2022年5月10日"

> result = /(\d+)(\d+)(\d+)/.match(text)
=> #<MatchData "2022年5月10日" 1:"2022" 2:"5" 3:"10">

# キャプチャできる
> result[1]
=> "2022"
> result[2]
=> "5"
> result[3]
=> "10"

# ちなみに組み込み変数を使うこともできる
> $1
=> "2022"
> $2
=> "5"
> $3
> $~
=> #<MatchData "2022年5月10日" 1:"2022" 2:"5" 3:"10">

ただ result[1] だと実際何を表す値かわからないですよね。 そこでキャプチャに名前をつけることができます。

> text = '2022年5月10日'
=> "2022年5月10日"

> result = /(?<year>\d+)(?<month>\d+)(?<day>\d+)/.match(text)
=> #<MatchData "2022年5月10日" 1:"2022" 2:"5" 3:"10">

# キャプチャできる
> result[:year]
=> "2022"
> result[:month]
=> "5"
> result[:day]
=> "10"

ちなみに =~ で書くとキャプチャ名をそのままローカル変数として使えます。今まで組み込み変数を使ってたので良いことを知れました。

> /(?<year>\d+)(?<month>\d+)(?<day>\d+)/ =~ text
=> 0

> year
=> "2022"
> month
=> "5"
> day
=> "10"

1行パターンマッチ

パターンマッチはRuby3.0で正式に導入されました。ただまだ実務で使ったことがなく知らないことばかりです。 その中でも1行で書くことができる1行パターンマッチはとても便利そうです。

> full_name = { first: 'taro', last: 'kitabeppu' }
> full_name in { first: String, last: String }
=> true

> full_name = { hoge: 'taro', last: 'jiro' }
>  full_name in { first: String, last: String }
=> false

> full_name = { first: 1, last: 'hoge' }
>  full_name in { first: String, last: String }
=> false

番号指定パラメータ

ブロックパラメータを明示的に指定せずに、番号指定パラメータを使って書くことができます。ただ分かりづらいのでそこまで利用頻度はなさそうです。

> [1, 2, 3].map { _1.to_s }
=> ["1", "2", "3"]

全範囲オブジェクト

下のように書くと全範囲を表す範囲オブジェクトになるそうです。

nil..nil
..nil
nil..

> (nil..nil).cover?(1000000000000000000000000000000000000000000000)
=> true

まとめ

社内で共有したところ「これは知ってた」「これは知らなかった」などの声があり、意外に自分が当たり前だと思っていることも他の人は知らなかったりすることに気づきました。こういう共有はとても有効だと思うので、今後も続けていきたいです。

改めて再度、本の紹介をして終わります。意外な気付きがあったりするのでぜひ読んでみてください。

プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発デバッグ技法まで (Software Design plus)

www.amazon.co.jp

クラウドワークスに興味のある方へ

株式会社クラウドワークスでは、共によりよい社会を築いてくれる仲間を募集しています! ぜひご応募ください!

crowdworks.co.jp

© 2016 CrowdWorks, Inc., All rights reserved.