13
0

S3互換ストレージWasabiの内容をSendGridでメール配信してみる

Last updated at Posted at 2023-12-19

概要

こんにちは。Wano株式会社でエンジニアをしている@kotobuki5991 です。
この記事はWano Group Advent Calendar20223 20日目の記事です。

弊社のプロダクトで新しく実装中の機能の一部に、S3(等のストレージ)に保存されたデータを読み取りメールを送信するという要件があります。

技術選定の中で、ストレージとしてWasabiを、メール配信サービスとしてSendGridを試しに触ってみたので設定・実装などを紹介します。

Wasabiとは?

S3完全互換のクラウドストレージで、現在13のリージョンが使用可能です。
すでにS3を使用しているアプリケーションであれば、修正の必要なく移行することが可能です。
また、S3に対して以下のような利点があります。

  • 高速
  • 下り料金無料!!!

wasabiの値段.gif

SendGridとは?

クラウド型のメール配信サービスです。
アカウントを作成するだけで即日メールを送信でき、面倒でコストのかかるメールサーバの構築は不要です。
以下のようなメリットがあります。

  • 12000通/月まで無料
  • 独自ドメインは無料で使用できる。
  • Fromのメールアドレスも自由に設定できる。

ただし、送信元を固定IPアドレスにするにはProプラン以上の選択が必要です。(10,000円〜)

Wasabiの設定からファイルのアップロードまで

まずはWasabiの設定です。

1. バケットの作成

wasabiのダッシュボードにログインし、Create Bucketをクリックします。

バケットの情報を入力し、Create Bucketを再度クリックします。

バケットのが完了しました!

ユーザーの作成

ダッシュボードのUsersから、ユーザー情報を入力してCreate Userをクリックします。
今回は、S3FullAccessのポリシーを付与しておきます。

作成後、アクセスキーのCSVがダウンロードできるので、忘れずにしておきましょう。

ダウンロードしたCSVに記載されているキーを~/.aws/credentialsに登録します。
以下のようになっていたらOKです。

$ cat ~/.aws/credentials
[wasabi] // 任意の名前
aws_access_key_id = XXXXXXXXXXXXXXXXXXX
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

3. ポリシーの追加

次に先ほど作成したバケットにポリシーを追加します。

以下の画面でポリシーを追加します。

今回は以下のように追加しました。ユーザーやバケットは自身のものに修正してください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAccessToBucketNamedBucket2",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456123456:user/sample-wasabi-user-1" // 作成したユーザーを指定
      },
      "Action": [
        "s3:ListBucket",
        "s3:GetObject",
        "s3:PutObject",
        "s3:GetObjectAcl",
        "s3:PutObjectAcl",
        "s3:DeleteObject"
      ],
      "Resource": [
        "arn:aws:s3:::sample-wasabi", // 作成したバケットを指定
        "arn:aws:s3:::sample-wasabi/*" // 作成したバケットを指定
      ]
    }
  ]
}

4. ファイルのアップロード

実際にファイルをアップロードします。
ここで重要なのはエンドポイントのURLです。
ap-northeast-1の場合は、https://s3.ap-northeast-1.wasabisys.comとなります。
上記を指定しないと、AWSを見に行ってしまいます。

$ aws s3 --profile wasabi --endpoint-url https://s3.ap-northeast-1.wasabisys.com cp ./wasabifile1.txt s3://sample-wasabi/hoge.txt 
upload: ./wasabifile1.txt to s3://sample-wasabi/hoge.txt

先ほどアップロードしたファイルが確認できました。

Wasabiからファイルリスト取得〜メール送信の実装例

プロジェクトの構成は以下の通りです。

 root/
  ├ aws-util/s3.go
   ├ sendgrid
  │     └ main.go
   │      └ sendgrid.env
  ├ .env
  ├ go.mod
   └ go.sum

Wasabiからデータを読み取る実装

今回は、アップロードされたファイル一覧を確認したいため、ListObjectsメソッドを実装しています。

s3.go
package awsutil

import (
	"fmt"
	"log"
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/joho/godotenv"
)

type S3Client interface {
	ListObjects(bucketName string) ([]*s3.Object, error)
}
type implS3Client struct {
	S3Client *s3.S3
}

func NewS3Client() (i *implS3Client, err error) {

	err = godotenv.Load("../.env")
	
	if err != nil {
		fmt.Printf("読み込み出来ませんでした: %v", err)
		return nil, err
	}
	profile := os.Getenv("WASABI_AWS_PROFILE")
	region := os.Getenv("WASABI_AWS_REGION")

	// create a configuration for profile
	s3Config := aws.Config{
		Endpoint:         aws.String("https://s3.ap-northeast-1.wasabisys.com"),
		Region:           aws.String(region),
		S3ForcePathStyle: aws.Bool(true),
	}

	// create a new session using the config above and profile
	goSession, err := session.NewSessionWithOptions(session.Options{
		Config:  s3Config,
		Profile: profile,
	})

	// check if the session was created correctly.
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	// create a s3 client session
	s3Client := s3.New(goSession)

	i = &implS3Client{
		S3Client: s3Client,
	}

	return i, err
}

func (s *implS3Client) ListObjects() ([]*s3.Object, error) {
	bucketName := os.Getenv("WASABI_AWS_BUCKET")
	result, err := s.S3Client.ListObjectsV2(&s3.ListObjectsV2Input{
		Bucket: aws.String(bucketName),
	})
	var contents []*s3.Object
	if err != nil {
		log.Printf("Couldn't list objects in bucket %v. Here's why: %v\n", bucketName, err)
	} else {
		contents = result.Contents
	}
	return contents, err
}

メール送信までの実装

次に、S3の情報を読み取ってメールを送信する実装です。
SendGridのアカウントは持っている前提で説明します。

マイページのダッシュボードから、Email API >> Integration Guideへ進みます。

今回はWeb APIを選択します。

Goを選択します。

画面の指示の通りにコマンドを実行します。

main.go
// using SendGrid's Go Library
// https://github.com/sendgrid/sendgrid-go
package main

import (
	"fmt"
	"log"
	"os"

	awsutil "github.com/fukuju-t/mail-project/aws-util"
	"github.com/sendgrid/sendgrid-go"
	"github.com/sendgrid/sendgrid-go/helpers/mail"
)

const confirmMessage = `
ファイル数: %d件

%s
`


func main() {

	s3, err := awsutil.NewS3Client()
	if err != nil {
		log.Fatal(err)
	}
	objects, err := s3.ListObjects()
	if err != nil {
		log.Fatal(err)
	}
	objectsCount := len(objects)
	objectNames := ""
	for _, v := range objects {
		objectNames = "・" + objectNames + *v.Key + "\n"
	}

	from := mail.NewEmail("Wasabi テストメール", "test@example.com")
	subject := "Wasabiのファイルリストです"
	to := mail.NewEmail("hoge", "yourmailaddress@test.com") // 送信先(今回は自身の)メールアドレス

	plainTextContent := fmt.Sprintf(confirmMessage, objectsCount, objectNames)
	htmlContent := ""
	message := mail.NewSingleEmail(from, subject, to, plainTextContent, htmlContent)
	client := sendgrid.NewSendClient(os.Getenv("SENDGRID_API_KEY"))
	response, err := client.Send(message)
	if err != nil {
		log.Println(err)
	} else {
		fmt.Println(response.StatusCode)
		fmt.Println(response.Body)
		fmt.Println(response.Headers)
	}
}

メール送信!

成功すると以下のような出力となります。

$ go run main.go
202

map[Access-Control-Allow-Headers:[Authorization, Content-Type, On-behalf-of, 
x-sg-elas-acl] Access-Control-Allow-Methods:[POST] Access-Control-Allow-Origin:
// 以下省略

以下のようなメールが届きます。

まとめ

今回はWasabiとSendGridを試してみました。

WasabiはバケットからのDLなど下り通信が多いほどS3よりお得に使えそうです。
また、SendGridは実装が非常に楽で無料なのがいいですね。


現在、Wanoグループ / TuneCore Japan では人材募集をしています。興味のある方は下記を参照してください。
Wano | Wano Group JOBS
TuneCore Japan | TuneCore Japan JOBS

13
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
0