Files
portainer/pkg/libhelm/sdk/common_test.go

214 lines
5.9 KiB
Go

package sdk
import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/portainer/portainer/api/filesystem"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"helm.sh/helm/v4/pkg/action"
v2chart "helm.sh/helm/v4/pkg/chart/v2"
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
repo "helm.sh/helm/v4/pkg/repo/v1"
)
func TestAppendChartReferenceAnnotations(t *testing.T) {
t.Parallel()
tests := []struct {
name string
chartPath string
repoURL string
registryID int
stackID int
existing map[string]string
want map[string]string
}{
{
name: "with registry ID",
chartPath: "charts/nginx",
registryID: 5,
stackID: 123,
want: map[string]string{
ChartPathAnnotation: "charts/nginx",
RegistryIDAnnotation: "5",
StackIDAnnotation: "123",
// repoURL is NOT added when registryID != 0
},
},
{
name: "with repo URL (no registry)",
chartPath: "charts/nginx",
repoURL: "https://charts.example.com",
stackID: 123,
want: map[string]string{
ChartPathAnnotation: "charts/nginx",
RepoURLAnnotation: "https://charts.example.com",
StackIDAnnotation: "123",
},
},
{
name: "preserves custom annotations",
chartPath: "my-chart",
existing: map[string]string{"custom": "value"},
want: map[string]string{
ChartPathAnnotation: "my-chart",
"custom": "value",
},
},
{
name: "replaces old chart annotations",
chartPath: "new-chart",
repoURL: "https://new.com",
existing: map[string]string{
ChartPathAnnotation: "old-chart",
RepoURLAnnotation: "https://old.com",
},
want: map[string]string{
ChartPathAnnotation: "new-chart",
RepoURLAnnotation: "https://new.com",
},
},
{
name: "omits zero values",
chartPath: "chart",
want: map[string]string{
ChartPathAnnotation: "chart",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := appendChartReferenceAnnotations(
tt.chartPath, tt.repoURL, tt.registryID, tt.stackID,
nil, tt.existing,
)
assert.Equal(t, tt.want, result)
})
}
}
func TestAppendChartReferenceAnnotations_RepoURLLogic(t *testing.T) {
t.Parallel()
t.Run("repoURL only added when registryID is zero", func(t *testing.T) {
// With registry ID - no repoURL
result := appendChartReferenceAnnotations("chart", "url", 5, 0, nil, nil)
_, hasRepoURL := result[RepoURLAnnotation]
assert.False(t, hasRepoURL)
// Without registry ID - includes repoURL
result = appendChartReferenceAnnotations("chart", "url", 0, 0, nil, nil)
assert.Equal(t, "url", result[RepoURLAnnotation])
})
t.Run("does not mutate existing map", func(t *testing.T) {
existing := map[string]string{"key": "value"}
appendChartReferenceAnnotations("chart", "", 0, 0, nil, existing)
assert.Equal(t, map[string]string{"key": "value"}, existing)
})
}
func TestLoadAndValidateChartWithPathOptions(t *testing.T) {
t.Parallel()
depTgzPath := saveMinimalDepChart(t, "dep-chart", "0.1.0")
depTgzName := filepath.Base(depTgzPath)
server := newHelmHTTPRepoServer(t, depTgzPath)
parentDir := writeParentChart(t, "parent-chart", "dep-chart", "0.1.0", server.URL)
hspm := newIsolatedHelmSDKPackageManager(t)
ch, err := hspm.loadAndValidateChartWithPathOptions(
new(action.Configuration),
&action.ChartPathOptions{},
parentDir, "", "", true, "test",
)
require.NoError(t, err)
require.NotNil(t, ch)
assert.Equal(t, "parent-chart", ch.Metadata.Name)
assert.FileExists(t, filesystem.JoinPaths(parentDir, "charts", depTgzName),
"dependency tarball should have been downloaded into charts/ — proves ContentCache was threaded into downloader.Manager")
}
// saveMinimalDepChart produces a valid dependency chart tarball on disk and returns its path.
func saveMinimalDepChart(t *testing.T, name, version string) string {
t.Helper()
tgzPath, err := chartutil.Save(&v2chart.Chart{
Metadata: &v2chart.Metadata{
Name: name,
Version: version,
APIVersion: "v2",
},
}, t.TempDir())
require.NoError(t, err)
return tgzPath
}
func newHelmHTTPRepoServer(t *testing.T, tgzPath string) *httptest.Server {
t.Helper()
tgzName := filepath.Base(tgzPath)
mux := http.NewServeMux()
server := httptest.NewServer(mux)
t.Cleanup(server.Close)
mux.HandleFunc("/"+tgzName, func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, tgzPath)
})
index := fmt.Sprintf(`apiVersion: v1
entries:
dep-chart:
- name: dep-chart
version: 0.1.0
apiVersion: v2
urls:
- %s/%s
`, server.URL, tgzName)
mux.HandleFunc("/index.yaml", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/yaml")
_, _ = w.Write([]byte(index))
})
return server
}
// writeParentChart writes a Chart.yaml declaring a single HTTP dependency and returns the chart dir.
func writeParentChart(t *testing.T, parentName, depName, depVersion, repoURL string) string {
t.Helper()
dir := t.TempDir()
chartYAML := fmt.Sprintf(`apiVersion: v2
name: %s
version: 0.1.0
dependencies:
- name: %s
version: %q
repository: %q
`, parentName, depName, depVersion, repoURL)
require.NoError(t, os.WriteFile(filesystem.JoinPaths(dir, "Chart.yaml"), []byte(chartYAML), 0o644))
return dir
}
func newIsolatedHelmSDKPackageManager(t *testing.T) *HelmSDKPackageManager {
t.Helper()
tmp := t.TempDir()
hspm := NewHelmSDKPackageManager()
hspm.settings.RepositoryConfig = filesystem.JoinPaths(tmp, "repositories.yaml")
hspm.settings.RepositoryCache = filesystem.JoinPaths(tmp, "repository")
hspm.settings.ContentCache = filesystem.JoinPaths(tmp, "content")
require.NoError(t, os.MkdirAll(hspm.settings.RepositoryCache, 0o700))
require.NoError(t, os.MkdirAll(hspm.settings.ContentCache, 0o700))
f := repo.NewFile()
require.NoError(t, f.WriteFile(hspm.settings.RepositoryConfig, 0o644))
return hspm
}