GAミント至上主義

Web Monomaniacal Developer.

Firebaseでユーザー作成/削除時にFunctionsでなんかやる

今更ゾンビランドサガを見始めて宮野節を堪能してます。
yagish履歴書でユーザー側でのアカウント削除を実装するにあたり、退会率などをみるため、せめてログには残したいと思った。
おかげさまで退会率は1%以下で使用するユーザーはかなり限られるため最低限の実装を予定。

自分でアカウントを削除させる処理

削除は下記公式どおり、ユーザーオブジェクトのdelete()を実行するだけ。
だけどログインしてから一定(詳細は不明)時間経つとエラーになるので、一度ログアウトし、再度ログインをしてもらう必要があった。
実行するまで切れてるか分からないので、エラーが出たら再ログインを求める案内文言でカバー。

var user = firebase.auth().currentUser;

user.delete().then(function() {
  // User deleted.
}).catch(function(error) {
  // An error happened.
});

https://firebase.google.com/docs/auth/web/manage-users?hl=ja#delete_a_user

Functions側

とりあえずログに出すだけ。
user(UserRecord)を受け取れるのでそれを使っていろいろできる。

exports.loggingUserCreate = functions
  .auth
  .user()
  .onCreate(user => {
    const data = JSON.stringify(user.toJSON())
    console.log(`account created ${data}`)
    return true
  })

userが持っている情報は下記ドキュメント
Interface: UserRecord  |  Firebase

最後のreturnは

Function returned undefined, expected Promise or value

というエラーが出るのでつけているが値に特に意味はなさそう。

ログであればemail, uidなど指定で出したほうがいいけど、とりあえずtoJSONで全部出して試してた段階。

登録、退会時に通知メールを送るにはSendGridなどを使う必要がありそう。
社内向けなら以前書いたようなHangOutsチャット連携もありかもしれない。

uyamazak.hatenablog.com

あとはuidを元にfirestoreに保存してあるデータを削除するのがよくやりそうだけど、yagishの場合はユーザー側に機能があるのでそちらで明示的にやってもらうことを考えている。

Docker + Google Cloud Buildを使ってCIしようとしてる

これまでLinuxの開発サーバー上のシェルコマンドでDockerのビルドやイメージのプッシュ、GKEへのデプロイをしていたけど、先日のGitHub Actionsの発表など世の中CI/CDが当たり前になっているので、DockerとGCPでできる範囲でやってみる。

Cloud Buildについて

Gitレポジトリへのプッシュなどに反応して、Dockerfileのビルド、もしくはcloudbuild.yamlに書いたコマンドを実行してくれる。

今回は元々Dockerでやってたので、Dockerfileでやる。

ビルドしたあと、Container Registryにイメージをプッシュしてくれるところまで。

GKEへのデプロイはまだどうやるかわからない。

cloud.google.com

ソースコードのレポジトリを用意

対象のソースコードは下記。今回はGitHubに公開していたので連携して使う。
github.com

node.jsで動いてHTMLをPDFにするだけのシンプルなアプリケーションなので自動テストもしやすくて。ちょうどいいため。

GCPをすでに使っていて、GitHubか公開する気がないのであればCloud Source Repositoriesでいいと思う。

Cloud Source Repositories - コードを保存、管理するためのプライベート Git リポジトリ  |  Source Repositories  |  Google Cloud

* テストを準備
今回はmocha, chaiユニットテスト、superagentを使ってリクエストのテストを書いた。
網羅性は気にせず、本番で使っている動かないとまずいリクエストや、手動でテストが難しく不安なところだけ書いた。

テストが書けたら稼働中のコンテナ内でmochaコマンドを実行して、大丈夫だったらDockerfileに下記を追加してdocker build時に実行する。

Dockerfile

// 省略
COPY test /hcep/test
RUN mocha
// 省略

expressが終了しない問題

テストが終わってもexpressのコネクションが残っているようで、express.close()しても終わらず、ちょっとハマった。

下記のような丁寧な解決策もあるようだけど、今回はテストと割り切りprocess.exit()することに。
node.js - How Do I Shut Down My Express Server Gracefully When Its Process Is Killed? - Stack Overflow

describeのafter内でprocess.exit()してしまうとChrome側でまだ生きているというエラーが出たので最後に5秒sleepするようにしたら大丈夫だった。

test/requests.js

// 省略
const sleep = (waitSeconds, someFunction) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(someFunction())
    }, waitSeconds)
  })
}

describe('requests routes', (done) => {
  let app
  before(beforeDone =>{
    (async() => {
      const browserPage = await hcPage()
      app =  await expressApp(browserPage)
      beforeDone()
    })()
  })
  after(afterDone => {
    (async ()=> {
      await app.close() 
      sleep(5000, () => {
        console.log('process.exit!')
        process.exit()
      })
      afterDone()
    })()
  })

  const req = request(SERVER_URL)
  it('Health Check GET /hc', async () => {
    await req.get('/hc')
      .expect(200, 'ok', done)
  })
//以下略

before、afterの中でasync無名関数を実行しているのはbefore、afterがPromise返しに対応していないのか、だめだったため内部で同期処理するようにした。
もっとスマートな方法がありそう。

テストがこけるとビルドも止まりエラーで終了する。
ビルド結果のslack通知もほしいけど、Cloud Pub/SubとCloud Functions連携が必要になるのだろうか。めんどい。

Cloud Buildを設定

トリガーの設定は先程のGihHubのレポジトリを指定して、アカウント連携する。
イメージ名のタグに$COMMIT_SHAを入れた以外、特に設定は変えずそのまま。

f:id:uyamazak:20181227150357p:plain

これでビルドが成功したらコミットハッシュ付きのイメージがContainer Registryにプッシュされていくようになった。

動作確認

Cloud Buildの履歴で標準出力されたログを確認することができる

f:id:uyamazak:20181227155324p:plain

無事テストも実行されて通っている。

実行時間もcorei5 4コア、メモリ32GBのローカルサーバーに比べると、コミットごとと思えば遅いけどまあ問題ないレベル。

前回のビルド中にまたpushしてもビルドは再実行されない模様。その場合は手動で実行すればよさげ。

これから

他のCIツールと比べるとできることは少なそうだけど、GitとDockerfileを使っていて、Container Registryにアップするまでならシンプルですぐ使えると思った。

もっといろいろしようとすると独自のcloudbuild.yamlで書く必要があるのでちょっと面倒。

あとはステージング、本番環境への自動デプロイができればCDまでいけそう。

個人的にはすべての開発をクラウド上でやりたくてローカルマシンは端末、エディタとしてだけ使うような開発環境が理想。


Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

Firebase Functionsのデプロイ時にHTTP Error: 400, The request has errorsで失敗する

Firebase FunctionsをデプロイするときにHTTP Error: 400が出て少しハマった。

?  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
?  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (69.6 KB) for uploading
?  functions: functions folder uploaded successfully
i  functions: current functions in project: callCharge(us-central1), charge(us-central1), hello(us-central1)
i  functions: uploading functions in project: sendReceptionMail(us-central1)
i  functions: creating Node.js 8 function sendReceptionMail(us-central1)...
?  functions: failed to create function sendReceptionMail
HTTP Error: 400, The request has errors


Functions deploy had errors. To continue deploying other features (such as database), run:
    firebase deploy --except functions

Error: Functions did not deploy properly.

400ということでプロジェクトやGoogleアカウントの認証周りを疑ったけど違った。

原因はdocumentを参照しなければいけないところでcollectionのパスを指定してしまっていたことだった。
エラーがちょっと不親切だけどCLIではなくコードの実行時のエラーだと分かっていれば次回はなんとかなりそう。

exports.sendReceptionMail = functions
  .firestore
  .document('share_data/{userId}/corrections/')
  .onCreate((snap, context) => {
//略

exports.sendReceptionMail = functions
  .firestore
  .document('share_data/{userId}/corrections/{correctionId}')
  .onCreate((snap, context) => {
//略

WEB+DB PRESS Vol.105

WEB+DB PRESS Vol.105

  • 作者: 小笠原みつき,西村公宏,柳佳音,志甫侑紀,池田友洋,木村涼平,?橋優介,大塚雅和,飯塚直,吉川竜太,末永恭正,久保田祐史,浜田真成,穴井宏幸,大島一将,桑原仁雄,牧大輔,池田拓司,はまちや2,竹原,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/06/23
  • メディア: 単行本
  • この商品を含むブログを見る