fix(operator): skip PackageSource on empty URL, add comprehensive tests
Guard PackageSource creation on platformSourceURL != "" to avoid dangling SourceRef when no Flux source resource exists. Add tests covering all new and modified functions: - parsePlatformSourceURL (OCI, HTTPS, SSH, empty, unsupported scheme) - parseRefSpec (single/multi values, whitespace, equals in value, missing equals, empty key/value, trailing comma) - validateOCIRef / validateGitRef (valid keys, invalid keys, format) - generateOCIRepository / generateGitRepository (with ref, no ref, invalid ref) - installPlatformPackageSource variant valuesFiles validation - installPlatformPackageSource custom name Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
parent
8b9a11360e
commit
4843a617bc
1 changed files with 414 additions and 0 deletions
|
|
@ -137,6 +137,151 @@ func TestInstallPlatformPackageSource_Updates(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParsePlatformSourceURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantType string
|
||||
wantURL string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "OCI URL",
|
||||
url: "oci://ghcr.io/cozystack/cozystack/cozystack-packages",
|
||||
wantType: "oci",
|
||||
wantURL: "oci://ghcr.io/cozystack/cozystack/cozystack-packages",
|
||||
},
|
||||
{
|
||||
name: "HTTPS URL",
|
||||
url: "https://github.com/cozystack/cozystack",
|
||||
wantType: "git",
|
||||
wantURL: "https://github.com/cozystack/cozystack",
|
||||
},
|
||||
{
|
||||
name: "SSH URL",
|
||||
url: "ssh://git@github.com/cozystack/cozystack",
|
||||
wantType: "git",
|
||||
wantURL: "ssh://git@github.com/cozystack/cozystack",
|
||||
},
|
||||
{
|
||||
name: "empty URL",
|
||||
url: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unsupported scheme",
|
||||
url: "ftp://example.com/repo",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sourceType, repoURL, err := parsePlatformSourceURL(tt.url)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for URL %q, got nil", tt.url)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if sourceType != tt.wantType {
|
||||
t.Errorf("expected type %q, got %q", tt.wantType, sourceType)
|
||||
}
|
||||
if repoURL != tt.wantURL {
|
||||
t.Errorf("expected URL %q, got %q", tt.wantURL, repoURL)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallPlatformPackageSource_VariantValuesFiles(t *testing.T) {
|
||||
s := newTestScheme()
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
|
||||
err := installPlatformPackageSource(context.Background(), k8sClient, "cozystack-platform", "OCIRepository")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
ps := &cozyv1alpha1.PackageSource{}
|
||||
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cozystack.cozystack-platform"}, ps); err != nil {
|
||||
t.Fatalf("PackageSource not found: %v", err)
|
||||
}
|
||||
|
||||
expectedValuesFiles := map[string][]string{
|
||||
"default": {"values.yaml"},
|
||||
"isp-full": {"values.yaml", "values-isp-full.yaml"},
|
||||
"isp-hosted": {"values.yaml", "values-isp-hosted.yaml"},
|
||||
"isp-full-generic": {"values.yaml", "values-isp-full-generic.yaml"},
|
||||
}
|
||||
|
||||
for _, v := range ps.Spec.Variants {
|
||||
expected, ok := expectedValuesFiles[v.Name]
|
||||
if !ok {
|
||||
t.Errorf("unexpected variant %q", v.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(v.Components) != 1 {
|
||||
t.Errorf("variant %q: expected 1 component, got %d", v.Name, len(v.Components))
|
||||
continue
|
||||
}
|
||||
|
||||
comp := v.Components[0]
|
||||
if comp.Name != "platform" {
|
||||
t.Errorf("variant %q: expected component name %q, got %q", v.Name, "platform", comp.Name)
|
||||
}
|
||||
if comp.Path != "core/platform" {
|
||||
t.Errorf("variant %q: expected component path %q, got %q", v.Name, "core/platform", comp.Path)
|
||||
}
|
||||
if comp.Install == nil {
|
||||
t.Errorf("variant %q: expected Install to be set", v.Name)
|
||||
} else {
|
||||
if comp.Install.Namespace != "cozy-system" {
|
||||
t.Errorf("variant %q: expected install namespace %q, got %q", v.Name, "cozy-system", comp.Install.Namespace)
|
||||
}
|
||||
if comp.Install.ReleaseName != "cozystack-platform" {
|
||||
t.Errorf("variant %q: expected install releaseName %q, got %q", v.Name, "cozystack-platform", comp.Install.ReleaseName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(comp.ValuesFiles) != len(expected) {
|
||||
t.Errorf("variant %q: expected %d valuesFiles, got %d", v.Name, len(expected), len(comp.ValuesFiles))
|
||||
continue
|
||||
}
|
||||
for i, f := range expected {
|
||||
if comp.ValuesFiles[i] != f {
|
||||
t.Errorf("variant %q: expected valuesFiles[%d] %q, got %q", v.Name, i, f, comp.ValuesFiles[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallPlatformPackageSource_CustomName(t *testing.T) {
|
||||
s := newTestScheme()
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
|
||||
err := installPlatformPackageSource(context.Background(), k8sClient, "custom-source", "OCIRepository")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
ps := &cozyv1alpha1.PackageSource{}
|
||||
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cozystack.custom-source"}, ps); err != nil {
|
||||
t.Fatalf("PackageSource not found: %v", err)
|
||||
}
|
||||
|
||||
if ps.Name != "cozystack.custom-source" {
|
||||
t.Errorf("expected name %q, got %q", "cozystack.custom-source", ps.Name)
|
||||
}
|
||||
if ps.Spec.SourceRef.Name != "custom-source" {
|
||||
t.Errorf("expected sourceRef.name %q, got %q", "custom-source", ps.Spec.SourceRef.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallPlatformPackageSource_GitRepository(t *testing.T) {
|
||||
s := newTestScheme()
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).Build()
|
||||
|
|
@ -158,3 +303,272 @@ func TestInstallPlatformPackageSource_GitRepository(t *testing.T) {
|
|||
t.Errorf("expected sourceRef.name %q, got %q", "my-source", ps.Spec.SourceRef.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRefSpec(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty string",
|
||||
input: "",
|
||||
want: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "single key-value",
|
||||
input: "tag=v1.0",
|
||||
want: map[string]string{"tag": "v1.0"},
|
||||
},
|
||||
{
|
||||
name: "multiple key-values",
|
||||
input: "digest=sha256:abc123,tag=v1.0",
|
||||
want: map[string]string{"digest": "sha256:abc123", "tag": "v1.0"},
|
||||
},
|
||||
{
|
||||
name: "whitespace around pairs",
|
||||
input: " tag=v1.0 , branch=main ",
|
||||
want: map[string]string{"tag": "v1.0", "branch": "main"},
|
||||
},
|
||||
{
|
||||
name: "equals sign in value",
|
||||
input: "digest=sha256:abc=123",
|
||||
want: map[string]string{"digest": "sha256:abc=123"},
|
||||
},
|
||||
{
|
||||
name: "missing equals sign",
|
||||
input: "tag",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty key",
|
||||
input: "=value",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty value",
|
||||
input: "tag=",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "trailing comma",
|
||||
input: "tag=v1.0,",
|
||||
want: map[string]string{"tag": "v1.0"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseRefSpec(tt.input)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for input %q, got nil", tt.input)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(got) != len(tt.want) {
|
||||
t.Fatalf("expected %d entries, got %d: %v", len(tt.want), len(got), got)
|
||||
}
|
||||
for k, v := range tt.want {
|
||||
if got[k] != v {
|
||||
t.Errorf("expected %q=%q, got %q=%q", k, v, k, got[k])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateOCIRef(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
refMap map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid tag",
|
||||
refMap: map[string]string{"tag": "v1.0"},
|
||||
},
|
||||
{
|
||||
name: "valid digest",
|
||||
refMap: map[string]string{"digest": "sha256:abc123def456"},
|
||||
},
|
||||
{
|
||||
name: "valid semver",
|
||||
refMap: map[string]string{"semver": ">=1.0.0"},
|
||||
},
|
||||
{
|
||||
name: "multiple valid keys",
|
||||
refMap: map[string]string{"tag": "v1.0", "digest": "sha256:abc"},
|
||||
},
|
||||
{
|
||||
name: "empty map",
|
||||
refMap: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "invalid key",
|
||||
refMap: map[string]string{"branch": "main"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid digest format",
|
||||
refMap: map[string]string{"digest": "md5:abc"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateOCIRef(tt.refMap)
|
||||
if tt.wantErr && err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !tt.wantErr && err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateGitRef(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
refMap map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid branch",
|
||||
refMap: map[string]string{"branch": "main"},
|
||||
},
|
||||
{
|
||||
name: "valid commit",
|
||||
refMap: map[string]string{"commit": "abc1234"},
|
||||
},
|
||||
{
|
||||
name: "valid tag and branch",
|
||||
refMap: map[string]string{"tag": "v1.0", "branch": "release"},
|
||||
},
|
||||
{
|
||||
name: "empty map",
|
||||
refMap: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "invalid key",
|
||||
refMap: map[string]string{"digest": "sha256:abc"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "commit too short",
|
||||
refMap: map[string]string{"commit": "abc"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "commit not hex",
|
||||
refMap: map[string]string{"commit": "zzzzzzz"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateGitRef(tt.refMap)
|
||||
if tt.wantErr && err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !tt.wantErr && err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOCIRepository(t *testing.T) {
|
||||
refMap := map[string]string{"tag": "v1.0", "digest": "sha256:abc123"}
|
||||
obj, err := generateOCIRepository("my-repo", "oci://registry.example.com/repo", refMap)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if obj.Name != "my-repo" {
|
||||
t.Errorf("expected name %q, got %q", "my-repo", obj.Name)
|
||||
}
|
||||
if obj.Namespace != "cozy-system" {
|
||||
t.Errorf("expected namespace %q, got %q", "cozy-system", obj.Namespace)
|
||||
}
|
||||
if obj.Spec.URL != "oci://registry.example.com/repo" {
|
||||
t.Errorf("expected URL %q, got %q", "oci://registry.example.com/repo", obj.Spec.URL)
|
||||
}
|
||||
if obj.Spec.Reference == nil {
|
||||
t.Fatal("expected Reference to be set")
|
||||
}
|
||||
if obj.Spec.Reference.Tag != "v1.0" {
|
||||
t.Errorf("expected tag %q, got %q", "v1.0", obj.Spec.Reference.Tag)
|
||||
}
|
||||
if obj.Spec.Reference.Digest != "sha256:abc123" {
|
||||
t.Errorf("expected digest %q, got %q", "sha256:abc123", obj.Spec.Reference.Digest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOCIRepository_NoRef(t *testing.T) {
|
||||
obj, err := generateOCIRepository("my-repo", "oci://registry.example.com/repo", map[string]string{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if obj.Spec.Reference != nil {
|
||||
t.Error("expected Reference to be nil for empty refMap")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateOCIRepository_InvalidRef(t *testing.T) {
|
||||
_, err := generateOCIRepository("my-repo", "oci://registry.example.com/repo", map[string]string{"branch": "main"})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid OCI ref key, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateGitRepository(t *testing.T) {
|
||||
refMap := map[string]string{"branch": "main", "commit": "abc1234def5678"}
|
||||
obj, err := generateGitRepository("my-repo", "https://github.com/user/repo", refMap)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if obj.Name != "my-repo" {
|
||||
t.Errorf("expected name %q, got %q", "my-repo", obj.Name)
|
||||
}
|
||||
if obj.Namespace != "cozy-system" {
|
||||
t.Errorf("expected namespace %q, got %q", "cozy-system", obj.Namespace)
|
||||
}
|
||||
if obj.Spec.URL != "https://github.com/user/repo" {
|
||||
t.Errorf("expected URL %q, got %q", "https://github.com/user/repo", obj.Spec.URL)
|
||||
}
|
||||
if obj.Spec.Reference == nil {
|
||||
t.Fatal("expected Reference to be set")
|
||||
}
|
||||
if obj.Spec.Reference.Branch != "main" {
|
||||
t.Errorf("expected branch %q, got %q", "main", obj.Spec.Reference.Branch)
|
||||
}
|
||||
if obj.Spec.Reference.Commit != "abc1234def5678" {
|
||||
t.Errorf("expected commit %q, got %q", "abc1234def5678", obj.Spec.Reference.Commit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateGitRepository_NoRef(t *testing.T) {
|
||||
obj, err := generateGitRepository("my-repo", "https://github.com/user/repo", map[string]string{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if obj.Spec.Reference != nil {
|
||||
t.Error("expected Reference to be nil for empty refMap")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateGitRepository_InvalidRef(t *testing.T) {
|
||||
_, err := generateGitRepository("my-repo", "https://github.com/user/repo", map[string]string{"digest": "sha256:abc"})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid Git ref key, got nil")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue