From 7619acc4dc2582e421a2dfac255d196da31a6499 Mon Sep 17 00:00:00 2001 From: fujiwara Date: Wed, 29 Nov 2023 14:17:01 +0900 Subject: [PATCH] add status command delete is revival. --- cli.go | 6 +++ delete.go | 18 +++++++-- lambroll.go | 2 +- status.go | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 status.go diff --git a/cli.go b/cli.go index 39161d7..e89acdc 100644 --- a/cli.go +++ b/cli.go @@ -39,6 +39,8 @@ type CLIOptions struct { Logs *LogsOption `cmd:"logs" help:"show logs of function"` Diff *DiffOption `cmd:"diff" help:"show diff of function"` Render *RenderOption `cmd:"render" help:"render function.json"` + Status *StatusOption `cmd:"status" help:"show status of function"` + Delete *DeleteOption `cmd:"delete" help:"delete function"` Versions *VersionsOption `cmd:"versions" help:"show versions of function"` Version struct{} `cmd:"version" help:"show version"` @@ -128,6 +130,10 @@ func dispatchCLI(ctx context.Context, sub string, usage func(), opts *CLIOptions return app.Render(ctx, opts.Render) case "diff": return app.Diff(ctx, opts.Diff) + case "delete": + return app.Delete(ctx, opts.Delete) + case "status": + return app.Status(ctx, opts.Status) default: usage() } diff --git a/delete.go b/delete.go index 7a03f10..a8e7692 100644 --- a/delete.go +++ b/delete.go @@ -5,23 +5,25 @@ import ( "fmt" "log" + "github.com/Songmu/prompter" "github.com/aws/aws-sdk-go-v2/service/lambda" ) // DeleteOption represents options for Delete() type DeleteOption struct { - DryRun *bool + DryRun bool `help:"dry run" default:"false" negatable:""` + Force bool `help:"delete without confirmation" default:"false"` } func (opt DeleteOption) label() string { - if *opt.DryRun { + if opt.DryRun { return "**DRY RUN**" } return "" } // Delete deletes function -func (app *App) Delete(ctx context.Context, opt DeleteOption) error { +func (app *App) Delete(ctx context.Context, opt *DeleteOption) error { fn, err := app.loadFunction(app.functionFilePath) if err != nil { return fmt.Errorf("failed to load function: %w", err) @@ -29,9 +31,15 @@ func (app *App) Delete(ctx context.Context, opt DeleteOption) error { log.Println("[info] deleting function", *fn.FunctionName, opt.label()) - if *opt.DryRun { + if opt.DryRun { return nil } + + if !opt.Force && !prompter.YN("Do you want to delete the function?", false) { + log.Println("[info] canceled to delete function", *fn.FunctionName) + return nil + } + _, err = app.lambda.DeleteFunction(ctx, &lambda.DeleteFunctionInput{ FunctionName: fn.FunctionName, }) @@ -39,5 +47,7 @@ func (app *App) Delete(ctx context.Context, opt DeleteOption) error { return fmt.Errorf("failed to delete function: %w", err) } + log.Println("[info] completed to delete function", *fn.FunctionName) + return nil } diff --git a/lambroll.go b/lambroll.go index 930d5b5..0b6d4a7 100644 --- a/lambroll.go +++ b/lambroll.go @@ -40,7 +40,7 @@ var retryPolicy = retry.Policy{ type Function = lambda.CreateFunctionInput // Tags represents tags of function -type Tags = map[string]string +type Tags map[string]string func (app *App) functionArn(ctx context.Context, name string) string { return fmt.Sprintf( diff --git a/status.go b/status.go new file mode 100644 index 0000000..a0614d4 --- /dev/null +++ b/status.go @@ -0,0 +1,106 @@ +package lambroll + +import ( + "context" + "fmt" + "log" + "strings" + + "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" +) + +// StatusOption represents options for Status() +type StatusOption struct { + Qualifier string `help:"compare with" default:"$LATEST"` + Output string `default:"text" enum:"text,json" help:"output format"` +} + +type FunctionStatusOutput struct { + Configuration *types.FunctionConfiguration + Code *types.FunctionCodeLocation + Tags Tags +} + +// Status prints status of function +func (app *App) Status(ctx context.Context, opt *StatusOption) error { + fn, err := app.loadFunction(app.functionFilePath) + if err != nil { + return fmt.Errorf("failed to load function: %w", err) + } + name := *fn.FunctionName + + var configuration *types.FunctionConfiguration + var code *types.FunctionCodeLocation + var tags Tags + + if res, err := app.lambda.GetFunction(ctx, &lambda.GetFunctionInput{ + FunctionName: &name, + Qualifier: &opt.Qualifier, + }); err != nil { + return fmt.Errorf("failed to GetFunction %s: %w", name, err) + } else { + configuration = res.Configuration + code = res.Code + { + log.Println("[debug] list tags Resource", app.functionArn(ctx, name)) + res, err := app.lambda.ListTags(ctx, &lambda.ListTagsInput{ + // Tagging operations are permitted on Lambda functions only. + // Tags on aliases and versions are not supported. + Resource: aws.String(app.functionArn(ctx, name)), + }) + if err != nil { + return fmt.Errorf("failed to list tags: %w", err) + } + tags = res.Tags + } + } + st := &FunctionStatusOutput{ + Configuration: configuration, + Code: code, + Tags: tags, + } + switch opt.Output { + case "text": + fmt.Print(st.String()) + case "json": + b, err := marshalJSON(st) + if err != nil { + return fmt.Errorf("failed to marshal json: %w", err) + } + fmt.Print(string(b)) + } + return nil +} + +func (st *FunctionStatusOutput) String() string { + tags := make(map[string]string, len(st.Tags)) + for k, v := range st.Tags { + tags[k] = v + } + res := strings.Join([]string{ + "FunctionName: " + aws.ToString(st.Configuration.FunctionName), + "Description: " + aws.ToString(st.Configuration.Description), + "Version: " + aws.ToString(st.Configuration.Version), + "FunctionArn: " + aws.ToString(st.Configuration.FunctionArn), + "State: " + string(st.Configuration.State), + "LastUpdateStatus: " + string(st.Configuration.LastUpdateStatus), + "PackageType: " + string(st.Configuration.PackageType), + "Runtime: " + string(st.Configuration.Runtime), + "Handler: " + aws.ToString(st.Configuration.Handler), + "Timeout: " + fmt.Sprintf("%d", aws.ToInt32(st.Configuration.Timeout)), + "MemorySize: " + fmt.Sprintf("%d", aws.ToInt32(st.Configuration.MemorySize)), + "Role: " + aws.ToString(st.Configuration.Role), + "LastModified: " + aws.ToString(st.Configuration.LastModified), + "CodeSize: " + fmt.Sprintf("%d", st.Configuration.CodeSize), + "CodeSha256: " + aws.ToString(st.Configuration.CodeSha256), + }, "\n") + "\n" + if len(tags) > 0 { + res += "Tags:\n" + for k, v := range tags { + res += fmt.Sprintf(" %s: %s\n", k, v) + } + } + return res +}