Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ go 1.25.5
// TODO: awaiting release of derailed/k9s to include grype version bump: from https://github.com/zarf-dev/zarf/blob/6243c86df7c2de9abd28631108b6e84b5fecf29f/go.mod#L12
replace github.com/anchore/grype => github.com/anchore/grype v0.104.1

replace github.com/zarf-dev/zarf => github.com/michalszynkiewicz/zarf v0.0.0-20260117212807-4ea3e68a3870

require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,8 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
github.com/michalszynkiewicz/zarf v0.0.0-20260117212807-4ea3e68a3870 h1:NU97ZLLvVeITNFY56CPXaL4WpZ8jpLuXRtT9qZGNh5c=
github.com/michalszynkiewicz/zarf v0.0.0-20260117212807-4ea3e68a3870/go.mod h1:YoEIXlqAbWkz2vlayiwAgeNQ7vHjo0s6mTHKuoyc3Dc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
Expand Down Expand Up @@ -1750,8 +1752,6 @@ github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
github.com/zarf-dev/zarf v0.68.1 h1:GfboOML5faPaPIA5K2a3cPE0VwdcbpRn1MDj6n+eFUU=
github.com/zarf-dev/zarf v0.68.1/go.mod h1:95WeDzbeS1g7pWiO9Fonfk3K6UeKXI0ldE+WR/DWr+4=
github.com/zclconf/go-cty v1.14.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
Expand Down
4 changes: 4 additions & 0 deletions src/cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/pterm/pterm"
"github.com/spf13/cobra"
zarfConfig "github.com/zarf-dev/zarf/src/config"
"github.com/zarf-dev/zarf/src/pkg/feature"
"github.com/zarf-dev/zarf/src/pkg/logger"
zarfTypes "github.com/zarf-dev/zarf/src/types"
)
Expand Down Expand Up @@ -90,6 +91,9 @@ func configureZarf() {
zarfConfig.CommonOptions.PlainHTTP = true
zarfConfig.CommonOptions.InsecureSkipTLSVerify = true
}
if err := feature.Set([]feature.Feature{{Name: feature.Values, Enabled: true}}); err != nil {
message.Warnf("Failed to enable the values feature automatically: %s. If you set ZARF_FEATURES env variable, please make sure to enable `values` feature", err.Error())
}
}

func setBundleFile(args []string) error {
Expand Down
88 changes: 88 additions & 0 deletions src/pkg/bundle/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ func (b *Bundle) confirmBundleCreation() (confirm bool) {
func (b *Bundle) processValuesFiles() error {
// Populate values from valuesFiles if provided
for i, pkg := range b.bundle.Packages {
// Process package-level values files (for Zarf values feature)
if pkg.Values != nil && len(pkg.Values.Files) > 0 {
if err := b.processPackageValuesFiles(i); err != nil {
return err
}
}

// Process chart override values files (existing behavior)
for componentName, overrides := range pkg.Overrides {
for chartName, bundleChartOverrides := range overrides {
valuesFilesToMerge := make([][]types.BundleChartValue, 0)
Expand Down Expand Up @@ -177,6 +185,86 @@ func (b *Bundle) processValuesFiles() error {
return nil
}

// processPackageValuesFiles reads package-level values files and merges them into Set
// This is processed at create time so the values are embedded in the bundle
func (b *Bundle) processPackageValuesFiles(pkgIdx int) error {
pkg := &b.bundle.Packages[pkgIdx]

// Initialize Set map if nil
if pkg.Values.Set == nil {
pkg.Values.Set = make(map[string]interface{})
}

// Build merged values from all files (later files override earlier)
mergedValues := make(map[string]interface{})

for _, valuesFile := range pkg.Values.Files {
// Resolve file path relative to bundle source directory
fileName := filepath.Join(b.cfg.CreateOpts.SourceDirectory, valuesFile)
if filepath.IsAbs(valuesFile) {
fileName = valuesFile
}

// Read and parse the values file
values, err := chartutil.ReadValuesFile(fileName)
if err != nil {
return err
}

// Deep merge into accumulated values
for key, value := range values {
mergedValues[key] = deepMergeForCreate(mergedValues[key], value)
}
}

// Merge file values into Set, with existing Set values taking precedence
// Files are processed first (lower precedence), then existing Set values override
finalSet := make(map[string]interface{})

// First, add all values from files using dot-notation paths
flattenToSet(mergedValues, ".", finalSet)

// Then, apply existing Set values (higher precedence)
for path, val := range pkg.Values.Set {
finalSet[path] = val
}

pkg.Values.Set = finalSet

// Clear Files since they've been processed and embedded in Set
pkg.Values.Files = nil

return nil
}

// deepMergeForCreate recursively merges src into dst, returning the merged result
func deepMergeForCreate(dst, src interface{}) interface{} {
if dst == nil {
return src
}

dstMap, dstIsMap := dst.(map[string]interface{})
srcMap, srcIsMap := src.(map[string]interface{})

if dstIsMap && srcIsMap {
for key, srcVal := range srcMap {
dstMap[key] = deepMergeForCreate(dstMap[key], srcVal)
}
return dstMap
}

// Non-map values: src overwrites dst
return src
}

// flattenToSet converts a nested map to dot-notation paths in the Set map
func flattenToSet(values map[string]interface{}, prefix string, set map[string]interface{}) {
for key, value := range values {
path := prefix + key
set[path] = value
}
}

// mergeBundleChartValues merges lists of BundleChartValue using the values from the last list if there are any duplicates
// such that values from the last list will take precedence over the values from previous lists
func mergeBundleChartValues(bundleChartValueLists ...[]types.BundleChartValue) []types.BundleChartValue {
Expand Down
23 changes: 22 additions & 1 deletion src/pkg/bundle/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/zarf-dev/zarf/src/pkg/state"
zarfState "github.com/zarf-dev/zarf/src/pkg/state"
zarfUtils "github.com/zarf-dev/zarf/src/pkg/utils"
"github.com/zarf-dev/zarf/src/pkg/value"
"golang.org/x/exp/slices"
)

Expand Down Expand Up @@ -109,6 +110,14 @@ func deployPackages(ctx context.Context, packagesToDeploy []types.Package, b *Bu
if err != nil {
return err
}
valuesVariables := getValuesVariables(variableData)
zarfValues, err := b.loadPackageValues(pkg, valuesVariables)
if err != nil {
return err
}
if len(zarfValues) > 0 {
message.Debugf("Loaded Zarf values for package %s: %v", pkg.Name, zarfValues)
}

remoteOpts := packager.RemoteOptions{
PlainHTTP: config.CommonOptions.Insecure,
Expand All @@ -134,6 +143,7 @@ func deployPackages(ctx context.Context, packagesToDeploy []types.Package, b *Bu
deployOpts := packager.DeployOptions{
Timeout: config.HelmTimeout,
SetVariables: pkgVars,
Values: value.Values(zarfValues),
ValuesOverridesMap: valuesOverrides,
Retries: b.cfg.DeployOpts.Retries,
RemoteOptions: remoteOpts,
Expand Down Expand Up @@ -371,6 +381,11 @@ func (b *Bundle) ConfirmBundleDeploy() (confirm bool) {
if err := zarfUtils.ColorPrintYAML(pkg.meta, nil, false); err != nil {
message.WarnErr(err, "unable to print package metadata yaml")
}
if len(pkg.values) > 0 {
if err := zarfUtils.ColorPrintYAML(map[string]interface{}{"values": pkg.values}, nil, false); err != nil {
message.WarnErr(err, "unable to print package values yaml")
}
}
if err := zarfUtils.ColorPrintYAML(pkg.overrides, nil, false); err != nil {
message.WarnErr(err, "unable to print package overrides yaml")
}
Expand All @@ -395,6 +410,7 @@ func (b *Bundle) ConfirmBundleDeploy() (confirm bool) {

type PkgView struct {
meta map[string]string
values map[string]interface{}
overrides map[string]interface{}
}

Expand Down Expand Up @@ -428,7 +444,12 @@ func formPkgViews(b *Bundle) []PkgView {
}

variables = addZarfVars(variableData, variables)
pkgViews = append(pkgViews, PkgView{meta: formPkgMeta(pkg), overrides: map[string]interface{}{"overrides": variables}})
valuesVariables := getValuesVariables(variableData)
zarfValues, err := b.loadPackageValues(pkg, valuesVariables)
if err != nil {
message.Warnf("Failed to load package %s values: %v", pkg.Name, err)
}
pkgViews = append(pkgViews, PkgView{meta: formPkgMeta(pkg), values: zarfValues, overrides: map[string]interface{}{"overrides": variables}})
}
return pkgViews
}
Expand Down
43 changes: 43 additions & 0 deletions src/pkg/bundle/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,49 @@ func TestFormPkgViews(t *testing.T) {
}
}

func TestFormPkgViewsValues(t *testing.T) {
t.Run("values.set appears in view as nested object", func(t *testing.T) {
b := newTestBundle(nil, nil, nil, "", "")
b.bundle = types.UDSBundle{
Packages: []types.Package{{
Name: "test-pkg",
Values: &types.PackageValues{
Set: map[string]interface{}{
".app.replicas": 3,
".database.host": "localhost",
},
},
}},
}

pkgViews := formPkgViews(&b)

require.Len(t, pkgViews, 1)
require.NotEmpty(t, pkgViews[0].values)

app := pkgViews[0].values["app"].(map[string]interface{})
require.Equal(t, 3, app["replicas"])

db := pkgViews[0].values["database"].(map[string]interface{})
require.Equal(t, "localhost", db["host"])
})

t.Run("no values configured returns empty", func(t *testing.T) {
b := newTestBundle(nil, nil, nil, "", "")
b.bundle = types.UDSBundle{
Packages: []types.Package{{
Name: "test-pkg",
// Values: nil
}},
}

pkgViews := formPkgViews(&b)

require.Len(t, pkgViews, 1)
require.Empty(t, pkgViews[0].values)
})
}

func TestFilterOverrides(t *testing.T) {
chartVars := []types.BundleChartVariable{{Name: "over1"}, {Name: "over2"}}
pkgVars := map[string]overrideData{"OVER1": {"val", valuesources.Config}, "ZARFVAR": {"val", valuesources.Env}}
Expand Down
Loading