diff --git a/project/config.go b/project/config.go index 344a2221a..9936f4792 100644 --- a/project/config.go +++ b/project/config.go @@ -50,6 +50,12 @@ type Config struct { // created by administrator users or admin-level automation, like Terraform // and/or SCIM provisioning. Assertions *Assertions `json:"assertions,omitempty"` + + // Environments contain this project's defined environments. + // They can be used to differentiate settings and resources between + // development, staging, production, etc. + // The project assumes an environment named "development" is always defined. + Environments map[string]Environment `json:"environments"` } func (c Config) IsDevClusterDefined() bool { diff --git a/project/environment.go b/project/environment.go new file mode 100644 index 000000000..91ab2589e --- /dev/null +++ b/project/environment.go @@ -0,0 +1,41 @@ +package project + +import ( + "os" + + "github.com/spf13/cobra" +) + +const bricksEnv = "BRICKS_ENV" + +const defaultEnvironment = "development" + +// Workspace defines configurables at the workspace level. +type Workspace struct { + Profile string `json:"profile,omitempty"` +} + +// Environment defines all configurables for a single environment. +type Environment struct { + Workspace Workspace `json:"workspace"` +} + +// 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(bricksEnv) + if value != "" { + return + } + + return defaultEnvironment +} diff --git a/project/environment_test.go b/project/environment_test.go new file mode 100644 index 000000000..1b8a997dd --- /dev/null +++ b/project/environment_test.go @@ -0,0 +1,38 @@ +package project + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func TestEnvironmentFromCommand(t *testing.T) { + var cmd cobra.Command + cmd.Flags().String("environment", "", "specify environment") + cmd.Flags().Set("environment", "env-from-arg") + t.Setenv(bricksEnv, "") + + value := getEnvironment(&cmd) + assert.Equal(t, "env-from-arg", value) +} + +func TestEnvironmentFromEnvironment(t *testing.T) { + var cmd cobra.Command + cmd.Flags().String("environment", "", "specify environment") + cmd.Flags().Set("environment", "") + t.Setenv(bricksEnv, "env-from-env") + + value := getEnvironment(&cmd) + assert.Equal(t, "env-from-env", value) +} + +func TestEnvironmentDefault(t *testing.T) { + var cmd cobra.Command + cmd.Flags().String("environment", "", "specify environment") + cmd.Flags().Set("environment", "") + t.Setenv(bricksEnv, "") + + value := getEnvironment(&cmd) + assert.Equal(t, defaultEnvironment, value) +} diff --git a/project/project.go b/project/project.go index cdf2ade67..9c66ea672 100644 --- a/project/project.go +++ b/project/project.go @@ -17,10 +17,12 @@ type project struct { mu sync.Mutex root string + env string - config *Config - wsc *workspaces.WorkspacesClient - me *scim.User + config *Config + environment *Environment + wsc *workspaces.WorkspacesClient + me *scim.User } // Configure is used as a PreRunE function for all commands that @@ -32,7 +34,7 @@ func Configure(cmd *cobra.Command, args []string) error { return err } - ctx, err := Initialize(cmd.Context(), root) + ctx, err := Initialize(cmd.Context(), root, getEnvironment(cmd)) if err != nil { return err } @@ -44,21 +46,30 @@ func Configure(cmd *cobra.Command, args []string) error { // Placeholder to use as unique key in context.Context. var projectKey int -// Initialize loads a project configuration given a root. +// Initialize loads a project configuration given a root and environment. // It stores the project on a new context. // The project is available through the `Get()` function. -func Initialize(ctx context.Context, root string) (context.Context, error) { +func Initialize(ctx context.Context, root, env string) (context.Context, error) { config, err := loadProjectConf(root) if err != nil { return nil, err } - p := project{ - root: root, - config: &config, + // Confirm that the specified environment is valid. + environment, ok := config.Environments[env] + if !ok { + return nil, fmt.Errorf("environment [%s] not defined", env) } - p.wsc = workspaces.New(&databricks.Config{Profile: config.Profile}) + p := project{ + root: root, + env: env, + + config: &config, + environment: &environment, + } + + p.wsc = workspaces.New(&databricks.Config{Profile: environment.Workspace.Profile}) return context.WithValue(ctx, &projectKey, &p), nil } @@ -81,6 +92,14 @@ func (p *project) Root() string { return p.root } +func (p *project) Config() Config { + return *p.config +} + +func (p *project) Environment() Environment { + return *p.environment +} + func (p *project) Me() (*scim.User, error) { p.mu.Lock() defer p.mu.Unlock() diff --git a/project/project_test.go b/project/project_test.go index 8d01fe3de..0698a71a6 100644 --- a/project/project_test.go +++ b/project/project_test.go @@ -9,7 +9,7 @@ import ( ) func TestProjectInitialize(t *testing.T) { - ctx, err := Initialize(context.Background(), "./testdata") + ctx, err := Initialize(context.Background(), "./testdata", defaultEnvironment) require.NoError(t, err) assert.Equal(t, Get(ctx).config.Name, "dev") } diff --git a/project/testdata/databricks.yml b/project/testdata/databricks.yml index 3b8eb81fb..77a126e9f 100644 --- a/project/testdata/databricks.yml +++ b/project/testdata/databricks.yml @@ -1,4 +1,7 @@ name: dev profile: demo dev_cluster: - cluster_name: Shared Autoscaling \ No newline at end of file + cluster_name: Shared Autoscaling + +environments: + development: