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

feat: add file type variables to bundles #631

Merged
merged 33 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bf334e6
feat: add file type variables to bundles
TristanHoladay May 22, 2024
fbf8229
add FileVariables to BundleDeployOptions.
TristanHoladay May 22, 2024
3a63013
adding loadFileContents() to loadVariables().
TristanHoladay May 22, 2024
0b6f219
add file handling from loadVariables()
TristanHoladay May 23, 2024
1e271cd
fix deploy_test call of loadVariables()
TristanHoladay May 23, 2024
405327e
test for zarf var setting; adding file handling to processOverrideVar…
TristanHoladay May 24, 2024
d5b375b
small refactor; attempting helm.FileValues (bad format errors)
TristanHoladay May 24, 2024
5c97215
cleanup helm file handling; base64 test pub key.
TristanHoladay May 28, 2024
9a835a3
lint fixes
TristanHoladay May 28, 2024
7dd839b
merge main and resolve; refactor file var handling
TristanHoladay May 29, 2024
39d2187
fix loadViperConfig duplicate; fix file handling in override add
TristanHoladay May 29, 2024
cd23c1a
refactor ChartVariableType; refactor tests.
TristanHoladay May 29, 2024
bec07c9
lint fix
TristanHoladay May 29, 2024
a2fe420
file path handling made more reusable; fixed tests.
TristanHoladay May 29, 2024
dc8613c
Merge branch 'main' into add-file-type-var
TristanHoladay May 29, 2024
3a407ec
fix typo in test
TristanHoladay May 29, 2024
a5afc3e
Merge branch 'main' into add-file-type-var
TristanHoladay May 29, 2024
513ed8e
lint fix'
TristanHoladay May 29, 2024
daf9076
add docs; make test less brittle.
TristanHoladay May 29, 2024
05080fa
add to docs; rename test
TristanHoladay May 29, 2024
a2b4276
move formAndCheckFilePath and annotate
TristanHoladay May 30, 2024
2d11b46
file handling and docs refactors
TristanHoladay May 31, 2024
bc7d4d0
handle different value source paths.
TristanHoladay Jun 3, 2024
18b13ec
refactor tests.
TristanHoladay Jun 3, 2024
ee2e768
update schema and fix test conflicts
TristanHoladay Jun 3, 2024
3c420ce
change ValueSource to Source to remove confusion with type.
TristanHoladay Jun 3, 2024
6d2914a
fix wrong property name in unit test
TristanHoladay Jun 3, 2024
d72cde9
revert e2e secret test removals
TristanHoladay Jun 3, 2024
33daef1
clean up tests from previous iterations
TristanHoladay Jun 4, 2024
861f15f
lifted unit tests up to processOverrideVariables
TristanHoladay Jun 4, 2024
85a5ad3
check override map in unit test for correct FileValues
TristanHoladay Jun 4, 2024
e995e65
Update src/pkg/bundle/deploy_test.go
TristanHoladay Jun 5, 2024
9b457c9
switch Equals to Equal
TristanHoladay Jun 5, 2024
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
42 changes: 42 additions & 0 deletions docs/overrides.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,48 @@ Variable precedence is as follows:
1. `uds-config.yaml` variables
1. Variables `default` in the`uds-bundle.yaml`

#### Variable Types
Variables can be of either type `raw` or `file`. The type will default to raw if not set explicitly.

> [!WARNING]
> If a variable is set to accept a file as it's value, but is missing the `file` type, then the file will not be processed.

```yaml
kind: UDSBundle
metadata:
name: example-bundle
version: 0.0.1

packages:
- name: helm-overrides-package
path: "../../packages/helm"
ref: 0.0.1
overrides:
podinfo-component:
unicorn-podinfo:
variables:
- name: test_secret
path: "testSecret"
description: "Key to be set in a secret"
type: file
```
UncleGedd marked this conversation as resolved.
Show resolved Hide resolved

**File Paths**

If a file path is not absolute it will be set as relative to the uds-config.yaml path.

e.g. the following `uds-config.yaml` is in [`src/test/bundles/07-helm-overrides/variable-files/`](../src/test/bundles/07-helm-overrides/variable-files/uds-config.yaml)
```yaml
variables:
helm-overrides:
test_secret: test.pub
```

This means when `test.pub` is evalutated it will first be appended to the config path like so `src/test/bundles/07-helm-overrides/variable-files/test.pub`.

If the file path is already set to the same relative path as the config, then no merging will take place.


### Namespace
It's also possible to specify a namespace for a packaged Helm chart to be installed in. For example, to deploy the a chart in the `custom-podinfo` namespace, you can specify the `namespace` in the `overrides` block:

Expand Down
10 changes: 10 additions & 0 deletions src/cmd/uds.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ var deployCmd = &cobra.Command{
bundleCfg.DeployOpts.Source = chooseBundle(args)
configureZarf()

// load uds-config if it exists
config := v.ConfigFileUsed()
if config != "" {
bundleCfg.DeployOpts.Config = config
if err := loadViperConfig(); err != nil {
TristanHoladay marked this conversation as resolved.
Show resolved Hide resolved
message.Fatalf(err, "Failed to load uds-config: %s", err.Error())
return
}
}

// create new bundle client and deploy
bndlClient := bundle.NewOrDie(&bundleCfg)
defer bndlClient.ClearPaths()
Expand Down
52 changes: 43 additions & 9 deletions src/pkg/bundle/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle) error {
publicKeyPath = ""
}

pkgVars := b.loadVariables(pkg, bundleExportedVars)
pkgVars, err := b.loadVariables(pkg, bundleExportedVars)
if err != nil {
return err
}

opts := zarfTypes.ZarfPackageOptions{
PackageSource: pkgTmp,
Expand Down Expand Up @@ -152,8 +155,28 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle) error {
return nil
}

func formAndCheckFilePath(anchorPath string, filePath string) (string, error) {
if !filepath.IsAbs(filePath) {
// set path relative to anchorPath (i.e. cwd or config), unless they are the same
if filepath.Dir(anchorPath) != filepath.Dir(filePath) {
filePath = filepath.Join(filepath.Dir(anchorPath), filePath)
}
}

if helpers.InvalidPath(filePath) {
return "", fmt.Errorf("Unable to find file %s", filePath)
}

_, err := helpers.IsTextFile(filePath)
if err != nil {
return "", err
}

return filePath, nil
}

// loadVariables loads and sets precedence for config-level and imported variables
func (b *Bundle) loadVariables(pkg types.Package, bundleExportedVars map[string]map[string]string) map[string]string {
func (b *Bundle) loadVariables(pkg types.Package, bundleExportedVars map[string]map[string]string) (map[string]string, error) {
TristanHoladay marked this conversation as resolved.
Show resolved Hide resolved
pkgVars := make(map[string]string)

// load all exported variables
Expand Down Expand Up @@ -196,7 +219,7 @@ func (b *Bundle) loadVariables(pkg types.Package, bundleExportedVars map[string]
pkgVars[strings.ToUpper(name)] = fmt.Sprint(val)
}
}
return pkgVars
return pkgVars, nil
}

// ConfirmBundleDeploy uses Zarf's pterm logging to prompt the user to confirm bundle creation
Expand Down Expand Up @@ -340,7 +363,7 @@ func (b *Bundle) processOverrideNamespaces(overrideMap sources.NamespaceOverride
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, pkgVars); err != nil {
if err := b.addOverrideValue(*overrideMap, componentName, chartName, v.Path, "raw", v.Value, pkgVars); err != nil {
return err
}
}
Expand Down Expand Up @@ -386,7 +409,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, nil); err != nil {
if err := b.addOverrideValue(*overrideMap, componentName, chartName, v.Path, v.Type, overrideVal, nil); err != nil {
return err
}

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

// addOverrideValue adds a value to a PkgOverrideMap
func addOverrideValue(overrides map[string]map[string]*values.Options, component string, chart string, valuePath string, value interface{}, pkgVars map[string]string) error {
func (b *Bundle) addOverrideValue(overrides map[string]map[string]*values.Options, component string, chart string, valuePath string, valueType types.ChartVariableType, 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 Down Expand Up @@ -443,9 +466,20 @@ func addOverrideValue(overrides map[string]map[string]*values.Options, component
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)

if valueType == "file" {
TristanHoladay marked this conversation as resolved.
Show resolved Hide resolved
verifiedPath, err := formAndCheckFilePath(b.cfg.DeployOpts.Config, value.(string))
if err != nil {
return err
}
helmVal := fmt.Sprintf("%s=%v", valuePath, verifiedPath)
overrides[component][chart].FileValues = append(overrides[component][chart].FileValues, helmVal)
} else {

// 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
}
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/bundle/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func TestLoadVariablesPrecedence(t *testing.T) {
if tc.loadEnvVar {
os.Setenv("UDS_FOO", "set using env var")
}
actualPkgVars := tc.bundle.loadVariables(tc.pkg, tc.bundleExportVars)
actualPkgVars, _ := tc.bundle.loadVariables(tc.pkg, tc.bundleExportVars)
require.Equal(t, tc.expectedPkgVars, actualPkgVars)
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
options:
log_level: debug

variables:
helm-overrides:
test_secret: not-there.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
debug
1 change: 1 addition & 0 deletions src/test/bundles/07-helm-overrides/variable-files/test.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCZ1FDS2MyK2JHMjF5YkNUdUMwVTg4RlB2ZWF2cWFkbjlTckJqa0NHcGFISU5xMmhaOThlVmxlNXlJbk9PU3ExbEdNQzg5Rnl4N1ZDbFl3R0sxRHhNS3RSVzZ1K0hVV29PanN4Smg0R0dJY2xyUkc0eEdPU1dRazdWUjJrUnFPM3pLK3hnRUFVbzR5UW85TCtXUHA5R01HR3VNWnVNRkxzS3lwVUpuM1AyWFZVS2d5Mm9TQk1UcVZ1SFpqWjRubjR1YlhPYjlXZk96LzBBRll2c0lCdUpNTXEvSzNxZmZXQWN5RzJ6KzNmQTBQTUJGZmpnOGhpM1FMazdyeFpXdVl4bUtHcklHY3V2NXNYYjN5Q0pNUmF2NmtpbHhZQmxyREVORm5QYjkxUC93MUZBWG8zcnlDU3NYVDRkWFdUT0IvWkdBNHhTRTVGWmFnYUJMSEdrUmxFd0xCT3V5SFhEQ2hUNkI4WWhQYmxTZHp2S1dIZmdNUi9aWnFrLysyNkpEWkFFaVExUXJaNkMzOWNDSUpYUm5RSHlydGRtYWM3YXBrUEpDYlRFbk5RSmRKNTdkS3ltbVBUNmtFZ0dVSDlSLzJmbEtHcnludzFTQ0Q5ckIxT1QvZmJnamhCbGRJMzhNMjBNQUdxdFJvQ0VSaEJBTzd0WVdQLzBGanU2Qi9jQjRWWEZqajA9IHRyaXN0YW5AdHJpc3Rhbi1OVUMxM0FOSGk3
23 changes: 23 additions & 0 deletions src/test/bundles/07-helm-overrides/variable-files/uds-bundle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
kind: UDSBundle
metadata:
name: variable-files
description: testing a bundle with Helm overrides
version: 0.0.1

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

overrides:
podinfo-component:
unicorn-podinfo:
variables:
- name: log_level
path: "podinfo.logLevel"
description: "Set the log level for podinfo"
type: file
- name: TEST_SECRET
path: "testSecret"
description: "base64 encoded key to place in the test secret resource"
type: file
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
options:
log_level: debug

variables:
helm-overrides:
test_secret: test.pub
56 changes: 56 additions & 0 deletions src/test/e2e/variable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package test

import (
"encoding/base64"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -373,3 +374,58 @@ func TestExportVarsAsGlobalVars(t *testing.T) {

remove(t, bundlePath)
}

func TestVariableFilesFileNotFound(t *testing.T) {
deployZarfInit(t)
e2e.HelmDepUpdate(t, "src/test/packages/helm/unicorn-podinfo")
e2e.CreateZarfPkg(t, "src/test/packages/helm", false)
bundleDir := "src/test/bundles/07-helm-overrides/variable-files"
bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-variable-files-%s-0.0.1.tar.zst", e2e.Arch))
os.Setenv("UDS_CONFIG", filepath.Join(bundleDir, "file-not-found-config.yaml"))

createLocal(t, bundleDir, e2e.Arch)

cmd := strings.Split(fmt.Sprintf("deploy %s --retries 1 --confirm", bundlePath), " ")
TristanHoladay marked this conversation as resolved.
Show resolved Hide resolved
_, stderr, _ := e2e.UDS(cmd...)

require.Contains(t, stderr, fmt.Sprintf("Unable to find file %s/not-there.pub", bundleDir))
}

func TestVariableFilesHelmOverrides(t *testing.T) {
deployZarfInit(t)
e2e.HelmDepUpdate(t, "src/test/packages/helm/unicorn-podinfo")
e2e.CreateZarfPkg(t, "src/test/packages/helm", false)
bundleDir := "src/test/bundles/07-helm-overrides/variable-files"
bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-variable-files-%s-0.0.1.tar.zst", e2e.Arch))
createLocal(t, bundleDir, e2e.Arch)

os.Setenv("UDS_CONFIG", filepath.Join(bundleDir, "uds-config.yaml"))
os.Setenv("UDS_TEST_FILE", fmt.Sprintf("%s/test-zarf-var-file.txt", bundleDir))
cmd := strings.Split(fmt.Sprintf("deploy %s --retries 1 --confirm --set helm-overrides.log_level=%s/log-level.txt", bundlePath, bundleDir), " ")
_, stderr, err := e2e.UDS(cmd...)
require.NoError(t, err)

t.Run("test test.pub file contents set by config", func(t *testing.T) {
cmd := strings.Split("zarf tools kubectl get secret -n podinfo test-secret -o=jsonpath={.data.test}", " ")
stdout, _, err := e2e.UDS(cmd...)
require.NoError(t, err)
decoded, err := base64.StdEncoding.DecodeString(stdout)
require.NoError(t, err)
require.Contains(t, string(decoded), "ssh-rsa")
})

t.Run("test log-level.txt set by --set", func(t *testing.T) {
cmd := strings.Split("zarf tools kubectl get deployment -n podinfo unicorn-podinfo -o=jsonpath={.spec.template.spec.containers[0].command}", " ")
stdout, _, err := e2e.UDS(cmd...)
require.NoError(t, err)
require.Contains(t, stdout, "--level=debug")
})

t.Run("test domain zarf var set by env variable", func(t *testing.T) {
// checking output of action in the helm-overrides package
// zarf will handle actually parsing the files passed to it
require.Contains(t, stderr, fmt.Sprintf("TEST_FILE set as %s/test-zarf-var-file.txt", bundleDir))
})

remove(t, bundlePath)
}
3 changes: 3 additions & 0 deletions src/test/packages/helm/zarf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ metadata:
variables:
- name: DOMAIN
default: uds.dev
- name: TEST_FILE
type: file

components:
- name: podinfo-component
Expand All @@ -24,3 +26,4 @@ components:
after:
- cmd: |
echo "shared var in helm-overrides pkg: "${ZARF_VAR_DOMAIN}""
- cmd: echo "TEST_FILE set as ${ZARF_VAR_TEST_FILE}"
16 changes: 12 additions & 4 deletions src/types/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
// Package types contains all the types used by UDS.
package types

type ChartVariableType string

const (
File ChartVariableType = "file"
Raw ChartVariableType = "raw"
)

// UDSBundle is the top-level structure of a UDS bundle
type UDSBundle struct {
Kind string `json:"kind" jsonschema:"description=The kind of UDS package,enum=UDSBundle"`
Expand Down Expand Up @@ -40,10 +47,11 @@ type BundleChartValue struct {
}

type BundleChartVariable struct {
Path string `json:"path" jsonschema:"name=Path to the Helm chart value to set. The format is <chart-value>, example=controller.service.type"`
Name string `json:"name" jsonschema:"name=Name of the variable to set"`
Description string `json:"description,omitempty" jsonschema:"name=Description of the variable"`
Default interface{} `json:"default,omitempty" jsonschema:"name=The default value to set"`
Path string `json:"path" jsonschema:"name=Path to the Helm chart value to set. The format is <chart-value>, example=controller.service.type"`
Name string `json:"name" jsonschema:"name=Name of the variable to set"`
Description string `json:"description,omitempty" jsonschema:"name=Description of the variable"`
Default interface{} `json:"default,omitempty" jsonschema:"name=The default value to set"`
Type ChartVariableType `json:"type,omitempty" jsonschema:"description=The type of value to be processed,enum=raw,enum=file"`
}

// BundleVariableImport represents variables in the bundle
Expand Down
1 change: 1 addition & 0 deletions src/types/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type BundleCreateOptions struct {
type BundleDeployOptions struct {
Resume bool
Source string
Config string
Packages []string
PublicKeyPath string
SetVariables map[string]string `json:"setVariables" jsonschema:"description=Key-Value map of variable names and their corresponding values that will be used by Zarf packages in a bundle"`
Expand Down
8 changes: 8 additions & 0 deletions uds.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
},
"default": {
"additionalProperties": true
},
"type": {
"enum": [
"raw",
"file"
],
"type": "string",
"description": "The type of value to be processed"
}
},
"additionalProperties": false,
Expand Down
Loading