From 3b351d3b00e83588e4611bbb4b7cfd01bb18cd71 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Mon, 21 Nov 2022 15:39:53 +0100 Subject: [PATCH] Add command that writes the materialized bundle configuration to stdout (#95) Used to inspect the bundle configuration after loading and merging all files. Once we add variable interpolation this command could show the result after interpolation as well. Each of the mutations to this configuration is observable, so we could add a mode that writes each of the intermediate versions to disk for even more fine grained introspection. --- bundle/bundle.go | 29 +++++++++++++++++++++++++++++ bundle/context.go | 24 ++++++++++++++++++++++++ bundle/context_test.go | 24 ++++++++++++++++++++++++ cmd/bundle/environment.go | 31 +++++++++++++++++++++++++++++++ cmd/bundle/root.go | 36 ++++++++++++++++++++++++++++++++++++ cmd/bundle/validate.go | 28 ++++++++++++++++++++++++++++ main.go | 1 + 7 files changed, 173 insertions(+) create mode 100644 bundle/context.go create mode 100644 bundle/context_test.go create mode 100644 cmd/bundle/environment.go create mode 100644 cmd/bundle/root.go create mode 100644 cmd/bundle/validate.go 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"