mirror of https://github.com/databricks/cli.git
feat: Read variables from file
This commit is contained in:
parent
8f34fc7961
commit
a794490b64
|
@ -260,6 +260,22 @@ func (r *Root) InitializeVariables(vars []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initializes variables parsed from variable file
|
||||||
|
// Variables can have any type of value, including complex types
|
||||||
|
func (r *Root) InitializeAnyTypeVariables(vars map[string]any) error {
|
||||||
|
for name, val := range vars {
|
||||||
|
if _, ok := r.Variables[name]; !ok {
|
||||||
|
return fmt.Errorf("variable %s has not been defined", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.Variables[name].Set(val)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to assign %s to %s: %s", val, name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Root) Merge(other *Root) error {
|
func (r *Root) Merge(other *Root) error {
|
||||||
// Merge dynamic configuration values.
|
// Merge dynamic configuration values.
|
||||||
return r.Mutate(func(root dyn.Value) (dyn.Value, error) {
|
return r.Mutate(func(root dyn.Value) (dyn.Value, error) {
|
||||||
|
|
|
@ -51,6 +51,56 @@ func TestInitializeVariables(t *testing.T) {
|
||||||
assert.Equal(t, "456", (root.Variables["bar"].Value))
|
assert.Equal(t, "456", (root.Variables["bar"].Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInitializeAnyTypeVariables(t *testing.T) {
|
||||||
|
root := &Root{
|
||||||
|
Variables: map[string]*variable.Variable{
|
||||||
|
"string": {
|
||||||
|
Default: "default",
|
||||||
|
Description: "string variable",
|
||||||
|
},
|
||||||
|
"int": {
|
||||||
|
Default: 0,
|
||||||
|
Description: "int variable",
|
||||||
|
},
|
||||||
|
"complex": {
|
||||||
|
Default: []map[string]int{{"a": 1}, {"b": 2}},
|
||||||
|
Description: "complex variable",
|
||||||
|
Type: variable.VariableTypeComplex,
|
||||||
|
},
|
||||||
|
"unused": {
|
||||||
|
Default: "should remain default",
|
||||||
|
Description: "unused variable",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := root.InitializeAnyTypeVariables(map[string]any{
|
||||||
|
"string": "value",
|
||||||
|
"int": 1,
|
||||||
|
"complex": []map[string]int{{"c": 3}},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "value", (root.Variables["string"].Value))
|
||||||
|
assert.Equal(t, 1, (root.Variables["int"].Value))
|
||||||
|
assert.Equal(t, []map[string]int{{"c": 3}}, (root.Variables["complex"].Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitializeAnyTypeVariablesUndeclared(t *testing.T) {
|
||||||
|
root := &Root{
|
||||||
|
Variables: map[string]*variable.Variable{
|
||||||
|
"string": {
|
||||||
|
Default: "default",
|
||||||
|
Description: "string variable",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := root.InitializeAnyTypeVariables(map[string]any{
|
||||||
|
"not_declared": "value",
|
||||||
|
})
|
||||||
|
assert.ErrorContains(t, err, "variable not_declared has not been defined")
|
||||||
|
}
|
||||||
|
|
||||||
func TestInitializeVariablesWithAnEqualSignInValue(t *testing.T) {
|
func TestInitializeVariablesWithAnEqualSignInValue(t *testing.T) {
|
||||||
root := &Root{
|
root := &Root{
|
||||||
Variables: map[string]*variable.Variable{
|
Variables: map[string]*variable.Variable{
|
||||||
|
|
|
@ -36,9 +36,11 @@ type Variable struct {
|
||||||
// This field stores the resolved value for the variable. The variable are
|
// This field stores the resolved value for the variable. The variable are
|
||||||
// resolved in the following priority order (from highest to lowest)
|
// resolved in the following priority order (from highest to lowest)
|
||||||
//
|
//
|
||||||
// 1. Command line flag. For example: `--var="foo=bar"`
|
// 1. Command line flag, one of these is used
|
||||||
// 2. Target variable. eg: BUNDLE_VAR_foo=bar
|
// a. Variable value obtained from arguments, example: `--var="foo=bar"`
|
||||||
// 3. Default value as defined in the applicable environments block
|
// b. Variable value obtained from the file, example: `--vars-file-path="/path/to/file"`
|
||||||
|
// 2. Environment variable. eg: BUNDLE_VAR_foo=bar
|
||||||
|
// 3. Default value as defined in the applicable targets block
|
||||||
// 4. Default value defined in variable definition
|
// 4. Default value defined in variable definition
|
||||||
// 5. Throw error, since if no default value is defined, then the variable
|
// 5. Throw error, since if no default value is defined, then the variable
|
||||||
// is required
|
// is required
|
||||||
|
|
|
@ -2,6 +2,9 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/databricks/cli/bundle"
|
"github.com/databricks/cli/bundle"
|
||||||
"github.com/databricks/cli/cmd/root"
|
"github.com/databricks/cli/cmd/root"
|
||||||
|
@ -16,6 +19,30 @@ func configureVariables(cmd *cobra.Command, b *bundle.Bundle, variables []string
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureVariablesFromFile(cmd *cobra.Command, b *bundle.Bundle, filePath string) diag.Diagnostics {
|
||||||
|
return bundle.ApplyFunc(cmd.Context(), b, func(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
|
f, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(fmt.Errorf("failed to read variables file: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := map[string]any{}
|
||||||
|
err = json.Unmarshal(f, &vars)
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(fmt.Errorf("failed to parse variables file: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vars) > 0 {
|
||||||
|
err = b.Config.InitializeAnyTypeVariables(vars)
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func ConfigureBundleWithVariables(cmd *cobra.Command) (*bundle.Bundle, diag.Diagnostics) {
|
func ConfigureBundleWithVariables(cmd *cobra.Command) (*bundle.Bundle, diag.Diagnostics) {
|
||||||
// Load bundle config and apply target
|
// Load bundle config and apply target
|
||||||
b, diags := root.MustConfigureBundle(cmd)
|
b, diags := root.MustConfigureBundle(cmd)
|
||||||
|
@ -27,9 +54,20 @@ func ConfigureBundleWithVariables(cmd *cobra.Command) (*bundle.Bundle, diag.Diag
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return b, diag.FromErr(err)
|
return b, diag.FromErr(err)
|
||||||
}
|
}
|
||||||
|
variableFilePath, err := cmd.Flags().GetString("vars-file-path")
|
||||||
|
if err != nil {
|
||||||
|
return b, diag.FromErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize variables by assigning them values passed as command line flags
|
if len(variables) > 0 && variableFilePath != "" {
|
||||||
diags = diags.Extend(configureVariables(cmd, b, variables))
|
return b, diag.Errorf("cannot specify both --var and --vars-file-path flags")
|
||||||
|
} else if len(variables) > 0 {
|
||||||
|
// Initialize variables by assigning them values passed as command line flags
|
||||||
|
diags = diags.Extend(configureVariables(cmd, b, variables))
|
||||||
|
} else if variableFilePath != "" {
|
||||||
|
// Initialize variables by loading them from a file
|
||||||
|
diags = diags.Extend(configureVariablesFromFile(cmd, b, variableFilePath))
|
||||||
|
}
|
||||||
|
|
||||||
return b, diags
|
return b, diags
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,5 @@ import (
|
||||||
|
|
||||||
func initVariableFlag(cmd *cobra.Command) {
|
func initVariableFlag(cmd *cobra.Command) {
|
||||||
cmd.PersistentFlags().StringSlice("var", []string{}, `set values for variables defined in bundle config. Example: --var="foo=bar"`)
|
cmd.PersistentFlags().StringSlice("var", []string{}, `set values for variables defined in bundle config. Example: --var="foo=bar"`)
|
||||||
|
cmd.PersistentFlags().String("vars-file-path", "", `file path to a JSON file containing variables. Example: --vars-file-path="/path/to/vars.json"`)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue