diff --git a/api/v1alpha1/localbuild_types.go b/api/v1alpha1/localbuild_types.go index 62d53ba6..5b8d41cb 100644 --- a/api/v1alpha1/localbuild_types.go +++ b/api/v1alpha1/localbuild_types.go @@ -14,9 +14,12 @@ const ( CliStartTimeAnnotation = "cnoe.io/cli-start-time" FieldManager = "idpbuilder" // If GetSecretLabelKey is set to GetSecretLabelValue on a kubernetes secret, secret key and values can be used by the get command. - CLISecretLabelKey = "cnoe.io/cli-secret" - CLISecretLabelValue = "true" - PackageNameLabelKey = "cnoe.io/package-name" + CLISecretLabelKey = "cnoe.io/cli-secret" + CLISecretLabelValue = "true" + PackageNameLabelKey = "cnoe.io/package-name" + PackageTypeLabelKey = "cnoe.io/package-type" + PackageTypeLabelCore = "core" + PackageTypeLabelCustom = "custom" ArgoCDPackageName = "argocd" GiteaPackageName = "gitea" diff --git a/pkg/controllers/custompackage/controller.go b/pkg/controllers/custompackage/controller.go index 7ffda9e2..72d1bffb 100644 --- a/pkg/controllers/custompackage/controller.go +++ b/pkg/controllers/custompackage/controller.go @@ -93,6 +93,7 @@ func (r *Reconciler) reconcileCustomPackage(ctx context.Context, resource *v1alp if !ok { return ctrl.Result{}, fmt.Errorf("object is not an ArgoCD application %s", resource.Spec.ArgoCD.ApplicationFile) } + util.SetPackageLabels(app) res, err := r.reconcileArgoCDApp(ctx, resource, app) if err != nil { @@ -112,7 +113,7 @@ func (r *Reconciler) reconcileCustomPackage(ctx context.Context, resource *v1alp } return ctrl.Result{}, fmt.Errorf("getting argocd application object: %w", err) } - + util.SetPackageLabels(&foundAppObj) foundAppObj.Spec = app.Spec foundAppObj.ObjectMeta.Annotations = app.GetAnnotations() foundAppObj.ObjectMeta.Labels = app.GetLabels() @@ -128,10 +129,14 @@ func (r *Reconciler) reconcileCustomPackage(ctx context.Context, resource *v1alp if !ok { return ctrl.Result{}, fmt.Errorf("object is not an ArgoCD application set %s", resource.Spec.ArgoCD.ApplicationFile) } + + util.SetPackageLabels(appSet) + res, err := r.reconcileArgoCDAppSet(ctx, resource, appSet) if err != nil { return ctrl.Result{}, err } + foundAppSetObj := argov1alpha1.ApplicationSet{} err = r.Client.Get(ctx, client.ObjectKeyFromObject(appSet), &foundAppSetObj) if err != nil { @@ -145,6 +150,7 @@ func (r *Reconciler) reconcileCustomPackage(ctx context.Context, resource *v1alp return ctrl.Result{}, fmt.Errorf("getting argocd application set object: %w", err) } + util.SetPackageLabels(&foundAppSetObj) foundAppSetObj.Spec = appSet.Spec foundAppSetObj.ObjectMeta.Annotations = appSet.GetAnnotations() foundAppSetObj.ObjectMeta.Labels = appSet.GetLabels() diff --git a/pkg/controllers/custompackage/controller_test.go b/pkg/controllers/custompackage/controller_test.go index 578138b6..1ff72582 100644 --- a/pkg/controllers/custompackage/controller_test.go +++ b/pkg/controllers/custompackage/controller_test.go @@ -14,6 +14,7 @@ import ( argov1alpha1 "github.com/cnoe-io/argocd-api/api/argo/application/v1alpha1" "github.com/cnoe-io/idpbuilder/api/v1alpha1" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sruntime "k8s.io/apimachinery/pkg/runtime" @@ -48,17 +49,13 @@ func TestReconcileCustomPkg(t *testing.T) { } cfg, err := testEnv.Start() - if err != nil { - t.Fatalf("Starting testenv: %v", err) - } + require.NoError(t, err) defer testEnv.Stop() mgr, err := ctrl.NewManager(cfg, ctrl.Options{ Scheme: s, }) - if err != nil { - t.Fatalf("getting manager: %v", err) - } + require.NoError(t, err) ctx, ctxCancel := context.WithCancel(context.Background()) stoppedCh := make(chan error) @@ -82,9 +79,8 @@ func TestReconcileCustomPkg(t *testing.T) { Recorder: mgr.GetEventRecorderFor("test-custompkg-controller"), } cwd, err := os.Getwd() - if err != nil { - t.Fatalf("getting cwd %v", err) - } + require.NoError(t, err) + customPkgs := []v1alpha1.CustomPackage{ { ObjectMeta: metav1.ObjectMeta{ @@ -191,45 +187,58 @@ func TestReconcileCustomPkg(t *testing.T) { } assert.Equal(t, repo.Spec, expectedRepo.Spec) ok := reflect.DeepEqual(repo.Spec, expectedRepo.Spec) - if !ok { - t.Fatalf("expected spec does not match") - } + assert.True(t, ok) - // verify argocd apps - localApp := argov1alpha1.Application{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-app", - Namespace: "argocd", + tcs := []struct { + name string + }{ + { + name: "my-app", + }, + { + name: "my-app2", + }, + { + name: "guestbook", }, - } - err = c.Get(context.Background(), client.ObjectKeyFromObject(&localApp), &localApp) - if err != nil { - t.Fatalf("failed getting my-app %v", err) - } - if strings.HasPrefix(localApp.Spec.Source.RepoURL, v1alpha1.CNOEURIScheme) { - t.Fatalf("%s prefix should be removed", v1alpha1.CNOEURIScheme) } - for _, n := range []string{"guestbook", "guestbook2"} { - err = c.Get(context.Background(), client.ObjectKeyFromObject(&localApp), &localApp) - if err != nil { - t.Fatalf("expected %s arogapp : %v", n, err) + for _, tc := range tcs { + app := argov1alpha1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.name, + Namespace: "argocd", + }, } - } + err = c.Get(context.Background(), client.ObjectKeyFromObject(&app), &app) + assert.NoError(t, err) - localApp2 := argov1alpha1.Application{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-app2", - Namespace: "argocd", - }, - } - err = c.Get(context.Background(), client.ObjectKeyFromObject(&localApp2), &localApp2) - if err != nil { - t.Fatalf("failed getting my-app2 %v", err) - } + if app.ObjectMeta.Labels == nil { + t.Fatalf("labels not set") + } + + _, ok := app.ObjectMeta.Labels[v1alpha1.PackageNameLabelKey] + if !ok { + t.Fatalf("label %s not set", v1alpha1.PackageTypeLabelKey) + } + + _, ok = app.ObjectMeta.Labels[v1alpha1.PackageNameLabelKey] + if !ok { + t.Fatalf("label %s not set", v1alpha1.PackageNameLabelKey) + } + + if app.Spec.Sources == nil { + if strings.HasPrefix(app.Spec.Source.RepoURL, v1alpha1.CNOEURIScheme) { + t.Fatalf("%s prefix should be removed", v1alpha1.CNOEURIScheme) + } + continue + } + for _, s := range app.Spec.Sources { + if strings.HasPrefix(s.RepoURL, v1alpha1.CNOEURIScheme) { + t.Fatalf("%s prefix should be removed", v1alpha1.CNOEURIScheme) + } + } - if strings.HasPrefix(localApp2.Spec.Sources[0].RepoURL, v1alpha1.CNOEURIScheme) { - t.Fatalf("%s prefix should be removed", v1alpha1.CNOEURIScheme) } } diff --git a/pkg/controllers/localbuild/controller.go b/pkg/controllers/localbuild/controller.go index 20a513ed..6b8b2ff8 100644 --- a/pkg/controllers/localbuild/controller.go +++ b/pkg/controllers/localbuild/controller.go @@ -19,8 +19,10 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/selection" "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -234,6 +236,8 @@ func (r *LocalbuildReconciler) reconcileEmbeddedApp(ctx context.Context, appName }, } + util.SetPackageLabels(app) + if err := controllerutil.SetControllerReference(resource, app, r.Scheme); err != nil { return ctrl.Result{}, err } @@ -282,6 +286,30 @@ func (r *LocalbuildReconciler) shouldShutDown(ctx context.Context, resource *v1a return false, err } + // check if core packages are ready + selector := labels.NewSelector() + req, err := labels.NewRequirement(v1alpha1.PackageTypeLabelKey, selection.Equals, []string{v1alpha1.PackageTypeLabelCore}) + if err != nil { + return false, fmt.Errorf("building labels with key %s and value %s : %w", v1alpha1.PackageTypeLabelKey, v1alpha1.PackageTypeLabelCore, err) + } + + opts := client.ListOptions{ + LabelSelector: selector.Add(*req), + Namespace: "", + } + apps := argov1alpha1.ApplicationList{} + err = r.Client.List(ctx, &apps, &opts) + if err != nil { + return false, fmt.Errorf("listing core packages: %w", err) + } + + for _, app := range apps.Items { + if app.Status.Health.Status != "Healthy" { + return false, nil + } + } + + // check if repositories are ready repos := &v1alpha1.GitRepositoryList{} err = r.Client.List(ctx, repos, client.InNamespace(resource.Namespace)) if err != nil { @@ -313,6 +341,7 @@ func (r *LocalbuildReconciler) shouldShutDown(ctx context.Context, resource *v1a } } + // check if custom packages are ready pkgs := &v1alpha1.CustomPackageList{} err = r.Client.List(ctx, pkgs, client.InNamespace(resource.Namespace)) if err != nil { diff --git a/pkg/util/util.go b/pkg/util/util.go index 86f4fad3..dbd606fe 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -167,3 +167,19 @@ func DetectKindNodeProvider() (cluster.ProviderOption, error) { return cluster.DetectNodeProvider() } } + +func SetPackageLabels(obj client.Object) { + labels := obj.GetLabels() + if labels == nil { + labels = map[string]string{} + obj.SetLabels(labels) + } + labels[v1alpha1.PackageNameLabelKey] = obj.GetName() + + switch n := obj.GetName(); n { + case v1alpha1.ArgoCDPackageName, v1alpha1.GiteaPackageName, v1alpha1.IngressNginxPackageName: + labels[v1alpha1.PackageTypeLabelKey] = v1alpha1.PackageTypeLabelCore + default: + labels[v1alpha1.PackageTypeLabelKey] = v1alpha1.PackageTypeLabelCustom + } +} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index e5ae343f..a93ed026 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -3,6 +3,12 @@ package util import ( "strconv" "testing" + + "github.com/cnoe-io/idpbuilder/api/v1alpha1" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ) var specialCharMap = make(map[string]struct{}) @@ -42,3 +48,89 @@ func TestGeneratePassword(t *testing.T) { } } } + +type MockObject struct { + v1.ObjectMeta +} + +func (m *MockObject) GetObjectKind() schema.ObjectKind { + return nil +} + +func (m *MockObject) DeepCopyObject() runtime.Object { + return nil +} + +func TestSetPackageLabels(t *testing.T) { + testCases := []struct { + name string + objectName string + initialLabels map[string]string + expectedLabels map[string]string + }{ + { + name: "No initial labels", + objectName: "test-package", + initialLabels: nil, + expectedLabels: map[string]string{ + v1alpha1.PackageNameLabelKey: "test-package", + v1alpha1.PackageTypeLabelKey: v1alpha1.PackageTypeLabelCustom, + }, + }, + { + name: "With initial labels", + objectName: "test-package-one", + initialLabels: map[string]string{ + "existing": "label", + v1alpha1.PackageNameLabelKey: "incorrect", + }, + expectedLabels: map[string]string{ + "existing": "label", + v1alpha1.PackageNameLabelKey: "test-package-one", + v1alpha1.PackageTypeLabelKey: v1alpha1.PackageTypeLabelCustom, + }, + }, + { + name: "ArgoCD package", + objectName: v1alpha1.ArgoCDPackageName, + initialLabels: nil, + expectedLabels: map[string]string{ + v1alpha1.PackageNameLabelKey: v1alpha1.ArgoCDPackageName, + v1alpha1.PackageTypeLabelKey: v1alpha1.PackageTypeLabelCore, + }, + }, + { + name: "Gitea package", + objectName: v1alpha1.GiteaPackageName, + initialLabels: nil, + expectedLabels: map[string]string{ + v1alpha1.PackageNameLabelKey: v1alpha1.GiteaPackageName, + v1alpha1.PackageTypeLabelKey: v1alpha1.PackageTypeLabelCore, + }, + }, + { + name: "IngressNginx package", + objectName: v1alpha1.IngressNginxPackageName, + initialLabels: nil, + expectedLabels: map[string]string{ + v1alpha1.PackageNameLabelKey: v1alpha1.IngressNginxPackageName, + v1alpha1.PackageTypeLabelKey: v1alpha1.PackageTypeLabelCore, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + obj := &MockObject{ + ObjectMeta: v1.ObjectMeta{ + Name: tc.objectName, + Labels: tc.initialLabels, + }, + } + + SetPackageLabels(obj) + + assert.Equal(t, tc.expectedLabels, obj.GetLabels()) + }) + } +}