Skip to content

Commit 8dd1b2e

Browse files
authored
Merge pull request #315 from fujiwara/kong
use Kong instead of kingpin.
2 parents 308e2b3 + 15420e8 commit 8dd1b2e

20 files changed

+289
-150
lines changed

archive.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ import (
1717

1818
// Archive archives zip
1919
func (app *App) Archive(ctx context.Context, opt DeployOption) error {
20-
excludes, err := expandExcludeFile(*opt.ExcludeFile)
20+
excludes, err := expandExcludeFile(opt.ExcludeFile)
2121
if err != nil {
2222
return fmt.Errorf("failed to parse exclude file: %w", err)
2323
}
24-
opt.Excludes = append(opt.Excludes, excludes...)
24+
opt.excludes = append(opt.excludes, excludes...)
2525

26-
zipfile, _, err := createZipArchive(*opt.Src, opt.Excludes)
26+
zipfile, _, err := createZipArchive(opt.Src, opt.excludes)
2727
if err != nil {
2828
return err
2929
}

cli.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package lambroll
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"os"
8+
"strings"
9+
10+
"github.com/alecthomas/kong"
11+
"github.com/fatih/color"
12+
"github.com/fujiwara/logutils"
13+
)
14+
15+
type Option struct {
16+
Function string `help:"Function file path"`
17+
LogLevel string `help:"log level (trace, debug, info, warn, error)" default:"info" enum:"trace,debug,info,warn,error"`
18+
Color bool `help:"enable colored output" default:"false"`
19+
20+
Region *string `help:"AWS region" environment:"AWS_REGION"`
21+
Profile *string `help:"AWS credential profile name" environment:"AWS_PROFILE"`
22+
TFState *string `name:"tfstate" help:"URL to terraform.tfstate"`
23+
PrefixedTFState map[string]string `name:"prefixed-tfstate" help:"key value pair of the prefix for template function name and URL to terraform.tfstate"`
24+
Endpoint *string `help:"AWS API Lambda Endpoint"`
25+
Envfile []string `help:"environment files"`
26+
ExtStr map[string]string `help:"external string values for Jsonnet"`
27+
ExtCode map[string]string `help:"external code values for Jsonnet"`
28+
}
29+
30+
type CLIOptions struct {
31+
Option
32+
33+
Deploy *DeployOption `cmd:"deploy" help:"deploy or create function"`
34+
Init *InitOption `cmd:"init" help:"init function.json"`
35+
List *ListOption `cmd:"list" help:"list functions"`
36+
Rollback *RollbackOption `cmd:"rollback" help:"rollback function"`
37+
Invoke *InvokeOption `cmd:"invoke" help:"invoke function"`
38+
Archive *DeployOption `cmd:"archive" help:"archive function"`
39+
Logs *LogsOption `cmd:"logs" help:"show logs of function"`
40+
Diff *DiffOption `cmd:"diff" help:"show diff of function"`
41+
Versions *VersionsOption `cmd:"versions" help:"show versions of function"`
42+
43+
Version struct{} `cmd:"version" help:"show version"`
44+
}
45+
46+
type CLIParseFunc func([]string) (string, *CLIOptions, func(), error)
47+
48+
func ParseCLI(args []string) (string, *CLIOptions, func(), error) {
49+
// compatible with v1
50+
if len(args) == 0 || len(args) > 0 && args[0] == "help" {
51+
args = []string{"--help"}
52+
}
53+
54+
var opts CLIOptions
55+
parser, err := kong.New(&opts, kong.Vars{"version": Version})
56+
if err != nil {
57+
return "", nil, nil, fmt.Errorf("failed to new kong: %w", err)
58+
}
59+
c, err := parser.Parse(args)
60+
if err != nil {
61+
return "", nil, nil, fmt.Errorf("failed to parse args: %w", err)
62+
}
63+
sub := strings.Fields(c.Command())[0]
64+
return sub, &opts, func() { c.PrintUsage(true) }, nil
65+
}
66+
67+
func CLI(ctx context.Context, parse CLIParseFunc) (int, error) {
68+
sub, opts, usage, err := parse(os.Args[1:])
69+
if err != nil {
70+
return 1, err
71+
}
72+
73+
color.NoColor = opts.Color
74+
filter := &logutils.LevelFilter{
75+
Levels: []logutils.LogLevel{"debug", "info", "warn", "error"},
76+
ModifierFuncs: []logutils.ModifierFunc{
77+
logutils.Color(color.FgHiBlack), // debug
78+
nil, // info
79+
logutils.Color(color.FgYellow), // warn
80+
logutils.Color(color.FgRed), // error
81+
},
82+
MinLevel: logutils.LogLevel(opts.LogLevel),
83+
Writer: os.Stderr,
84+
}
85+
log.SetOutput(filter)
86+
87+
if err := dispatchCLI(ctx, sub, usage, opts); err != nil {
88+
return 1, err
89+
}
90+
return 0, nil
91+
}
92+
93+
func dispatchCLI(ctx context.Context, sub string, usage func(), opts *CLIOptions) error {
94+
switch sub {
95+
case "version", "":
96+
fmt.Println("lambroll", Version)
97+
return nil
98+
}
99+
100+
app, err := New(ctx, &opts.Option)
101+
if err != nil {
102+
return err
103+
}
104+
if opts.Function != "" {
105+
log.Printf("[info] lambroll %s with %s", Version, opts.Function)
106+
} else {
107+
log.Printf("[info] lambroll %s", Version)
108+
}
109+
switch sub {
110+
case "init":
111+
return app.Init(ctx, *opts.Init)
112+
case "list":
113+
return app.List(ctx, *opts.List)
114+
case "deploy":
115+
return app.Deploy(ctx, *opts.Deploy)
116+
case "invoke":
117+
return app.Invoke(ctx, *opts.Invoke)
118+
case "logs":
119+
return app.Logs(ctx, *opts.Logs)
120+
case "versions":
121+
return app.Versions(ctx, *opts.Versions)
122+
case "archive":
123+
return app.Archive(ctx, *opts.Archive)
124+
case "rollback":
125+
return app.Rollback(ctx, *opts.Rollback)
126+
case "diff":
127+
return app.Diff(ctx, *opts.Diff)
128+
default:
129+
usage()
130+
}
131+
return nil
132+
}

cmd/lambroll/main.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,38 @@ package main
22

33
import (
44
"context"
5-
"fmt"
5+
"errors"
66
"log"
77
"os"
8+
"os/signal"
89

9-
"github.com/alecthomas/kingpin"
10-
"github.com/fatih/color"
1110
"github.com/fujiwara/lambroll"
12-
"github.com/fujiwara/logutils"
13-
"github.com/mattn/go-isatty"
11+
"golang.org/x/sys/unix"
1412
)
1513

1614
// Version number
1715
var Version = "current"
1816

1917
func main() {
20-
os.Exit(_main())
18+
os.Exit(_mainv2())
2119
}
2220

21+
func _mainv2() int {
22+
lambroll.Version = Version
23+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, unix.SIGTERM)
24+
defer stop()
25+
26+
exitCode, err := lambroll.CLI(ctx, lambroll.ParseCLI)
27+
if err != nil {
28+
if errors.Is(err, context.Canceled) {
29+
log.Println("[warn] Interrupted")
30+
} else {
31+
log.Printf("[error] FAILED. %s", err)
32+
}
33+
}
34+
return exitCode
35+
}
36+
/*
2337
func _main() int {
2438
kingpin.Command("version", "show version")
2539
logLevel := kingpin.Flag("log-level", "log level (trace, debug, info, warn, error)").Default("info").Enum("trace", "debug", "info", "warn", "error")
@@ -173,3 +187,4 @@ func _main() int {
173187
log.Println("[info] completed")
174188
return 0
175189
}
190+
*/

create.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ func (app *App) prepareFunctionCodeForDeploy(ctx context.Context, opt DeployOpti
4747
return nil
4848
}
4949

50-
if opt.SkipArchive != nil && *opt.SkipArchive {
50+
if opt.SkipArchive {
5151
if fn.Code == nil || fn.Code.S3Bucket == nil || fn.Code.S3Key == nil {
5252
return fmt.Errorf("--skip-archive requires Code.S3Bucket and Code.S3key elements in function definition")
5353
}
5454
return nil
5555
}
5656

57-
zipfile, info, err := prepareZipfile(*opt.Src, opt.Excludes)
57+
zipfile, info, err := prepareZipfile(opt.Src, opt.excludes)
5858
if err != nil {
5959
return err
6060
}
@@ -99,8 +99,8 @@ func (app *App) create(ctx context.Context, opt DeployOption, fn *Function) erro
9999
log.Println("[info] creating function", opt.label())
100100

101101
version := "(created)"
102-
if !*opt.DryRun {
103-
fn.Publish = *opt.Publish
102+
if !opt.DryRun {
103+
fn.Publish = opt.Publish
104104
res, err := app.createFunction(ctx, fn)
105105
if err != nil {
106106
return fmt.Errorf("failed to create function: %w", err)
@@ -117,16 +117,16 @@ func (app *App) create(ctx context.Context, opt DeployOption, fn *Function) erro
117117
return err
118118
}
119119

120-
if !*opt.Publish {
120+
if !opt.Publish {
121121
return nil
122122
}
123123

124-
log.Printf("[info] creating alias set %s to version %s %s", *opt.AliasName, version, opt.label())
125-
if !*opt.DryRun {
124+
log.Printf("[info] creating alias set %s to version %s %s", opt.AliasName, version, opt.label())
125+
if !opt.DryRun {
126126
_, err := app.lambda.CreateAlias(ctx, &lambda.CreateAliasInput{
127127
FunctionName: fn.FunctionName,
128128
FunctionVersion: aws.String(version),
129-
Name: aws.String(*opt.AliasName),
129+
Name: aws.String(opt.AliasName),
130130
})
131131
if err != nil {
132132
return fmt.Errorf("failed to create alias: %w", err)

delete.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import (
1010

1111
// DeleteOption represents options for Delete()
1212
type DeleteOption struct {
13-
FunctionFilePath *string
14-
DryRun *bool
13+
DryRun *bool
1514
}
1615

1716
func (opt DeleteOption) label() string {
@@ -23,7 +22,7 @@ func (opt DeleteOption) label() string {
2322

2423
// Delete deletes function
2524
func (app *App) Delete(ctx context.Context, opt DeleteOption) error {
26-
fn, err := app.loadFunction(*opt.FunctionFilePath)
25+
fn, err := app.loadFunction(app.functionFilePath)
2726
if err != nil {
2827
return fmt.Errorf("failed to load function: %w", err)
2928
}

deploy.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@ import (
1616

1717
// DeployOption represens an option for Deploy()
1818
type DeployOption struct {
19-
FunctionFilePath *string
20-
Src *string
21-
Excludes []string
22-
ExcludeFile *string
23-
Publish *bool
24-
AliasName *string
25-
AliasToLatest *bool
26-
DryRun *bool
27-
SkipArchive *bool
28-
KeepVersions *int
19+
Src string `help:"function zip archive or src dir" default:"."`
20+
ExcludeFile string `help:"exclude file" default:".lambdaignore"`
21+
Publish bool `help:"publish function" default:"true"`
22+
AliasName string `help:"alias name for publish" default:"current"`
23+
AliasToLatest bool `help:"set alias to unpublished $LATEST version" default:"false"`
24+
DryRun bool `help:"dry run" default:"false"`
25+
SkipArchive bool `help:"skip to create zip archive. requires Code.S3Bucket and Code.S3Key in function definition" default:"false"`
26+
KeepVersions int `help:"Number of latest versions to keep. Older versions will be deleted. (Optional value: default 0)." default:"0"`
27+
28+
excludes []string
2929
}
3030

3131
func (opt DeployOption) label() string {
32-
if *opt.DryRun {
32+
if opt.DryRun {
3333
return "**DRY RUN**"
3434
}
3535
return ""
@@ -69,14 +69,14 @@ func (opt *DeployOption) String() string {
6969

7070
// Deploy deployes a new lambda function code
7171
func (app *App) Deploy(ctx context.Context, opt DeployOption) error {
72-
excludes, err := expandExcludeFile(*opt.ExcludeFile)
72+
excludes, err := expandExcludeFile(opt.ExcludeFile)
7373
if err != nil {
7474
return fmt.Errorf("failed to parse exclude-file: %w", err)
7575
}
76-
opt.Excludes = append(opt.Excludes, excludes...)
76+
opt.excludes = append(opt.excludes, excludes...)
7777
log.Printf("[debug] %s", opt.String())
7878

79-
fn, err := app.loadFunction(*opt.FunctionFilePath)
79+
fn, err := app.loadFunction(app.functionFilePath)
8080
if err != nil {
8181
return fmt.Errorf("failed to load function: %w", err)
8282
}
@@ -129,7 +129,7 @@ func (app *App) Deploy(ctx context.Context, opt DeployOption) error {
129129
log.Printf("[debug]\n%s", ToJSONString(confIn))
130130

131131
var newerVersion string
132-
if !*opt.DryRun {
132+
if !opt.DryRun {
133133
proc := func(ctx context.Context) error {
134134
return app.updateFunctionConfiguration(ctx, confIn)
135135
}
@@ -150,10 +150,10 @@ func (app *App) Deploy(ctx context.Context, opt DeployOption) error {
150150
S3ObjectVersion: fn.Code.S3ObjectVersion,
151151
ImageUri: fn.Code.ImageUri,
152152
}
153-
if *opt.DryRun {
153+
if opt.DryRun {
154154
codeIn.DryRun = true
155155
} else {
156-
codeIn.Publish = *opt.Publish
156+
codeIn.Publish = opt.Publish
157157
}
158158

159159
var res *lambda.UpdateFunctionCodeOutput
@@ -173,17 +173,17 @@ func (app *App) Deploy(ctx context.Context, opt DeployOption) error {
173173
newerVersion = versionLatest
174174
log.Printf("[info] deployed version %s %s", newerVersion, opt.label())
175175
}
176-
if *opt.DryRun {
176+
if opt.DryRun {
177177
return nil
178178
}
179-
if *opt.Publish || *opt.AliasToLatest {
180-
err := app.updateAliases(ctx, *fn.FunctionName, versionAlias{newerVersion, *opt.AliasName})
179+
if opt.Publish || opt.AliasToLatest {
180+
err := app.updateAliases(ctx, *fn.FunctionName, versionAlias{newerVersion, opt.AliasName})
181181
if err != nil {
182182
return err
183183
}
184184
}
185-
if *opt.KeepVersions > 0 { // Ignore zero-value.
186-
return app.deleteVersions(ctx, *fn.FunctionName, *opt.KeepVersions)
185+
if opt.KeepVersions > 0 { // Ignore zero-value.
186+
return app.deleteVersions(ctx, *fn.FunctionName, opt.KeepVersions)
187187
}
188188
return nil
189189
}

deploy_test.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,18 @@ var expectExcludes = []string{
1313

1414
func TestDeployOptionExpand(t *testing.T) {
1515
file := "test/src/.lambdaignore"
16-
opt := lambroll.DeployOption{
17-
ExcludeFile: &file,
18-
}
19-
excludes, err := lambroll.ExpandExcludeFile(*opt.ExcludeFile)
16+
excludes := []string{}
17+
ex, err := lambroll.ExpandExcludeFile(file)
2018
if err != nil {
2119
t.Error("failed to expand", err)
2220
}
23-
opt.Excludes = append(opt.Excludes, excludes...)
24-
if len(opt.Excludes) != len(expectExcludes) {
25-
t.Errorf("unexpeted expanded excludes %#v", opt.Excludes)
21+
excludes = append(excludes, ex...)
22+
if len(excludes) != len(expectExcludes) {
23+
t.Errorf("unexpeted expanded excludes %#v", excludes)
2624
}
2725
for i, line := range expectExcludes {
28-
if line != opt.Excludes[i] {
29-
t.Errorf("unexpected expanded excludes[%d] expected:%s, got:%s", i, line, opt.Excludes[i])
26+
if line != excludes[i] {
27+
t.Errorf("unexpected expanded excludes[%d] expected:%s, got:%s", i, line, excludes[i])
3028
}
3129
}
3230
}

0 commit comments

Comments
 (0)