【Cloud SQL】GolangでDBインスタンスの開始停止をDiscordで通知する

1600 語
8 分
【Cloud SQL】GolangでDBインスタンスの開始停止をDiscordで通知する

はじまり#

リサちゃん avatar
リサちゃん
うーん、あの関数って実行されたのかなあ・・・
135ml avatar
135ml
いちいちコンソールを開くのは面倒だよなあ

以前に作ったCloud SQLを自動で制御するツール#

以前にCloud SQLにDBインスタンスを自動で起動もしくは停止するCloud Run Functionsを作成して、自動実行させるようにしました。

いやぁ、ちゃんと自動で実行されているとは思うんですけど、もしかしたらちゃんと実行されてないかもしれないじゃないですか。いや、されてると思うんですけど。しかし、Google Cloudのコンソールをいちいち開くのは面倒くさい。

・・・という心配の種が生まれないように、今回はGoで実装したCloud Run Functionsに、Discordに関数の実行を通知する機能を追加したいと思います。それではやっていきましょう。

DiscordのWebhookを叩きます。#

「Discord Go」でググると、こんなパッケージがヒットします。

しかし今回は、ただWebhookを叩けばいいだけなので、その「discordgo」のパッケージは使わないことにします。 Discord Webhook APIのリファレンスがこちら。

Discordを開いて、Webhook URLを取得しておきます。

それでは早速Discordに通知する機能の実装部分です。

// Package mypkg contains a Function to request to Discord Webhooks.
package mypkg
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
)
// Payload represents the payload structure for Discord notification.
type Payload struct {
Username string `json:"username"`
Content string `json:"content"`
Embeds interface{} `json:"embeds,omitempty"`
TTS bool `json:"tts"`
}
// Embed represents a single embed structure.
type Embed struct {
Title string `json:"title"`
Color int `json:"color"`
URL string `json:"url"`
Footer Footer `json:"footer"`
Timestamp string `json:"timestamp,omitempty"`
}
// Footer represents the footer for an embed.
type Footer struct {
Text string `json:"text"`
IconURL string `json:"icon_url"`
}
// CreatePayloadOfDiscordNotification creates a payload for Discord webhook notification.
func CreatePayloadOfDiscordNotification(botName, content string, embeds []Embed, isTTS bool) (Payload, error) {
if len(botName) > 2000 {
return Payload{}, errors.New("'botName' must be less than or equal to 2000 characters")
}
if len(content) > 2000 {
return Payload{}, errors.New("'content' must be less than or equal to 2000 characters")
}
if len(embeds) > 10 {
return Payload{}, errors.New("length of 'embeds' must be less than or equal to 10")
}
return Payload{
Username: botName,
Content: content,
Embeds: embeds,
TTS: isTTS,
}, nil
}
// CreateEmbedsToNotifyCloudSql creates embeds for notification about DB booting.
func CreateEmbedsToNotifyCloudSql(textToTransit, urlToTransit, iconURL string) []Embed {
return []Embed{
{
Title: textToTransit,
Color: 4569935,
URL: urlToTransit,
Footer: Footer{
Text: "GoogleCloudSQL",
IconURL: iconURL,
},
},
}
}
// NotifyToDiscord sends a notification to a Discord webhook.
func NotifyToDiscord(webhookURL string, payload Payload) (string, error) {
payloadBytes, err := json.Marshal(payload)
if err != nil {
return "", fmt.Errorf("failed to marshal payload: %v", err)
}
req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(payloadBytes))
if err != nil {
return "", fmt.Errorf("failed to create HTTP request: %v", err)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("failed to send HTTP request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
return "", fmt.Errorf("unexpected HTTP response: %s", resp.Status)
}
return "Notification sent successfully", nil
}
// NotifyDbBooting sends a notification about Google Cloud SQL.
func NotifyDbBooting(content, textToTransit, urlOfConsoleForDbInstance, webhookUrl, iconUrl string) (map[string]string, error) {
botName := "CloudSQLあゆ"
embeds := CreateEmbedsToNotifyCloudSql(textToTransit, urlOfConsoleForDbInstance, iconUrl)
payload, err := CreatePayloadOfDiscordNotification(botName, content, embeds, false)
if err != nil {
return nil, err
}
response, err := NotifyToDiscord(webhookUrl, payload)
if err != nil {
return nil, err
}
fmt.Println(response)
return map[string]string{
"destinations": "discord",
"discord_content": content,
}, nil
}

こんな風に叩けば動く「NotifyDbBooting」という関数が出来ました。

func main() {
content := "データベース起動!"
textToTransit := "DBを起動したよ!"
urlOfConsoleForDbInstance, ok := os.LookupEnv("MY_DB_INSTANCE_URL"); if !ok {
fmt.Printf("Error: %v\n", ok)
}
webhookURL, ok := os.LookupEnv("MY_DISCORD_WEBHOOK_URL"); if !ok {
fmt.Printf("Error: %v\n", ok)
}
iconURL, ok := os.LookupEnv("CLOUD_SQL_ICON_URL"); if !ok {
fmt.Printf("Error: %v\n", ok)
}
result, err := mypkg.NotifyDbBooting(content, textToTransit, urlOfConsoleForDbInstance, webhookURL, iconURL)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Notification result: %+v\n", result)
}
}

リクエストでpostするJSONの内容(ペイロード)は、このように構成されています。

  • Username:Webhookで送られる通知の送り主(ボット)の名前。
  • Content:Webhookで送られる通知の文章。
  • Embeds:Webhookで送られる通知では一部の情報を埋め込んで、その情報を目立たせることが出来る。その「埋め込み」の内容。
  • TTS:そのWebhookで送られた通知を表示する時に、その内容を音声として再生させるかどうか。

リクエストでpostするJSON内の"Embeds"フィールドの中身は、このように構成されています。

  • Title:埋込み内のテキストの内容。
  • Color:埋込みの帯の色。
  • URL:埋込み内のテキストにリンクされているURL。
  • Footer
    • Text:フッターのテキストの内容。
    • IconURL:フッターにはアイコンを付けられる。そのアイコン画像のURL。

Cloud Run Functionsの更新。#

それでは、「cloudsql-launcher」という名前のCloud Run Fucntionsをデプロイします。既に同じ関数名でデプロイしてあったら上書きされます。

Terminal window
gcloud functions deploy cloudsql-launcher \
--gen2 \
--runtime=go123 \
--project={My_PROJECT} \
--region={MY_REGION} \
--source=. \
--entry-point=ProcessPubSub \
--trigger-service-account={MY_APP_ENGINE_SERVICE_ACCOUNT} \
--trigger-topic={MY_PUBSUB_TOPIC_ID} \
--allow-unauthenticated \
--timeout=180s \

Cloud Schedulerメッセージの追加。#

Pub/Subトピックから決まった時間に発行するCloud Schedulerジョブを新規作成します。 まずは、DBインスタンス起動用のジョブを追加します。

Terminal window
gcloud scheduler jobs create pubsub start-{MY_DB_INSTANCE}-instance \
--schedule="0 4 * * 0-6" \
--description="Trigger Cloud Functions to start Cloud SQL instance." \
--project="{My_PROJECT}" \
--location="{MY_LOCATION}" \
--time-zone="Asia/Tokyo" \
--topic="{MY_PUBSUB_TOPIC_ID}" \
--message-body="{ \
\"Instance\": \"{MY_DB_INSTANCE_ID}\", \
\"Project\": \"{MY_PROJECT_ID}\", \
\"Action\": \"start\", \
\"DbInstanceUrl\": \"{MY_DB_INSTANCE_URL}\", \
\"DiscordWebhookUrl\": \"{MY_DISCORD_WEBHOOK_URL}\", \
\"CloudSqlIconUrl\": \"{CLOUD_SQL_ICON_URL}\" \
}" \

まずは、DBインスタンス停止用のジョブを追加します。

Terminal window
gcloud scheduler jobs create pubsub stop-{MY_DB_INSTANCE}-instance \
--schedule="0 7 * * 0-6" \
--description="Trigger Cloud Functions to stop Cloud SQL instance." \
--project="{My_PROJECT}" \
--location="{MY_LOCATION}" \
--time-zone="Asia/Tokyo" \
--topic="{MY_PUBSUB_TOPIC_ID}" \
--message-body="{ \
\"Instance\": \"{MY_DB_INSTANCE_ID}\", \
\"Project\": \"{MY_PROJECT_ID}\", \
\"Action\": \"stop\", \
\"DbInstanceUrl\": \"{MY_DB_INSTANCE_URL}\", \
\"DiscordWebhookUrl\": \"{MY_DISCORD_WEBHOOK_URL}\", \
\"CloudSqlIconUrl\": \"{CLOUD_SQL_ICON_URL}\" \
}" \

Cloud Schdulerで発行されるジョブを新しく追加しました。古いものは後で一時停止しておきます。

Note

ちなみに、gcloud scheduler jobs createでジョブを作成した後[1]+ Done gcloud scheduler jobs createというメッセージがCLI上で表示されたら、コマンドのどこかが間違っている可能性があります。例えば、JSONの中のURLが間違ってたり。(クエリパラメーターの?(クエスチョンマーク)や&(アンパサンド)をエスケープしていなかったり。でも二重引用符をエスケープしていたらエスケープする必要がなかったり。)

Cloud Run関数の実行結果を確認。#

それではデプロイしたCloud Run FunctionsおよびPub/Subの実行結果をDiscordで確認します。 しっかりDBの起動もしくは停止の関数が動くと、関数が起動した時間にDiscordでボットから通知が飛んでいます。

まとめ#

今回は、Go言語でCloud SQLに作成したDBインスタンスを自動で開始および停止するCloud Run Functionsが実行された時に、Discord上で通知を飛ばす機能を実装する手順を紹介しました。

これで、Cloud Run Functionsが実行されたことをDiscord上で把握できます。助かるぞお。

おしまい#

リサちゃん avatar
リサちゃん
よし、今日も実行されたな
135ml avatar
135ml
DB停止!

以上になります!

記事を共有

この記事が役に立ったなら、ぜひ他の人と共有してください!

【Cloud SQL】GolangでDBインスタンスの開始停止をDiscordで通知する
https://endorphinbath.com/posts/golang-cloud-sql-saving-then-discord-notification/
著者
kinkinbeer135ml
公開日
2024-12-09
ライセンス
CC BY-NC-SA 4.0
関連記事 スマート
1
【Cloud SQL】GolangでDBインスタンスの開始停止をスケジュール実行して節約する
Code Golangで書いたCloud Run FunctionでCloud SQLインスタンスの起動と停止を行い、Cloud SchedularとPub/Subでスケジュール実行してコスト削減を図ります。
2
【Cloud SQL】GolangでDBインスタンスへのオペレーション完了まで待つ
Code Go言語でCloud SQLに作成したDBインスタンスに対する処理が完了するまで待つようにします。「Error 409: Operation failed because another operation was already in progress. Try your request after the current operation is complete., operationInProgress」というエラーを回避します。
3
【Go、Docker】「api」という名前のパッケージを作るとビルド出来なくなる
Code Goでアプリケーションを開発する際、「api」というパッケージ名を含んだモジュールをDockerコンテナ内でビルドしようとするとバグります。そんな奇妙な事象に遭遇して沼りました。
4
【Cloud Functions】デプロイ直前にランタイム環境変数を利用した関数のテストは出来ない、のかもしれない
Code Google Cloud Functionsを使ってデプロイ直前にランタイム環境変数を利用した関数のテストができない事象に関する記事です。PythonとGoでテストしましたが一部のキーの環境変数以外は取得できませんでした。
5
【GitHub】Goでリポジトリの情報を取得するCloud Functionsを作って、Pythonと比較する
Code Go言語でGitHubのリポジトリ情報を取得するCloud Functionsを開発し、Pythonで作成した同様の機能と比較した記事です。使ったツールや、並行処理の比較も行っています。
ランダム記事 ランダム
Profile Image of the Author
kinkinbeer135ml
SIerをやめて、プログラミングを勉強しています。※Amazonアソシエイトに参加しています。
お知らせ
私のブログへようこそ!これはサンプルのお知らせです。
音楽
カバー

音楽

再生中なし

0:00 0:00
歌詞なし
カテゴリ
タグ
サイト統計
記事
287
カテゴリー
8
タグ
93
総文字数
486,174
運用日数
0
最終活動
0 日前

目次