Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support authenticated Helm repositories from helm repo add #2196

Merged
merged 40 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0765147
adding test
AustinAbro321 Dec 14, 2023
4c8db23
WIP
AustinAbro321 Dec 14, 2023
8b359c3
updated k8s / helm k8s version
AustinAbro321 Dec 14, 2023
aa6327f
Merge branch 'main' into default-kube-version-helm-1607
AustinAbro321 Dec 14, 2023
53fc1a2
Merge branch 'main' into default-kube-version-helm-1607
Racer159 Dec 15, 2023
6c27c5b
fix tests to reflect that we no longer need to set kube version
AustinAbro321 Dec 15, 2023
c26a489
Merge branch 'default-kube-version-helm-1607' of github.com:defenseun…
AustinAbro321 Dec 15, 2023
a268caa
Merge branch 'main' into default-kube-version-helm-1607
AustinAbro321 Dec 15, 2023
3a6a33a
Merge branch 'main' into support-authed-helm-repos-2189
AustinAbro321 Dec 15, 2023
69314bf
Update Zarf test for kubeVersion to be more relevant to new changes
AustinAbro321 Dec 15, 2023
8d43fb9
WIP
AustinAbro321 Dec 15, 2023
b167a53
Merge branch 'main' into support-authed-helm-repos-2189
AustinAbro321 Dec 15, 2023
bcc9bbe
Merge branch 'default-kube-version-helm-1607' into support-authed-hel…
AustinAbro321 Dec 15, 2023
2e683e0
WIP
AustinAbro321 Dec 15, 2023
0dbf189
WIP
AustinAbro321 Dec 18, 2023
b18f566
WIP
AustinAbro321 Dec 19, 2023
b5c2ca4
merge
AustinAbro321 Dec 19, 2023
671c126
WIP
AustinAbro321 Dec 19, 2023
0473f05
WIP
AustinAbro321 Dec 19, 2023
acac632
refactor + fix tests
AustinAbro321 Jan 5, 2024
fb14a49
Merge branch 'main' into support-authed-helm-repos-2189
AustinAbro321 Jan 5, 2024
dc7c1aa
error messaging
AustinAbro321 Jan 5, 2024
ace28f9
Merge branch 'main' into support-authed-helm-repos-2189
AustinAbro321 Jan 5, 2024
208484c
downloading podinfo binary
AustinAbro321 Jan 5, 2024
4024503
loading repo file the same way as helm + refactors
AustinAbro321 Jan 5, 2024
762b940
refactoring tests
AustinAbro321 Jan 5, 2024
7106e67
fix version number
AustinAbro321 Jan 5, 2024
c28075c
Apply suggestions from code review
AustinAbro321 Jan 8, 2024
ac4e97b
refactoring tests / adding comments
AustinAbro321 Jan 8, 2024
b8c65ab
update docs for helm auth
AustinAbro321 Jan 8, 2024
53da122
Merge branch 'main' into support-authed-helm-repos-2189
Racer159 Jan 10, 2024
2c7a857
Merge branch 'main' into support-authed-helm-repos-2189
Racer159 Jan 23, 2024
dc13d24
Merge branch 'main' into support-authed-helm-repos-2189
Racer159 Jan 23, 2024
d37b655
reword
AustinAbro321 Jan 23, 2024
f9f6466
reword
AustinAbro321 Jan 23, 2024
d2e9bd2
repo file check
AustinAbro321 Jan 23, 2024
465d650
rename folder
AustinAbro321 Jan 23, 2024
7c62bd5
packaging chart in test
AustinAbro321 Jan 23, 2024
75d797c
Merge branch 'main' into support-authed-helm-repos-2189
Racer159 Jan 23, 2024
8e74f41
Merge branch 'main' into support-authed-helm-repos-2189
Racer159 Jan 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/3-create-a-zarf-package/2-zarf-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ Charts using the `url` key can be:
- A remote URL (oci://) to an OCI registry
- A remote URL (http/https) to a Helm repository

:::note

To use a private Helm repository the repo must be added to Helm. You can add a repo to Helm with the [`helm repo add`](https://helm.sh/docs/helm/helm_repo_add/) command or the internal [`zarf tools helm repo add`](../2-the-zarf-cli/100-cli-commands/zarf_tools_helm_repo_add.md) command.

:::

#### Chart Examples

<ExampleYAML src={require('../../examples/helm-charts/zarf.yaml')} component="demo-helm-charts" />
Expand Down
2 changes: 1 addition & 1 deletion examples/helm-charts/zarf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ components:
localPath: chart
valuesFiles:
- values.yaml

- name: podinfo-oci
version: 6.4.0
namespace: podinfo-from-oci
Expand Down
24 changes: 22 additions & 2 deletions src/internal/packager/helm/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ func (h *Helm) DownloadPublishedChart(cosignKeyPath string) error {
chartURL string
err error
)
repoFile, err := repo.LoadFile(pull.Settings.RepositoryConfig)

// Not returning the error here since the repo file is only needed if we are pulling from a repo that requires authentication
if err != nil {
message.Debugf("Unable to load the repo file at %q: %s", pull.Settings.RepositoryConfig, err.Error())
}

var username string
var password string

// Handle OCI registries
if registry.IsOCI(h.chart.URL) {
Expand All @@ -159,8 +168,18 @@ func (h *Helm) DownloadPublishedChart(cosignKeyPath string) error {
chartName = h.chart.RepoName
}

// Perform simple chart download
chartURL, err = repo.FindChartInRepoURL(h.chart.URL, chartName, h.chart.Version, pull.CertFile, pull.KeyFile, pull.CaFile, getter.All(pull.Settings))
if repoFile != nil {
// TODO: @AustinAbro321 Currently this selects the last repo with the same url
// We should introduce a new field in zarf to allow users to specify the local repo they want
for _, repo := range repoFile.Repositories {
if repo.URL == h.chart.URL {
username = repo.Username
password = repo.Password
}
}
}

chartURL, err = repo.FindChartInAuthRepoURL(h.chart.URL, username, password, chartName, h.chart.Version, pull.CertFile, pull.KeyFile, pull.CaFile, getter.All(pull.Settings))
if err != nil {
if strings.Contains(err.Error(), "not found") {
// Intentionally dogsled this error since this is just a nice to have helper
Expand All @@ -179,6 +198,7 @@ func (h *Helm) DownloadPublishedChart(cosignKeyPath string) error {
Getters: getter.All(pull.Settings),
Options: []getter.Option{
getter.WithInsecureSkipVerifyTLS(config.CommonOptions.Insecure),
getter.WithBasicAuth(username, password),
},
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/external/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ make test-external

``` bash
# If you are in the root folder of the repository and already have everything built (i.e., the binary, the init-package and the flux-test example package):
go test ./src/test/external-test/...
go test ./src/test/external/... -v
```
135 changes: 122 additions & 13 deletions src/test/external/ext_out_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,43 @@
package external

import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"testing"

"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/defenseunicorns/zarf/src/pkg/utils/exec"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"helm.sh/helm/v3/pkg/repo"
)

// Docker/k3d networking constants
const (
network = "k3d-k3s-external-test"
subnet = "172.31.0.0/16"
gateway = "172.31.0.1"
giteaIP = "172.31.0.99"
giteaHost = "gitea.localhost"
registryHost = "registry.localhost"
clusterName = "zarf-external-test"
network = "k3d-k3s-external-test"
subnet = "172.31.0.0/16"
gateway = "172.31.0.1"
giteaIP = "172.31.0.99"
giteaHost = "gitea.localhost"
registryHost = "registry.localhost"
clusterName = "zarf-external-test"
giteaUser = "git-user"
registryUser = "push-user"
commonPassword = "superSecurePassword"
)

var outClusterCredentialArgs = []string{
"--git-push-username=git-user",
"--git-push-password=superSecurePassword",
"--git-push-username=" + giteaUser,
"--git-push-password=" + commonPassword,
"--git-url=http://" + giteaHost + ":3000",
"--registry-push-username=push-user",
"--registry-push-password=superSecurePassword",
"--registry-push-username=" + registryUser,
"--registry-push-password=" + commonPassword,
"--registry-url=k3d-" + registryHost + ":5000"}

type ExtOutClusterTestSuite struct {
Expand All @@ -40,6 +50,7 @@ type ExtOutClusterTestSuite struct {
}

func (suite *ExtOutClusterTestSuite) SetupSuite() {

suite.Assertions = require.New(suite.T())

// Teardown any leftovers from previous tests
Expand Down Expand Up @@ -97,7 +108,7 @@ func (suite *ExtOutClusterTestSuite) Test_0_Mirror() {
suite.NoError(err, "unable to mirror the package with zarf")

// Check that the registry contains the images we want
regCatalogURL := fmt.Sprintf("http://push-user:superSecurePassword@k3d-%s:5000/v2/_catalog", registryHost)
regCatalogURL := fmt.Sprintf("http://%s:%s@k3d-%s:5000/v2/_catalog", registryUser, commonPassword, registryHost)
respReg, err := http.Get(regCatalogURL)
suite.NoError(err)
regBody, err := io.ReadAll(respReg.Body)
Expand All @@ -107,7 +118,7 @@ func (suite *ExtOutClusterTestSuite) Test_0_Mirror() {
suite.Contains(string(regBody), "stefanprodan/podinfo", "registry did not contain the expected image")

// Check that the git server contains the repos we want
gitRepoURL := fmt.Sprintf("http://git-user:superSecurePassword@%s:3000/api/v1/repos/search", giteaHost)
gitRepoURL := fmt.Sprintf("http://%s:%s@%s:3000/api/v1/repos/search", giteaUser, commonPassword, giteaHost)
respGit, err := http.Get(gitRepoURL)
suite.NoError(err)
gitBody, err := io.ReadAll(respGit.Body)
Expand Down Expand Up @@ -136,6 +147,104 @@ func (suite *ExtOutClusterTestSuite) Test_1_Deploy() {
suite.True(success, errorStr)
}

func (suite *ExtOutClusterTestSuite) Test_2_AuthToPrivateHelmChart() {
baseURL := fmt.Sprintf("http://%s:3000", giteaHost)

suite.createHelmChartInGitea(baseURL, giteaUser, commonPassword)
suite.makeGiteaUserPrivate(baseURL, giteaUser, commonPassword)

tempDir := suite.T().TempDir()
repoPath := filepath.Join(tempDir, "repositories.yaml")
os.Setenv("HELM_REPOSITORY_CONFIG", repoPath)
defer os.Unsetenv("HELM_REPOSITORY_CONFIG")

packagePath := filepath.Join("..", "packages", "external-helm-auth")
findImageArgs := []string{"dev", "find-images", packagePath}
err := exec.CmdWithPrint(zarfBinPath, findImageArgs...)
suite.Error(err, "Since auth has not been setup, this should fail")

repoFile := repo.NewFile()

chartURL := fmt.Sprintf("%s/api/packages/%s/helm", baseURL, giteaUser)
entry := &repo.Entry{
Name: "temp_entry",
Username: giteaUser,
Password: commonPassword,
URL: chartURL,
}
repoFile.Add(entry)
utils.WriteYaml(repoPath, repoFile, 0600)

err = exec.CmdWithPrint(zarfBinPath, findImageArgs...)
suite.NoError(err, "Unable to find images, helm auth likely failed")

packageCreateArgs := []string{"package", "create", packagePath, fmt.Sprintf("--output=%s", tempDir), "--confirm"}
err = exec.CmdWithPrint(zarfBinPath, packageCreateArgs...)
suite.NoError(err, "Unable to create package, helm auth likely failed")
}

func (suite *ExtOutClusterTestSuite) createHelmChartInGitea(baseURL string, username string, password string) {
tempDir := suite.T().TempDir()
podInfoVersion := "6.4.0"
podinfoChartPath := filepath.Join("..", "..", "..", "examples", "helm-charts", "chart")
err := exec.CmdWithPrint("helm", "package", podinfoChartPath, "--destination", tempDir)
podinfoTarballPath := filepath.Join(tempDir, fmt.Sprintf("podinfo-%s.tgz", podInfoVersion))
suite.NoError(err, "Unable to package chart")

utils.DownloadToFile(fmt.Sprintf("https://stefanprodan.github.io/podinfo/podinfo-%s.tgz", podInfoVersion), podinfoTarballPath, "")
url := fmt.Sprintf("%s/api/packages/%s/helm/api/charts", baseURL, username)

file, err := os.Open(podinfoTarballPath)
suite.NoError(err)
defer file.Close()

body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", podinfoTarballPath)
suite.NoError(err)
_, err = io.Copy(part, file)
suite.NoError(err)
writer.Close()

req, err := http.NewRequest("POST", url, body)
suite.NoError(err)

req.Header.Set("Content-Type", writer.FormDataContentType())
req.SetBasicAuth(username, password)

client := &http.Client{}

resp, err := client.Do(req)
suite.NoError(err)
resp.Body.Close()
}

func (suite *ExtOutClusterTestSuite) makeGiteaUserPrivate(baseURL string, username string, password string) {
url := fmt.Sprintf("%s/api/v1/admin/users/%s", baseURL, username)

userOption := map[string]interface{}{
"visibility": "private",
"login_name": username,
}

jsonData, err := json.Marshal(userOption)
suite.NoError(err)

req, err := http.NewRequest(http.MethodPatch, url, bytes.NewBuffer(jsonData))
suite.NoError(err)

req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(username, password)

client := &http.Client{}
resp, err := client.Do(req)
suite.NoError(err)
defer resp.Body.Close()

_, err = io.ReadAll(resp.Body)
suite.NoError(err)
}

func TestExtOurClusterTestSuite(t *testing.T) {
suite.Run(t, new(ExtOutClusterTestSuite))
}
14 changes: 14 additions & 0 deletions src/test/packages/external-helm-auth/zarf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
kind: ZarfPackageConfig
metadata:
name: helm-auth
version: 0.0.1
components:
- name: private-chart
required: true
charts:
- name: podinfo
version: 6.4.0
url: http://gitea.localhost:3000/api/packages/git-user/helm
repoName: podinfo
namespace: podinfo-from-repo
releaseName: cool-release-name
Loading