今天我們來寫code操作前幾天設定好的AWS SSM,SSM可以用來安全的儲存一些機密的參數,我們在部屬上AWS Lambda後,像是Line的Access Token和Secret這類參數都會存放在裡面,而本機端開發方便的存放在環境變數裡就好。
那接著我們就一步步來寫操作SSM的function以及對應的Unit Test吧~
一樣我們先開一個資料夾專門處理ssm,並且建立ssm.go以及對應的ssm_test.go,還有一個config.json用來mock參數的
config.json,填入我們可能會存的參數名稱,這邊以Secret和AccessToken為例
{
"MockChannelSecret":"secret-name",
"MockChannelAccessToken":"token-name"
}
接著ssm.go,我們一樣建立一個SSM的結構,會存放一個操作SSM的Client
type SSM struct {
Client *ssm.Client
}
func NewSSM() *SSM {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic(err)
}
client := ssm.NewFromConfig(cfg)
return &SSM{
Client: client,
}
}
這邊我們定義一個interface SSMGetParameterAPI,並且它裡面定義了GetParameter的方法,GetParameter是用來從SSM拿取參數的方法,我們將他定義在interface裡面的話,可以在測試的時候使用SSMGetParameterAPI模擬AWS取SSM參數的動作。
type SSMGetParameterAPI interface {
GetParameter(ctx context.Context,
params *ssm.GetParameterInput,
optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
}
func (s *SSM) FindParameter(c context.Context, api SSMGetParameterAPI, name string) (string, error) {
input := &ssm.GetParameterInput{
Name: &name,
}
results, err := api.GetParameter(c, input)
if err != nil {
fmt.Println(err.Error())
return "", err
}
fmt.Println(*results.Parameter.Value)
return *results.Parameter.Value, nil
}
完整的ssm.go檔案如下
package ssm
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ssm"
)
type SSMGetParameterAPI interface {
GetParameter(ctx context.Context,
params *ssm.GetParameterInput,
optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error)
}
func (s *SSM) FindParameter(c context.Context, api SSMGetParameterAPI, name string) (string, error) {
input := &ssm.GetParameterInput{
Name: &name,
}
results, err := api.GetParameter(c, input)
if err != nil {
fmt.Println(err.Error())
return "", err
}
fmt.Println(*results.Parameter.Value)
return *results.Parameter.Value, nil
}
type SSM struct {
Client *ssm.Client
}
func NewSSM() *SSM {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic(err)
}
client := ssm.NewFromConfig(cfg)
return &SSM{
Client: client,
}
}
接著打開ssm_test.go,寫好TestMain,New一個共用的testSSM
var testSSM *SSM
func TestMain(m *testing.M) {
testSSM = NewSSM()
os.Exit(m.Run())
}
我們先建立一個struct來implement剛剛的SSMGetParameterAPI,我們自己實現一個GetParameter,讓他依照讀到的參數名稱傳出對應的Parameter Value,來模擬AWS取得SSM參數的功能。
type SSMGetParameterImpl struct{}
func (dt SSMGetParameterImpl) GetParameter(ctx context.Context,
params *ssm.GetParameterInput,
optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) {
var parameter *types.Parameter
if *params.Name == "secret-name" {
parameter = &types.Parameter{Value: aws.String("secret-value")}
}
if *params.Name == "token-name" {
parameter = &types.Parameter{Value: aws.String("token-value")}
}
output := &ssm.GetParameterOutput{
Parameter: parameter,
}
return output, nil
}
解析config.json,取出json存放對應的參數名稱
type Config struct {
MockChannelSecret string `json:"MockChannelSecret"`
MockChannelAccessToken string `json:"MockChannelAccessToken"`
}
var configFileName = "config.json"
var globalConfig Config
func populateConfiguration(t *testing.T) error {
content, err := os.ReadFile(configFileName)
if err != nil {
return err
}
text := string(content)
err = json.Unmarshal([]byte(text), &globalConfig)
if err != nil {
return err
}
if globalConfig.MockChannelSecret == "" {
msg := "You must supply a value for MockChannelSecret in " + configFileName
return errors.New(msg)
}
if globalConfig.MockChannelAccessToken == "" {
msg := "You must supply a value for MockChannelAccessToken in " + configFileName
return errors.New(msg)
}
return nil
}
補上測試,實際去調用FindParameter,由於我們放進FindParameter的是api := &SSMGetParameterImpl{},而他的GetParameter已經被我們替換掉了,所以就能取出我們在GetParameter預設要存放的值瞜~
func TestFindParameter(t *testing.T) {
thisTime := time.Now()
nowString := thisTime.Format("2006-01-02 15:04:05 Monday")
t.Log("Starting unit test at " + nowString)
err := populateConfiguration(t)
if err != nil {
t.Fatal(err)
}
api := &SSMGetParameterImpl{}
respSecret, err := testSSM.FindParameter(context.Background(), *api, globalConfig.MockChannelSecret)
if err != nil {
t.Log("Got an error ...:")
t.Log(err)
return
}
t.Log("MockChannelSecret value: " + respSecret)
respToken, err := testSSM.FindParameter(context.Background(), *api, globalConfig.MockChannelAccessToken)
if err != nil {
t.Log("Got an error ...:")
t.Log(err)
return
}
t.Log("MockChannelAccessToken value: " + respToken)
}
最後測試跑過就會像下面這樣囉~