Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/anchore/syft v1.46.0
github.com/avast/retry-go/v4 v4.7.0
github.com/defenseunicorns/pkg/helpers/v2 v2.0.4
github.com/defenseunicorns/pkg/oci v1.3.0
github.com/defenseunicorns/pkg/oci v1.3.1
github.com/derailed/k9s v0.51.0
github.com/distribution/distribution/v3 v3.1.1
github.com/distribution/reference v0.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -559,8 +559,8 @@ github.com/defenseunicorns/gojsonschema v0.0.0-20231116163348-e00f069122d6 h1:gw
github.com/defenseunicorns/gojsonschema v0.0.0-20231116163348-e00f069122d6/go.mod h1:StKLYMmPj1R5yIs6CK49EkcW1TvUYuw5Vri+LRk7Dy8=
github.com/defenseunicorns/pkg/helpers/v2 v2.0.4 h1:niBIdhRUpJghWthzJJq/SKr/dYQHW9Mn97UJT5I/2SI=
github.com/defenseunicorns/pkg/helpers/v2 v2.0.4/go.mod h1:7demM0eE/+nMqPT7PCKgO9/KVUmRSyi9UpDzb0KfYDM=
github.com/defenseunicorns/pkg/oci v1.3.0 h1:U9f+ye/a82MX5P5LLWCWB/JJ6QY4zbSWrVvG1KVsTBY=
github.com/defenseunicorns/pkg/oci v1.3.0/go.mod h1:iXzZXG7jNjkOIt5MqQp12iZH3c762zP0AOVwKTGhNhY=
github.com/defenseunicorns/pkg/oci v1.3.1 h1:hy5iKqK2Gdg8uVpEn3GA78m7JrOSQuMJ2p5sG0Da4Cs=
github.com/defenseunicorns/pkg/oci v1.3.1/go.mod h1:iXzZXG7jNjkOIt5MqQp12iZH3c762zP0AOVwKTGhNhY=
github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb h1:4W/2rQ3wzEimF5s+J6OY3ODiQtJZ5W1sForSgogVXkY=
github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 h1:foGzavPWwtoyBvjWyKJYDYsyzy+23iBV7NKTwdk+LRY=
Expand Down
11 changes: 7 additions & 4 deletions src/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/zarf-dev/zarf/src/pkg/lint"
"github.com/zarf-dev/zarf/src/pkg/logger"
"github.com/zarf-dev/zarf/src/pkg/packager"
"github.com/zarf-dev/zarf/src/pkg/packager/assemble"
"github.com/zarf-dev/zarf/src/pkg/packager/layout"
"github.com/zarf-dev/zarf/src/pkg/packager/load"
"github.com/zarf-dev/zarf/src/pkg/state"
Expand Down Expand Up @@ -160,15 +161,17 @@ func (o *devGenerateSchemaOptions) run(ctx context.Context, args []string) error

for _, component := range defined.Pkg.Components {
for _, chart := range component.Charts {
chartPath := filepath.Join(tmpDir, "charts", chart.Name)
valuesFilePath := filepath.Join(tmpDir, "values")
chartPaths := layout.ChartPaths{
ChartsDir: filepath.Join(tmpDir, "charts", chart.Name),
ValuesDir: filepath.Join(tmpDir, "values"),
}

err := layout.PackageChart(ctx, chart, basePath, chartPath, valuesFilePath, cachePath, defaultRemoteOptions())
err := assemble.PackageChart(ctx, chart, basePath, chartPaths, cachePath, defaultRemoteOptions())
if err != nil {
return fmt.Errorf("unable to package chart %q for schema generation: %w", chart.Name, err)
}

helmChart, valuesFilesValues, err := helm.LoadChartData(chart, chartPath, valuesFilePath, nil)
helmChart, valuesFilesValues, err := helm.LoadChartData(chart, chartPaths, nil)
if err != nil {
return fmt.Errorf("unable to load default values for chart %q: %w", chart.Name, err)
}
Expand Down
2 changes: 1 addition & 1 deletion src/internal/agent/hooks/flux-helmrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func populateHelmRepoPatchOperations(repoURL string, isInternal bool, useMTLS bo
}

if useMTLS {
patches = append(patches, operations.AddPatchOperation("/spec/certSecretRef", meta.LocalObjectReference{Name: cluster.RegistryClientTLSSecret}))
patches = append(patches, operations.AddPatchOperation("/spec/certSecretRef", meta.LocalObjectReference{Name: state.RegistryClientTLSSecret}))
}

patches = append(patches, operations.AddPatchOperation("/spec/secretRef", meta.LocalObjectReference{Name: config.ZarfImagePullSecretName}))
Expand Down
2 changes: 1 addition & 1 deletion src/internal/agent/hooks/flux-ocirepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func populateOCIRepoPatchOperations(repoURL string, isInternal bool, useMTLS boo
}

if useMTLS {
patches = append(patches, operations.AddPatchOperation("/spec/certSecretRef", meta.LocalObjectReference{Name: cluster.RegistryClientTLSSecret}))
patches = append(patches, operations.AddPatchOperation("/spec/certSecretRef", meta.LocalObjectReference{Name: state.RegistryClientTLSSecret}))
}

// If semver is used we don't want to add the ":latest" tag + crc to the spec
Expand Down
3 changes: 1 addition & 2 deletions src/internal/agent/hooks/flux-ocirepo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/zarf-dev/zarf/src/config"
"github.com/zarf-dev/zarf/src/internal/agent/http/admission"
"github.com/zarf-dev/zarf/src/internal/agent/operations"
"github.com/zarf-dev/zarf/src/pkg/cluster"
"github.com/zarf-dev/zarf/src/pkg/state"
"github.com/zarf-dev/zarf/src/pkg/transform"
"github.com/zarf-dev/zarf/src/test/testutil"
Expand Down Expand Up @@ -544,7 +543,7 @@ func TestFluxOCIMutationWebhook(t *testing.T) {
),
operations.AddPatchOperation(
"/spec/certSecretRef",
fluxmeta.LocalObjectReference{Name: cluster.RegistryClientTLSSecret},
fluxmeta.LocalObjectReference{Name: state.RegistryClientTLSSecret},
),
operations.ReplacePatchOperation(
"/spec/ref/tag",
Expand Down
56 changes: 20 additions & 36 deletions src/internal/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,48 +49,32 @@ func ParseServiceURL(serviceURL string) (string, string, int, error) {
return matches[2], matches[1], remotePort, nil
}

// Inspired from https://github.com/google/go-containerregistry/blob/098045d5e61ff426a61a0eecc19ad0c433cd35a9/pkg/name/registry.go
// Originally inspired from https://github.com/google/go-containerregistry/blob/098045d5e61ff426a61a0eecc19ad0c433cd35a9/pkg/name/registry.go

// Detect more complex forms of local references.
var reLocal = regexp.MustCompile(`.*\.local(?:host)?(?::\d{1,5})?$`)

// Detect the loopback IP (127.0.0.1)
var reLoopback = regexp.MustCompile(regexp.QuoteMeta("127.0.0.1"))

// Detect the loopback IPV6 (::1)
var reipv6Loopback = regexp.MustCompile(regexp.QuoteMeta("::1"))

func isRFC1918(URL string) bool {
ipStr := strings.Split(URL, ":")[0]
ip := net.ParseIP(ipStr)
if ip == nil {
return false
}
for _, cidr := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} {
_, block, _ := net.ParseCIDR(cidr) //nolint:errcheck
if block.Contains(ip) {
return true
}
}
return false
}

// IsLocalhost returns whether or not a URL without an existing scheme is on localhost
func IsLocalhost(URL string) bool {
if isRFC1918(URL) {
return true
}
if strings.HasPrefix(URL, "localhost:") {
return true
// IsLocalOrPrivate reports whether hostURL (without a scheme) refers to a host
// reachable without a public network round-trip - loopback/localhost, a
// private-network IP (RFC1918/RFC4193), or a .local/.localhost domain - used for
// choosing to access services over plain HTTP. hostURL may include a port.
func IsLocalOrPrivate(hostURL string) bool {
host := hostURL
if h, _, err := net.SplitHostPort(hostURL); err == nil {
host = h
}
if reLocal.MatchString(URL) {
if IsLocalhost(host) {
return true
}
if reLoopback.MatchString(URL) {
if ip := net.ParseIP(host); ip != nil && ip.IsPrivate() {
return true
}
if reipv6Loopback.MatchString(URL) {
return strings.HasSuffix(host, ".local") || strings.HasSuffix(host, ".localhost")
}

// IsLocalhost reports whether host is the localhost hostname or a loopback IP
// (127.0.0.0/8 or ::1). host must be a bare hostname or IP, without a port.
func IsLocalhost(host string) bool {
if host == "localhost" {
return true
}
return false
ip := net.ParseIP(host)
return ip != nil && ip.IsLoopback()
}
28 changes: 26 additions & 2 deletions src/internal/dns/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestServiceURL(t *testing.T) {
}
}

func TestIsLocalHost(t *testing.T) {
func TestIsLocalOrPrivate(t *testing.T) {
t.Parallel()
tests := []struct {
URL string
Expand Down Expand Up @@ -96,7 +96,31 @@ func TestIsLocalHost(t *testing.T) {
for _, tt := range tests {
t.Run(tt.URL, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.expected, IsLocalhost(tt.URL))
require.Equal(t, tt.expected, IsLocalOrPrivate(tt.URL))
})
}
}

func TestIsLocalhost(t *testing.T) {
t.Parallel()
tests := []struct {
host string
expected bool
}{
{host: "localhost", expected: true},
{host: "127.0.0.1", expected: true},
{host: "127.0.0.2", expected: true},
{host: "::1", expected: true},
{host: "10.2.3.4", expected: false},
{host: "192.168.1.5", expected: false},
{host: "foo.local", expected: false},
{host: "gcr.io", expected: false},
}

for _, tt := range tests {
t.Run(tt.host, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.expected, IsLocalhost(tt.host))
})
}
}
7 changes: 4 additions & 3 deletions src/internal/packager/helm/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/zarf-dev/zarf/src/api/v1alpha1"
"github.com/zarf-dev/zarf/src/pkg/cluster"
"github.com/zarf-dev/zarf/src/pkg/logger"
"github.com/zarf-dev/zarf/src/pkg/packager/layout"
"github.com/zarf-dev/zarf/src/pkg/state"
"github.com/zarf-dev/zarf/src/pkg/variables"

Expand Down Expand Up @@ -377,13 +378,13 @@ func uninstallChart(name string, actionConfig *action.Configuration, timeout tim
}

// LoadChartData loads a chart from a tarball and returns the Helm SDK representation of the chart and it's values
func LoadChartData(zarfChart v1alpha1.ZarfChart, chartPath string, valuesPath string, valuesOverrides map[string]any) (*chartv2.Chart, common.Values, error) {
loadedChart, err := loadChartFromTarball(zarfChart, chartPath)
func LoadChartData(zarfChart v1alpha1.ZarfChart, paths layout.ChartPaths, valuesOverrides map[string]any) (*chartv2.Chart, common.Values, error) {
loadedChart, err := loadChartFromTarball(zarfChart, paths)
if err != nil {
return nil, nil, fmt.Errorf("unable to load chart tarball: %w", err)
}

chartValues, err := parseChartValues(zarfChart, valuesPath, valuesOverrides)
chartValues, err := parseChartValues(zarfChart, paths, valuesOverrides)
if err != nil {
return loadedChart, nil, fmt.Errorf("unable to parse chart values: %w", err)
}
Expand Down
29 changes: 6 additions & 23 deletions src/internal/packager/helm/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/zarf-dev/zarf/src/api/v1alpha1"
"github.com/zarf-dev/zarf/src/config"
"github.com/zarf-dev/zarf/src/pkg/logger"
"github.com/zarf-dev/zarf/src/pkg/packager/layout"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chart/common"
chartv2 "helm.sh/helm/v4/pkg/chart/v2"
Expand Down Expand Up @@ -76,21 +77,6 @@ func ChartFromZarfManifest(manifest v1alpha1.ZarfManifest, manifestPath, package
return chart, tmpChart, nil
}

// StandardName generates a predictable full path for a helm chart for Zarf.
func StandardName(destination string, chart v1alpha1.ZarfChart) string {
// While it is not possible for v1alpha1 packages to be created with an empty version, future API versions will allow this.
// This check is introduced in v0.65.0, allowing future packages with empty versions to have backwards compatibility
if chart.Version == "" {
return filepath.Join(destination, chart.Name)
}
return filepath.Join(destination, chart.Name+"-"+chart.Version)
}

// StandardValuesName generates a predictable full path for the values file for a helm chart for zarf
func StandardValuesName(destination string, chart v1alpha1.ZarfChart, idx int) string {
return fmt.Sprintf("%s-%d", StandardName(destination, chart), idx)
}

// ChartValuesFile represents a single values file for a Helm chart with its global sequential index.
// Template indicates whether Go template rendering should be applied at deploy time.
type ChartValuesFile struct {
Expand All @@ -101,7 +87,7 @@ type ChartValuesFile struct {

// GetChartValuesFiles returns a flat ordered list of all values files for a chart.
// ValuesFiles appear first (indices 0..n-1), followed by TemplatedValuesFiles (indices n..n+m-1).
// All files share the same global sequential index space and are stored via StandardValuesName.
// All files share the same global sequential index space and are stored via ChartPaths.ValuesFile.
func GetChartValuesFiles(chart v1alpha1.ZarfChart) []ChartValuesFile {
files := make([]ChartValuesFile, 0, len(chart.ValuesFiles)+len(chart.TemplatedValuesFiles))
for i, src := range chart.ValuesFiles {
Expand All @@ -114,12 +100,9 @@ func GetChartValuesFiles(chart v1alpha1.ZarfChart) []ChartValuesFile {
}

// loadChartFromTarball returns a helm chart from a tarball.
func loadChartFromTarball(chart v1alpha1.ZarfChart, chartPath string) (*chartv2.Chart, error) {
// Get the path the temporary helm chart tarball
sourceFile := StandardName(chartPath, chart) + ".tgz"

func loadChartFromTarball(chart v1alpha1.ZarfChart, paths layout.ChartPaths) (*chartv2.Chart, error) {
// Load the loadedChart tarball
loadedChart, err := loader.Load(sourceFile)
loadedChart, err := loader.Load(paths.Archive(chart.Name, chart.Version))
if err != nil {
return nil, fmt.Errorf("unable to load helm chart archive: %w", err)
}
Expand All @@ -132,11 +115,11 @@ func loadChartFromTarball(chart v1alpha1.ZarfChart, chartPath string) (*chartv2.
}

// parseChartValues reads the context of the chart values into an interface if it exists.
func parseChartValues(chart v1alpha1.ZarfChart, valuesPath string, valuesOverrides map[string]any) (common.Values, error) {
func parseChartValues(chart v1alpha1.ZarfChart, paths layout.ChartPaths, valuesOverrides map[string]any) (common.Values, error) {
valueOpts := &values.Options{}

for _, f := range GetChartValuesFiles(chart) {
valueOpts.ValueFiles = append(valueOpts.ValueFiles, StandardValuesName(valuesPath, chart, f.GlobalIdx))
valueOpts.ValueFiles = append(valueOpts.ValueFiles, paths.ValuesFile(chart.Name, chart.Version, f.GlobalIdx))
}

httpProvider := getter.Provider{
Expand Down
8 changes: 5 additions & 3 deletions src/internal/packager/helm/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/stretchr/testify/require"
"github.com/zarf-dev/zarf/src/api/v1alpha1"
"github.com/zarf-dev/zarf/src/pkg/packager/layout"
)

func TestParseChartValues(t *testing.T) {
Expand All @@ -25,13 +26,14 @@ func TestParseChartValues(t *testing.T) {
tmpDir := t.TempDir()

// ValuesFiles land at global index 0; TemplatedValuesFiles at global index 1 (len(ValuesFiles) + local index).
regularPath := StandardValuesName(tmpDir, chart, 0)
templatedPath := StandardValuesName(tmpDir, chart, 1)
paths := layout.ChartPaths{ValuesDir: tmpDir}
regularPath := paths.ValuesFile(chart.Name, chart.Version, 0)
templatedPath := paths.ValuesFile(chart.Name, chart.Version, 1)

require.NoError(t, os.WriteFile(regularPath, []byte("shared: from-regular\nregularOnly: present"), 0o644))
require.NoError(t, os.WriteFile(templatedPath, []byte("shared: from-templated\ntemplatedOnly: present"), 0o644))

merged, err := parseChartValues(chart, tmpDir, nil)
merged, err := parseChartValues(chart, paths, nil)
require.NoError(t, err)

// TemplatedValuesFiles are appended after ValuesFiles in the Helm merge list,
Expand Down
Loading