Skip to content

Commit

Permalink
read lambroll.json(net) as default options.
Browse files Browse the repository at this point in the history
refs #449
  • Loading branch information
fujiwara committed Nov 29, 2024
1 parent 7ceb4f1 commit 33ffea3
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 28 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,49 @@ Commands:
Run "lambroll <command> --help" for more information on a command.
```

### Global flags

lambroll has global flags for all commands.

These flags can be set by environment variables or option file (`lambroll.json` or `lambroll.jsonnet`).

#### Environment variables

For example, `--log-level=debug` can be set by `LAMBROLL_LOGLEVEL=debug`.

See the above usage for the environment variable names.

#### Option file

`lambroll.json` or `lambroll.jsonnet` can be used as an option file.

If the option file is found in the current directory, lambroll reads the file and applies the options.

```jsonnet
{
logLevel: 'info',
color: true,
region: 'ap-northeast-1',
profile: 'default',
tfstate: 's3://my-bucket/terraform.tfstate',
prefixedTfstate: {
my_first_: 's3://my-bucket/first.tfstate',
my_second_: 's3://my-bucket/second.tfstate',
},
endpoint: 'http://localhost:9001',
envfile: ['.env1', '.env2'],
extStr: {
accountID: '0123456789012',
},
extCode: {
memorySize: '128 * 4',
},
}
```

All fields are optional. If the field is not defined, the default value is used.
When passed by command line options, the command line option takes precedence over the option file.

### Init

`lambroll init` initialize function.json by existing function.
Expand Down
54 changes: 41 additions & 13 deletions cli.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package lambroll

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"log"
"os"
Expand All @@ -13,18 +16,18 @@ import (
)

type Option struct {
Function string `help:"Function file path" env:"LAMBROLL_FUNCTION"`
LogLevel string `help:"log level (trace, debug, info, warn, error)" default:"info" enum:"trace,debug,info,warn,error" env:"LAMBROLL_LOGLEVEL"`
Color bool `help:"enable colored output" default:"true" env:"LAMBROLL_COLOR" negatable:""`

Region *string `help:"AWS region" env:"AWS_REGION"`
Profile *string `help:"AWS credential profile name" env:"AWS_PROFILE"`
TFState *string `name:"tfstate" help:"URL to terraform.tfstate" env:"LAMBROLL_TFSTATE"`
PrefixedTFState map[string]string `name:"prefixed-tfstate" help:"key value pair of the prefix for template function name and URL to terraform.tfstate" env:"LAMBROLL_PREFIXED_TFSTATE"`
Endpoint *string `help:"AWS API Lambda Endpoint" env:"AWS_LAMBDA_ENDPOINT"`
Envfile []string `help:"environment files" env:"LAMBROLL_ENVFILE"`
ExtStr map[string]string `help:"external string values for Jsonnet" env:"LAMBROLL_EXTSTR"`
ExtCode map[string]string `help:"external code values for Jsonnet" env:"LAMBROLL_EXTCODE"`
Function string `help:"Function file path" env:"LAMBROLL_FUNCTION" json:"function,omitempty"`
LogLevel string `help:"log level (trace, debug, info, warn, error)" default:"info" enum:",trace,debug,info,warn,error" env:"LAMBROLL_LOGLEVEL" json:"log_level"`
Color bool `help:"enable colored output" default:"true" env:"LAMBROLL_COLOR" negatable:"" json:"color,omitempty"`

Region *string `help:"AWS region" env:"AWS_REGION" json:"region,omitempty"`
Profile *string `help:"AWS credential profile name" env:"AWS_PROFILE" json:"profile,omitempty"`
TFState *string `name:"tfstate" help:"URL to terraform.tfstate" env:"LAMBROLL_TFSTATE" json:"tfstate,omitempty"`
PrefixedTFState map[string]string `name:"prefixed-tfstate" help:"key value pair of the prefix for template function name and URL to terraform.tfstate" env:"LAMBROLL_PREFIXED_TFSTATE" json:"prefixed_tfstate,omitempty"`
Endpoint *string `help:"AWS API Lambda Endpoint" env:"AWS_LAMBDA_ENDPOINT" json:"endpoint,omitempty"`
Envfile []string `help:"environment files" env:"LAMBROLL_ENVFILE" json:"envfile,omitempty"`
ExtStr map[string]string `help:"external string values for Jsonnet" env:"LAMBROLL_EXTSTR" json:"extstr,omitempty"`
ExtCode map[string]string `help:"external code values for Jsonnet" env:"LAMBROLL_EXTCODE" json:"extcode,omitempty"`
}

type CLIOptions struct {
Expand Down Expand Up @@ -54,8 +57,30 @@ func ParseCLI(args []string) (string, *CLIOptions, func(), error) {
args = []string{"--help"}
}

kongOpts := []kong.Option{kong.Vars{"version": Version}}

// load default options from lambroll.json or .jsonnet
defaultOpt, err := loadDefinitionFile[Option](nil, "", DefaultOptionFilenames)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
// ignore not found error
} else {
return "", nil, nil, fmt.Errorf("failed to load default options: %w", err)
}
} else {
defaultOptBytes, err := json.Marshal(defaultOpt)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to marshal default options: %w", err)
}
resolver, err := kong.JSON(bytes.NewReader(defaultOptBytes))
if err != nil {
return "", nil, nil, fmt.Errorf("failed to parse default options: %w", err)
}
kongOpts = append(kongOpts, kong.Resolvers(resolver))
}

var opts CLIOptions
parser, err := kong.New(&opts, kong.Vars{"version": Version})
parser, err := kong.New(&opts, kongOpts...)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to new kong: %w", err)
}
Expand All @@ -74,6 +99,9 @@ func CLI(ctx context.Context, parse CLIParseFunc) (int, error) {
}

color.NoColor = !opts.Color
if opts.LogLevel == "" {
opts.LogLevel = DefaultLogLevel
}
filter := &logutils.LevelFilter{
Levels: []logutils.LogLevel{"trace", "debug", "info", "warn", "error"},
ModifierFuncs: []logutils.ModifierFunc{
Expand Down
58 changes: 44 additions & 14 deletions lambroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"os"
"path/filepath"
"reflect"
"text/template"
"time"

Expand Down Expand Up @@ -53,6 +54,9 @@ func (app *App) functionArn(ctx context.Context, name string) string {
}

var (
// DefaultLogLevel is default log level
DefaultLogLevel = "info"

// IgnoreFilename defines file name includes ignore patterns at creating zip archive.
IgnoreFilename = ".lambdaignore"

Expand All @@ -62,11 +66,18 @@ var (
"function.jsonnet",
}

// DefaultFunctionURLFilenames defines file name for function URL definition.
DefaultFunctionURLFilenames = []string{
"function_url.json",
"function_url.jsonnet",
}

// DefaultOptionFilenames defines file name for option definition.
DefaultOptionFilenames = []string{
"lambroll.json",
"lambroll.jsonnet",
}

// FunctionZipFilename defines file name for zip archive downloaded at init.
FunctionZipFilename = "function.zip"

Expand All @@ -77,6 +88,8 @@ var (
DefaultFunctionFilenames[1],
DefaultFunctionURLFilenames[0],
DefaultFunctionURLFilenames[1],
DefaultOptionFilenames[0],
DefaultOptionFilenames[1],
FunctionZipFilename,
".git/*",
".terraform/*",
Expand Down Expand Up @@ -222,6 +235,9 @@ func loadDefinitionFile[T any](app *App, path string, defaults []string) (*T, er
}
path = p
}
var instance T
typeName := reflect.TypeOf(instance).Name()
log.Printf("[info] loading %s from %s", typeName, path)

var (
src []byte
Expand All @@ -230,27 +246,41 @@ func loadDefinitionFile[T any](app *App, path string, defaults []string) (*T, er
switch filepath.Ext(path) {
case ".jsonnet":
vm := jsonnet.MakeVM()
for _, f := range app.nativeFuncs {
vm.NativeFunction(f)
}
for k, v := range app.extStr {
vm.ExtVar(k, v)
}
for k, v := range app.extCode {
vm.ExtCode(k, v)
if app != nil {
for _, f := range app.nativeFuncs {
vm.NativeFunction(f)
}
for k, v := range app.extStr {
vm.ExtVar(k, v)
}
for k, v := range app.extCode {
vm.ExtCode(k, v)
}
}
jsonStr, err := vm.EvaluateFile(path)
if err != nil {
return nil, err
}
src, err = app.loader.ReadWithEnvBytes([]byte(jsonStr))
if err != nil {
return nil, err
if app != nil {
src, err = app.loader.ReadWithEnvBytes([]byte(jsonStr))
if err != nil {
return nil, err
}
} else {
// no app, just return jsonnet result
src = []byte(jsonStr)
}
default:
src, err = app.loader.ReadWithEnv(path)
if err != nil {
return nil, err
if app != nil {
src, err = app.loader.ReadWithEnv(path)
if err != nil {
return nil, err
}
} else {
src, err = os.ReadFile(path)
if err != nil {
return nil, err
}
}
}
var v T
Expand Down
2 changes: 1 addition & 1 deletion utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func findDefinitionFile(preferred string, defaults []string) (string, error) {
return name, nil
}
}
return "", fmt.Errorf("function file (%s) not found", strings.Join(DefaultFunctionFilenames, " or "))
return "", fmt.Errorf("file (%s) not found: %w", strings.Join(DefaultFunctionFilenames, " or "), os.ErrNotExist)
}

func jsonToJsonnet(src []byte, filepath string) ([]byte, error) {
Expand Down

0 comments on commit 33ffea3

Please sign in to comment.