Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for variables in bundle config #359

Merged
merged 30 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d2b37de
Add support for variables in bundle config
shreyas-goenka Apr 25, 2023
02feb41
Merge remote-tracking branch 'origin' into variables
shreyas-goenka May 1, 2023
1902008
wip
shreyas-goenka May 1, 2023
caf9e5c
first version
shreyas-goenka May 1, 2023
1121ff3
added some tests
shreyas-goenka May 1, 2023
8ef243c
cleanup and adding black box tests
shreyas-goenka May 2, 2023
9d39680
small cleanup
shreyas-goenka May 2, 2023
782a1f7
Update bundle/config/mutator/set_variables.go
shreyas-goenka May 2, 2023
7f9cf03
Update bundle/config/variables.go
shreyas-goenka May 2, 2023
ad5df41
Update bundle/config/variables.go
shreyas-goenka May 2, 2023
14d1ca9
remove type from variables
shreyas-goenka May 4, 2023
0ab5bf6
Merge remote-tracking branch 'origin' into variables
shreyas-goenka May 4, 2023
74f8641
define var as a persistant flag
shreyas-goenka May 4, 2023
8b0efed
fmt and created package variables
shreyas-goenka May 4, 2023
4f2307d
-
shreyas-goenka May 4, 2023
8b3f602
-
shreyas-goenka May 4, 2023
58f702e
Merge remote-tracking branch 'origin' into variables
shreyas-goenka May 10, 2023
3968268
register alias for variables at start
shreyas-goenka May 10, 2023
d65322d
error message more descriptive
shreyas-goenka May 10, 2023
a81e154
added omitempty to variables field
shreyas-goenka May 10, 2023
a6518d9
using splitN in variable initialization
shreyas-goenka May 10, 2023
58c7205
lint
shreyas-goenka May 10, 2023
6f3ae7a
return err
shreyas-goenka May 10, 2023
d49272e
add todo for cmdio to prompt for variable values
shreyas-goenka May 10, 2023
2a8fde0
remove hiding var flag from bundle schema command
shreyas-goenka May 10, 2023
f7ad556
Add unit tests for set_variables mutator
shreyas-goenka May 10, 2023
d6615ac
use t.setenv
shreyas-goenka May 15, 2023
d42a849
rename package
shreyas-goenka May 15, 2023
b534f86
move variable flag defination
shreyas-goenka May 15, 2023
f988ff4
nit
shreyas-goenka May 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bundle/config/interpolation/interpolation.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (a *accumulator) Resolve(path string, seenPaths []string, fns ...LookupFunc
// fetch the string node to resolve
field, ok := a.strings[path]
if !ok {
return fmt.Errorf("could not find string field with path %s", path)
return fmt.Errorf("could not resolve reference %s", path)
}

// return early if the string field has no variables to interpolate
Expand Down
55 changes: 55 additions & 0 deletions bundle/config/mutator/load_variables_from_environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package mutator

import (
"context"
"fmt"
"os"
"strings"

"github.com/databricks/bricks/bundle"
)

const bundleVarPrefix = "BUNDLE_VAR_"

type loadVariablesFromEnvironment struct{}

func LoadVariablesFromEnvionment() bundle.Mutator {
return &loadVariablesFromEnvironment{}
}

func (m *loadVariablesFromEnvironment) Name() string {
return "LoadVariablesFromEnvionment"
}

func parseBundleVar(s string) (key string, val string, err error) {
// env var name show have the bundle prefix to parse it
if !strings.HasPrefix(s, bundleVarPrefix) {
return "", "", fmt.Errorf("environment variable %s does not have expected prefix %s", s, bundleVarPrefix)
}

// split the env var entry at '=' to get the variable name and variable value
components := strings.Split(s, "=")
if len(components) != 2 {
return "", "", fmt.Errorf("unexpected format for environment variable: %s", s)
}
key = strings.ToLower(
strings.TrimPrefix(components[0], bundleVarPrefix),
)
val = components[1]
return
}

func (m *loadVariablesFromEnvironment) Apply(ctx context.Context, b *bundle.Bundle) ([]bundle.Mutator, error) {
envVars := os.Environ()
for _, envVar := range envVars {
if !strings.HasPrefix(envVar, bundleVarPrefix) {
continue
}
varName, varVal, err := parseBundleVar(envVar)
if err != nil {
return nil, err
}
b.Config.Variables[varName] = varVal
}
return nil, nil
}
27 changes: 27 additions & 0 deletions bundle/config/mutator/load_variables_from_environment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package mutator

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestVariablesParseBundleVar(t *testing.T) {
s := `BUNDLE_VAR_FOO=Bar`
key, val, err := parseBundleVar(s)
assert.NoError(t, err)
assert.Equal(t, "foo", key)
assert.Equal(t, "Bar", val)
}

func TestVariablesParseBundleVarOnInvalidPrefix(t *testing.T) {
s := `INVALID_PREFIX_FOO=Bar`
_, _, err := parseBundleVar(s)
assert.ErrorContains(t, err, "environment variable INVALID_PREFIX_FOO=Bar does not have expected prefix BUNDLE_VAR_")
}

func TestVariablesParseBundleVarOnInvalidFormat(t *testing.T) {
s := `BUNDLE_VAR_FOO=Bar=bar`
_, _, err := parseBundleVar(s)
assert.ErrorContains(t, err, "unexpected format for environment variable: BUNDLE_VAR_FOO=Bar=bar")
}
4 changes: 4 additions & 0 deletions bundle/config/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ type Root struct {
// It is set when loading `bundle.yml`.
Path string `json:"-" bundle:"readonly"`

// Contains user defined variables, either defined in the bundle config or
// loaded through env vars
Variables map[string]string `json:"variables"`

// Bundle contains details about this bundle, such as its name,
// version of the spec (TODO), default cluster, default warehouse, etc.
Bundle Bundle `json:"bundle"`
Expand Down
2 changes: 2 additions & 0 deletions bundle/phases/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ func Initialize() bundle.Mutator {
mutator.DefineDefaultWorkspaceRoot(),
mutator.ExpandWorkspaceRoot(),
mutator.DefineDefaultWorkspacePaths(),
mutator.LoadVariablesFromEnvionment(),
interpolation.Interpolate(
interpolation.IncludeLookupsInPath("bundle"),
interpolation.IncludeLookupsInPath("workspace"),
interpolation.IncludeLookupsInPath("variables"),
),
mutator.TranslatePaths(),
terraform.Initialize(),
Expand Down
7 changes: 7 additions & 0 deletions bundle/tests/variables/bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
variables:
a: foo
b: bar


bundle:
name: "name: ${variables.a} ${variables.b} ${variables.c}"
42 changes: 42 additions & 0 deletions bundle/tests/variables_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package config_tests

import (
"context"
"testing"

"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config/interpolation"
"github.com/databricks/bricks/bundle/config/mutator"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestVariables(t *testing.T) {
t.Setenv("BUNDLE_VAR_B", "bar_from_env")
t.Setenv("BUNDLE_VAR_C", "world")
b := load(t, "./variables")
err := bundle.Apply(context.Background(), b, []bundle.Mutator{
mutator.LoadVariablesFromEnvionment(),
interpolation.Interpolate(
interpolation.IncludeLookupsInPath("bundle"),
interpolation.IncludeLookupsInPath("workspace"),
interpolation.IncludeLookupsInPath("variables"),
)})
require.NoError(t, err)
assert.Equal(t, "name: foo bar_from_env world", b.Config.Bundle.Name)
assert.Equal(t, "foo", b.Config.Variables["a"])
assert.Equal(t, "bar_from_env", b.Config.Variables["b"])
assert.Equal(t, "world", b.Config.Variables["c"])
}

func TestVariablesWhenUndefinedVarIsUsed(t *testing.T) {
b := load(t, "./variables")
err := bundle.Apply(context.Background(), b, []bundle.Mutator{
mutator.LoadVariablesFromEnvionment(),
interpolation.Interpolate(
interpolation.IncludeLookupsInPath("bundle"),
interpolation.IncludeLookupsInPath("workspace"),
interpolation.IncludeLookupsInPath("variables"),
)})
assert.ErrorContains(t, err, "could not resolve reference variables.c")
}