diff --git a/controllers/gitlab/group_controller.go b/controllers/gitlab/group_controller.go index 6c6da8fd94c119ba8281ecf3a85e0dfdb3a1c6ee..ea3aa37df0a8ffce9d9f3df95c35dbb753c0a9d5 100644 --- a/controllers/gitlab/group_controller.go +++ b/controllers/gitlab/group_controller.go @@ -128,8 +128,6 @@ func (r *GroupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl } // Delete the Group if required - // name of our custom finalizer - // examine DeletionTimestamp to determine if object is under deletion if group.ObjectMeta.DeletionTimestamp.IsZero() { // The object is not being deleted, so if it does not have our finalizer, @@ -470,7 +468,7 @@ func (r *GroupReconciler) deleteProjectsAndGitlabGroup(ctx context.Context, grou r.Log.Error(err, errorWhileDeletingGroup) return err } - if err = r.updateState(ctx, group, errorWhileDeletingGroup); err != nil { + if err = r.updateState(ctx, group, "Terminating"); err != nil { return err } diff --git a/controllers/gitlab/project_controller.go b/controllers/gitlab/project_controller.go index e8555ee176df05b755f5410331bc8cda5d2c9fdc..d0254bf2b4929669f8410c9bf676e837286aba85 100644 --- a/controllers/gitlab/project_controller.go +++ b/controllers/gitlab/project_controller.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "strconv" apisGitlab "valkyrie.dso.mil/valkyrie-api/apis/gitlab" "valkyrie.dso.mil/valkyrie-api/apis/gitlab/v1alpha1" @@ -55,6 +56,8 @@ const ( errorGettingProjectVariableFromGitlab = "Error getting project variable from Gitlab" errorCreatingProjectVariableInGitlab = "Error creating project variable in Gitlab" errorWhileUpdatingProjectVariableInGitlab = "Error while updating project variable in gitlab" + errorRunningProjectFinalizer = "Error running project deleting finalizer" + errorWhileDeletingProject = "Error while deleting project" ) // statuses @@ -67,6 +70,8 @@ const ManifestImageVariableName = "MANIFEST_IMAGE" const annotationKeyID = "ID" +const projectFinalizerName = "valkyrie.dso.mil/valkyrie-api/apis/gitlab/project/finalizer" + // Reconcile is the main reconciliation loop that will create/edit/delete Gitlab Projects. func (r *ProjectReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { r.Log = r.Log.WithValues("project", req.NamespacedName) @@ -97,8 +102,42 @@ func (r *ProjectReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } + // Delete the Group if required + // examine DeletionTimestamp to determine if object is under deletion + if project.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted, so if it does not have our finalizer, + // then lets add the finalizer and update the object. This is equivalent + // registering our finalizer. + if !containsString(project.GetFinalizers(), projectFinalizerName) { + controllerutil.AddFinalizer(project, projectFinalizerName) + if err = r.Update(ctx, project); err != nil { + r.Log.Error(err, errorAddingFinalizer) + return ctrl.Result{}, err + } + } + } else { + // The object is being deleted + if containsString(project.GetFinalizers(), projectFinalizerName) { + // our finalizer is present, so lets handle any external dependency + if err = r.deleteProjectsFromGitlab(project); err != nil { + r.Log.Error(err, errorRunningProjectFinalizer) + return ctrl.Result{}, err + } + + // remove our finalizer from the list and update it. + controllerutil.RemoveFinalizer(project, groupFinalizerName) + if err = r.Update(ctx, project); err != nil { + r.Log.Error(err, errorRemovingFinalizer) + return ctrl.Result{}, err + } + } + + // Stop reconciliation as the item is being deleted + return ctrl.Result{}, nil + } + // Make Sure the Group Exists in Gitlab - if _, err = r.getGitlabGroup(ctx, project, req); err != nil { + if _, err = r.getGitlabGroup(project); err != nil { r.Log.Error(err, errorGettingGroupFromGitlab, "request", req) _ = r.updateStatus(ctx, project, groupDoesntExist) return ctrl.Result{Requeue: true}, err @@ -169,7 +208,7 @@ func (r *ProjectReconciler) getProject(ctx context.Context, request ctrl.Request return &project, nil } -func (r *ProjectReconciler) getGitlabGroup(ctx context.Context, project *v1alpha1.Project, req ctrl.Request) (*gitlab.Group, error) { +func (r *ProjectReconciler) getGitlabGroup(project *v1alpha1.Project) (*gitlab.Group, error) { var gitlabGroup *gitlab.Group var response int var err error @@ -286,3 +325,22 @@ func (r *ProjectReconciler) updateManifestImageSetting(id int, manifestPath stri return projectVariable, err } + +func (r *ProjectReconciler) deleteProjectsFromGitlab(project *v1alpha1.Project) error { + var id int + var err error + var statusCode int + + id, _ = strconv.Atoi(project.ObjectMeta.Annotations["ID"]) + + if statusCode, err = r.gitlabClient.DeleteProject(id, 0, 0); err != nil { + if statusCode == 404 { + // Assume it's deleted + return nil + } + r.Log.Error(err, errorWhileDeletingProject, "statusCode", statusCode) + return err + } + + return nil +} diff --git a/controllers/gitlab/project_controller_test.go b/controllers/gitlab/project_controller_test.go index 5215e38891a58a64731b4417c8c0e1266f320e22..2e2c1c935c8f91b7032e01a9abb4fb57f31b43cc 100644 --- a/controllers/gitlab/project_controller_test.go +++ b/controllers/gitlab/project_controller_test.go @@ -397,26 +397,19 @@ var _ = Describe("reconcile", func() { }) }) Context("updateProjectIDAnnotation returns an error", func() { - sut, _, log, _ := getProjectControllerWithMocksInGreenTestState() + sut, _, _, _ := getProjectControllerWithMocksInGreenTestState() sut.Client.(*MockClient).UpdateFunction = func(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { + return &MockError{message: errorUpdatingProjectIDAnnotation} } - request := getGreenProjectRequest() - - result, err := sut.Reconcile(context.TODO(), request) + project := getGreenProject() + err := sut.updateProjectIDAnnotation(context.TODO(), &project, &gitlab.Project{ ID: 1}) It("should return an error", func() { Expect(err).ToNot(BeNil()) }) - It("should requeue the object", func() { - Expect(result).To(Equal(ctrl.Result{Requeue: true})) - }) - It("should log the error", func() { - Expect(log.logLevelCalled).To(Equal("Error")) - Expect(log.loggedMessage).To(Equal(errorUpdatingProjectIDAnnotation)) - }) }) }) @@ -480,7 +473,7 @@ var _ = Describe("getGitlabGroup", func() { Context("green state", func() { sut, _, _, _ := getProjectControllerWithMocksInGreenTestState() project := getGreenProject() - group, err := sut.getGitlabGroup(context.TODO(), &project, getGreenProjectRequest()) + group, err := sut.getGitlabGroup(&project) It("should return a group, and no error.", func() { Expect(group).To(BeAssignableToTypeOf(&gitlab.Group{})) Expect(err).To(BeNil()) @@ -494,7 +487,7 @@ var _ = Describe("getGitlabGroup", func() { message: "failure in getGitlabGroup", } } - group, err := sut.getGitlabGroup(context.TODO(), &project, getGreenProjectRequest()) + group, err := sut.getGitlabGroup(&project) It("should return an error", func() { Expect(err).ToNot(BeNil()) })