mirror of https://github.com/helm/helm
Signed-off-by: Terry Howe <terrylhowe@gmail.com>pull/31590/head
parent
dbd07fa16c
commit
6453933519
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
||||||
Binary file not shown.
@ -0,0 +1,4 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
description: Test chart with unicode Kröpke 日本語 中文 한글
|
||||||
|
name: unicode-chart
|
||||||
|
version: 0.1.0
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
Thank you for installing {{ .Chart.Name }}.
|
||||||
|
|
||||||
|
This chart includes unicode: Kröpke 日本語 中文 한글
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
# Default values for unicode-chart
|
||||||
|
replicaCount: 1
|
||||||
Loading…
Reference in new issue