Skip to content

Commit

Permalink
Merge pull request #306 from fujiwara/aws-sdk-go-v2
Browse files Browse the repository at this point in the history
aws-sdk-go-v2
  • Loading branch information
fujiwara authored Sep 22, 2023
2 parents cc75712 + 129607e commit 308e2b3
Show file tree
Hide file tree
Showing 22 changed files with 748 additions and 563 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ jobs:
strategy:
matrix:
go:
- "1.19"
- "1.20"
- "1.21"
name: Build
runs-on: ubuntu-latest

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
uses: actions/setup-go@v4
with:
go-version: "1.20"
go-version: "1.21"
check-latest: true

- name: Check out code into the Go module directory
Expand Down
28 changes: 14 additions & 14 deletions archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ package lambroll

import (
"archive/zip"
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/fujiwara/lambroll/wildcard"
"github.com/pkg/errors"
)

// Archive archives zip
func (app *App) Archive(opt DeployOption) error {
func (app *App) Archive(ctx context.Context, opt DeployOption) error {
excludes, err := expandExcludeFile(*opt.ExcludeFile)
if err != nil {
return errors.Wrap(err, "failed to parse exclude file")
return fmt.Errorf("failed to parse exclude file: %w", err)
}
opt.Excludes = append(opt.Excludes, excludes...)

Expand All @@ -36,7 +36,7 @@ func loadZipArchive(src string) (*os.File, os.FileInfo, error) {
log.Printf("[info] reading zip archive from %s", src)
r, err := zip.OpenReader(src)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to open zip file %s", src)
return nil, nil, fmt.Errorf("failed to open zip file %s: %w", src, err)
}
for _, f := range r.File {
header := f.FileHeader
Expand All @@ -50,7 +50,7 @@ func loadZipArchive(src string) (*os.File, os.FileInfo, error) {
r.Close()
info, err := os.Stat(src)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to stat %s", src)
return nil, nil, fmt.Errorf("failed to stat %s: %w", src, err)
}
log.Printf("[info] zip archive %d bytes", info.Size())
fh, err := os.Open(src)
Expand All @@ -60,9 +60,9 @@ func loadZipArchive(src string) (*os.File, os.FileInfo, error) {
// createZipArchive creates a zip archive
func createZipArchive(src string, excludes []string) (*os.File, os.FileInfo, error) {
log.Printf("[info] creating zip archive from %s", src)
tmpfile, err := ioutil.TempFile("", "archive")
tmpfile, err := os.CreateTemp("", "archive")
if err != nil {
return nil, nil, errors.Wrap(err, "failed to open tempFile")
return nil, nil, fmt.Errorf("failed to open tempFile: %w", err)
}
w := zip.NewWriter(tmpfile)
err = filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
Expand All @@ -83,7 +83,7 @@ func createZipArchive(src string, excludes []string) (*os.File, os.FileInfo, err
return addToZip(w, path, relpath, info)
})
if err := w.Close(); err != nil {
return nil, nil, errors.Wrap(err, "failed to create zip archive")
return nil, nil, fmt.Errorf("failed to create zip archive: %w", err)
}
tmpfile.Seek(0, os.SEEK_SET)
stat, _ := tmpfile.Stat()
Expand Down Expand Up @@ -129,11 +129,11 @@ func addToZip(z *zip.Writer, path, relpath string, info os.FileInfo) error {
return err
}

func (app *App) uploadFunctionToS3(f *os.File, bucket, key string) (string, error) {
svc := s3.New(app.sess)
func (app *App) uploadFunctionToS3(ctx context.Context, f *os.File, bucket, key string) (string, error) {
svc := s3.NewFromConfig(app.awsConfig)
log.Printf("[debug] PutObjcet to s3://%s/%s", bucket, key)
// TODO multipart upload
res, err := svc.PutObject(&s3.PutObjectInput{
res, err := svc.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
Body: f,
Expand Down
24 changes: 13 additions & 11 deletions cmd/lambroll/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"log"
"os"
Expand Down Expand Up @@ -134,34 +135,35 @@ func _main() int {
}
log.SetOutput(filter)

app, err := lambroll.New(&opt)
app, err := lambroll.New(context.TODO(), &opt)
if err != nil {
log.Println("[error]", err)
return 1
}

ctx := context.TODO()
log.Printf("[info] lambroll %s with %s", Version, *function)
switch command {
case "init":
err = app.Init(initOption)
err = app.Init(ctx, initOption)
case "list":
err = app.List(listOption)
err = app.List(ctx, listOption)
case "deploy":
err = app.Deploy(deployOption)
err = app.Deploy(ctx, deployOption)
case "rollback":
err = app.Rollback(rollbackOption)
err = app.Rollback(ctx, rollbackOption)
case "delete":
err = app.Delete(deleteOption)
err = app.Delete(ctx, deleteOption)
case "invoke":
err = app.Invoke(invokeOption)
err = app.Invoke(ctx, invokeOption)
case "archive":
err = app.Archive(archiveOption)
err = app.Archive(ctx, archiveOption)
case "logs":
err = app.Logs(logsOption)
err = app.Logs(ctx, logsOption)
case "diff":
err = app.Diff(diffOption)
err = app.Diff(ctx, diffOption)
case "versions":
err = app.Versions(versionsOption)
err = app.Versions(ctx, versionsOption)
}

if err != nil {
Expand Down
57 changes: 27 additions & 30 deletions create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ package lambroll
import (
"context"
"fmt"
"io/ioutil"
"io"
"log"
"os"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/lambda"
"github.com/pkg/errors"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/lambda"
"github.com/aws/aws-sdk-go-v2/service/lambda/types"
)

var directUploadThreshold = int64(50 * 1024 * 1024) // 50MB

func prepareZipfile(src string, excludes []string) (*os.File, os.FileInfo, error) {
if fi, err := os.Stat(src); err != nil {
return nil, nil, errors.Wrapf(err, "src %s is not found", src)
return nil, nil, fmt.Errorf("src %s is not found: %w", src, err)
} else if fi.IsDir() {
zipfile, info, err := createZipArchive(src, excludes)
if err != nil {
Expand All @@ -33,23 +33,23 @@ func prepareZipfile(src string, excludes []string) (*os.File, os.FileInfo, error
return nil, nil, fmt.Errorf("src %s is not found", src)
}

func (app *App) prepareFunctionCodeForDeploy(opt DeployOption, fn *Function) error {
if aws.StringValue(fn.PackageType) == packageTypeImage {
func (app *App) prepareFunctionCodeForDeploy(ctx context.Context, opt DeployOption, fn *Function) error {
if fn.PackageType == types.PackageTypeImage {
if fn.Code == nil || fn.Code.ImageUri == nil {
return errors.New("PackageType=Image requires Code.ImageUri in function definition")
return fmt.Errorf("PackageType=Image requires Code.ImageUri in function definition")
}
// deploy docker image. no need to preprare
log.Printf("[info] using docker image %s", *fn.Code.ImageUri)

if fn.ImageConfig == nil {
fn.ImageConfig = &lambda.ImageConfig{} // reset explicitly
fn.ImageConfig = &types.ImageConfig{} // reset explicitly
}
return nil
}

if opt.SkipArchive != nil && *opt.SkipArchive {
if fn.Code == nil || fn.Code.S3Bucket == nil || fn.Code.S3Key == nil {
return errors.New("--skip-archive requires Code.S3Bucket and Code.S3key elements in function definition")
return fmt.Errorf("--skip-archive requires Code.S3Bucket and Code.S3key elements in function definition")
}
return nil
}
Expand All @@ -63,9 +63,9 @@ func (app *App) prepareFunctionCodeForDeploy(opt DeployOption, fn *Function) err
if fn.Code != nil {
if bucket, key := fn.Code.S3Bucket, fn.Code.S3Key; bucket != nil && key != nil {
log.Printf("[info] uploading function %d bytes to s3://%s/%s", info.Size(), *bucket, *key)
versionID, err := app.uploadFunctionToS3(zipfile, *bucket, *key)
versionID, err := app.uploadFunctionToS3(ctx, zipfile, *bucket, *key)
if err != nil {
errors.Wrapf(err, "failed to upload function zip to s3://%s/%s", *bucket, *key)
fmt.Errorf("failed to upload function zip to s3://%s/%s: %w", *bucket, *key, err)
}
if versionID != "" {
log.Printf("[info] object created as version %s", versionID)
Expand All @@ -75,37 +75,35 @@ func (app *App) prepareFunctionCodeForDeploy(opt DeployOption, fn *Function) err
fn.Code.S3ObjectVersion = nil
}
} else {
return errors.New("Code.S3Bucket or Code.S3Key are not defined")
return fmt.Errorf("Code.S3Bucket or Code.S3Key are not defined")
}
} else {
// try direct upload
if s := info.Size(); s > directUploadThreshold {
return fmt.Errorf("cannot use a zip file for update function directly. Too large file %d bytes. Please define Code.S3Bucket and Code.S3Key in function.json", s)
}
b, err := ioutil.ReadAll(zipfile)
b, err := io.ReadAll(zipfile)
if err != nil {
return errors.Wrap(err, "failed to read zipfile content")
return fmt.Errorf("failed to read zipfile content: %w", err)
}
fn.Code = &lambda.FunctionCode{ZipFile: b}
fn.Code = &types.FunctionCode{ZipFile: b}
}
return nil
}

func (app *App) create(opt DeployOption, fn *Function) error {
ctx := context.Background()
err := app.prepareFunctionCodeForDeploy(opt, fn)
func (app *App) create(ctx context.Context, opt DeployOption, fn *Function) error {
err := app.prepareFunctionCodeForDeploy(ctx, opt, fn)
if err != nil {
return errors.Wrap(err, "failed to prepare function code")
return fmt.Errorf("failed to prepare function code: %w", err)
}
log.Println("[info] creating function", opt.label())
log.Println("[debug]\n", fn.String())

version := "(created)"
if !*opt.DryRun {
fn.Publish = opt.Publish
fn.Publish = *opt.Publish
res, err := app.createFunction(ctx, fn)
if err != nil {
return errors.Wrap(err, "failed to create function")
return fmt.Errorf("failed to create function: %w", err)
}
if res.Version != nil {
version = *res.Version
Expand All @@ -115,7 +113,7 @@ func (app *App) create(opt DeployOption, fn *Function) error {
}
}

if err := app.updateTags(fn, opt); err != nil {
if err := app.updateTags(ctx, fn, opt); err != nil {
return err
}

Expand All @@ -125,23 +123,22 @@ func (app *App) create(opt DeployOption, fn *Function) error {

log.Printf("[info] creating alias set %s to version %s %s", *opt.AliasName, version, opt.label())
if !*opt.DryRun {
alias, err := app.lambda.CreateAlias(&lambda.CreateAliasInput{
_, err := app.lambda.CreateAlias(ctx, &lambda.CreateAliasInput{
FunctionName: fn.FunctionName,
FunctionVersion: aws.String(version),
Name: aws.String(*opt.AliasName),
})
if err != nil {
return errors.Wrap(err, "failed to create alias")
return fmt.Errorf("failed to create alias: %w", err)
}
log.Println("[info] alias created")
log.Printf("[debug]\n%s", alias.String())
}
return nil
}

func (app *App) createFunction(ctx context.Context, fn *lambda.CreateFunctionInput) (*lambda.FunctionConfiguration, error) {
if res, err := app.lambda.CreateFunctionWithContext(ctx, fn); err != nil {
return nil, errors.Wrap(err, "failed to create function")
func (app *App) createFunction(ctx context.Context, fn *lambda.CreateFunctionInput) (*lambda.CreateFunctionOutput, error) {
if res, err := app.lambda.CreateFunction(ctx, fn); err != nil {
return nil, fmt.Errorf("failed to create function: %w", err)
} else {
return res, app.waitForLastUpdateStatusSuccessful(ctx, *fn.FunctionName)
}
Expand Down
13 changes: 7 additions & 6 deletions delete.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package lambroll

import (
"context"
"fmt"
"log"

"github.com/aws/aws-sdk-go/service/lambda"
"github.com/pkg/errors"
"github.com/aws/aws-sdk-go-v2/service/lambda"
)

// DeleteOption represents options for Delete()
Expand All @@ -21,22 +22,22 @@ func (opt DeleteOption) label() string {
}

// Delete deletes function
func (app *App) Delete(opt DeleteOption) error {
func (app *App) Delete(ctx context.Context, opt DeleteOption) error {
fn, err := app.loadFunction(*opt.FunctionFilePath)
if err != nil {
return errors.Wrap(err, "failed to load function")
return fmt.Errorf("failed to load function: %w", err)
}

log.Println("[info] deleting function", *fn.FunctionName, opt.label())

if *opt.DryRun {
return nil
}
_, err = app.lambda.DeleteFunction(&lambda.DeleteFunctionInput{
_, err = app.lambda.DeleteFunction(ctx, &lambda.DeleteFunctionInput{
FunctionName: fn.FunctionName,
})
if err != nil {
return errors.Wrap(err, "failed to delete function")
return fmt.Errorf("failed to delete function: %w", err)
}

return nil
Expand Down
Loading

0 comments on commit 308e2b3

Please sign in to comment.