From 7f8f1099be3778866f72b3178b6b8853e91b46b1 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Thu, 6 Jul 2023 19:54:53 -0400 Subject: [PATCH 01/13] [TASK] TransformGitURL helm helper Signed-off-by: Case Wylie --- src/internal/packager/helm/repo.go | 16 ++++++++++++++++ src/pkg/packager/create.go | 7 +++++-- src/pkg/packager/prepare.go | 7 +++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index 5816d7e5cb..351f070f7b 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -12,6 +12,7 @@ import ( "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/internal/packager/git" "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/transform" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/registry" @@ -165,3 +166,18 @@ func (h *Helm) DownloadChartFromGitToTemp(spinner *message.Spinner) (string, err return gitCfg.GitPath, nil } + +// Helper function src/pkg/packager/prepare.goL118 src/pkg/packager/create.goL328 +func (h *Helm) TransformGitURL(charts string) (string, error) { + // calling GitTransformURLSplitRef() to see if refPlain is empty + _, refPlain, _ := transform.GitTransformURLSplitRef(h.Chart.URL) + + // if it is append chart version as if its a tag + if refPlain == "" { + h.Chart.URL = fmt.Sprintf("%s@%s", h.Chart.URL, h.Chart.Version) + } + + // reduce code duplication + return h.PackageChartFromGit(charts) + +} diff --git a/src/pkg/packager/create.go b/src/pkg/packager/create.go index fb31a11590..22f5f3d238 100755 --- a/src/pkg/packager/create.go +++ b/src/pkg/packager/create.go @@ -326,7 +326,9 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel // If any helm charts are defined, process them. for chartIdx, chart := range component.Charts { - re := regexp.MustCompile(`\.git$`) + // https://regex101.com/r/jYLoUy/1 + re := regexp.MustCompile(`\.git@`) + // check if the chart is a git url with a ref isGitURL := re.MatchString(chart.URL) helmCfg := helm.Helm{ Chart: chart, @@ -344,7 +346,8 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel p.cfg.Pkg.Components[index].Charts[chartIdx].LocalPath = rel } else if isGitURL { - _, err = helmCfg.PackageChartFromGit(componentPath.Charts) + _, err = helmCfg.TransformGitURL(componentPath.Charts) + // _, err = helmCfg.PackageChartFromGit(componentPath.Charts) if err != nil { return fmt.Errorf("error creating chart archive, unable to pull the chart from git: %s", err.Error()) } diff --git a/src/pkg/packager/prepare.go b/src/pkg/packager/prepare.go index d6e9926f49..c16f92e2cb 100644 --- a/src/pkg/packager/prepare.go +++ b/src/pkg/packager/prepare.go @@ -113,9 +113,11 @@ func (p *Packager) FindImages(baseDir, repoHelmChartPath string, kubeVersionOver if len(component.Charts) > 0 { _ = utils.CreateDirectory(componentPath.Charts, 0700) _ = utils.CreateDirectory(componentPath.Values, 0700) - re := regexp.MustCompile(`\.git$`) + // https://regex101.com/r/jYLoUy/1 + re := regexp.MustCompile(`\.git@`) for _, chart := range component.Charts { + // check if the chart is a git url with a ref isGitURL := re.MatchString(chart.URL) helmCfg := helm.Helm{ Chart: chart, @@ -124,7 +126,8 @@ func (p *Packager) FindImages(baseDir, repoHelmChartPath string, kubeVersionOver helmCfg.Cfg.State = types.ZarfState{} if isGitURL { - path, err := helmCfg.PackageChartFromGit(componentPath.Charts) + path, err := helmCfg.TransformGitURL(componentPath.Charts) + // path, err := helmCfg.PackageChartFromGit(componentPath.Charts) if err != nil { return fmt.Errorf("unable to download chart from git repo (%s): %w", chart.URL, err) } From 46d1b6df5f9c7814f34e14cf1c4614475266dcb5 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Fri, 7 Jul 2023 16:10:23 -0400 Subject: [PATCH 02/13] [TASK] refactor for urls/reduce duplication Signed-off-by: Case Wylie --- src/internal/packager/helm/repo.go | 90 +++++++++++++++--------------- src/pkg/packager/create.go | 21 ++----- src/pkg/packager/prepare.go | 21 ++----- src/pkg/transform/git.go | 4 +- 4 files changed, 56 insertions(+), 80 deletions(-) diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index 351f070f7b..67831f9da7 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -8,6 +8,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/internal/packager/git" @@ -23,15 +24,49 @@ import ( "helm.sh/helm/v3/pkg/repo" ) +func (h *Helm) PackageChart(destination string) error { + if len(h.Chart.URL) > 0 { + url, refPlain, err := transform.GitTransformURLSplitRef(h.Chart.URL) + // check if the chart is a git url with a ref (if an error is returned url will be empty) + isGitURL := strings.HasSuffix(url, ".git") + if err != nil { + message.Debugf("unable to parse the url, continuing with %s", h.Chart.URL) + } + + if isGitURL { + // if it is append chart version as if its a tag + if refPlain == "" { + h.Chart.URL = fmt.Sprintf("%s@%s", h.Chart.URL, h.Chart.Version) + } + + _, err = h.PackageChartFromGit(destination) + + if err != nil { + return fmt.Errorf("error creating chart archive, unable to pull the chart from git: %s", err.Error()) + } + } else { + h.DownloadPublishedChart(destination) + } + + } else { + _, err := h.PackageChartFromLocalFiles(destination) + if err != nil { + return fmt.Errorf("error creating chart archive, unable to package the chart: %s", err.Error()) + } + } + return nil +} + // PackageChartFromLocalFiles creates a chart archive from a path to a chart on the host os. -func (h *Helm) PackageChartFromLocalFiles(destination string) string { +func (h *Helm) PackageChartFromLocalFiles(destination string) (string, error) { spinner := message.NewProgressSpinner("Processing helm chart %s:%s from %s", h.Chart.Name, h.Chart.Version, h.Chart.LocalPath) defer spinner.Stop() // Validate the chart _, err := loader.LoadDir(h.Chart.LocalPath) if err != nil { - spinner.Fatalf(err, "Validation failed for chart from %s (%s)", h.Chart.LocalPath, err.Error()) + spinner.Errorf(err, "Validation failed for chart from %s (%s)", h.Chart.LocalPath, err.Error()) + return "", err } client := action.NewPackage() @@ -40,12 +75,13 @@ func (h *Helm) PackageChartFromLocalFiles(destination string) string { path, err := client.Run(h.Chart.LocalPath, nil) if err != nil { - spinner.Fatalf(err, "Helm is unable to save the archive and create the package %s", path) + spinner.Errorf(err, "Helm is unable to save the archive and create the package %s", path) + return "", err } spinner.Success() - return path + return path, nil } // PackageChartFromGit is a special implementation of chart archiving that supports the https://p1.dso.mil/#/products/big-bang/ model. @@ -53,8 +89,6 @@ func (h *Helm) PackageChartFromGit(destination string) (string, error) { spinner := message.NewProgressSpinner("Processing helm chart %s", h.Chart.Name) defer spinner.Stop() - client := action.NewPackage() - // Retrieve the repo containing the chart gitPath, err := h.DownloadChartFromGitToTemp(spinner) if err != nil { @@ -62,26 +96,9 @@ func (h *Helm) PackageChartFromGit(destination string) (string, error) { } defer os.RemoveAll(gitPath) - // Set the directory for the chart - chartPath := filepath.Join(gitPath, h.Chart.GitPath) - - // Validate the chart - if _, err := loader.LoadDir(chartPath); err != nil { - spinner.Errorf(err, "Validation failed for chart %s (%s)", h.Chart.Name, err.Error()) - return "", err - } - - // Tell helm where to save the archive and create the package - client.Destination = destination - name, err := client.Run(chartPath, nil) - if err != nil { - spinner.Errorf(err, "Helm is unable to save the archive and create the package %s", name) - return "", err - } - - spinner.Success() - - return name, nil + // Set the directory for the chart and package it + h.Chart.LocalPath = filepath.Join(gitPath, h.Chart.GitPath) + return h.PackageChartFromLocalFiles(destination) } // DownloadPublishedChart loads a specific chart version from a remote repo. @@ -155,29 +172,12 @@ func (h *Helm) DownloadChartFromGitToTemp(spinner *message.Spinner) (string, err // Create the Git configuration and download the repo gitCfg := git.NewWithSpinner(h.Cfg.State.GitServer, spinner) - gitRepoWithRef := fmt.Sprintf("%s@%s", h.Chart.URL, h.Chart.Version) - // Download the git repo to a temporary directory - err := gitCfg.DownloadRepoToTemp(gitRepoWithRef) + err := gitCfg.DownloadRepoToTemp(h.Chart.URL) if err != nil { - spinner.Errorf(err, "Unable to download the git repo %s", gitRepoWithRef) + spinner.Errorf(err, "Unable to download the git repo %s", h.Chart.URL) return "", err } return gitCfg.GitPath, nil } - -// Helper function src/pkg/packager/prepare.goL118 src/pkg/packager/create.goL328 -func (h *Helm) TransformGitURL(charts string) (string, error) { - // calling GitTransformURLSplitRef() to see if refPlain is empty - _, refPlain, _ := transform.GitTransformURLSplitRef(h.Chart.URL) - - // if it is append chart version as if its a tag - if refPlain == "" { - h.Chart.URL = fmt.Sprintf("%s@%s", h.Chart.URL, h.Chart.Version) - } - - // reduce code duplication - return h.PackageChartFromGit(charts) - -} diff --git a/src/pkg/packager/create.go b/src/pkg/packager/create.go index 22f5f3d238..ae333d94b1 100755 --- a/src/pkg/packager/create.go +++ b/src/pkg/packager/create.go @@ -10,7 +10,6 @@ import ( "fmt" "os" "path/filepath" - "regexp" "strconv" "strings" "time" @@ -326,10 +325,7 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel // If any helm charts are defined, process them. for chartIdx, chart := range component.Charts { - // https://regex101.com/r/jYLoUy/1 - re := regexp.MustCompile(`\.git@`) - // check if the chart is a git url with a ref - isGitURL := re.MatchString(chart.URL) + helmCfg := helm.Helm{ Chart: chart, Cfg: p.cfg, @@ -345,19 +341,10 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel } p.cfg.Pkg.Components[index].Charts[chartIdx].LocalPath = rel - } else if isGitURL { - _, err = helmCfg.TransformGitURL(componentPath.Charts) - // _, err = helmCfg.PackageChartFromGit(componentPath.Charts) - if err != nil { - return fmt.Errorf("error creating chart archive, unable to pull the chart from git: %s", err.Error()) - } - } else if len(chart.URL) > 0 { - helmCfg.DownloadPublishedChart(componentPath.Charts) } else { - path := helmCfg.PackageChartFromLocalFiles(componentPath.Charts) - zarfFilename := fmt.Sprintf("%s-%s.tgz", chart.Name, chart.Version) - if !strings.HasSuffix(path, zarfFilename) { - return fmt.Errorf("error creating chart archive, user provided chart name and/or version does not match given chart") + err := helmCfg.PackageChart(componentPath.Charts) + if err != nil { + return err } } diff --git a/src/pkg/packager/prepare.go b/src/pkg/packager/prepare.go index c16f92e2cb..7651d451ad 100644 --- a/src/pkg/packager/prepare.go +++ b/src/pkg/packager/prepare.go @@ -113,30 +113,19 @@ func (p *Packager) FindImages(baseDir, repoHelmChartPath string, kubeVersionOver if len(component.Charts) > 0 { _ = utils.CreateDirectory(componentPath.Charts, 0700) _ = utils.CreateDirectory(componentPath.Values, 0700) - // https://regex101.com/r/jYLoUy/1 - re := regexp.MustCompile(`\.git@`) for _, chart := range component.Charts { - // check if the chart is a git url with a ref - isGitURL := re.MatchString(chart.URL) + helmCfg := helm.Helm{ Chart: chart, Cfg: p.cfg, } helmCfg.Cfg.State = types.ZarfState{} - if isGitURL { - path, err := helmCfg.TransformGitURL(componentPath.Charts) - // path, err := helmCfg.PackageChartFromGit(componentPath.Charts) - if err != nil { - return fmt.Errorf("unable to download chart from git repo (%s): %w", chart.URL, err) - } - // track the actual chart path - chartOverrides[chart.Name] = path - } else if len(chart.URL) > 0 { - helmCfg.DownloadPublishedChart(componentPath.Charts) - } else { - helmCfg.PackageChartFromLocalFiles(componentPath.Charts) + + err := helmCfg.PackageChart(componentPath.Charts) + if err != nil { + return fmt.Errorf("unable to package the chart %s: %s", chart.URL, err.Error()) } for idx, path := range chart.ValuesFiles { diff --git a/src/pkg/transform/git.go b/src/pkg/transform/git.go index 95e08c6be5..eaba467f2e 100644 --- a/src/pkg/transform/git.go +++ b/src/pkg/transform/git.go @@ -12,8 +12,8 @@ import ( "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" ) -// For further explanation: https://regex101.com/r/YxpfhC/3 -var gitURLRegex = regexp.MustCompile(`^(?P[a-z]+:\/\/)(?P.+?)\/(?P[\w\-\.]+?)(?P\.git)?(?P@(?P\+)?(?P[\/\+\w\-\.]+))?(?P\/(?Pinfo\/.*|git-upload-pack|git-receive-pack))?$`) +// For further explanation: https://regex101.com/r/YxpfhC/5 +var gitURLRegex = regexp.MustCompile(`^(?P[a-z]+:\/\/)(?P.+?)\/(?P[\w\-\.]+?)?(?P\.git)?(\/)?(?P@(?P\+)?(?P[\/\+\w\-\.]+))?(?P\/(?Pinfo\/.*|git-upload-pack|git-receive-pack))?$`) // MutateGitURLsInText changes the gitURL hostname to use the repository Zarf is configured to use. func MutateGitURLsInText(logger Log, targetBaseURL string, text string, pushUser string) string { From 9048dd0772eb5dee3223d8eb4c24db3d354a5f27 Mon Sep 17 00:00:00 2001 From: razzle Date: Fri, 7 Jul 2023 13:17:46 -0500 Subject: [PATCH 03/13] `zarf bundle` ADR (#1820) ## Description Creates a `pending` ADR for initial work on `zarf bundle`. ## Related Issue Relates to https://github.com/defenseunicorns/zarf/issues/1711 ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Other (security config, docs update, etc) ## Checklist before merging - [ ] Test, docs, adr added or updated as needed - [x] [Contributor Guide Steps](https://github.com/defenseunicorns/zarf/blob/main/CONTRIBUTING.md#developer-workflow) followed --------- Signed-off-by: razzle Co-authored-by: Wayne Starr Co-authored-by: Casey Wylie --- adr/0017-zarf-bundle.md | 123 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 adr/0017-zarf-bundle.md diff --git a/adr/0017-zarf-bundle.md b/adr/0017-zarf-bundle.md new file mode 100644 index 0000000000..fc62487181 --- /dev/null +++ b/adr/0017-zarf-bundle.md @@ -0,0 +1,123 @@ +# 17. Bundles + +Date: 2023-06-13 + +## Status + +Pending + +## Context + +Orchestrating capabilities from multiple Zarf packages into meta-packages is a current weak point for Zarf. The core of Zarf was built around components as capabilities, but as Zarf packages have scaled, there has been a need to create a new boundary to manage these capabilities efficiently. + +Currently there is no official way to enable the deployment, publishing, pulling, and creation of multiple Zarf packages together, and due to this some in the community have resorted to patterns such as: + +```yaml +- name: init + required: true + files: + - source: zarf-init-amd64-v0.27.0.tar.zst + target: zarf-init-amd64-v0.27.0.tar.zst + actions: + onDeploy: + after: + - cmd: zarf package deploy zarf-init-amd64-v0.27.0.tar.zst --components git-server --confirm -l warn +``` + +While this _does_ fulfill the need to deploy two packages in one command, it does so in such a way that is verbose within the `zarf.yaml`, brittle across Zarf versions, inefficient within the package structure (it doesn't share layers), and is difficult to use `variables` with. + +### Proposed Solutions + +There are currently three proposed solutions to this, each with their own pros and cons: + +#### Zarf Bundle + +Uses OCI and a separate Zarf schema / declarative YAML definition to pull packages into a single artifact and orchestrate them together. + +Pros: + +- maintains efficient OCI layering / deduping of shared package resources +- allows flexibility in defining what `zarf bundle` (or a separate command) would look like as its own command without polluting `zarf package` + +Cons: + +- variables set within packages with `setVariables` may be difficult to share across packages +- package sources in bundles would be best to keep as OCI _only_, without support for local packages. This would help us ensure there are versions for packages and would help with efficiency by taking advantage of things like layer deduping. + +#### Super Zarf Packages + +Adds a packages key or another way to overlay packages into a larger package with the same internal structure as current Zarf packages. + +Pros: + +- packages would maintain the same syntax under `zarf package` between normal / meta packages. + +Cons: + +- it would be difficult to properly scope things like variables and helm chart names properly across packages. +- this continues to add to `zarf package` making it more complex and harder to test + +#### Zarf Package Manager + +Uses a separate binary (not `zarf`) to pull and manage packages together - this would also include dependency declaration and resolution between packages. + +Pros: + +- this is a familiar/expressive way to solve the package problem and would be familiar to developers and system administrators +- allows flexibility in defining what the package manager would look like as its own command without polluting `zarf package` + +Cons: + +- dependencies may be difficult to determine whether they are "installed"/"deployed" - particularly for pre-cluster resources +- variables set within packages with `setVariables` may be difficult to share across packages +- this would necessitate a separate binary with it's own CLI that would need its own release schedule and maintenance + +> :warning: **NOTE**: The package manager could also be made to be OCI-only but would come with the same OCI pros/cons. + +## Decision + +The current proposition (subject to change before acceptance) is **Zarf Bundles**, which a following PR will focus on and create a POC of. + +In essense the `zarf-bundle.yaml` would look something like so: + +```yaml +metadata: + name: omnibus + description: an example Zarf bundle + version: 0.0.1 + architecture: amd64 + +packages: + - repository: localhost:888/init + ref: "###ZARF_BNDL_TMPL_INIT_VERSION###" + optional-components: + - git-server + - repository: ghcr.io/defenseunicorns/packages/dubbd + ref: 0.0.1 # OCI spec compliant reference + # arch is not needed as it will use w/e arch is set in the bundle's metadata + optional-components: # by default, all required components will be included + - "*" # include all optional components + - repository: docker.io// + ref: 0.0.1 + optional-components: + - preflight + - aws-* # include all optional components that start with "aws-" +``` + +Bundle would be a new top-level command in Zarf with a full compliment of sub-commands (mirroring the pattern of `zarf package`): + +- `zarf bundle create -o oci://|` +- `zarf bundle deploy oci://|` +- `zarf bundle inspect oci://|` +- `zarf bundle list` --> will probably just show the same as `zarf package list` +- ~~`zarf bundle publish`~~ --> Bundles will be OCI only, so there is no need for a publish command, `create` will handle that. +- `zarf bundle pull oci:// -o ` +- `zarf bundle remove oci://|` + +## Consequences + +This does add complexity to the Zarf codebase, as it is the addition of an entire suite of commands, JSON schema, schema docs, CLI docs, and a chunk of library code + tests. It is a good litmus test of the current packager and OCI codebase to see how ready it is to be consumed as a library. + +Additionally, this does add a new layer of complexity to the Zarf ecosystem, as meta-package maintainers must now also be aware of this bundling process, syntax and schema. This is a necessary evil however, as the current pattern of using `zarf package deploy` to deploy multiple packages is not sustainable at the scale we are seeing. + +There is also the hard requirement that packages bundled must be first published to a registry available to the person performing the bundle operation. This removes some ability to develop bundles on an air gapped environment, but the team believes that in such scenarios, the air gapped environment should be _receiving_ a bundle, rather than developing one internally. If this assumption is incorrect however there are options for us to allow the creation of bundles from OCI directories on local systems if we need to or we could provide more provisions within Zarf to make it easier to connect to air gap registries to mirror bundles. From ea6ca342a2ded8ab047c6233a24061be5a9f9499 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Fri, 7 Jul 2023 17:00:24 -0400 Subject: [PATCH 04/13] [TASK] buildChartDependencies Signed-off-by: Case Wylie --- src/internal/packager/helm/repo.go | 34 ++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index 67831f9da7..7de5c01e30 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -34,7 +34,7 @@ func (h *Helm) PackageChart(destination string) error { } if isGitURL { - // if it is append chart version as if its a tag + // if it is git url append chart version as if its a tag if refPlain == "" { h.Chart.URL = fmt.Sprintf("%s@%s", h.Chart.URL, h.Chart.Version) } @@ -68,7 +68,7 @@ func (h *Helm) PackageChartFromLocalFiles(destination string) (string, error) { spinner.Errorf(err, "Validation failed for chart from %s (%s)", h.Chart.LocalPath, err.Error()) return "", err } - + h.buildChartDependencies(spinner) client := action.NewPackage() client.Destination = destination @@ -181,3 +181,33 @@ func (h *Helm) DownloadChartFromGitToTemp(spinner *message.Spinner) (string, err return gitCfg.GitPath, nil } + +// buildChartDependencies builds the helm chart dependencies +func (h *Helm) buildChartDependencies(spinner *message.Spinner) error { + regClient, err := registry.NewClient(registry.ClientOptEnableCache(true)) + if err != nil { + spinner.Fatalf(err, "Unable to create a new registry client") + } + + settings := cli.New() + + man := &downloader.Manager{ + Out: os.Stdout, + ChartPath: h.Chart.LocalPath, + Getters: getter.All(settings), + RegistryClient: regClient, + + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, + Debug: false, + } + // Verify the chart + man.Verify = downloader.VerifyIfPossible + + // Build the deps from the helm chart + err = man.Build() + if e, ok := err.(downloader.ErrRepoNotFound); ok { + return fmt.Errorf("%s. Please add the missing repos via 'helm repo add'", e.Error()) + } + return nil +} From 42019e5e3e654e3164536372599b71f3c95d0022 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Sun, 9 Jul 2023 15:35:09 -0400 Subject: [PATCH 05/13] [TASK] dependency build Signed-off-by: Case Wylie --- src/internal/packager/helm/common.go | 2 ++ src/internal/packager/helm/repo.go | 10 ++++------ src/internal/packager/helm/utils.go | 7 ++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/internal/packager/helm/common.go b/src/internal/packager/helm/common.go index e5682f0092..34e9047702 100644 --- a/src/internal/packager/helm/common.go +++ b/src/internal/packager/helm/common.go @@ -11,6 +11,7 @@ import ( "github.com/defenseunicorns/zarf/src/types" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/cli" ) // Helm is a config object for working with helm charts. @@ -25,6 +26,7 @@ type Helm struct { Cluster *cluster.Cluster Cfg *types.PackagerConfig KubeVersion string + Settings *cli.EnvSettings actionConfig *action.Configuration } diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index 7de5c01e30..f0d813e48d 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -188,17 +188,15 @@ func (h *Helm) buildChartDependencies(spinner *message.Spinner) error { if err != nil { spinner.Fatalf(err, "Unable to create a new registry client") } - - settings := cli.New() - + h.Settings = cli.New() man := &downloader.Manager{ Out: os.Stdout, ChartPath: h.Chart.LocalPath, - Getters: getter.All(settings), + Getters: getter.All(h.Settings), RegistryClient: regClient, - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, + RepositoryConfig: h.Settings.RepositoryConfig, + RepositoryCache: h.Settings.RepositoryCache, Debug: false, } // Verify the chart diff --git a/src/internal/packager/helm/utils.go b/src/internal/packager/helm/utils.go index 13e30d978b..472a5c4978 100644 --- a/src/internal/packager/helm/utils.go +++ b/src/internal/packager/helm/utils.go @@ -66,13 +66,14 @@ func (h *Helm) parseChartValues() (map[string]any, error) { func (h *Helm) createActionConfig(namespace string, spinner *message.Spinner) error { // Initialize helm SDK actionConfig := new(action.Configuration) - settings := cli.New() + // Set the setings for the helm SDK + h.Settings = cli.New() // Set the namespace for helm - settings.SetNamespace(namespace) + h.Settings.SetNamespace(namespace) // Setup K8s connection - err := actionConfig.Init(settings.RESTClientGetter(), namespace, "", spinner.Updatef) + err := actionConfig.Init(h.Settings.RESTClientGetter(), namespace, "", spinner.Updatef) // Set the actionConfig is the received Helm pointer h.actionConfig = actionConfig From 718f55327e8291a301f5312ff79876e6838ee30e Mon Sep 17 00:00:00 2001 From: razzle Date: Mon, 10 Jul 2023 09:57:59 -0500 Subject: [PATCH 06/13] Hotfix: only verify authentication to registry if creds exist (#1893) ## Description Fixes what I broke. This does re-architect some portions of OrasRemote and locks it down more. Everything in `*remote.Registry` is no longer exposed to outside usage and users of this remote client are restricted to the public receiver methods written in `pkg/oci`. The context is now private as it really should not be edited outside of private receivers within OrasRemote. During the writing of this PR I found out that ORAs already handles scopes at the request level and there is zero need to handle scopes yourself. I have not checked if I never had to do this, or if ORAs updated. ## Related Issue Fixes #1881 Fixes #1795 Fixes #1821 ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Other (security config, docs update, etc) ## Checklist before merging - [x] Test, docs, adr added or updated as needed - [x] [Contributor Guide Steps](https://github.com/defenseunicorns/zarf/blob/main/CONTRIBUTING.md#developer-workflow) followed --------- Signed-off-by: razzle --- src/pkg/oci/common.go | 70 ++++++------------- src/pkg/oci/pull.go | 4 +- src/pkg/oci/push.go | 43 +++++------- src/pkg/oci/utils.go | 4 +- src/pkg/packager/publish.go | 2 +- src/test/common.go | 15 +--- src/test/e2e/50_oci_package_test.go | 5 ++ .../e2e/52_oci_compose_differential_test.go | 2 +- 8 files changed, 49 insertions(+), 96 deletions(-) diff --git a/src/pkg/oci/common.go b/src/pkg/oci/common.go index 4eab41dfc3..a61d09a8b6 100644 --- a/src/pkg/oci/common.go +++ b/src/pkg/oci/common.go @@ -6,7 +6,6 @@ package oci import ( "context" - "errors" "fmt" "net/http" "strings" @@ -31,8 +30,8 @@ const ( // OrasRemote is a wrapper around the Oras remote repository that includes a progress bar for interactive feedback. type OrasRemote struct { - *remote.Repository - context.Context + repo *remote.Repository + ctx context.Context Transport *utils.Transport CopyOpts oras.CopyOptions client *auth.Client @@ -47,18 +46,13 @@ func NewOrasRemote(url string) (*OrasRemote, error) { return nil, fmt.Errorf("failed to parse OCI reference: %w", err) } o := &OrasRemote{} - o.Context = context.TODO() + o.ctx = context.TODO() err = o.WithRepository(ref) if err != nil { return nil, err } - err = o.CheckAuth() - if err != nil { - return nil, fmt.Errorf("unable to authenticate to %s: %s", ref.Registry, err.Error()) - } - copyOpts := oras.DefaultCopyOptions copyOpts.OnCopySkipped = o.printLayerSuccess copyOpts.PostCopy = o.printLayerSuccess @@ -86,32 +80,35 @@ func (o *OrasRemote) WithRepository(ref registry.Reference) error { } repo.PlainHTTP = zarfconfig.CommonOptions.Insecure repo.Client = o.client - o.Repository = repo + o.repo = repo return nil } -// withScopes returns a context with the given scopes. -// -// This is needed for pushing to Docker Hub. -func withScopes(ref registry.Reference) context.Context { - // For pushing to Docker Hub, we need to set the scope to the repository with pull+push actions, otherwise a 401 is returned - scopes := []string{ - fmt.Sprintf("repository:%s:pull,push", ref.Repository), - } - return auth.WithScopes(context.TODO(), scopes...) -} - // withAuthClient returns an auth client for the given reference. // // The credentials are pulled using Docker's default credential store. func (o *OrasRemote) withAuthClient(ref registry.Reference) (*auth.Client, error) { + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.TLSClientConfig.InsecureSkipVerify = zarfconfig.CommonOptions.Insecure + + o.Transport = utils.NewTransport(transport, nil) + + client := &auth.Client{ + Cache: auth.DefaultCache, + Client: &http.Client{ + Transport: o.Transport, + }, + } + client.SetUserAgent("zarf/" + zarfconfig.CLIVersion) + message.Debugf("Loading docker config file from default config location: %s", config.Dir()) cfg, err := config.Load(config.Dir()) if err != nil { return nil, err } if !cfg.ContainsAuth() { - return nil, errors.New("no docker config file found, run 'zarf tools registry login --help'") + message.Debug("no docker config file found, run 'zarf tools registry login --help'") + return client, nil } configs := []*configfile.ConfigFile{cfg} @@ -127,10 +124,6 @@ func (o *OrasRemote) withAuthClient(ref registry.Reference) (*auth.Client, error return nil, fmt.Errorf("unable to get credentials for %s: %w", key, err) } - if authConf.ServerAddress != "" { - o.Context = withScopes(ref) - } - cred := auth.Credential{ Username: authConf.Username, Password: authConf.Password, @@ -138,30 +131,7 @@ func (o *OrasRemote) withAuthClient(ref registry.Reference) (*auth.Client, error RefreshToken: authConf.IdentityToken, } - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.TLSClientConfig.InsecureSkipVerify = zarfconfig.CommonOptions.Insecure - - o.Transport = utils.NewTransport(transport, nil) - - client := &auth.Client{ - Credential: auth.StaticCredential(ref.Registry, cred), - Cache: auth.NewCache(), - Client: &http.Client{ - Transport: o.Transport, - }, - } - client.SetUserAgent("zarf/" + zarfconfig.CLIVersion) + client.Credential = auth.StaticCredential(ref.Registry, cred) return client, nil } - -// CheckAuth checks if the user is authenticated to the remote registry. -func (o *OrasRemote) CheckAuth() error { - reg, err := remote.NewRegistry(o.Repository.Reference.Registry) - if err != nil { - return err - } - reg.PlainHTTP = zarfconfig.CommonOptions.Insecure - reg.Client = o.client - return reg.Ping(o.Context) -} diff --git a/src/pkg/oci/pull.go b/src/pkg/oci/pull.go index 42dc71b568..076ffa942d 100644 --- a/src/pkg/oci/pull.go +++ b/src/pkg/oci/pull.go @@ -123,7 +123,7 @@ func (o *OrasRemote) LayersFromRequestedComponents(requestedComponents []string) // - zarf.yaml.sig func (o *OrasRemote) PullPackage(destinationDir string, concurrency int, layersToPull ...ocispec.Descriptor) (partialPaths []string, err error) { isPartialPull := len(layersToPull) > 0 - ref := o.Reference + ref := o.repo.Reference pterm.Println() message.Debugf("Pulling %s", ref.String()) @@ -181,7 +181,7 @@ func (o *OrasRemote) PullPackage(destinationDir string, concurrency int, layersT var wg sync.WaitGroup wg.Add(1) go utils.RenderProgressBarForLocalDirWrite(destinationDir, estimatedBytes, &wg, doneSaving, "Pulling Zarf package data") - _, err = oras.Copy(o.Context, o.Repository, ref.String(), dst, ref.String(), copyOpts) + _, err = oras.Copy(o.ctx, o.repo, ref.String(), dst, ref.String(), copyOpts) if err != nil { return partialPaths, err } diff --git a/src/pkg/oci/push.go b/src/pkg/oci/push.go index 109bb7d2e1..dcee9728ed 100644 --- a/src/pkg/oci/push.go +++ b/src/pkg/oci/push.go @@ -36,19 +36,10 @@ type ConfigPartial struct { Annotations map[string]string `json:"annotations,omitempty"` } -// PushFile pushes the file at the given path to the remote repository. -func (o *OrasRemote) PushFile(path string) (*ocispec.Descriptor, error) { - b, err := os.ReadFile(path) - if err != nil { - return nil, err - } - return o.PushBytes(b, ZarfLayerMediaTypeBlob) -} - -// PushBytes pushes the given bytes to the remote repository. -func (o *OrasRemote) PushBytes(b []byte, mediaType string) (*ocispec.Descriptor, error) { +// PushLayer pushes the given layer (bytes) to the remote repository. +func (o *OrasRemote) PushLayer(b []byte, mediaType string) (*ocispec.Descriptor, error) { desc := content.NewDescriptorFromBytes(mediaType, b) - return &desc, o.Push(o.Context, desc, bytes.NewReader(b)) + return &desc, o.repo.Push(o.ctx, desc, bytes.NewReader(b)) } func (o *OrasRemote) pushManifestConfigFromMetadata(metadata *types.ZarfMetadata, build *types.ZarfBuildData) (*ocispec.Descriptor, error) { @@ -65,7 +56,7 @@ func (o *OrasRemote) pushManifestConfigFromMetadata(metadata *types.ZarfMetadata if err != nil { return nil, err } - return o.PushBytes(manifestConfigBytes, ocispec.MediaTypeImageConfig) + return o.PushLayer(manifestConfigBytes, ocispec.MediaTypeImageConfig) } func (o *OrasRemote) manifestAnnotationsFromMetadata(metadata *types.ZarfMetadata) map[string]string { @@ -98,11 +89,11 @@ func (o *OrasRemote) generatePackManifest(src *file.Store, descs []ocispec.Descr packOpts.PackImageManifest = true packOpts.ManifestAnnotations = o.manifestAnnotationsFromMetadata(metadata) - root, err := oras.Pack(o.Context, src, ocispec.MediaTypeImageManifest, descs, packOpts) + root, err := oras.Pack(o.ctx, src, ocispec.MediaTypeImageManifest, descs, packOpts) if err != nil { return ocispec.Descriptor{}, err } - if err = src.Tag(o.Context, root, root.Digest.String()); err != nil { + if err = src.Tag(o.ctx, root, root.Digest.String()); err != nil { return ocispec.Descriptor{}, err } @@ -111,7 +102,7 @@ func (o *OrasRemote) generatePackManifest(src *file.Store, descs []ocispec.Descr // PublishPackage publishes the package to the remote repository. func (o *OrasRemote) PublishPackage(pkg *types.ZarfPackage, sourceDir string, concurrency int) error { - ctx := o.Context + ctx := o.ctx // source file store src, err := file.New(sourceDir) if err != nil { @@ -119,7 +110,7 @@ func (o *OrasRemote) PublishPackage(pkg *types.ZarfPackage, sourceDir string, co } defer src.Close() - message.Infof("Publishing package to %s", o.Reference.String()) + message.Infof("Publishing package to %s", o.repo.Reference) spinner := message.NewProgressSpinner("") defer spinner.Stop() @@ -167,7 +158,7 @@ func (o *OrasRemote) PublishPackage(pkg *types.ZarfPackage, sourceDir string, co } // assumes referrers API is not supported since OCI artifact // media type is not supported - o.SetReferrersCapability(false) + o.repo.SetReferrersCapability(false) // push the manifest config // since this config is so tiny, and the content is not used again @@ -182,17 +173,17 @@ func (o *OrasRemote) PublishPackage(pkg *types.ZarfPackage, sourceDir string, co } total += root.Size + manifestConfigDesc.Size - o.Transport.ProgressBar = message.NewProgressBar(total, fmt.Sprintf("Publishing %s:%s", o.Reference.Repository, o.Reference.Reference)) + o.Transport.ProgressBar = message.NewProgressBar(total, fmt.Sprintf("Publishing %s:%s", o.repo.Reference.Repository, o.repo.Reference.Reference)) defer o.Transport.ProgressBar.Stop() // attempt to push the image manifest - _, err = oras.Copy(ctx, src, root.Digest.String(), o, o.Reference.Reference, copyOpts) + _, err = oras.Copy(ctx, src, root.Digest.String(), o.repo, o.repo.Reference.Reference, copyOpts) if err != nil { return err } - o.Transport.ProgressBar.Successf("Published %s [%s]", o.Reference, root.MediaType) + o.Transport.ProgressBar.Successf("Published %s [%s]", o.repo.Reference, root.MediaType) message.HorizontalRule() - if strings.HasSuffix(o.Reference.String(), SkeletonSuffix) { + if strings.HasSuffix(o.repo.Reference.String(), SkeletonSuffix) { message.Title("How to import components from this skeleton:", "") ex := []types.ZarfComponent{} for _, c := range pkg.Components { @@ -200,7 +191,7 @@ func (o *OrasRemote) PublishPackage(pkg *types.ZarfPackage, sourceDir string, co Name: fmt.Sprintf("import-%s", c.Name), Import: types.ZarfComponentImport{ ComponentName: c.Name, - URL: fmt.Sprintf("oci://%s", o.Reference), + URL: fmt.Sprintf("oci://%s", o.repo.Reference), }, }) } @@ -211,9 +202,9 @@ func (o *OrasRemote) PublishPackage(pkg *types.ZarfPackage, sourceDir string, co flags = "--insecure" } message.Title("To inspect/deploy/pull:", "") - message.ZarfCommand("package inspect oci://%s %s", o.Reference, flags) - message.ZarfCommand("package deploy oci://%s %s", o.Reference, flags) - message.ZarfCommand("package pull oci://%s %s", o.Reference, flags) + message.ZarfCommand("package inspect oci://%s %s", o.repo.Reference, flags) + message.ZarfCommand("package deploy oci://%s %s", o.repo.Reference, flags) + message.ZarfCommand("package pull oci://%s %s", o.repo.Reference, flags) } return nil diff --git a/src/pkg/oci/utils.go b/src/pkg/oci/utils.go index 8c0298c8e0..dcde4fd252 100644 --- a/src/pkg/oci/utils.go +++ b/src/pkg/oci/utils.go @@ -55,7 +55,7 @@ func ReferenceFromMetadata(registryLocation string, metadata *types.ZarfMetadata // FetchRoot fetches the root manifest from the remote repository. func (o *OrasRemote) FetchRoot() (*ZarfOCIManifest, error) { // get the manifest descriptor - descriptor, err := o.Resolve(o.Context, o.Reference.Reference) + descriptor, err := o.repo.Resolve(o.ctx, o.repo.Reference.Reference) if err != nil { return nil, err } @@ -88,7 +88,7 @@ func (o *OrasRemote) FetchManifest(desc ocispec.Descriptor) (manifest *ZarfOCIMa // FetchLayer fetches the layer with the given descriptor from the remote repository. func (o *OrasRemote) FetchLayer(desc ocispec.Descriptor) (bytes []byte, err error) { - return content.FetchAll(o.Context, o, desc) + return content.FetchAll(o.ctx, o.repo, desc) } // FetchZarfYAML fetches the zarf.yaml file from the remote repository. diff --git a/src/pkg/packager/publish.go b/src/pkg/packager/publish.go index cb951e31d6..1f7a0875fb 100644 --- a/src/pkg/packager/publish.go +++ b/src/pkg/packager/publish.go @@ -66,7 +66,7 @@ func (p *Packager) Publish() error { } } - message.HeaderInfof("📦 PACKAGE PUBLISH %s:%s", p.cfg.Pkg.Metadata.Name, p.remote.Reference.Reference) + message.HeaderInfof("📦 PACKAGE PUBLISH %s:%s", p.cfg.Pkg.Metadata.Name, ref) // Publish the package/skeleton to the registry return p.remote.PublishPackage(&p.cfg.Pkg, p.tmp.Base, config.CommonOptions.OCIConcurrency) diff --git a/src/test/common.go b/src/test/common.go index e538fadf52..9b0af1d26d 100644 --- a/src/test/common.go +++ b/src/test/common.go @@ -15,8 +15,6 @@ import ( "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" - dconfig "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/config/configfile" "github.com/stretchr/testify/require" ) @@ -101,22 +99,11 @@ func (e2e *ZarfE2ETest) GetLogFileContents(t *testing.T, stdErr string) string { } // SetupDockerRegistry uses the host machine's docker daemon to spin up a local registry for testing purposes. -func (e2e *ZarfE2ETest) SetupDockerRegistry(t *testing.T, port int) *configfile.ConfigFile { +func (e2e *ZarfE2ETest) SetupDockerRegistry(t *testing.T, port int) { // spin up a local registry registryImage := "registry:2.8.2" err := exec.CmdWithPrint("docker", "run", "-d", "--restart=always", "-p", fmt.Sprintf("%d:5000", port), "--name", "registry", registryImage) require.NoError(t, err) - - // docker config folder - cfg, err := dconfig.Load(dconfig.Dir()) - require.NoError(t, err) - if !cfg.ContainsAuth() { - // make a docker config file w/ some blank creds - _, _, err := e2e.Zarf("tools", "registry", "login", "--username", "zarf", "-p", "zarf", "localhost:6000") - require.NoError(t, err) - } - - return cfg } // GetZarfVersion returns the current build/zarf version diff --git a/src/test/e2e/50_oci_package_test.go b/src/test/e2e/50_oci_package_test.go index 1034c832b3..f93675a2e8 100644 --- a/src/test/e2e/50_oci_package_test.go +++ b/src/test/e2e/50_oci_package_test.go @@ -122,6 +122,11 @@ func (suite *RegistryClientTestSuite) Test_3_Inspect() { // Test inspect w/ bad ref. _, stdErr, err = e2e.Zarf("package", "inspect", "oci://"+badRef.String(), "--insecure") suite.Error(err, stdErr) + + // Test inspect on a public package. + // NOTE: This also makes sure that Zarf does not attempt auth when inspecting a public package. + _, stdErr, err = e2e.Zarf("package", "inspect", "oci://ghcr.io/defenseunicorns/packages/dubbd-k3d:0.3.0-amd64") + suite.NoError(err, stdErr) } func (suite *RegistryClientTestSuite) Test_4_Pull_And_Deploy() { diff --git a/src/test/e2e/52_oci_compose_differential_test.go b/src/test/e2e/52_oci_compose_differential_test.go index 6167221853..78f6c815a8 100644 --- a/src/test/e2e/52_oci_compose_differential_test.go +++ b/src/test/e2e/52_oci_compose_differential_test.go @@ -41,7 +41,7 @@ func (suite *OCIDifferentialSuite) SetupSuite() { differentialPackageName = fmt.Sprintf("zarf-package-podinfo-with-oci-flux-%s-v0.24.0-differential-v0.25.0.tar.zst", e2e.Arch) normalPackageName = fmt.Sprintf("zarf-package-podinfo-with-oci-flux-%s-v0.24.0.tar.zst", e2e.Arch) - _ = e2e.SetupDockerRegistry(suite.T(), 555) + e2e.SetupDockerRegistry(suite.T(), 555) suite.Reference.Registry = "localhost:555" } From 7d173a2c92fe3f3752584503380ac7e913dd2f07 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 10 Jul 2023 16:01:10 -0500 Subject: [PATCH 07/13] Add ExitOnInterrupt utils function; catch interrupts when using wait (#1891) Add ExitOnInterrupt utils function; catch interrupts when using wait ## Description Add ability to catch interrupts when using wait command. ## Related Issue Fixes #1864 ## Type of change - [X] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Other (security config, docs update, etc) ## Checklist before merging - [X] Test, docs, adr added or updated as needed - [X] [Contributor Guide Steps](https://github.com/defenseunicorns/zarf/blob/main/CONTRIBUTING.md#developer-workflow) followed --------- Co-authored-by: Wayne Starr --- src/cmd/root.go | 3 +++ src/cmd/tools/common.go | 2 ++ src/cmd/tools/crane.go | 9 +++++++-- src/config/lang/english.go | 1 + src/pkg/utils/exec/exec.go | 13 +++++++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index d9c1d4e1db..8117d4eeab 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -13,6 +13,7 @@ import ( "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/types" "github.com/pterm/pterm" "github.com/spf13/cobra" @@ -37,6 +38,8 @@ var rootCmd = &cobra.Command{ return } + exec.ExitOnInterrupt() + // Don't add the logo to the help command if cmd.Parent() == nil { config.SkipLogFile = true diff --git a/src/cmd/tools/common.go b/src/cmd/tools/common.go index 9ce2bf54d8..18f5da8c7c 100644 --- a/src/cmd/tools/common.go +++ b/src/cmd/tools/common.go @@ -10,6 +10,7 @@ import ( "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" + "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" "github.com/spf13/cobra" ) @@ -35,6 +36,7 @@ var toolsCmd = &cobra.Command{ Aliases: []string{"t"}, PersistentPreRun: func(cmd *cobra.Command, args []string) { config.SkipLogFile = true + exec.ExitOnInterrupt() }, Short: lang.CmdToolsShort, } diff --git a/src/cmd/tools/crane.go b/src/cmd/tools/crane.go index ef33aa447b..8a9b82d445 100644 --- a/src/cmd/tools/crane.go +++ b/src/cmd/tools/crane.go @@ -6,17 +6,19 @@ package tools import ( "fmt" + "os" + "strings" + "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/internal/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/utils/exec" craneCmd "github.com/google/go-containerregistry/cmd/crane/cmd" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/logs" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/spf13/cobra" - "os" - "strings" ) func init() { @@ -33,6 +35,9 @@ func init() { Aliases: []string{"r", "crane"}, Short: lang.CmdToolsRegistryShort, PersistentPreRun: func(cmd *cobra.Command, args []string) { + + exec.ExitOnInterrupt() + // The crane options loading here comes from the rootCmd of crane craneOptions = append(craneOptions, crane.WithContext(cmd.Context())) // TODO(jonjohnsonjr): crane.Verbose option? diff --git a/src/config/lang/english.go b/src/config/lang/english.go index ca50cabf18..86498572df 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -513,4 +513,5 @@ const ( var ( ErrInitNotFound = errors.New("this command requires a zarf-init package, but one was not found on the local system. Re-run the last command again without '--confirm' to download the package") ErrUnableToCheckArch = errors.New("unable to get the configured cluster's architecture") + ErrInterrupt = errors.New("Failed due to interrupt") ) diff --git a/src/pkg/utils/exec/exec.go b/src/pkg/utils/exec/exec.go index 1df4f93611..e5bebeb745 100644 --- a/src/pkg/utils/exec/exec.go +++ b/src/pkg/utils/exec/exec.go @@ -12,10 +12,13 @@ import ( "io" "os" "os/exec" + "os/signal" "runtime" "strings" "sync" + "syscall" + "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/types" ) @@ -207,3 +210,13 @@ func GetOSShell(shellPref types.ZarfComponentActionShell) (string, string) { func IsPowershell(shellName string) bool { return shellName == "powershell" || shellName == "pwsh" } + +// ExitOnInterrupt catches an interrupt and exits with fatal error +func ExitOnInterrupt() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + message.Fatal(lang.ErrInterrupt, lang.ErrInterrupt.Error()) + }() +} From 036760d767200edb9d3f7612ad8aaeb4a4e120dd Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Mon, 10 Jul 2023 17:01:15 -0400 Subject: [PATCH 08/13] [TASK] Update bb test for new development Signed-off-by: Case Wylie --- src/extensions/bigbang/bigbang.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/extensions/bigbang/bigbang.go b/src/extensions/bigbang/bigbang.go index a0715fbb8c..59b9e7b0b6 100644 --- a/src/extensions/bigbang/bigbang.go +++ b/src/extensions/bigbang/bigbang.go @@ -84,12 +84,14 @@ func Run(YOLO bool, tmpPaths types.ComponentPaths, c types.ZarfComponent) (types } } + bbRepo := fmt.Sprintf("%s@%s", cfg.Repo, cfg.Version) + // Configure helm to pull down the Big Bang chart. helmCfg := helm.Helm{ Chart: types.ZarfChart{ Name: bb, Namespace: bb, - URL: cfg.Repo, + URL: bbRepo, Version: cfg.Version, ValuesFiles: cfg.ValuesFiles, GitPath: "./chart", @@ -494,7 +496,7 @@ func findImagesforBBChartRepo(repo string, values chartutil.Values) (images []st chart := types.ZarfChart{ Name: repo, - URL: matches[0], + URL: repo, Version: matches[1], GitPath: "chart", } From 55723c3eb47f05af85e9ef6bcb4261b312914e6a Mon Sep 17 00:00:00 2001 From: Casey Wylie Date: Mon, 10 Jul 2023 17:25:33 -0400 Subject: [PATCH 09/13] Update src/internal/packager/helm/repo.go Co-authored-by: Wayne Starr --- src/internal/packager/helm/repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index f0d813e48d..b0d42798ea 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -34,7 +34,7 @@ func (h *Helm) PackageChart(destination string) error { } if isGitURL { - // if it is git url append chart version as if its a tag + // if it is a git url append chart version as if its a tag if refPlain == "" { h.Chart.URL = fmt.Sprintf("%s@%s", h.Chart.URL, h.Chart.Version) } From 8e0e38b4c08852a3962f1d9786aab3047bd9abd3 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Mon, 10 Jul 2023 17:33:03 -0400 Subject: [PATCH 10/13] [TASK] lint PackageChart Signed-off-by: Case Wylie --- src/internal/packager/helm/repo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index b0d42798ea..6db4b3620e 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -24,6 +24,7 @@ import ( "helm.sh/helm/v3/pkg/repo" ) +// PackageChart creates a chart archive from a path to a chart on the host os and builds chart dependencies func (h *Helm) PackageChart(destination string) error { if len(h.Chart.URL) > 0 { url, refPlain, err := transform.GitTransformURLSplitRef(h.Chart.URL) From 0d0cac8496e397ef63adcc276956b4fcdd50811f Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Tue, 11 Jul 2023 08:25:36 -0400 Subject: [PATCH 11/13] [TASK] remove package name from func and add test cases Signed-off-by: Case Wylie --- src/internal/packager/git/pull.go | 2 +- src/internal/packager/helm/repo.go | 2 +- src/pkg/packager/create.go | 2 +- src/pkg/transform/git.go | 4 ++-- src/pkg/transform/git_test.go | 11 ++++++++--- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/internal/packager/git/pull.go b/src/internal/packager/git/pull.go index 355ae21dee..9601331b48 100644 --- a/src/internal/packager/git/pull.go +++ b/src/internal/packager/git/pull.go @@ -38,7 +38,7 @@ func (g *Git) Pull(gitURL, targetFolder string, shallow bool) error { g.Spinner.Updatef("Processing git repo %s", gitURL) // Split the remote url and the zarf reference - gitURLNoRef, refPlain, err := transform.GitTransformURLSplitRef(gitURL) + gitURLNoRef, refPlain, err := transform.GitURLSplitRef(gitURL) if err != nil { return err } diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index 6db4b3620e..e16667a95b 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -27,7 +27,7 @@ import ( // PackageChart creates a chart archive from a path to a chart on the host os and builds chart dependencies func (h *Helm) PackageChart(destination string) error { if len(h.Chart.URL) > 0 { - url, refPlain, err := transform.GitTransformURLSplitRef(h.Chart.URL) + url, refPlain, err := transform.GitURLSplitRef(h.Chart.URL) // check if the chart is a git url with a ref (if an error is returned url will be empty) isGitURL := strings.HasSuffix(url, ".git") if err != nil { diff --git a/src/pkg/packager/create.go b/src/pkg/packager/create.go index ae333d94b1..ef138461d3 100755 --- a/src/pkg/packager/create.go +++ b/src/pkg/packager/create.go @@ -684,7 +684,7 @@ func (p *Packager) removeCopiesFromDifferentialPackage() error { // Generate a list of all unique repos for this component for _, repoURL := range component.Repos { // Split the remote url and the zarf reference - _, refPlain, err := transform.GitTransformURLSplitRef(repoURL) + _, refPlain, err := transform.GitURLSplitRef(repoURL) if err != nil { return err } diff --git a/src/pkg/transform/git.go b/src/pkg/transform/git.go index eaba467f2e..a54627a881 100644 --- a/src/pkg/transform/git.go +++ b/src/pkg/transform/git.go @@ -29,8 +29,8 @@ func MutateGitURLsInText(logger Log, targetBaseURL string, text string, pushUser return output } -// GitTransformURLSplitRef takes a git url and returns a separated source url and zarf reference. -func GitTransformURLSplitRef(sourceURL string) (string, string, error) { +// GitURLSplitRef takes a git url and returns a separated source url and zarf reference. +func GitURLSplitRef(sourceURL string) (string, string, error) { get, err := helpers.MatchRegex(gitURLRegex, sourceURL) if err != nil { diff --git a/src/pkg/transform/git_test.go b/src/pkg/transform/git_test.go index 4b7b0fd52c..a9f09388e0 100644 --- a/src/pkg/transform/git_test.go +++ b/src/pkg/transform/git_test.go @@ -26,6 +26,7 @@ var gitURLs = []string{ "https://github.com/defenseunicorns/zarf.helm.git", "https://github.com/defenseunicorns/zarf.git@refs/tags/v0.16.0", "https://github.com/DoD-Platform-One/big-bang.git@refs/heads/release-1.54.x", + "https://github.com/prometheus-community/helm-charts.git@kube-prometheus-stack-47.3.0", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "https://github.com/defenseunicorns/zarf.helm.git/info/refs", @@ -70,7 +71,7 @@ func TestMutateGitURLsInText(t *testing.T) { require.Equal(t, expectedText, resultingText) } -func TestGitTransformURLSplitRef(t *testing.T) { +func TestGitURLSplitRef(t *testing.T) { var expectedResult = [][]string{ // Normal git repos and references for pushing/pulling {"https://repo1.dso.mil/platform-one/big-bang/apps/security-tools/twistlock.git", ""}, @@ -87,6 +88,7 @@ func TestGitTransformURLSplitRef(t *testing.T) { {"https://github.com/defenseunicorns/zarf.helm.git", ""}, {"https://github.com/defenseunicorns/zarf.git", "refs/tags/v0.16.0"}, {"https://github.com/DoD-Platform-One/big-bang.git", "refs/heads/release-1.54.x"}, + {"https://github.com/prometheus-community/helm-charts.git", "kube-prometheus-stack-47.3.0"}, // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) {"https://github.com/defenseunicorns/zarf.helm.git", ""}, @@ -97,14 +99,14 @@ func TestGitTransformURLSplitRef(t *testing.T) { } for idx, url := range gitURLs { - gitURLNoRef, refPlain, err := GitTransformURLSplitRef(url) + gitURLNoRef, refPlain, err := GitURLSplitRef(url) require.NoError(t, err) require.Equal(t, expectedResult[idx][0], gitURLNoRef) require.Equal(t, expectedResult[idx][1], refPlain) } for _, url := range badGitURLs { - _, _, err := GitTransformURLSplitRef(url) + _, _, err := GitURLSplitRef(url) require.Error(t, err) } } @@ -126,6 +128,7 @@ func TestGitTransformURLtoFolderName(t *testing.T) { "zarf.helm-2570741950", "zarf-2175050463", "big-bang-2705706079", + "helm-charts-1319967699", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "zarf.helm-2570741950", @@ -164,6 +167,7 @@ func TestGitTransformURLtoRepoName(t *testing.T) { "zarf.helm-842267124", "zarf-1211668992", "big-bang-2366614037", + "helm-charts-3648076006", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "zarf.helm-842267124", @@ -202,6 +206,7 @@ func TestGitTransformURL(t *testing.T) { "https://gitlab.com/repo-owner/zarf.helm-842267124.git", "https://gitlab.com/repo-owner/zarf-1211668992.git", "https://gitlab.com/repo-owner/big-bang-2366614037.git", + "https://gitlab.com/repo-owner/helm-charts-3648076006.git", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "https://gitlab.com/repo-owner/zarf.helm-842267124.git/info/refs", From b6bcc4f4a84bdfc6b67660b25c86a534747b3efe Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Tue, 11 Jul 2023 11:22:29 -0400 Subject: [PATCH 12/13] [TASK] rename gitTransform functions appropriately according to go standards Signed-off-by: Case Wylie --- src/internal/agent/hooks/flux.go | 2 +- src/internal/agent/http/proxy.go | 2 +- src/internal/packager/git/pull.go | 2 +- src/internal/packager/git/push.go | 8 ++++---- src/pkg/transform/git.go | 16 ++++++++-------- src/pkg/transform/git_test.go | 18 +++++++++--------- src/test/e2e/51_oci_compose_test.go | 2 +- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/internal/agent/hooks/flux.go b/src/internal/agent/hooks/flux.go index 21ad215192..98981b2ef6 100644 --- a/src/internal/agent/hooks/flux.go +++ b/src/internal/agent/hooks/flux.go @@ -80,7 +80,7 @@ func mutateGitRepo(r *v1.AdmissionRequest) (result *operations.Result, err error // Mutate the git URL if necessary if isCreate || (isUpdate && !isPatched) { // Mutate the git URL so that the hostname matches the hostname in the Zarf state - transformedURL, err := transform.GitTransformURL(zarfState.GitServer.Address, patchedURL, zarfState.GitServer.PushUsername) + transformedURL, err := transform.GitURL(zarfState.GitServer.Address, patchedURL, zarfState.GitServer.PushUsername) if err != nil { message.Warnf("Unable to transform the git url, using the original url we have: %s", patchedURL) } diff --git a/src/internal/agent/http/proxy.go b/src/internal/agent/http/proxy.go index 8c563fcc3a..b805d53564 100644 --- a/src/internal/agent/http/proxy.go +++ b/src/internal/agent/http/proxy.go @@ -73,7 +73,7 @@ func proxyRequestTransform(r *http.Request) error { } else { switch { case isGitUserAgent(r.UserAgent()): - targetURL, err = transform.GitTransformURL(zarfState.GitServer.Address, getTLSScheme(r.TLS)+r.Host+r.URL.String(), zarfState.GitServer.PushUsername) + targetURL, err = transform.GitURL(zarfState.GitServer.Address, getTLSScheme(r.TLS)+r.Host+r.URL.String(), zarfState.GitServer.PushUsername) case isPipUserAgent(r.UserAgent()): targetURL, err = transform.PipTransformURL(zarfState.ArtifactServer.Address, getTLSScheme(r.TLS)+r.Host+r.URL.String()) case isNpmUserAgent(r.UserAgent()): diff --git a/src/internal/packager/git/pull.go b/src/internal/packager/git/pull.go index 9601331b48..79313cde32 100644 --- a/src/internal/packager/git/pull.go +++ b/src/internal/packager/git/pull.go @@ -51,7 +51,7 @@ func (g *Git) Pull(gitURL, targetFolder string, shallow bool) error { } // Construct a path unique to this git repo - repoFolder, err := transform.GitTransformURLtoFolderName(gitURL) + repoFolder, err := transform.GitURLtoFolderName(gitURL) if err != nil { return err } diff --git a/src/internal/packager/git/push.go b/src/internal/packager/git/push.go index 12ffd4f36b..0d6a6b942d 100644 --- a/src/internal/packager/git/push.go +++ b/src/internal/packager/git/push.go @@ -25,7 +25,7 @@ func (g *Git) PushRepo(srcURL, targetFolder string) error { defer spinner.Stop() // Setup git paths, including a unique name for the repo based on the hash of the git URL to avoid conflicts. - repoFolder, err := transform.GitTransformURLtoFolderName(srcURL) + repoFolder, err := transform.GitURLtoFolderName(srcURL) if err != nil { return fmt.Errorf("unable to parse git url (%s): %w", srcURL, err) } @@ -34,7 +34,7 @@ func (g *Git) PushRepo(srcURL, targetFolder string) error { // Check that this package is using the new repo format (if not fallback to the format from <= 0.24.x) _, err = os.Stat(repoPath) if os.IsNotExist(err) { - repoFolder, err = transform.GitTransformURLtoRepoName(srcURL) + repoFolder, err = transform.GitURLtoRepoName(srcURL) if err != nil { return fmt.Errorf("unable to parse git url (%s): %w", srcURL, err) } @@ -63,7 +63,7 @@ func (g *Git) PushRepo(srcURL, targetFolder string) error { return err } remoteURL := remote.Config().URLs[0] - repoName, err := transform.GitTransformURLtoRepoName(remoteURL) + repoName, err := transform.GitURLtoRepoName(remoteURL) if err != nil { message.Warnf("Unable to add the read-only user to the repo: %s\n", repoName) return err @@ -94,7 +94,7 @@ func (g *Git) prepRepoForPush() (*git.Repository, error) { } remoteURL := remote.Config().URLs[0] - targetURL, err := transform.GitTransformURL(g.Server.Address, remoteURL, g.Server.PushUsername) + targetURL, err := transform.GitURL(g.Server.Address, remoteURL, g.Server.PushUsername) if err != nil { return nil, fmt.Errorf("unable to transform the git url: %w", err) } diff --git a/src/pkg/transform/git.go b/src/pkg/transform/git.go index a54627a881..c3b1cb306b 100644 --- a/src/pkg/transform/git.go +++ b/src/pkg/transform/git.go @@ -19,7 +19,7 @@ var gitURLRegex = regexp.MustCompile(`^(?P[a-z]+:\/\/)(?P.+?)\/ func MutateGitURLsInText(logger Log, targetBaseURL string, text string, pushUser string) string { extractPathRegex := regexp.MustCompile(`[a-z]+:\/\/[^\/]+\/(.*\.git)`) output := extractPathRegex.ReplaceAllStringFunc(text, func(match string) string { - output, err := GitTransformURL(targetBaseURL, match, pushUser) + output, err := GitURL(targetBaseURL, match, pushUser) if err != nil { logger("Unable to transform the git url, using the original url we have: %s", match) return match @@ -43,8 +43,8 @@ func GitURLSplitRef(sourceURL string) (string, string, error) { return gitURLNoRef, refPlain, nil } -// GitTransformURLtoFolderName takes a git url and returns the folder name for the repo in the Zarf package. -func GitTransformURLtoFolderName(sourceURL string) (string, error) { +// GitURLtoFolderName takes a git url and returns the folder name for the repo in the Zarf package. +func GitURLtoFolderName(sourceURL string) (string, error) { get, err := helpers.MatchRegex(gitURLRegex, sourceURL) if err != nil { @@ -64,8 +64,8 @@ func GitTransformURLtoFolderName(sourceURL string) (string, error) { return newRepoName, nil } -// GitTransformURLtoRepoName takes a git url and returns the name of the repo in the remote airgap repository. -func GitTransformURLtoRepoName(sourceURL string) (string, error) { +// GitURLtoRepoName takes a git url and returns the name of the repo in the remote airgap repository. +func GitURLtoRepoName(sourceURL string) (string, error) { get, err := helpers.MatchRegex(gitURLRegex, sourceURL) if err != nil { @@ -86,9 +86,9 @@ func GitTransformURLtoRepoName(sourceURL string) (string, error) { return newRepoName, nil } -// GitTransformURL takes a base URL, a source url and a username and returns a Zarf-compatible url. -func GitTransformURL(targetBaseURL string, sourceURL string, pushUser string) (*url.URL, error) { - repoName, err := GitTransformURLtoRepoName(sourceURL) +// GitURL takes a base URL, a source url and a username and returns a Zarf-compatible url. +func GitURL(targetBaseURL string, sourceURL string, pushUser string) (*url.URL, error) { + repoName, err := GitURLtoRepoName(sourceURL) if err != nil { return nil, err } diff --git a/src/pkg/transform/git_test.go b/src/pkg/transform/git_test.go index a9f09388e0..7c11cc5f8e 100644 --- a/src/pkg/transform/git_test.go +++ b/src/pkg/transform/git_test.go @@ -111,7 +111,7 @@ func TestGitURLSplitRef(t *testing.T) { } } -func TestGitTransformURLtoFolderName(t *testing.T) { +func TestGitURLtoFolderName(t *testing.T) { var expectedResult = []string{ // Normal git repos and references for pushing/pulling "twistlock-1590638614", @@ -139,18 +139,18 @@ func TestGitTransformURLtoFolderName(t *testing.T) { } for idx, url := range gitURLs { - repoFolder, err := GitTransformURLtoFolderName(url) + repoFolder, err := GitURLtoFolderName(url) require.NoError(t, err) require.Equal(t, expectedResult[idx], repoFolder) } for _, url := range badGitURLs { - _, err := GitTransformURLtoFolderName(url) + _, err := GitURLtoFolderName(url) require.Error(t, err) } } -func TestGitTransformURLtoRepoName(t *testing.T) { +func TestGitURLtoRepoName(t *testing.T) { var expectedResult = []string{ // Normal git repos and references for pushing/pulling "twistlock-97328248", @@ -178,18 +178,18 @@ func TestGitTransformURLtoRepoName(t *testing.T) { } for idx, url := range gitURLs { - repoName, err := GitTransformURLtoRepoName(url) + repoName, err := GitURLtoRepoName(url) require.NoError(t, err) require.Equal(t, expectedResult[idx], repoName) } for _, url := range badGitURLs { - _, err := GitTransformURLtoRepoName(url) + _, err := GitURLtoRepoName(url) require.Error(t, err) } } -func TestGitTransformURL(t *testing.T) { +func TestGitURL(t *testing.T) { var expectedResult = []string{ // Normal git repos and references for pushing/pulling "https://gitlab.com/repo-owner/twistlock-97328248.git", @@ -217,13 +217,13 @@ func TestGitTransformURL(t *testing.T) { } for idx, url := range gitURLs { - repoURL, err := GitTransformURL("https://gitlab.com", url, "repo-owner") + repoURL, err := GitURL("https://gitlab.com", url, "repo-owner") require.NoError(t, err) require.Equal(t, expectedResult[idx], repoURL.String()) } for _, url := range badGitURLs { - _, err := GitTransformURL("https://gitlab.com", url, "repo-owner") + _, err := GitURL("https://gitlab.com", url, "repo-owner") require.Error(t, err) } } diff --git a/src/test/e2e/51_oci_compose_test.go b/src/test/e2e/51_oci_compose_test.go index d4f7ba2968..e45048d6e0 100644 --- a/src/test/e2e/51_oci_compose_test.go +++ b/src/test/e2e/51_oci_compose_test.go @@ -240,7 +240,7 @@ func (suite *SkeletonSuite) verifyComponentPaths(unpackedPath string, components if !isSkeleton { for _, repo := range component.Repos { - dir, err := transform.GitTransformURLtoFolderName(repo) + dir, err := transform.GitURLtoFolderName(repo) suite.NoError(err) suite.DirExists(filepath.Join(componentPaths.Repos, dir)) } From ae36435d329f261872c5dbda4a84c83d659333d7 Mon Sep 17 00:00:00 2001 From: Case Wylie Date: Tue, 11 Jul 2023 14:03:02 -0400 Subject: [PATCH 13/13] [TASK] add two more test cases Signed-off-by: Case Wylie --- src/pkg/transform/git_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pkg/transform/git_test.go b/src/pkg/transform/git_test.go index 7c11cc5f8e..0bee04260d 100644 --- a/src/pkg/transform/git_test.go +++ b/src/pkg/transform/git_test.go @@ -27,6 +27,8 @@ var gitURLs = []string{ "https://github.com/defenseunicorns/zarf.git@refs/tags/v0.16.0", "https://github.com/DoD-Platform-One/big-bang.git@refs/heads/release-1.54.x", "https://github.com/prometheus-community/helm-charts.git@kube-prometheus-stack-47.3.0", + "https://github.com/prometheus-community/", + "https://github.com/", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "https://github.com/defenseunicorns/zarf.helm.git/info/refs", @@ -89,6 +91,8 @@ func TestGitURLSplitRef(t *testing.T) { {"https://github.com/defenseunicorns/zarf.git", "refs/tags/v0.16.0"}, {"https://github.com/DoD-Platform-One/big-bang.git", "refs/heads/release-1.54.x"}, {"https://github.com/prometheus-community/helm-charts.git", "kube-prometheus-stack-47.3.0"}, + {"https://github.com/prometheus-community", ""}, + {"https://github.com/", ""}, // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) {"https://github.com/defenseunicorns/zarf.helm.git", ""}, @@ -129,6 +133,8 @@ func TestGitURLtoFolderName(t *testing.T) { "zarf-2175050463", "big-bang-2705706079", "helm-charts-1319967699", + "prometheus-community-3453166319", + "-1276058275", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "zarf.helm-2570741950", @@ -168,6 +174,8 @@ func TestGitURLtoRepoName(t *testing.T) { "zarf-1211668992", "big-bang-2366614037", "helm-charts-3648076006", + "prometheus-community-2749132599", + "-98306241", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "zarf.helm-842267124", @@ -207,6 +215,8 @@ func TestGitURL(t *testing.T) { "https://gitlab.com/repo-owner/zarf-1211668992.git", "https://gitlab.com/repo-owner/big-bang-2366614037.git", "https://gitlab.com/repo-owner/helm-charts-3648076006.git", + "https://gitlab.com/repo-owner/prometheus-community-2749132599", + "https://gitlab.com/repo-owner/-98306241", // Smart Git Protocol URLs for proxying (https://www.git-scm.com/docs/http-protocol) "https://gitlab.com/repo-owner/zarf.helm-842267124.git/info/refs",