diff --git a/PROJECT b/PROJECT index 5d8715d7..06edb9db 100644 --- a/PROJECT +++ b/PROJECT @@ -30,9 +30,4 @@ resources: group: appstudio kind: ComponentDetectionQuery version: v1alpha1 -- controller: true - domain: redhat.com - group: appstudio - kind: SnapshotEnvironmentBinding - version: v1alpha1 version: "3" diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e9768fe2..2244dc5d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -83,48 +83,6 @@ rules: - get - patch - update -- apiGroups: - - appstudio.redhat.com - resources: - - environments - verbs: - - get - - list - - watch -- apiGroups: - - appstudio.redhat.com - resources: - - snapshotenvironmentbindings - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - appstudio.redhat.com - resources: - - snapshotenvironmentbindings/finalizers - verbs: - - update -- apiGroups: - - appstudio.redhat.com - resources: - - snapshotenvironmentbindings/status - verbs: - - get - - patch - - update -- apiGroups: - - appstudio.redhat.com - resources: - - snapshots - verbs: - - get - - list - - watch - apiGroups: - appstudio.redhat.com resources: diff --git a/controllers/applicationsnapshotenvironmentbinding_controller.go b/controllers/applicationsnapshotenvironmentbinding_controller.go deleted file mode 100644 index 93b7ac71..00000000 --- a/controllers/applicationsnapshotenvironmentbinding_controller.go +++ /dev/null @@ -1,272 +0,0 @@ -/* -Copyright 2022-2023 Red Hat, Inc. - -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 controllers - -import ( - "context" - "fmt" - - "golang.org/x/exp/maps" - - "github.com/go-logr/logr" - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - "github.com/redhat-appstudio/application-service/pkg/github" - logutil "github.com/redhat-appstudio/application-service/pkg/log" - "github.com/redhat-appstudio/application-service/pkg/util/ioutils" - "github.com/spf13/afero" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -// SnapshotEnvironmentBindingReconciler reconciles a SnapshotEnvironmentBinding object -type SnapshotEnvironmentBindingReconciler struct { - client.Client - Scheme *runtime.Scheme - Log logr.Logger - AppFS afero.Afero - GitHubTokenClient github.GitHubToken -} - -//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=snapshotenvironmentbindings,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=snapshotenvironmentbindings/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=snapshotenvironmentbindings/finalizers,verbs=update -//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=snapshots,verbs=get;list;watch -//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=environments,verbs=get;list;watch - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the SnapshotEnvironmentBinding object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile -func (r *SnapshotEnvironmentBindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := ctrl.LoggerFrom(ctx) - - // Fetch the SnapshotEnvironmentBinding instance - var appSnapshotEnvBinding appstudiov1alpha1.SnapshotEnvironmentBinding - err := r.Get(ctx, req.NamespacedName, &appSnapshotEnvBinding) - if err != nil { - if errors.IsNotFound(err) { - // Request object not found, could have been deleted after reconcile request. - // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. - // Return and don't requeue - return ctrl.Result{}, nil - } - // Error reading the object - requeue the request. - return ctrl.Result{}, err - } - - log.Info(fmt.Sprintf("Starting reconcile loop for %v %v", appSnapshotEnvBinding.Name, req.NamespacedName)) - - ghClient, err := r.GitHubTokenClient.GetNewGitHubClient("") - if err != nil { - log.Error(err, "Unable to create Go-GitHub client due to error") - return reconcile.Result{}, err - } - - // Add the Go-GitHub client name to the context - ctx = context.WithValue(ctx, github.GHClientKey, ghClient.TokenName) - - applicationName := appSnapshotEnvBinding.Spec.Application - environmentName := appSnapshotEnvBinding.Spec.Environment - snapshotName := appSnapshotEnvBinding.Spec.Snapshot - components := appSnapshotEnvBinding.Spec.Components - - // Check if the labels have been applied to the binding - requiredLabels := map[string]string{ - "appstudio.application": applicationName, - "appstudio.environment": environmentName, - } - bindingLabels := appSnapshotEnvBinding.GetLabels() - if bindingLabels["appstudio.application"] == "" || bindingLabels["appstudio.environment"] == "" { - if bindingLabels != nil { - maps.Copy(bindingLabels, requiredLabels) - } else { - bindingLabels = requiredLabels - } - appSnapshotEnvBinding.SetLabels(bindingLabels) - if err := r.Client.Update(ctx, &appSnapshotEnvBinding); err != nil { - return ctrl.Result{}, err - } - } - - // Get the Environment CR - environment := appstudiov1alpha1.Environment{} - err = r.Get(ctx, types.NamespacedName{Name: environmentName, Namespace: appSnapshotEnvBinding.Namespace}, &environment) - if err != nil { - log.Error(err, fmt.Sprintf("unable to get the Environment %s %v", environmentName, req.NamespacedName)) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - // Get the Snapshot CR - appSnapshot := appstudiov1alpha1.Snapshot{} - err = r.Get(ctx, types.NamespacedName{Name: snapshotName, Namespace: appSnapshotEnvBinding.Namespace}, &appSnapshot) - if err != nil { - log.Error(err, fmt.Sprintf("unable to get the Application Snapshot %s %v", snapshotName, req.NamespacedName)) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - if appSnapshot.Spec.Application != applicationName { - err := fmt.Errorf("application snapshot %s does not belong to the application %s", snapshotName, applicationName) - log.Error(err, "") - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - var tempDir string - - for _, component := range components { - componentName := component.Name - - // Get the Component CR - hasComponent := appstudiov1alpha1.Component{} - err = r.Get(ctx, types.NamespacedName{Name: componentName, Namespace: appSnapshotEnvBinding.Namespace}, &hasComponent) - if err != nil { - log.Error(err, fmt.Sprintf("unable to get the Component %s %v", componentName, req.NamespacedName)) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - return ctrl.Result{}, err - } - - if hasComponent.Spec.SkipGitOpsResourceGeneration { - continue - } - - // Sanity check to make sure the binding component has referenced the correct application - if hasComponent.Spec.Application != applicationName { - err := fmt.Errorf("component %s does not belong to the application %s", componentName, applicationName) - log.Error(err, "") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - var imageName string - - for _, snapshotComponent := range appSnapshot.Spec.Components { - if snapshotComponent.Name == componentName { - imageName = snapshotComponent.ContainerImage - break - } - } - - if imageName == "" { - err := fmt.Errorf("application snapshot %s did not reference component %s", snapshotName, componentName) - log.Error(err, "") - ioutils.RemoveFolderAndLogError(log, r.AppFS, tempDir) - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - // Set the BindingComponent status - componentStatus := appstudiov1alpha1.BindingComponentStatus{ - Name: componentName, - GitOpsRepository: appstudiov1alpha1.BindingComponentGitOpsRepository{ - URL: "", - Branch: "", - Path: "", - GeneratedResources: []string{}, - }, - } - - isNewComponent := true - for i := range appSnapshotEnvBinding.Status.Components { - if appSnapshotEnvBinding.Status.Components[i].Name == componentStatus.Name { - appSnapshotEnvBinding.Status.Components[i] = componentStatus - isNewComponent = false - break - } - } - if isNewComponent { - appSnapshotEnvBinding.Status.Components = append(appSnapshotEnvBinding.Status.Components, componentStatus) - } - - } - - // Update the binding status to reflect the GitOps data - err = r.Client.Status().Update(ctx, &appSnapshotEnvBinding) - if err != nil { - log.Error(err, "Unable to update App Snapshot Env Binding") - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, err) - return ctrl.Result{}, err - } - - r.SetConditionAndUpdateCR(ctx, req, &appSnapshotEnvBinding, nil) - - log.Info(fmt.Sprintf("Finished reconcile loop for %v", req.NamespacedName)) - return ctrl.Result{}, nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *SnapshotEnvironmentBindingReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - log := ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Environment") - return ctrl.NewControllerManagedBy(mgr). - For(&appstudiov1alpha1.SnapshotEnvironmentBinding{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). - // Watch for Environment CR updates and reconcile all the Bindings that reference the Environment - Watches(&source.Kind{Type: &appstudiov1alpha1.Environment{}}, - handler.EnqueueRequestsFromMapFunc(MapToBindingByBoundObjectName(r.Client, "Environment", "appstudio.environment")), builder.WithPredicates(predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - log := log.WithValues("namespace", e.Object.GetNamespace()) - logutil.LogAPIResourceChangeEvent(log, e.Object.GetName(), "Environment", logutil.ResourceCreate, nil) - return false - }, - UpdateFunc: func(e event.UpdateEvent) bool { - log := log.WithValues("namespace", e.ObjectNew.GetNamespace()) - logutil.LogAPIResourceChangeEvent(log, e.ObjectNew.GetName(), "Environment", logutil.ResourceUpdate, nil) - return true - }, - DeleteFunc: func(e event.DeleteEvent) bool { - log := log.WithValues("namespace", e.Object.GetNamespace()) - logutil.LogAPIResourceChangeEvent(log, e.Object.GetName(), "Environment", logutil.ResourceDelete, nil) - return false - }, - GenericFunc: func(e event.GenericEvent) bool { - return false - }, - })).WithEventFilter(predicate.Funcs{ - CreateFunc: func(e event.CreateEvent) bool { - log := log.WithValues("namespace", e.Object.GetNamespace()) - logutil.LogAPIResourceChangeEvent(log, e.Object.GetName(), "SnapshotEnvironmentBinding", logutil.ResourceCreate, nil) - return true - }, - UpdateFunc: func(e event.UpdateEvent) bool { - log := log.WithValues("namespace", e.ObjectNew.GetNamespace()) - logutil.LogAPIResourceChangeEvent(log, e.ObjectNew.GetName(), "SnapshotEnvironmentBinding", logutil.ResourceUpdate, nil) - return true - }, - DeleteFunc: func(e event.DeleteEvent) bool { - log := log.WithValues("namespace", e.Object.GetNamespace()) - logutil.LogAPIResourceChangeEvent(log, e.Object.GetName(), "SnapshotEnvironmentBinding", logutil.ResourceDelete, nil) - return false - }, - }). - Complete(r) -} diff --git a/controllers/applicationsnapshotenvironmentbinding_controller_conditions.go b/controllers/applicationsnapshotenvironmentbinding_controller_conditions.go deleted file mode 100644 index 73669055..00000000 --- a/controllers/applicationsnapshotenvironmentbinding_controller_conditions.go +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright 2022-2023 Red Hat, Inc. -// -// 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 controllers - -import ( - "context" - "fmt" - - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - logutil "github.com/redhat-appstudio/application-service/pkg/log" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func (r *SnapshotEnvironmentBindingReconciler) SetConditionAndUpdateCR(ctx context.Context, req ctrl.Request, appSnapshotEnvBinding *appstudiov1alpha1.SnapshotEnvironmentBinding, createError error) { - log := r.Log.WithValues("namespace", req.NamespacedName.Namespace) - - var currentSEB appstudiov1alpha1.SnapshotEnvironmentBinding - err := r.Get(ctx, req.NamespacedName, ¤tSEB) - if err != nil { - return - } - - patch := client.MergeFrom(currentSEB.DeepCopy()) - condition := metav1.Condition{} - if createError == nil { - condition = metav1.Condition{ - Type: "GitOpsResourcesGenerated", - Status: metav1.ConditionTrue, - Reason: "OK", - Message: "GitOps repository sync successful", - } - } else { - condition = metav1.Condition{ - Type: "GitOpsResourcesGenerated", - Status: metav1.ConditionFalse, - Reason: "GenerateError", - Message: fmt.Sprintf("GitOps repository sync failed: %v", createError), - } - - } - meta.SetStatusCondition(¤tSEB.Status.GitOpsRepoConditions, condition) - logutil.LogAPIResourceChangeEvent(log, currentSEB.Name, "SnapshotEnvironmentBinding", logutil.ResourceCreate, createError) - currentSEB.Status.Components = appSnapshotEnvBinding.Status.Components - - err = r.Client.Status().Patch(ctx, ¤tSEB, patch) - if err != nil { - log.Error(err, "Unable to update application snapshot environment binding") - - } -} diff --git a/controllers/applicationsnapshotenvironmentbinding_controller_test.go b/controllers/applicationsnapshotenvironmentbinding_controller_test.go deleted file mode 100644 index 8c682739..00000000 --- a/controllers/applicationsnapshotenvironmentbinding_controller_test.go +++ /dev/null @@ -1,980 +0,0 @@ -/* -Copyright 2022-2023 Red Hat, Inc. - -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 controllers - -import ( - "context" - "fmt" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - //+kubebuilder:scaffold:imports -) - -var _ = Describe("SnapshotEnvironmentBinding controller", func() { - - // Define utility constants for object names and testing timeouts/durations and intervals. - const ( - HASAppName = "test-app" - HASCompName = "test-comp" - HASSnapshotName = "test-snapshot" - HASBindingName = "test-binding" - HASAppNamespace = "default" - DisplayName = "petclinic" - Description = "Simple petclinic app" - ComponentName = "backend" - SampleRepoLink = "https://github.com/devfile-samples/devfile-sample-java-springboot-basic" - ) - - Context("Create SnapshotEnvironmentBinding with component configurations", func() { - It("Should reconcile successfully", func() { - ctx := context.Background() - - applicationName := HASAppName + "1" - componentName := HASCompName + "1" - snapshotName := HASSnapshotName + "1" - bindingName := HASBindingName + "1" - environmentName := "staging" + "1" - - replicas := 3 - - createAndFetchSimpleApp(applicationName, HASAppNamespace, DisplayName, Description) - - hasComp := createAndFetchSimpleComponent(componentName, HASAppNamespace, ComponentName, applicationName, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp.Status.Devfile).Should(Not(Equal(""))) - - appSnapshot := &appstudiov1alpha1.Snapshot{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Snapshot", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: snapshotName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotSpec{ - Application: applicationName, - DisplayName: "My Snapshot", - DisplayDescription: "My Snapshot", - Components: []appstudiov1alpha1.SnapshotComponent{ - { - Name: componentName, - ContainerImage: "image1", - }, - }, - }, - } - Expect(k8sClient.Create(ctx, appSnapshot)).Should(Succeed()) - - appSnapshotLookupKey := types.NamespacedName{Name: snapshotName, Namespace: HASAppNamespace} - createdAppSnapshot := &appstudiov1alpha1.Snapshot{} - Eventually(func() bool { - k8sClient.Get(context.Background(), appSnapshotLookupKey, createdAppSnapshot) - return len(createdAppSnapshot.Spec.Components) > 0 - }, timeout, interval).Should(BeTrue()) - - stagingEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: environmentName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - Expect(k8sClient.Create(ctx, stagingEnv)).Should(Succeed()) - - appBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: environmentName, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - }, - }, - } - - Expect(k8sClient.Create(ctx, appBinding)).Should(Succeed()) - - bindingLookupKey := types.NamespacedName{Name: bindingName, Namespace: HASAppNamespace} - createdBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - Eventually(func() bool { - k8sClient.Get(context.Background(), bindingLookupKey, createdBinding) - return len(createdBinding.Status.GitOpsRepoConditions) > 0 && len(createdBinding.Status.Components) == 1 - }, timeout, interval).Should(BeTrue()) - - Expect(createdBinding.Status.Components[0].Name).Should(Equal(componentName)) - - bindingLabels := createdBinding.GetLabels() - // If no prior labels exist, SEB controllers should only add 2 label entries - Expect(len(bindingLabels)).Should(Equal(2)) - Expect(bindingLabels["appstudio.application"]).Should(Equal(applicationName)) - Expect(bindingLabels["appstudio.environment"]).Should(Equal(environmentName)) - - // Delete the specified HASComp resource - hasCompLookupKey := types.NamespacedName{Name: componentName, Namespace: HASAppNamespace} - deleteHASCompCR(hasCompLookupKey) - - // Delete the specified HASApp resource - hasAppLookupKey := types.NamespacedName{Name: applicationName, Namespace: HASAppNamespace} - deleteHASAppCR(hasAppLookupKey) - - // Delete the specified binding - deleteBinding(bindingLookupKey) - - // Delete the specified snapshot - deleteSnapshot(appSnapshotLookupKey) - - // Delete the specified environment - stagingEnvLookupKey := types.NamespacedName{Name: environmentName, Namespace: HASAppNamespace} - deleteEnvironment(stagingEnvLookupKey) - }) - }) - - Context("Create SnapshotEnvironmentBinding with a missing component", func() { - It("Should fail if there is no such component by name", func() { - ctx := context.Background() - - applicationName := HASAppName + "2" - componentName := HASCompName + "2" - componentName2 := HASCompName + "2-2" - snapshotName := HASSnapshotName + "2" - bindingName := HASBindingName + "2" - environmentName := "staging" + "2" - - replicas := 3 - - createAndFetchSimpleApp(applicationName, HASAppNamespace, DisplayName, Description) - - hasComp := createAndFetchSimpleComponent(componentName, HASAppNamespace, ComponentName, applicationName, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp.Status.Devfile).Should(Not(Equal(""))) - - appSnapshot := &appstudiov1alpha1.Snapshot{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Snapshot", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: snapshotName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotSpec{ - Application: applicationName, - DisplayName: "My Snapshot", - DisplayDescription: "My Snapshot", - Components: []appstudiov1alpha1.SnapshotComponent{ - { - Name: componentName, - ContainerImage: "image1", - }, - }, - }, - } - Expect(k8sClient.Create(ctx, appSnapshot)).Should(Succeed()) - - appSnapshotLookupKey := types.NamespacedName{Name: snapshotName, Namespace: HASAppNamespace} - createdAppSnapshot := &appstudiov1alpha1.Snapshot{} - Eventually(func() bool { - k8sClient.Get(context.Background(), appSnapshotLookupKey, createdAppSnapshot) - return len(createdAppSnapshot.Spec.Components) > 0 - }, timeout, interval).Should(BeTrue()) - - stagingEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: environmentName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - Expect(k8sClient.Create(ctx, stagingEnv)).Should(Succeed()) - - appBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: environmentName, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - { - Name: componentName2, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - }, - }, - }, - }, - } - - Expect(k8sClient.Create(ctx, appBinding)).Should(Succeed()) - - bindingLookupKey := types.NamespacedName{Name: bindingName, Namespace: HASAppNamespace} - createdBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - Eventually(func() bool { - k8sClient.Get(context.Background(), bindingLookupKey, createdBinding) - return len(createdBinding.Status.GitOpsRepoConditions) > 0 - }, timeout, interval).Should(BeTrue()) - - Expect(createdBinding.Status.GitOpsRepoConditions[len(createdBinding.Status.GitOpsRepoConditions)-1].Message).Should(ContainSubstring(fmt.Sprintf("%q not found", componentName2))) - - // Delete the specified HASComp resource - hasCompLookupKey := types.NamespacedName{Name: componentName, Namespace: HASAppNamespace} - deleteHASCompCR(hasCompLookupKey) - - // Delete the specified HASApp resource - hasAppLookupKey := types.NamespacedName{Name: applicationName, Namespace: HASAppNamespace} - deleteHASAppCR(hasAppLookupKey) - - // Delete the specified binding - deleteBinding(bindingLookupKey) - - // Delete the specified snapshot - deleteSnapshot(appSnapshotLookupKey) - - // Delete the specified environment - stagingEnvLookupKey := types.NamespacedName{Name: environmentName, Namespace: HASAppNamespace} - deleteEnvironment(stagingEnvLookupKey) - }) - }) - - Context("Create SnapshotEnvironmentBinding with a missing snapshot", func() { - It("Should fail if there is no such snapshot by name", func() { - ctx := context.Background() - - applicationName := HASAppName + "3" - componentName := HASCompName + "3" - snapshotName := HASSnapshotName + "3" - bindingName := HASBindingName + "3" - environmentName := "staging" + "3" - - replicas := 3 - - createAndFetchSimpleApp(applicationName, HASAppNamespace, DisplayName, Description) - - hasComp := createAndFetchSimpleComponent(componentName, HASAppNamespace, ComponentName, applicationName, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp.Status.Devfile).Should(Not(Equal(""))) - - stagingEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: environmentName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - Expect(k8sClient.Create(ctx, stagingEnv)).Should(Succeed()) - - appBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: environmentName, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - }, - }, - } - - Expect(k8sClient.Create(ctx, appBinding)).Should(Succeed()) - - bindingLookupKey := types.NamespacedName{Name: bindingName, Namespace: HASAppNamespace} - createdBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - Eventually(func() bool { - k8sClient.Get(context.Background(), bindingLookupKey, createdBinding) - return len(createdBinding.Status.GitOpsRepoConditions) > 0 - }, timeout, interval).Should(BeTrue()) - - Expect(createdBinding.Status.GitOpsRepoConditions[len(createdBinding.Status.GitOpsRepoConditions)-1].Message).Should(ContainSubstring(fmt.Sprintf("%q not found", snapshotName))) - - // Delete the specified HASComp resource - hasCompLookupKey := types.NamespacedName{Name: componentName, Namespace: HASAppNamespace} - deleteHASCompCR(hasCompLookupKey) - - // Delete the specified HASApp resource - hasAppLookupKey := types.NamespacedName{Name: applicationName, Namespace: HASAppNamespace} - deleteHASAppCR(hasAppLookupKey) - - // Delete the specified binding - deleteBinding(bindingLookupKey) - - // Delete the specified environment - stagingEnvLookupKey := types.NamespacedName{Name: environmentName, Namespace: HASAppNamespace} - deleteEnvironment(stagingEnvLookupKey) - }) - }) - - Context("Create SnapshotEnvironmentBinding and Snapshot referencing a wrong Application", func() { - It("Should err out when Snapshot doesnt reference the same Application as the Binding", func() { - ctx := context.Background() - - applicationName := HASAppName + "4" - applicationName2 := HASAppName + "4-2" - componentName := HASCompName + "4" - snapshotName := HASSnapshotName + "4" - bindingName := HASBindingName + "4" - environmentName := "staging" + "4" - - replicas := 3 - - createAndFetchSimpleApp(applicationName, HASAppNamespace, DisplayName, Description) - - hasComp := createAndFetchSimpleComponent(componentName, HASAppNamespace, ComponentName, applicationName, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp.Status.Devfile).Should(Not(Equal(""))) - - appSnapshot := &appstudiov1alpha1.Snapshot{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Snapshot", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: snapshotName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotSpec{ - Application: applicationName2, - DisplayName: "My Snapshot", - DisplayDescription: "My Snapshot", - Components: []appstudiov1alpha1.SnapshotComponent{ - { - Name: componentName, - ContainerImage: "image1", - }, - }, - }, - } - Expect(k8sClient.Create(ctx, appSnapshot)).Should(Succeed()) - - appSnapshotLookupKey := types.NamespacedName{Name: snapshotName, Namespace: HASAppNamespace} - createdAppSnapshot := &appstudiov1alpha1.Snapshot{} - Eventually(func() bool { - k8sClient.Get(context.Background(), appSnapshotLookupKey, createdAppSnapshot) - return len(createdAppSnapshot.Spec.Components) > 0 - }, timeout, interval).Should(BeTrue()) - - stagingEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: environmentName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - Expect(k8sClient.Create(ctx, stagingEnv)).Should(Succeed()) - - appBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: environmentName, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - }, - }, - } - - Expect(k8sClient.Create(ctx, appBinding)).Should(Succeed()) - - bindingLookupKey := types.NamespacedName{Name: bindingName, Namespace: HASAppNamespace} - createdBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - Eventually(func() bool { - k8sClient.Get(context.Background(), bindingLookupKey, createdBinding) - return len(createdBinding.Status.GitOpsRepoConditions) > 0 - }, timeout, interval).Should(BeTrue()) - - Expect(createdBinding.Status.GitOpsRepoConditions[len(createdBinding.Status.GitOpsRepoConditions)-1].Message).Should(ContainSubstring(fmt.Sprintf("application snapshot %s does not belong to the application %s", snapshotName, applicationName))) - - // Delete the specified HASComp resource - hasCompLookupKey := types.NamespacedName{Name: componentName, Namespace: HASAppNamespace} - deleteHASCompCR(hasCompLookupKey) - - // Delete the specified HASApp resource - hasAppLookupKey := types.NamespacedName{Name: applicationName, Namespace: HASAppNamespace} - deleteHASAppCR(hasAppLookupKey) - - // Delete the specified binding - deleteBinding(bindingLookupKey) - - // Delete the specified snapshot - deleteSnapshot(appSnapshotLookupKey) - - // Delete the specified environment - stagingEnvLookupKey := types.NamespacedName{Name: environmentName, Namespace: HASAppNamespace} - deleteEnvironment(stagingEnvLookupKey) - }) - }) - - Context("Create SnapshotEnvironmentBinding and Component referencing a wrong Application", func() { - It("Should err out when Component doesnt reference the same Application as the Binding", func() { - ctx := context.Background() - - applicationName := HASAppName + "5" - applicationName2 := HASAppName + "5-2" - componentName := HASCompName + "5" - componentName2 := HASCompName + "5-2" - snapshotName := HASSnapshotName + "5" - bindingName := HASBindingName + "5" - environmentName := "staging" + "5" - - replicas := 3 - - createAndFetchSimpleApp(applicationName, HASAppNamespace, DisplayName, Description) - createAndFetchSimpleApp(applicationName2, HASAppNamespace, DisplayName, Description) - - hasComp := createAndFetchSimpleComponent(componentName, HASAppNamespace, ComponentName, applicationName, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp.Status.Devfile).Should(Not(Equal(""))) - - hasComp2 := createAndFetchSimpleComponent(componentName2, HASAppNamespace, ComponentName+"2", applicationName2, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp2.Status.Devfile).Should(Not(Equal(""))) - - appSnapshot := &appstudiov1alpha1.Snapshot{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Snapshot", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: snapshotName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotSpec{ - Application: applicationName, - DisplayName: "My Snapshot", - DisplayDescription: "My Snapshot", - Components: []appstudiov1alpha1.SnapshotComponent{ - { - Name: componentName, - ContainerImage: "image1", - }, - }, - }, - } - Expect(k8sClient.Create(ctx, appSnapshot)).Should(Succeed()) - - appSnapshotLookupKey := types.NamespacedName{Name: snapshotName, Namespace: HASAppNamespace} - createdAppSnapshot := &appstudiov1alpha1.Snapshot{} - Eventually(func() bool { - k8sClient.Get(context.Background(), appSnapshotLookupKey, createdAppSnapshot) - return len(createdAppSnapshot.Spec.Components) > 0 - }, timeout, interval).Should(BeTrue()) - - stagingEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: environmentName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - Expect(k8sClient.Create(ctx, stagingEnv)).Should(Succeed()) - - appBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: environmentName, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - { - Name: componentName2, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - }, - }, - }, - }, - } - - Expect(k8sClient.Create(ctx, appBinding)).Should(Succeed()) - - bindingLookupKey := types.NamespacedName{Name: bindingName, Namespace: HASAppNamespace} - createdBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - Eventually(func() bool { - k8sClient.Get(context.Background(), bindingLookupKey, createdBinding) - return len(createdBinding.Status.GitOpsRepoConditions) > 0 - }, timeout, interval).Should(BeTrue()) - - Expect(createdBinding.Status.GitOpsRepoConditions[len(createdBinding.Status.GitOpsRepoConditions)-1].Message).Should(ContainSubstring(fmt.Sprintf("component %s does not belong to the application %s", componentName2, applicationName))) - - // Delete the specified HASComp resource - hasCompLookupKey := types.NamespacedName{Name: componentName, Namespace: HASAppNamespace} - hasCompLookupKey2 := types.NamespacedName{Name: componentName2, Namespace: HASAppNamespace} - deleteHASCompCR(hasCompLookupKey) - deleteHASCompCR(hasCompLookupKey2) - - // Delete the specified HASApp resource - hasAppLookupKey := types.NamespacedName{Name: applicationName, Namespace: HASAppNamespace} - deleteHASAppCR(hasAppLookupKey) - hasAppLookupKey2 := types.NamespacedName{Name: applicationName2, Namespace: HASAppNamespace} - deleteHASAppCR(hasAppLookupKey2) - - // Delete the specified binding - deleteBinding(bindingLookupKey) - - // Delete the specified snapshot - deleteSnapshot(appSnapshotLookupKey) - - // Delete the specified environment - stagingEnvLookupKey := types.NamespacedName{Name: environmentName, Namespace: HASAppNamespace} - deleteEnvironment(stagingEnvLookupKey) - }) - }) - - Context("Create SnapshotEnvironmentBinding and Snapshot referencing a wrong Application", func() { - It("Should err out when Snapshot doesnt reference the same Application as the Binding", func() { - ctx := context.Background() - - applicationName := HASAppName + "6" - componentName := HASCompName + "6" - componentName2 := HASCompName + "6-2" - snapshotName := HASSnapshotName + "6" - bindingName := HASBindingName + "6" - environmentName := "staging" + "6" - - replicas := 3 - - createAndFetchSimpleApp(applicationName, HASAppNamespace, DisplayName, Description) - - hasComp := createAndFetchSimpleComponent(componentName, HASAppNamespace, ComponentName, applicationName, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp.Status.Devfile).Should(Not(Equal(""))) - - hasComp2 := createAndFetchSimpleComponent(componentName2, HASAppNamespace, ComponentName+"2", applicationName, SampleRepoLink, false) - // Make sure the devfile model was properly set in Component - Expect(hasComp2.Status.Devfile).Should(Not(Equal(""))) - - appSnapshot := &appstudiov1alpha1.Snapshot{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Snapshot", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: snapshotName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotSpec{ - Application: applicationName, - DisplayName: "My Snapshot", - DisplayDescription: "My Snapshot", - Components: []appstudiov1alpha1.SnapshotComponent{ - { - Name: componentName, - ContainerImage: "image1", - }, - }, - }, - } - Expect(k8sClient.Create(ctx, appSnapshot)).Should(Succeed()) - - appSnapshotLookupKey := types.NamespacedName{Name: snapshotName, Namespace: HASAppNamespace} - createdAppSnapshot := &appstudiov1alpha1.Snapshot{} - Eventually(func() bool { - k8sClient.Get(context.Background(), appSnapshotLookupKey, createdAppSnapshot) - return len(createdAppSnapshot.Spec.Components) > 0 - }, timeout, interval).Should(BeTrue()) - - stagingEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: environmentName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - Expect(k8sClient.Create(ctx, stagingEnv)).Should(Succeed()) - - appBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: HASAppNamespace, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: environmentName, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - Resources: &corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse("1"), - }, - }, - }, - }, - { - Name: componentName2, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - }, - }, - }, - }, - } - - Expect(k8sClient.Create(ctx, appBinding)).Should(Succeed()) - - bindingLookupKey := types.NamespacedName{Name: bindingName, Namespace: HASAppNamespace} - createdBinding := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - Eventually(func() bool { - k8sClient.Get(context.Background(), bindingLookupKey, createdBinding) - return len(createdBinding.Status.GitOpsRepoConditions) > 0 - }, timeout, interval).Should(BeTrue()) - - Expect(createdBinding.Status.GitOpsRepoConditions[len(createdBinding.Status.GitOpsRepoConditions)-1].Message).Should(ContainSubstring(fmt.Sprintf("application snapshot %s did not reference component %s", snapshotName, componentName2))) - - // Delete the specified HASComp resource - hasCompLookupKey := types.NamespacedName{Name: componentName, Namespace: HASAppNamespace} - hasCompLookupKey2 := types.NamespacedName{Name: componentName2, Namespace: HASAppNamespace} - deleteHASCompCR(hasCompLookupKey) - deleteHASCompCR(hasCompLookupKey2) - - // Delete the specified HASApp resource - hasAppLookupKey := types.NamespacedName{Name: applicationName, Namespace: HASAppNamespace} - deleteHASAppCR(hasAppLookupKey) - - // Delete the specified binding - deleteBinding(bindingLookupKey) - - // Delete the specified snapshot - deleteSnapshot(appSnapshotLookupKey) - - // Delete the specified environment - stagingEnvLookupKey := types.NamespacedName{Name: environmentName, Namespace: HASAppNamespace} - deleteEnvironment(stagingEnvLookupKey) - }) - }) - -}) - -// deleteBinding deletes the specified binding resource and verifies it was properly deleted -func deleteBinding(bindingLookupKey types.NamespacedName) { - // Delete - Eventually(func() error { - f := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - k8sClient.Get(context.Background(), bindingLookupKey, f) - return k8sClient.Delete(context.Background(), f) - }, timeout, interval).Should(Succeed()) - - // Wait for delete to finish - Eventually(func() error { - f := &appstudiov1alpha1.SnapshotEnvironmentBinding{} - return k8sClient.Get(context.Background(), bindingLookupKey, f) - }, timeout, interval).ShouldNot(Succeed()) -} - -// deleteSnapshot deletes the specified snapshot resource and verifies it was properly deleted -func deleteSnapshot(snapshotLookupKey types.NamespacedName) { - // Delete - Eventually(func() error { - f := &appstudiov1alpha1.Snapshot{} - k8sClient.Get(context.Background(), snapshotLookupKey, f) - return k8sClient.Delete(context.Background(), f) - }, timeout, interval).Should(Succeed()) - - // Wait for delete to finish - Eventually(func() error { - f := &appstudiov1alpha1.Snapshot{} - return k8sClient.Get(context.Background(), snapshotLookupKey, f) - }, timeout, interval).ShouldNot(Succeed()) -} - -// deleteEnvironment deletes the specified Environment resource and verifies it was properly deleted -func deleteEnvironment(environmentLookupKey types.NamespacedName) { - // Delete - Eventually(func() error { - f := &appstudiov1alpha1.Environment{} - k8sClient.Get(context.Background(), environmentLookupKey, f) - return k8sClient.Delete(context.Background(), f) - }, timeout, interval).Should(Succeed()) - - // Wait for delete to finish - Eventually(func() error { - f := &appstudiov1alpha1.Environment{} - return k8sClient.Get(context.Background(), environmentLookupKey, f) - }, timeout, interval).ShouldNot(Succeed()) -} - -func createAndFetchSimpleComponent(name, namespace, componentName, application, gitRepo string, skipGitOps bool) appstudiov1alpha1.Component { - comp := &appstudiov1alpha1.Component{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Component", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: appstudiov1alpha1.ComponentSpec{ - ComponentName: componentName, - Application: application, - Source: appstudiov1alpha1.ComponentSource{ - ComponentSourceUnion: appstudiov1alpha1.ComponentSourceUnion{ - GitSource: &appstudiov1alpha1.GitSource{ - URL: gitRepo, - }, - }, - }, - SkipGitOpsResourceGeneration: skipGitOps, - }, - } - Expect(k8sClient.Create(ctx, comp)).Should(Succeed()) - - // Look up the has app resource that was created. - // num(conditions) may still be < 1 on the first try, so retry until at least _some_ condition is set - hasCompLookupKey := types.NamespacedName{Name: name, Namespace: namespace} - createdComp := &appstudiov1alpha1.Component{} - Eventually(func() bool { - k8sClient.Get(context.Background(), hasCompLookupKey, createdComp) - return len(createdComp.Status.Conditions) > 0 - }, timeout, interval).Should(BeTrue()) - - Expect(createdComp.Status.Devfile).To(Not(Equal(""))) - - return *createdComp -} diff --git a/controllers/mapper.go b/controllers/mapper.go index b881a32e..a671aa14 100644 --- a/controllers/mapper.go +++ b/controllers/mapper.go @@ -16,54 +16,12 @@ package controllers import ( - "context" - "fmt" - appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -// MapToBindingByBoundObjectName maps the bound object (Environment) to the associated Bindings. -// The correct Bindings are listed using the given label whose value should equal to the object's name. -// Adapted from https://github.com/codeready-toolchain/host-operator/blob/master/controllers/spacebindingcleanup/mapper.go#L17 -func MapToBindingByBoundObjectName(cl client.Client, objectType, label string) func(object client.Object) []reconcile.Request { - return func(obj client.Object) []reconcile.Request { - mapperLog := ctrl.Log.WithName("MapToBindingByBoundObjectName") - log := mapperLog.WithValues("name", obj.GetName()).WithValues("namespace", obj.GetNamespace()).WithValues("controllerKind", obj.GetObjectKind()) - ctx := context.Background() - - bindingList := &appstudiov1alpha1.SnapshotEnvironmentBindingList{} - err := cl.List(ctx, bindingList, - client.InNamespace(obj.GetNamespace()), - client.MatchingLabels{label: obj.GetName()}) - if err != nil { - log.Error(err, fmt.Sprintf("unable to list SnapshotEnvironmentBinding for a %s object %s", objectType, obj.GetName())) - return []reconcile.Request{} - } - if len(bindingList.Items) == 0 { - log.Info(fmt.Sprintf("no SnapshotEnvironmentBinding found for a %s object %s", objectType, obj.GetName())) - return []reconcile.Request{} - } - - log.Info(fmt.Sprintf("Found %d SnapshotEnvironmentBindings for a %s object %s", len(bindingList.Items), objectType, obj.GetName())) - - req := make([]reconcile.Request, len(bindingList.Items)) - for i, item := range bindingList.Items { - req[i] = reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: item.Namespace, - Name: item.Name, - }, - } - log.Info(fmt.Sprintf("The corresponding SnapshotEnvironmentBinding %s will be reconciled", item.Name)) - } - return req - } -} - // MapComponentToApplication returns an event handler that will convert events on a Component CR to events on its parent Application func MapComponentToApplication() func(object client.Object) []reconcile.Request { return func(obj client.Object) []reconcile.Request { diff --git a/controllers/mapper_test.go b/controllers/mapper_test.go index 9b293f00..5b1f8bce 100644 --- a/controllers/mapper_test.go +++ b/controllers/mapper_test.go @@ -17,7 +17,6 @@ package controllers import ( "context" - "fmt" "testing" appstudiov1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1" @@ -32,224 +31,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -// Adapted from https://github.com/codeready-toolchain/host-operator/blob/master/controllers/spacebindingcleanup/mapper_test.go -func TestMapToBindingByBoundObject(t *testing.T) { - - const ( - HASAppName = "test-app" - HASCompName = "test-comp" - HASSnapshotName = "test-snapshot" - HASBindingName = "test-binding" - Namespace = "default" - DisplayName = "an environment" - ComponentName = "backend" - SampleRepoLink = "https://github.com/devfile-samples/devfile-sample-java-springboot-basic" - ) - - applicationName := HASAppName + "1" - applicationName2 := HASAppName + "2" - componentName := HASCompName + "1" - componentName2 := HASCompName + "2" - snapshotName := HASSnapshotName + "1" - snapshotName2 := HASSnapshotName + "2" - bindingName := HASBindingName + "1" - bindingName2 := HASBindingName + "2" - bindingName3 := HASBindingName + "3" - bindingName4 := HASBindingName + "4" - staging := "staging" - dev := "dev" - prod := "prod" - replicas := 3 - - // given - binding1 := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: Namespace, - Labels: map[string]string{ - "appstudio.environment": staging, - "appstudio.application": applicationName, - }, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: staging, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - }, - }, - }, - }, - } - - binding2 := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName2, - Namespace: Namespace, - Labels: map[string]string{ - "appstudio.environment": dev, - "appstudio.application": applicationName2, - }, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName2, - Environment: dev, - Snapshot: snapshotName2, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName2, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - }, - }, - }, - }, - } - - binding3 := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName3, - Namespace: Namespace, - Labels: map[string]string{ - "appstudio.environment": staging, - "appstudio.application": applicationName2, - }, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName2, - Environment: staging, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - }, - }, - }, - }, - } - - binding4 := &appstudiov1alpha1.SnapshotEnvironmentBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "SnapshotEnvironmentBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName4, - Namespace: Namespace, - }, - Spec: appstudiov1alpha1.SnapshotEnvironmentBindingSpec{ - Application: applicationName, - Environment: staging, - Snapshot: snapshotName, - Components: []appstudiov1alpha1.BindingComponent{ - { - Name: componentName, - Configuration: appstudiov1alpha1.BindingComponentConfiguration{ - Replicas: &replicas, - }, - }, - }, - }, - } - - stagingEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: staging, - Namespace: Namespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - - prodEnv := &appstudiov1alpha1.Environment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "appstudio.redhat.com/v1alpha1", - Kind: "Environment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: prod, - Namespace: Namespace, - }, - Spec: appstudiov1alpha1.EnvironmentSpec{ - Type: "Non-POC", - DisplayName: DisplayName, - DeploymentStrategy: appstudiov1alpha1.DeploymentStrategy_AppStudioAutomated, - Configuration: appstudiov1alpha1.EnvironmentConfiguration{ - Env: []appstudiov1alpha1.EnvVarPair{ - { - Name: "FOO", - Value: "BAR", - }, - }, - }, - }, - } - - fakeClient := NewFakeClient(t, binding1, binding2, binding3, binding4) - - t.Run("should return two Binding requests for staging Env", func(t *testing.T) { - // when - requests := MapToBindingByBoundObjectName(fakeClient, "Environment", "appstudio.environment")(stagingEnv) - - // then - require.Len(t, requests, 2) // binding4 is not returned because binding4 does not have a label matching the staging env - assert.Contains(t, requests, newRequest(binding1.Name)) - assert.Contains(t, requests, newRequest(binding3.Name)) - }) - - t.Run("should return no Binding requests for prod Env", func(t *testing.T) { - // when - requests := MapToBindingByBoundObjectName(fakeClient, "Environment", "appstudio.environment")(prodEnv) - - // then - require.Empty(t, requests) - }) - - t.Run("should return no Binding requests when Binding list fails", func(t *testing.T) { - fakeClient.MockList = func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { - return fmt.Errorf("some error") - } - // when - requests := MapToBindingByBoundObjectName(fakeClient, "Environment", "appstudio.environment")(prodEnv) - - // then - require.Empty(t, requests) - }) -} - func TestMapApplicationToComponent(t *testing.T) { const ( diff --git a/controllers/start_test_env.go b/controllers/start_test_env.go index 6bcceb4f..04dbf938 100644 --- a/controllers/start_test_env.go +++ b/controllers/start_test_env.go @@ -137,15 +137,6 @@ func SetupTestEnv() (client.Client, *envtest.Environment, context.Context, conte }).SetupWithManager(ctx, k8sManager) gomega.Expect(err).ToNot(gomega.HaveOccurred()) - err = (&SnapshotEnvironmentBindingReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("SnapshotEnvironmentBinding"), - AppFS: ioutils.NewMemoryFilesystem(), - GitHubTokenClient: mockGhTokenClient, - }).SetupWithManager(ctx, k8sManager) - gomega.Expect(err).ToNot(gomega.HaveOccurred()) - go func() { defer ginkgo.GinkgoRecover() err = k8sManager.Start(ctx) diff --git a/main.go b/main.go index 65b1e487..1115bfa8 100644 --- a/main.go +++ b/main.go @@ -219,16 +219,6 @@ func main() { setUpWebhooks(mgr) } - if err = (&controllers.SnapshotEnvironmentBindingReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Log: ctrl.Log.WithName("controllers").WithName("SnapshotEnvironmentBinding"), - AppFS: ioutils.NewFilesystem(), - GitHubTokenClient: ghTokenClient, - }).SetupWithManager(ctx, mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "SnapshotEnvironmentBinding") - os.Exit(1) - } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {