Skip to content

Commit

Permalink
feat: imported vars as override values (#423)
Browse files Browse the repository at this point in the history
Co-authored-by: UncleGedd <42304551+UncleGedd@users.noreply.github.com>
  • Loading branch information
decleaver and UncleGedd committed Feb 22, 2024
1 parent 5568be0 commit 266b76c
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 10 deletions.
28 changes: 28 additions & 0 deletions docs/overrides.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,34 @@ The `value` is the value to set at the `path`. Values can be simple values such
value:
customAnnotation: "customValue"
```
If using a variable that has been [exported](../README.md#importingexporting-variables) from another package, that variable can also be used to set a value, using the syntax `${...}`. In the example below the `COLOR` variable is being used to set the `podinfo.ui.color` value.
```
kind: UDSBundle
metadata:
name: export-vars
description: Example for using an imported variable to set an overrides value
version: 0.0.1

packages:
- name: output-var
repository: localhost:888/output-var
ref: 0.0.1
exports:
- name: COLOR

- name: helm-overrides
path: "../../packages/helm"
ref: 0.0.1

overrides:
podinfo-component:
unicorn-podinfo:
values:
- path: "podinfo.replicaCount"
value: 1
- path: "podinfo.ui.color"
value: ${COLOR}
```
### Variables
Variables are similar to [values](#values) in that they allow users to override values in a Zarf package component's underlying Helm chart; they also share a similar syntax. However, unlike `values`, `variables` can be overridden at deploy time. For example, consider the following `variables`:
Expand Down
50 changes: 42 additions & 8 deletions src/pkg/bundle/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/AlecAivazis/survey/v2"
Expand All @@ -30,6 +31,9 @@ import (
// ZarfOverrideMap is a map of Zarf packages -> components -> Helm charts -> values
type ZarfOverrideMap map[string]map[string]map[string]interface{}

// templatedVarRegex is the regex for templated variables
var templatedVarRegex = regexp.MustCompile(`\${([^}]+)}`)

// Deploy deploys a bundle
//
// : create a new provider
Expand Down Expand Up @@ -166,7 +170,7 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle, zarfPackag
SetVariables: pkgVars,
}

valuesOverrides, err := b.loadChartOverrides(pkg)
valuesOverrides, err := b.loadChartOverrides(pkg, pkgVars)
if err != nil {
return err
}
Expand Down Expand Up @@ -201,6 +205,10 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle, zarfPackag
// save exported vars
pkgExportedVars := make(map[string]string)
for _, exp := range pkg.Exports {
// ensure if variable exists in package
if _, ok := pkgCfg.SetVariableMap[exp.Name]; !ok {
return fmt.Errorf("cannot export variable %s because it does not exist in package %s", exp.Name, pkg.Name)
}
pkgExportedVars[strings.ToUpper(exp.Name)] = pkgCfg.SetVariableMap[exp.Name].Value
}
bundleExportedVars[pkg.Name] = pkgExportedVars
Expand Down Expand Up @@ -281,7 +289,7 @@ func (b *Bundle) confirmBundleDeploy() (confirm bool) {
}

// loadChartOverrides converts a helm path to a ValuesOverridesMap config for Zarf
func (b *Bundle) loadChartOverrides(pkg types.Package) (ZarfOverrideMap, error) {
func (b *Bundle) loadChartOverrides(pkg types.Package, pkgVars map[string]string) (ZarfOverrideMap, error) {

// Create a nested map to hold the values
overrideMap := make(map[string]map[string]*values.Options)
Expand All @@ -290,7 +298,7 @@ func (b *Bundle) loadChartOverrides(pkg types.Package) (ZarfOverrideMap, error)
for componentName, component := range pkg.Overrides {
for chartName, chart := range component {
chartCopy := chart // Create a copy of the chart
err := b.processOverrideValues(&overrideMap, &chartCopy.Values, componentName, chartName)
err := b.processOverrideValues(&overrideMap, &chartCopy.Values, componentName, chartName, pkgVars)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -332,10 +340,10 @@ func (b *Bundle) loadChartOverrides(pkg types.Package) (ZarfOverrideMap, error)
}

// processOverrideValues processes a bundles values overrides and adds them to the override map
func (b *Bundle) processOverrideValues(overrideMap *map[string]map[string]*values.Options, values *[]types.BundleChartValue, componentName string, chartName string) error {
func (b *Bundle) processOverrideValues(overrideMap *map[string]map[string]*values.Options, values *[]types.BundleChartValue, componentName string, chartName string, pkgVars map[string]string) error {
for _, v := range *values {
// Add the override to the map, or return an error if the path is invalid
if err := addOverrideValue(*overrideMap, componentName, chartName, v.Path, v.Value); err != nil {
if err := addOverrideValue(*overrideMap, componentName, chartName, v.Path, v.Value, pkgVars); err != nil {
return err
}
}
Expand All @@ -350,7 +358,7 @@ func (b *Bundle) processOverrideVariables(overrideMap *map[string]map[string]*va
v.Name = strings.ToUpper(v.Name)
// check for override in env vars
if envVarOverride, exists := os.LookupEnv(strings.ToUpper(config.EnvVarPrefix + v.Name)); exists {
if err := addOverrideValue(*overrideMap, componentName, chartName, v.Path, envVarOverride); err != nil {
if err := addOverrideValue(*overrideMap, componentName, chartName, v.Path, envVarOverride, nil); err != nil {
return err
}
continue
Expand All @@ -373,7 +381,7 @@ func (b *Bundle) processOverrideVariables(overrideMap *map[string]map[string]*va
}

// Add the override to the map, or return an error if the path is invalid
if err := addOverrideValue(*overrideMap, componentName, chartName, v.Path, overrideVal); err != nil {
if err := addOverrideValue(*overrideMap, componentName, chartName, v.Path, overrideVal, nil); err != nil {
return err
}

Expand All @@ -382,7 +390,7 @@ func (b *Bundle) processOverrideVariables(overrideMap *map[string]map[string]*va
}

// addOverrideValue adds a value to a ZarfOverrideMap
func addOverrideValue(overrides map[string]map[string]*values.Options, component string, chart string, valuePath string, value interface{}) error {
func addOverrideValue(overrides map[string]map[string]*values.Options, component string, chart string, valuePath string, value interface{}, pkgVars map[string]string) error {
// Create the component map if it doesn't exist
if _, ok := overrides[component]; !ok {
overrides[component] = make(map[string]*values.Options)
Expand All @@ -408,6 +416,9 @@ func addOverrideValue(overrides map[string]map[string]*values.Options, component
}
// use JSONValues because we can easily marshal the YAML to JSON and Helm understands it
jsonVals := fmt.Sprintf("%s=[%s]", valuePath, strings.Join(jsonStrs, ","))
if pkgVars != nil {
jsonVals = setTemplatedVariables(jsonVals, pkgVars)
}
overrides[component][chart].JSONValues = append(overrides[component][chart].JSONValues, jsonVals)
case map[string]interface{}:
// handle objects by parsing them as json and appending to Options.JSONValues
Expand All @@ -417,11 +428,34 @@ func addOverrideValue(overrides map[string]map[string]*values.Options, component
}
// use JSONValues because we can easily marshal the YAML to JSON and Helm understands it
val := fmt.Sprintf("%s=%s", valuePath, j)
if pkgVars != nil {
val = setTemplatedVariables(val, pkgVars)
}
overrides[component][chart].JSONValues = append(overrides[component][chart].JSONValues, val)
default:
// Check for any templated variables if pkgVars set
if pkgVars != nil {
templatedVariable := fmt.Sprintf("%v", v)
value = setTemplatedVariables(templatedVariable, pkgVars)
}
// handle default case of simple values like strings and numbers
helmVal := fmt.Sprintf("%s=%v", valuePath, value)
overrides[component][chart].Values = append(overrides[component][chart].Values, helmVal)
}
return nil
}

// setTemplatedVariables sets the value for the templated variables
func setTemplatedVariables(templatedVariables string, pkgVars map[string]string) string {
// Use ReplaceAllStringFunc to handle all occurrences of templated variables
replacedValue := templatedVarRegex.ReplaceAllStringFunc(templatedVariables, func(match string) string {
// returns slice with the templated variable and the variable name
variableName := templatedVarRegex.FindStringSubmatch(match)[1]
// If we have a templated variable, get the value from pkgVars
if varValue, ok := pkgVars[variableName]; ok {
return varValue
}
return fmt.Sprintf("${%s_not_found}", variableName)
})
return replacedValue
}
46 changes: 46 additions & 0 deletions src/test/bundles/12-exported-pkg-vars/uds-bundle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
kind: UDSBundle
metadata:
name: export-vars
description: testing a bundle using exported vars from zarf package
version: 0.0.1

packages:
- name: output-var
repository: localhost:888/output-var
ref: 0.0.1
exports:
- name: COLOR
- name: ANNOTATION
- name: DEFENSE
- name: BOOL

- name: helm-overrides
path: "src/test/packages/helm"
ref: 0.0.1
imports:
- name: COLOR
package: output-var
- name: ANNOTATION
package: output-var

overrides:
podinfo-component:
unicorn-podinfo:
values:
- path: "podinfo.replicaCount"
value: 1
- path: "podinfo.ui.color"
value: ${COLOR}
- path: podinfo.podAnnotations
value:
customAnnotation: ${COLOR}${ANNOTATION}
- path: "podinfo.tolerations"
value:
- key: "unicorn"
operator: "Equal"
value: ${DEFENSE}
effect: "NoSchedule"
- key: "uds"
operator: "Equal"
value: ${BOOL}
effect: "NoSchedule"
2 changes: 1 addition & 1 deletion src/test/e2e/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func createLocal(t *testing.T, bundlePath string, arch string) {
require.NoError(t, err)
}

func createLocalError(t *testing.T, bundlePath string, arch string) (stderr string) {
func createLocalError(bundlePath string, arch string) (stderr string) {
cmd := strings.Split(fmt.Sprintf("create %s --insecure --confirm -a %s", bundlePath, arch), " ")
_, stderr, _ = e2e.UDS(cmd...)
return stderr
Expand Down
45 changes: 44 additions & 1 deletion src/test/e2e/variable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestBundleVariables(t *testing.T) {

// Test with bad variable name in import
bundleDir = "src/test/bundles/02-simple-vars/import-all-bad-name"
stderr = createLocalError(t, bundleDir, e2e.Arch)
stderr = createLocalError(bundleDir, e2e.Arch)
require.Contains(t, stderr, "does not have a matching export")

// Test name collisions with exported variables
Expand Down Expand Up @@ -234,3 +234,46 @@ func TestVariablePrecedence(t *testing.T) {

remove(t, bundlePath)
}

func TestZarfPackageExportVarsAsGlobalBundleVars(t *testing.T) {
deployZarfInit(t)
zarfPkgPath1 := "src/test/packages/no-cluster/output-var"
e2e.CreateZarfPkg(t, zarfPkgPath1, false)

e2e.SetupDockerRegistry(t, 888)
defer e2e.TeardownRegistry(t, 888)

pkg := filepath.Join(zarfPkgPath1, fmt.Sprintf("zarf-package-output-var-%s-0.0.1.tar.zst", e2e.Arch))
zarfPublish(t, pkg, "localhost:888")

e2e.HelmDepUpdate(t, "src/test/packages/helm/unicorn-podinfo")
e2e.CreateZarfPkg(t, "src/test/packages/helm", false)
bundleDir := "src/test/bundles/12-exported-pkg-vars"
bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-export-vars-%s-0.0.1.tar.zst", e2e.Arch))

createLocal(t, bundleDir, e2e.Arch)
deploy(t, bundlePath)

// check templated variables overrides in values
cmd := strings.Split("zarf tools kubectl get deploy -n podinfo unicorn-podinfo -o=jsonpath='{.spec.template.spec.containers[0].env[?(@.name==\"PODINFO_UI_COLOR\")].value}'", " ")
outputUIColor, _, err := e2e.UDS(cmd...)
require.Equal(t, "'orange'", outputUIColor)
require.NoError(t, err)

// check multiple templated variables as object overrides in values
cmd = strings.Split("zarf tools kubectl get deployment -n podinfo unicorn-podinfo -o=jsonpath='{.spec.template.metadata.annotations}'", " ")
annotations, _, err := e2e.UDS(cmd...)
require.Contains(t, annotations, "\"customAnnotation\":\"orangeAnnotation\"")
require.NoError(t, err)

// check templated variable list-type overrides in values
cmd = strings.Split("zarf tools kubectl get deployment -n podinfo unicorn-podinfo -o=jsonpath='{.spec.template.spec.tolerations}'", " ")
tolerations, _, err := e2e.UDS(cmd...)
require.Contains(t, tolerations, "\"key\":\"uds\"")
require.Contains(t, tolerations, "\"value\":\"true\"")
require.Contains(t, tolerations, "\"key\":\"unicorn\"")
require.Contains(t, tolerations, "\"value\":\"defense\"")
require.NoError(t, err)

remove(t, bundlePath)
}
12 changes: 12 additions & 0 deletions src/test/packages/no-cluster/output-var/zarf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ components:
echo "shared var in output-var pkg: "${ZARF_VAR_DOMAIN}""
- cmd: |
echo "output-var SPECIFIC_PKG_VAR = "${ZARF_VAR_SPECIFIC_PKG_VAR}""
- cmd: echo "orange"
setVariables:
- name: COLOR
- cmd: echo "Annotation"
setVariables:
- name: ANNOTATION
- cmd: echo "defense"
setVariables:
- name: DEFENSE
- cmd: echo "true"
setVariables:
- name: BOOL

0 comments on commit 266b76c

Please sign in to comment.