486 lines
12 KiB
Go
486 lines
12 KiB
Go
package state
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/helmfile/helmfile/pkg/filesystem"
|
|
)
|
|
|
|
func TestRewriteChartDependencies(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
chartYaml string
|
|
expectModified bool
|
|
expectError bool
|
|
validate func(t *testing.T, chartPath string)
|
|
}{
|
|
{
|
|
name: "no Chart.yaml exists",
|
|
chartYaml: "",
|
|
expectModified: false,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "no dependencies",
|
|
chartYaml: `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
`,
|
|
expectModified: false,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "absolute file:// dependency - not modified",
|
|
chartYaml: `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
dependencies:
|
|
- name: dep1
|
|
repository: file:///absolute/path/to/chart
|
|
version: 1.0.0
|
|
`,
|
|
expectModified: false,
|
|
expectError: false,
|
|
validate: func(t *testing.T, chartPath string) {
|
|
data, err := os.ReadFile(filepath.Join(chartPath, "Chart.yaml"))
|
|
if err != nil {
|
|
t.Fatalf("failed to read Chart.yaml: %v", err)
|
|
}
|
|
content := string(data)
|
|
if !strings.Contains(content, "file:///absolute/path/to/chart") {
|
|
t.Errorf("absolute path should not be modified")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "relative file:// dependency - should be modified",
|
|
chartYaml: `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
dependencies:
|
|
- name: dep1
|
|
repository: file://../relative-chart
|
|
version: 1.0.0
|
|
`,
|
|
expectModified: true,
|
|
expectError: false,
|
|
validate: func(t *testing.T, chartPath string) {
|
|
data, err := os.ReadFile(filepath.Join(chartPath, "Chart.yaml"))
|
|
if err != nil {
|
|
t.Fatalf("failed to read Chart.yaml: %v", err)
|
|
}
|
|
content := string(data)
|
|
|
|
// Should have been converted to absolute path
|
|
if strings.Contains(content, "file://../relative-chart") {
|
|
t.Errorf("relative path should have been converted to absolute")
|
|
}
|
|
|
|
// Should now have an absolute path
|
|
if !strings.Contains(content, "file://") {
|
|
t.Errorf("should still have file:// prefix")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "mixed dependencies - only relative file:// modified",
|
|
chartYaml: `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
dependencies:
|
|
- name: dep1
|
|
repository: https://charts.example.com
|
|
version: 1.0.0
|
|
- name: dep2
|
|
repository: file://../relative-chart
|
|
version: 2.0.0
|
|
- name: dep3
|
|
repository: file:///absolute/chart
|
|
version: 3.0.0
|
|
- name: dep4
|
|
repository: oci://registry.example.com/charts/mychart
|
|
version: 4.0.0
|
|
`,
|
|
expectModified: true,
|
|
expectError: false,
|
|
validate: func(t *testing.T, chartPath string) {
|
|
data, err := os.ReadFile(filepath.Join(chartPath, "Chart.yaml"))
|
|
if err != nil {
|
|
t.Fatalf("failed to read Chart.yaml: %v", err)
|
|
}
|
|
content := string(data)
|
|
|
|
// HTTPS repo should remain unchanged
|
|
if !strings.Contains(content, "https://charts.example.com") {
|
|
t.Errorf("https repository should not be modified")
|
|
}
|
|
|
|
// Relative path should be converted
|
|
if strings.Contains(content, "file://../relative-chart") {
|
|
t.Errorf("relative file:// path should have been converted")
|
|
}
|
|
|
|
// Absolute path should remain unchanged
|
|
if !strings.Contains(content, "file:///absolute/chart") {
|
|
t.Errorf("absolute file:// path should not be modified")
|
|
}
|
|
|
|
// OCI repo should remain unchanged
|
|
if !strings.Contains(content, "oci://registry.example.com") {
|
|
t.Errorf("oci repository should not be modified")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "multiple relative dependencies",
|
|
chartYaml: `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
dependencies:
|
|
- name: dep1
|
|
repository: file://../chart1
|
|
version: 1.0.0
|
|
- name: dep2
|
|
repository: file://./chart2
|
|
version: 2.0.0
|
|
- name: dep3
|
|
repository: file://../../../chart3
|
|
version: 3.0.0
|
|
`,
|
|
expectModified: true,
|
|
expectError: false,
|
|
validate: func(t *testing.T, chartPath string) {
|
|
data, err := os.ReadFile(filepath.Join(chartPath, "Chart.yaml"))
|
|
if err != nil {
|
|
t.Fatalf("failed to read Chart.yaml: %v", err)
|
|
}
|
|
content := string(data)
|
|
|
|
// All relative paths should be converted
|
|
if strings.Contains(content, "file://../chart1") ||
|
|
strings.Contains(content, "file://./chart2") ||
|
|
strings.Contains(content, "file://../../../chart3") {
|
|
t.Errorf("all relative paths should have been converted")
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Create temporary directory for test
|
|
tempDir, err := os.MkdirTemp("", "helmfile-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Create Chart.yaml if provided
|
|
if tt.chartYaml != "" {
|
|
chartYamlPath := filepath.Join(tempDir, "Chart.yaml")
|
|
if err := os.WriteFile(chartYamlPath, []byte(tt.chartYaml), 0644); err != nil {
|
|
t.Fatalf("failed to write Chart.yaml: %v", err)
|
|
}
|
|
}
|
|
|
|
// Create HelmState with logger
|
|
logger := zap.NewNop().Sugar()
|
|
st := &HelmState{
|
|
logger: logger,
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
// Read original content if it exists
|
|
var originalContent []byte
|
|
chartYamlPath := filepath.Join(tempDir, "Chart.yaml")
|
|
if _, err := os.Stat(chartYamlPath); err == nil {
|
|
originalContent, _ = os.ReadFile(chartYamlPath)
|
|
}
|
|
|
|
// Call rewriteChartDependencies
|
|
cleanup, err := st.rewriteChartDependencies(tempDir)
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("expected error but got none")
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Validate the modified Chart.yaml
|
|
if tt.validate != nil {
|
|
tt.validate(t, tempDir)
|
|
}
|
|
|
|
// Call cleanup and verify restoration
|
|
if tt.chartYaml != "" {
|
|
cleanup()
|
|
|
|
// Read restored content
|
|
restoredContent, err := os.ReadFile(chartYamlPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read restored Chart.yaml: %v", err)
|
|
}
|
|
|
|
// Verify content was restored
|
|
if string(restoredContent) != string(originalContent) {
|
|
t.Errorf("cleanup did not restore original content\noriginal:\n%s\nrestored:\n%s",
|
|
string(originalContent), string(restoredContent))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRewriteChartDependencies_CleanupRestoresOriginal(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "helmfile-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
originalChart := `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
dependencies:
|
|
- name: dep1
|
|
repository: file://../relative-chart
|
|
version: 1.0.0
|
|
`
|
|
|
|
chartYamlPath := filepath.Join(tempDir, "Chart.yaml")
|
|
if err := os.WriteFile(chartYamlPath, []byte(originalChart), 0644); err != nil {
|
|
t.Fatalf("failed to write Chart.yaml: %v", err)
|
|
}
|
|
|
|
logger := zap.NewNop().Sugar()
|
|
st := &HelmState{
|
|
logger: logger,
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
cleanup, err := st.rewriteChartDependencies(tempDir)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
// Verify modification happened
|
|
modifiedContent, err := os.ReadFile(chartYamlPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read modified Chart.yaml: %v", err)
|
|
}
|
|
|
|
if string(modifiedContent) == originalChart {
|
|
t.Errorf("Chart.yaml should have been modified")
|
|
}
|
|
|
|
// Call cleanup
|
|
cleanup()
|
|
|
|
// Verify restoration
|
|
restoredContent, err := os.ReadFile(chartYamlPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read restored Chart.yaml: %v", err)
|
|
}
|
|
|
|
if string(restoredContent) != originalChart {
|
|
t.Errorf("cleanup did not restore original content\nexpected:\n%s\ngot:\n%s",
|
|
originalChart, string(restoredContent))
|
|
}
|
|
}
|
|
|
|
func TestRewriteChartDependencies_PreservesOtherFields(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "helmfile-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
chartYaml := `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
description: A test chart
|
|
keywords:
|
|
- test
|
|
- example
|
|
maintainers:
|
|
- name: Test User
|
|
email: test@example.com
|
|
dependencies:
|
|
- name: dep1
|
|
repository: file://../relative-chart
|
|
version: 1.0.0
|
|
`
|
|
|
|
chartYamlPath := filepath.Join(tempDir, "Chart.yaml")
|
|
if err := os.WriteFile(chartYamlPath, []byte(chartYaml), 0644); err != nil {
|
|
t.Fatalf("failed to write Chart.yaml: %v", err)
|
|
}
|
|
|
|
logger := zap.NewNop().Sugar()
|
|
st := &HelmState{
|
|
logger: logger,
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
cleanup, err := st.rewriteChartDependencies(tempDir)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
// Read modified content
|
|
modifiedContent, err := os.ReadFile(chartYamlPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read modified Chart.yaml: %v", err)
|
|
}
|
|
|
|
content := string(modifiedContent)
|
|
|
|
// Verify top-level fields are preserved
|
|
// Note: The implementation preserves top-level chart metadata but may not preserve
|
|
// extra dependency-level fields (like condition, tags) that are not in the ChartDependency struct
|
|
requiredFields := []string{
|
|
"apiVersion: v2",
|
|
"name: test-chart",
|
|
"version: 1.0.0",
|
|
"description: A test chart",
|
|
"keywords:",
|
|
"maintainers:",
|
|
}
|
|
|
|
for _, field := range requiredFields {
|
|
if !strings.Contains(content, field) {
|
|
t.Errorf("field %q should be preserved", field)
|
|
}
|
|
}
|
|
|
|
// Verify the dependency was rewritten
|
|
if strings.Contains(content, "file://../relative-chart") {
|
|
t.Errorf("relative path should have been converted to absolute")
|
|
}
|
|
}
|
|
|
|
func TestRewriteChartDependencies_ErrorHandling(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setupFunc func(tempDir string) error
|
|
expectError bool
|
|
errorMsg string
|
|
}{
|
|
{
|
|
name: "invalid yaml in Chart.yaml",
|
|
setupFunc: func(tempDir string) error {
|
|
invalidYaml := `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
dependencies:
|
|
- name: dep1
|
|
repository: file://../chart
|
|
version: 1.0.0
|
|
invalid yaml here!
|
|
`
|
|
return os.WriteFile(filepath.Join(tempDir, "Chart.yaml"), []byte(invalidYaml), 0644)
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "unreadable Chart.yaml",
|
|
setupFunc: func(tempDir string) error {
|
|
chartYamlPath := filepath.Join(tempDir, "Chart.yaml")
|
|
if err := os.WriteFile(chartYamlPath, []byte("test"), 0000); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "helmfile-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
if err := tt.setupFunc(tempDir); err != nil {
|
|
t.Fatalf("setup failed: %v", err)
|
|
}
|
|
|
|
logger := zap.NewNop().Sugar()
|
|
st := &HelmState{
|
|
logger: logger,
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
_, err = st.rewriteChartDependencies(tempDir)
|
|
|
|
if tt.expectError && err == nil {
|
|
t.Errorf("expected error but got none")
|
|
}
|
|
if !tt.expectError && err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRewriteChartDependencies_WindowsStylePath(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "helmfile-test-")
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
// Test with backslash (Windows-style) paths
|
|
// Note: file:// URLs should use forward slashes, but test handling of edge cases
|
|
chartYaml := `apiVersion: v2
|
|
name: test-chart
|
|
version: 1.0.0
|
|
dependencies:
|
|
- name: dep1
|
|
repository: file://./subdir/chart
|
|
version: 1.0.0
|
|
`
|
|
|
|
chartYamlPath := filepath.Join(tempDir, "Chart.yaml")
|
|
if err := os.WriteFile(chartYamlPath, []byte(chartYaml), 0644); err != nil {
|
|
t.Fatalf("failed to write Chart.yaml: %v", err)
|
|
}
|
|
|
|
logger := zap.NewNop().Sugar()
|
|
st := &HelmState{
|
|
logger: logger,
|
|
fs: filesystem.DefaultFileSystem(),
|
|
}
|
|
|
|
cleanup, err := st.rewriteChartDependencies(tempDir)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
// Should handle the path correctly
|
|
data, err := os.ReadFile(chartYamlPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read Chart.yaml: %v", err)
|
|
}
|
|
|
|
content := string(data)
|
|
// The relative path should have been converted to absolute
|
|
if strings.Contains(content, "file://./subdir/chart") {
|
|
t.Errorf("relative path with ./ should have been converted")
|
|
}
|
|
}
|