diff --git a/bundle/bundle.go b/bundle/bundle.go index 679a04f9..5c74c541 100644 --- a/bundle/bundle.go +++ b/bundle/bundle.go @@ -1,15 +1,21 @@ package bundle import ( + "context" "path/filepath" "github.com/databricks/bricks/bundle/config" + "github.com/databricks/bricks/bundle/config/mutator" ) type Bundle struct { Config config.Root } +func (b *Bundle) MutateForEnvironment(env string) error { + return mutator.Apply(&b.Config, mutator.DefaultMutatorsForEnvironment(env)) +} + func Load(path string) (*Bundle, error) { bundle := &Bundle{ Config: config.Root{ @@ -22,3 +28,26 @@ func Load(path string) (*Bundle, error) { } return bundle, nil } + +func LoadFromRoot() (*Bundle, error) { + root, err := getRoot() + if err != nil { + return nil, err + } + + return Load(root) +} + +func ConfigureForEnvironment(ctx context.Context, env string) (context.Context, error) { + b, err := LoadFromRoot() + if err != nil { + return nil, err + } + + err = b.MutateForEnvironment(env) + if err != nil { + return nil, err + } + + return Context(ctx, b), nil +} diff --git a/bundle/context.go b/bundle/context.go new file mode 100644 index 00000000..416d6e45 --- /dev/null +++ b/bundle/context.go @@ -0,0 +1,24 @@ +package bundle + +import ( + "context" +) + +// Placeholder to use as unique key in context.Context. +var bundleKey int + +// Context stores the specified bundle on a new context. +// The bundle is available through the `Get()` function. +func Context(ctx context.Context, b *Bundle) context.Context { + return context.WithValue(ctx, &bundleKey, b) +} + +// Get returns the bundle as configured on the context. +// It panics if it isn't configured. +func Get(ctx context.Context) *Bundle { + bundle, ok := ctx.Value(&bundleKey).(*Bundle) + if !ok { + panic("context not configured with bundle") + } + return bundle +} diff --git a/bundle/context_test.go b/bundle/context_test.go new file mode 100644 index 00000000..3a0f159d --- /dev/null +++ b/bundle/context_test.go @@ -0,0 +1,24 @@ +package bundle + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetPanics(t *testing.T) { + defer func() { + r := recover() + require.NotNil(t, r, "The function did not panic") + assert.Equal(t, r, "context not configured with bundle") + }() + + Get(context.Background()) +} + +func TestGetSuccess(t *testing.T) { + ctx := Context(context.Background(), &Bundle{}) + require.NotNil(t, Get(ctx)) +} diff --git a/cmd/bundle/environment.go b/cmd/bundle/environment.go new file mode 100644 index 00000000..d53328f5 --- /dev/null +++ b/cmd/bundle/environment.go @@ -0,0 +1,31 @@ +package bundle + +import ( + "os" + + "github.com/spf13/cobra" +) + +const envName = "DATABRICKS_BUNDLE_ENV" + +const defaultEnvironment = "default" + +// getEnvironment returns the name of the environment to operate in. +func getEnvironment(cmd *cobra.Command) (value string) { + // The command line flag takes precedence. + flag := cmd.Flag("environment") + if flag != nil { + value = flag.Value.String() + if value != "" { + return + } + } + + // If it's not set, use the environment variable. + value = os.Getenv(envName) + if value != "" { + return + } + + return defaultEnvironment +} diff --git a/cmd/bundle/root.go b/cmd/bundle/root.go new file mode 100644 index 00000000..7326f1bb --- /dev/null +++ b/cmd/bundle/root.go @@ -0,0 +1,36 @@ +package bundle + +import ( + "github.com/databricks/bricks/bundle" + "github.com/databricks/bricks/cmd/root" + "github.com/spf13/cobra" +) + +// rootCmd represents the root command for the bundle subcommand. +var rootCmd = &cobra.Command{ + Use: "bundle", + Short: "Databricks Application Bundles", +} + +// ConfigureBundle loads the bundle configuration +// and configures it on the command's context. +func ConfigureBundle(cmd *cobra.Command, args []string) error { + ctx, err := bundle.ConfigureForEnvironment(cmd.Context(), getEnvironment(cmd)) + if err != nil { + return err + } + + cmd.SetContext(ctx) + return nil +} + +func AddCommand(cmd *cobra.Command) { + rootCmd.AddCommand(cmd) +} + +func init() { + // All bundle commands take an "environment" parameter. + rootCmd.PersistentFlags().StringP("environment", "e", "", "Environment to use") + // Add to top level root. + root.RootCmd.AddCommand(rootCmd) +} diff --git a/cmd/bundle/validate.go b/cmd/bundle/validate.go new file mode 100644 index 00000000..af4416c3 --- /dev/null +++ b/cmd/bundle/validate.go @@ -0,0 +1,28 @@ +package bundle + +import ( + "encoding/json" + + "github.com/databricks/bricks/bundle" + "github.com/spf13/cobra" +) + +var validate = &cobra.Command{ + Use: "validate", + Short: "Validate configuration", + + PreRunE: ConfigureBundle, + RunE: func(cmd *cobra.Command, args []string) error { + b := bundle.Get(cmd.Context()) + buf, err := json.MarshalIndent(b.Config, "", " ") + if err != nil { + return err + } + cmd.OutOrStdout().Write(buf) + return nil + }, +} + +func init() { + AddCommand(validate) +} diff --git a/main.go b/main.go index 805c5bb6..a03285e4 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( _ "github.com/databricks/bricks/cmd/api" + _ "github.com/databricks/bricks/cmd/bundle" _ "github.com/databricks/bricks/cmd/configure" _ "github.com/databricks/bricks/cmd/fs" _ "github.com/databricks/bricks/cmd/init"