Add "default" flag to environment block (#142)

If the environment is not set through command line argument or
environment variable, the bundle loads either 1) the only environment,
2) the only environment with the default flag set.
This commit is contained in:
Pieter Noordhuis 2022-12-15 21:28:14 +01:00 committed by GitHub
parent 35243db33c
commit 24a3b90713
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 156 additions and 33 deletions

View File

@ -3,6 +3,10 @@ package config
// Environment defines overrides for a single environment. // Environment defines overrides for a single environment.
// This structure is recursively merged into the root configuration. // This structure is recursively merged into the root configuration.
type Environment struct { type Environment struct {
// Default marks that this environment must be used if one isn't specified
// by the user (through environment variable or command line argument).
Default bool `json:"default,omitempty"`
Bundle *Bundle `json:"bundle,omitempty"` Bundle *Bundle `json:"bundle,omitempty"`
Workspace *Workspace `json:"workspace,omitempty"` Workspace *Workspace `json:"workspace,omitempty"`

View File

@ -0,0 +1,54 @@
package mutator
import (
"context"
"fmt"
"strings"
"github.com/databricks/bricks/bundle"
"golang.org/x/exp/maps"
)
type selectDefaultEnvironment struct{}
// SelectDefaultEnvironment merges the default environment into the root configuration.
func SelectDefaultEnvironment() bundle.Mutator {
return &selectDefaultEnvironment{}
}
func (m *selectDefaultEnvironment) Name() string {
return "SelectDefaultEnvironment"
}
func (m *selectDefaultEnvironment) Apply(_ context.Context, b *bundle.Bundle) ([]bundle.Mutator, error) {
if len(b.Config.Environments) == 0 {
return nil, fmt.Errorf("no environments defined")
}
// One environment means there's only one default.
names := maps.Keys(b.Config.Environments)
if len(names) == 1 {
return []bundle.Mutator{SelectEnvironment(names[0])}, nil
}
// Multiple environments means we look for the `default` flag.
var defaults []string
for _, name := range names {
if b.Config.Environments[name].Default {
defaults = append(defaults, name)
}
}
// It is invalid to have multiple environments with the `default` flag set.
if len(defaults) > 1 {
return nil, fmt.Errorf("multiple environments are marked as default (%s)", strings.Join(defaults, ", "))
}
// If no environment has the `default` flag set, ask the user to specify one.
if len(defaults) == 0 {
return nil, fmt.Errorf("please specify environment")
}
// One default remaining.
return []bundle.Mutator{SelectEnvironment(defaults[0])}, nil
}

View File

@ -0,0 +1,79 @@
package mutator_test
import (
"context"
"testing"
"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config"
"github.com/databricks/bricks/bundle/config/mutator"
"github.com/stretchr/testify/assert"
)
func TestSelectDefaultEnvironmentNoEnvironments(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Environments: map[string]*config.Environment{},
},
}
_, err := mutator.SelectDefaultEnvironment().Apply(context.Background(), bundle)
assert.ErrorContains(t, err, "no environments defined")
}
func TestSelectDefaultEnvironmentSingleEnvironments(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Environments: map[string]*config.Environment{
"foo": {},
},
},
}
ms, err := mutator.SelectDefaultEnvironment().Apply(context.Background(), bundle)
assert.NoError(t, err)
assert.Len(t, ms, 1)
assert.Equal(t, "SelectEnvironment(foo)", ms[0].Name())
}
func TestSelectDefaultEnvironmentNoDefaults(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Environments: map[string]*config.Environment{
"foo": {},
"bar": {},
"qux": {},
},
},
}
_, err := mutator.SelectDefaultEnvironment().Apply(context.Background(), bundle)
assert.ErrorContains(t, err, "please specify environment")
}
func TestSelectDefaultEnvironmentMultipleDefaults(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Environments: map[string]*config.Environment{
"foo": {Default: true},
"bar": {Default: true},
"qux": {Default: true},
},
},
}
_, err := mutator.SelectDefaultEnvironment().Apply(context.Background(), bundle)
assert.ErrorContains(t, err, "multiple environments are marked as default")
}
func TestSelectDefaultEnvironmentSingleDefault(t *testing.T) {
bundle := &bundle.Bundle{
Config: config.Root{
Environments: map[string]*config.Environment{
"foo": {},
"bar": {Default: true},
"qux": {},
},
},
}
ms, err := mutator.SelectDefaultEnvironment().Apply(context.Background(), bundle)
assert.NoError(t, err)
assert.Len(t, ms, 1)
assert.Equal(t, "SelectEnvironment(bar)", ms[0].Name())
}

View File

@ -1,22 +0,0 @@
package loader
import (
"context"
"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config/mutator"
)
func ConfigureForEnvironment(ctx context.Context, env string) (context.Context, error) {
b, err := bundle.LoadFromRoot()
if err != nil {
return nil, err
}
err = bundle.Apply(ctx, b, mutator.DefaultMutatorsForEnvironment(env))
if err != nil {
return nil, err
}
return bundle.Context(ctx, b), nil
}

View File

@ -8,8 +8,6 @@ import (
const envName = "DATABRICKS_BUNDLE_ENV" const envName = "DATABRICKS_BUNDLE_ENV"
const defaultEnvironment = "default"
// getEnvironment returns the name of the environment to operate in. // getEnvironment returns the name of the environment to operate in.
func getEnvironment(cmd *cobra.Command) (value string) { func getEnvironment(cmd *cobra.Command) (value string) {
// The command line flag takes precedence. // The command line flag takes precedence.
@ -22,10 +20,5 @@ func getEnvironment(cmd *cobra.Command) (value string) {
} }
// If it's not set, use the environment variable. // If it's not set, use the environment variable.
value = os.Getenv(envName) return os.Getenv(envName)
if value != "" {
return
}
return defaultEnvironment
} }

View File

@ -1,7 +1,8 @@
package bundle package bundle
import ( import (
"github.com/databricks/bricks/bundle/loader" "github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config/mutator"
"github.com/databricks/bricks/cmd/root" "github.com/databricks/bricks/cmd/root"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -15,12 +16,26 @@ var rootCmd = &cobra.Command{
// ConfigureBundle loads the bundle configuration // ConfigureBundle loads the bundle configuration
// and configures it on the command's context. // and configures it on the command's context.
func ConfigureBundle(cmd *cobra.Command, args []string) error { func ConfigureBundle(cmd *cobra.Command, args []string) error {
ctx, err := loader.ConfigureForEnvironment(cmd.Context(), getEnvironment(cmd)) ctx := cmd.Context()
b, err := bundle.LoadFromRoot()
if err != nil { if err != nil {
return err return err
} }
cmd.SetContext(ctx) ms := mutator.DefaultMutators()
env := getEnvironment(cmd)
if env == "" {
ms = append(ms, mutator.SelectDefaultEnvironment())
} else {
ms = append(ms, mutator.SelectEnvironment(env))
}
err = bundle.Apply(ctx, b, ms)
if err != nil {
return err
}
cmd.SetContext(bundle.Context(ctx, b))
return nil return nil
} }