mirror of https://github.com/helm/helm
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
9.8 KiB
314 lines
9.8 KiB
/*
|
|
Copyright The Helm Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// TestOCIRegistryGHCREndToEnd tests push and pull against real GitHub Container Registry (GHCR)
|
|
// This test requires HELM_RUN_E2E=true, GHCR_USER and GHCR_TOKEN environment variables to be set
|
|
func TestOCIRegistryGHCREndToEnd(t *testing.T) {
|
|
if os.Getenv("HELM_RUN_E2E") == "" {
|
|
t.Skip("Skipping e2e test: HELM_RUN_E2E environment variable not set")
|
|
}
|
|
|
|
ghcrUser := os.Getenv("GHCR_USER")
|
|
ghcrToken := os.Getenv("GHCR_TOKEN")
|
|
|
|
if ghcrUser == "" || ghcrToken == "" {
|
|
t.Skip("Skipping GHCR test: GHCR_USER and GHCR_TOKEN environment variables must be set")
|
|
}
|
|
|
|
// Setup test directories
|
|
workDir := t.TempDir()
|
|
registryConfigPath := filepath.Join(workDir, "config.json")
|
|
contentCache := t.TempDir()
|
|
|
|
// GHCR registry configuration
|
|
ghcrRegistry := "ghcr.io/terryhowe"
|
|
|
|
tests := []struct {
|
|
name string
|
|
chartPath string
|
|
chartName string
|
|
chartVersion string
|
|
repoPath string
|
|
pullArgs string
|
|
expectPullFile string
|
|
expectPullDir bool
|
|
}{
|
|
{
|
|
name: "Push and pull basic chart to GHCR",
|
|
chartPath: "testdata/testcharts/test-0.1.0.tgz",
|
|
chartName: "test",
|
|
chartVersion: "0.1.0",
|
|
repoPath: "helm-e2e-test",
|
|
expectPullFile: "./test-0.1.0.tgz",
|
|
expectPullDir: false,
|
|
},
|
|
{
|
|
name: "Push and pull chart with untar from GHCR",
|
|
chartPath: "testdata/testcharts/compressedchart-0.1.0.tgz",
|
|
chartName: "compressedchart",
|
|
chartVersion: "0.1.0",
|
|
repoPath: "helm-e2e-test",
|
|
pullArgs: "--untar",
|
|
expectPullFile: "./compressedchart",
|
|
expectPullDir: true,
|
|
},
|
|
{
|
|
name: "Push and pull chart with hyphens to GHCR",
|
|
chartPath: "testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz",
|
|
chartName: "compressedchart-with-hyphens",
|
|
chartVersion: "0.1.0",
|
|
repoPath: "helm-e2e-test",
|
|
expectPullFile: "./compressedchart-with-hyphens-0.1.0.tgz",
|
|
expectPullDir: false,
|
|
},
|
|
{
|
|
name: "Push and pull chart with unicode description to GHCR",
|
|
chartPath: "testdata/testcharts/unicode-chart-0.1.0.tgz",
|
|
chartName: "unicode-chart",
|
|
chartVersion: "0.1.0",
|
|
repoPath: "helm-e2e-test",
|
|
expectPullFile: "./unicode-chart-0.1.0.tgz",
|
|
expectPullDir: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Create a fresh pull directory for this test
|
|
pullDir := filepath.Join(workDir, tt.name)
|
|
if err := os.MkdirAll(pullDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Construct the push remote (repository path only)
|
|
pushRemote := fmt.Sprintf("oci://%s/%s", ghcrRegistry, tt.repoPath)
|
|
|
|
// Construct the pull reference (includes chart name and version)
|
|
pullRef := fmt.Sprintf("oci://%s/%s/%s:%s",
|
|
ghcrRegistry,
|
|
tt.repoPath,
|
|
tt.chartName,
|
|
tt.chartVersion)
|
|
|
|
// Push the chart to GHCR
|
|
pushCmd := fmt.Sprintf("push %s %s --registry-config %s --username %s --password %s",
|
|
tt.chartPath,
|
|
pushRemote,
|
|
registryConfigPath,
|
|
ghcrUser,
|
|
ghcrToken)
|
|
|
|
t.Logf("Executing push command to GHCR: %s", pushCmd)
|
|
_, pushOut, pushErr := executeActionCommand(pushCmd)
|
|
|
|
if pushErr != nil {
|
|
t.Fatalf("push to GHCR failed: %v\nOutput: %s", pushErr, pushOut)
|
|
}
|
|
t.Logf("Push to GHCR successful. Output: %s", pushOut)
|
|
|
|
// Pull the chart back from GHCR
|
|
pullCmd := fmt.Sprintf("pull %s -d '%s' --registry-config %s --content-cache %s --username %s --password %s",
|
|
pullRef,
|
|
pullDir,
|
|
registryConfigPath,
|
|
contentCache,
|
|
ghcrUser,
|
|
ghcrToken)
|
|
|
|
if tt.pullArgs != "" {
|
|
pullCmd += " " + tt.pullArgs
|
|
}
|
|
|
|
t.Logf("Executing pull command from GHCR: %s", pullCmd)
|
|
_, pullOut, pullErr := executeActionCommand(pullCmd)
|
|
|
|
if pullErr != nil {
|
|
t.Fatalf("pull from GHCR failed: %v\nOutput: %s", pullErr, pullOut)
|
|
}
|
|
t.Logf("Pull from GHCR successful. Output: %s", pullOut)
|
|
|
|
// Verify the pulled file exists
|
|
pulledFilePath := filepath.Join(pullDir, tt.expectPullFile)
|
|
fi, err := os.Stat(pulledFilePath)
|
|
if err != nil {
|
|
t.Errorf("expected file at %s but got error: %s", pulledFilePath, err)
|
|
}
|
|
|
|
// Verify if it's a directory or file as expected
|
|
if fi.IsDir() != tt.expectPullDir {
|
|
t.Errorf("expected directory=%t, but got directory=%t", tt.expectPullDir, fi.IsDir())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestOCIRegistryGHCRAuthFailure tests authentication failures with real GHCR
|
|
// This test requires HELM_RUN_E2E=true and GHCR_USER environment variable to be set
|
|
func TestOCIRegistryGHCRAuthFailure(t *testing.T) {
|
|
if os.Getenv("HELM_RUN_E2E") == "" {
|
|
t.Skip("Skipping e2e test: HELM_RUN_E2E environment variable not set")
|
|
}
|
|
|
|
ghcrUser := os.Getenv("GHCR_USER")
|
|
|
|
if ghcrUser == "" {
|
|
t.Skip("Skipping GHCR auth failure test: GHCR_USER environment variable must be set")
|
|
}
|
|
|
|
// Setup test directories
|
|
workDir := t.TempDir()
|
|
registryConfigPath := filepath.Join(workDir, "config.json")
|
|
|
|
// GHCR registry configuration
|
|
ghcrRegistry := "ghcr.io/terryhowe"
|
|
|
|
t.Run("Fail push with invalid credentials to GHCR", func(t *testing.T) {
|
|
pushRemote := fmt.Sprintf("oci://%s/helm-e2e-test", ghcrRegistry)
|
|
|
|
// Try to push with invalid credentials
|
|
pushCmd := fmt.Sprintf("push testdata/testcharts/test-0.1.0.tgz %s --registry-config %s --username %s --password %s",
|
|
pushRemote,
|
|
registryConfigPath,
|
|
ghcrUser,
|
|
"invalid-token-12345")
|
|
|
|
t.Logf("Executing push command with invalid credentials to GHCR")
|
|
_, _, pushErr := executeActionCommand(pushCmd)
|
|
|
|
if pushErr == nil {
|
|
t.Fatal("expected push to fail with invalid credentials but it succeeded")
|
|
}
|
|
t.Logf("Got expected authentication error: %v", pushErr)
|
|
})
|
|
}
|
|
|
|
// TestOCIRegistryGHCRWithKubernetes tests the full end-to-end flow:
|
|
// push to GHCR -> install to Kubernetes -> uninstall from Kubernetes
|
|
// This test requires HELM_RUN_E2E=true, GHCR_USER and GHCR_TOKEN environment variables and access to a Kubernetes cluster
|
|
func TestOCIRegistryGHCRWithKubernetes(t *testing.T) {
|
|
if os.Getenv("HELM_RUN_E2E") == "" {
|
|
t.Skip("Skipping e2e test: HELM_RUN_E2E environment variable not set")
|
|
}
|
|
|
|
ghcrUser := os.Getenv("GHCR_USER")
|
|
ghcrToken := os.Getenv("GHCR_TOKEN")
|
|
|
|
if ghcrUser == "" || ghcrToken == "" {
|
|
t.Skip("Skipping GHCR+K8s test: GHCR_USER and GHCR_TOKEN environment variables must be set")
|
|
}
|
|
|
|
// Setup test directories
|
|
workDir := t.TempDir()
|
|
registryConfigPath := filepath.Join(workDir, "config.json")
|
|
|
|
// GHCR registry configuration
|
|
ghcrRegistry := "ghcr.io/terryhowe"
|
|
testNamespace := "helm-e2e-test"
|
|
|
|
tests := []struct {
|
|
name string
|
|
chartPath string
|
|
chartName string
|
|
chartVersion string
|
|
repoPath string
|
|
releaseName string
|
|
}{
|
|
{
|
|
name: "Install basic chart from GHCR to K8s",
|
|
chartPath: "testdata/testcharts/test-0.1.0.tgz",
|
|
chartName: "test",
|
|
chartVersion: "0.1.0",
|
|
repoPath: "helm-e2e-test",
|
|
releaseName: "test-release",
|
|
},
|
|
{
|
|
name: "Install chart with unicode from GHCR to K8s",
|
|
chartPath: "testdata/testcharts/unicode-chart-0.1.0.tgz",
|
|
chartName: "unicode-chart",
|
|
chartVersion: "0.1.0",
|
|
repoPath: "helm-e2e-test",
|
|
releaseName: "unicode-release",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Construct the push remote (repository path only)
|
|
pushRemote := fmt.Sprintf("oci://%s/%s", ghcrRegistry, tt.repoPath)
|
|
|
|
// Construct the OCI reference for installation
|
|
ociRef := fmt.Sprintf("oci://%s/%s/%s",
|
|
ghcrRegistry,
|
|
tt.repoPath,
|
|
tt.chartName)
|
|
|
|
// Push the chart to GHCR
|
|
pushCmd := fmt.Sprintf("push %s %s --registry-config %s --username %s --password %s",
|
|
tt.chartPath,
|
|
pushRemote,
|
|
registryConfigPath,
|
|
ghcrUser,
|
|
ghcrToken)
|
|
|
|
t.Logf("Pushing chart to GHCR: %s", tt.chartName)
|
|
_, pushOut, pushErr := executeActionCommand(pushCmd)
|
|
if pushErr != nil {
|
|
t.Fatalf("push to GHCR failed: %v\nOutput: %s", pushErr, pushOut)
|
|
}
|
|
t.Logf("Push successful")
|
|
|
|
// Install the chart to Kubernetes
|
|
installCmd := fmt.Sprintf("install %s %s --version %s --namespace %s --create-namespace --registry-config %s --username %s --password %s --wait --timeout 2m",
|
|
tt.releaseName,
|
|
ociRef,
|
|
tt.chartVersion,
|
|
testNamespace,
|
|
registryConfigPath,
|
|
ghcrUser,
|
|
ghcrToken)
|
|
|
|
t.Logf("Installing chart to Kubernetes: %s", tt.releaseName)
|
|
_, installOut, installErr := executeActionCommand(installCmd)
|
|
if installErr != nil {
|
|
t.Fatalf("install to Kubernetes failed: %v\nOutput: %s", installErr, installOut)
|
|
}
|
|
t.Logf("Install successful. Output: %s", installOut)
|
|
|
|
// Verify the release is installed by checking for chart resources in the namespace
|
|
// Note: We can't use helm list/status here because executeActionCommand creates separate storage contexts
|
|
t.Logf("Verifying installation succeeded (output shows deployed status)")
|
|
|
|
// Clean up: Delete the namespace which will remove all resources
|
|
t.Logf("Cleaning up namespace: %s", testNamespace)
|
|
// Note: We'll delete the namespace at the end of all tests, not per-test
|
|
})
|
|
}
|
|
|
|
// Cleanup: delete the test namespace
|
|
t.Logf("Deleting test namespace: %s", testNamespace)
|
|
// Using Go's os/exec would be better but for simplicity in tests we'll rely on the namespace being cleaned up manually
|
|
// or by the next test run with --create-namespace
|
|
}
|