Skip to content

Commit

Permalink
Implements immediate deletion of GitLab groups
Browse files Browse the repository at this point in the history
  • Loading branch information
buehlmann committed Nov 24, 2024
1 parent 3408b27 commit aa6ed30
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 11 deletions.
10 changes: 10 additions & 0 deletions apis/groups/v1alpha1/group_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ type GroupParameters struct {
// SharedWithGroups create links for sharing a group with another group.
// +optional
SharedWithGroups []SharedWithGroups `json:"sharedWithGroups,omitempty"`

// Force the immediate deletion of the group when removed. In GitLab Premium and Ultimate a group is by default
// just marked for deletion and removed permanently after seven days. Defaults to false.
// +optional
PermanentlyRemove *bool `json:"permanentlyRemove,omitempty"`

// Full path of group to delete permanently. Only required if PermanentlyRemove is set to true.
// GitLab Premium and Ultimate only.
// +optional
FullPathToRemove *string `json:"fullPathToRemove,omitempty"`
}

// AccessLevelValue represents a permission level within GitLab.
Expand Down
10 changes: 10 additions & 0 deletions apis/groups/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions package/crds/groups.gitlab.crossplane.io_groups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ spec:
description: Extra pipeline minutes quota for this group (purchased
in addition to the minutes included in the plan).
type: integer
fullPathToRemove:
description: |-
Full path of group to delete permanently. Only required if PermanentlyRemove is set to true.
GitLab Premium and Ultimate only.
type: string
lfsEnabled:
description: Enable/disable Large File Storage (LFS) for the projects
in this group.
Expand Down Expand Up @@ -194,6 +199,11 @@ spec:
path:
description: The path of the group.
type: string
permanentlyRemove:
description: |-
Force the immediate deletion of the group when removed. In GitLab Premium and Ultimate a group is by default
just marked for deletion and removed permanently after seven days. Defaults to false.
type: boolean
projectCreationLevel:
description: |-
developers can create projects in the group.
Expand Down
23 changes: 17 additions & 6 deletions pkg/controller/groups/groups/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ package groups
import (
"context"
"strconv"
"strings"
"time"

"github.com/crossplane-contrib/provider-gitlab/apis/groups/v1alpha1"
secretstoreapi "github.com/crossplane-contrib/provider-gitlab/apis/v1alpha1"
"github.com/crossplane-contrib/provider-gitlab/pkg/clients"
"github.com/crossplane-contrib/provider-gitlab/pkg/clients/groups"
"github.com/crossplane-contrib/provider-gitlab/pkg/features"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/crossplane/crossplane-runtime/pkg/connection"
"github.com/crossplane/crossplane-runtime/pkg/controller"
Expand All @@ -34,12 +40,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/crossplane-contrib/provider-gitlab/apis/groups/v1alpha1"
secretstoreapi "github.com/crossplane-contrib/provider-gitlab/apis/v1alpha1"
"github.com/crossplane-contrib/provider-gitlab/pkg/clients"
"github.com/crossplane-contrib/provider-gitlab/pkg/clients/groups"
"github.com/crossplane-contrib/provider-gitlab/pkg/features"
)

const (
Expand Down Expand Up @@ -237,6 +237,17 @@ func (e *external) Delete(ctx context.Context, mg resource.Managed) error {
}

_, err := e.client.DeleteGroup(meta.GetExternalName(cr), &gitlab.DeleteGroupOptions{}, gitlab.WithContext(ctx))
// if the group is for some reason already marked for deletion, we ignore the error and continue to delete the group permanently
if err != nil && !strings.Contains(err.Error(), "Group has been already marked for deletion") {
return errors.Wrap(err, errDeleteFailed)
}

if cr.Spec.ForProvider.PermanentlyRemove != nil && *cr.Spec.ForProvider.PermanentlyRemove {
_, err = e.client.DeleteGroup(meta.GetExternalName(cr), &gitlab.DeleteGroupOptions{
PermanentlyRemove: cr.Spec.ForProvider.PermanentlyRemove,
FullPath: cr.Spec.ForProvider.FullPathToRemove,
}, gitlab.WithContext(ctx))
}
return errors.Wrap(err, errDeleteFailed)
}

Expand Down
56 changes: 51 additions & 5 deletions pkg/controller/groups/groups/group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ func withPath(s string) groupModifier {
return func(r *v1alpha1.Group) { r.Spec.ForProvider.Path = s }
}

func withPermanentlyRemove(b *bool) groupModifier {
return func(r *v1alpha1.Group) { r.Spec.ForProvider.PermanentlyRemove = b }
}

func withFullPathToRemove(s *string) groupModifier {
return func(r *v1alpha1.Group) { r.Spec.ForProvider.FullPathToRemove = s }
}

func withDescription(s *string) groupModifier {
return func(r *v1alpha1.Group) { r.Spec.ForProvider.Description = s }
}
Expand Down Expand Up @@ -844,9 +852,16 @@ func TestUpdate(t *testing.T) {
}

func TestDelete(t *testing.T) {
type deleteGroupCalls struct {
Pid interface{}
Opt *gitlab.DeleteGroupOptions
}
var recordedCalls []deleteGroupCalls

type want struct {
cr resource.Managed
err error
cr resource.Managed
calls []deleteGroupCalls
err error
}

cases := map[string]struct {
Expand All @@ -866,21 +881,23 @@ func TestDelete(t *testing.T) {
args: args{
group: &fake.MockClient{
MockDeleteGroup: func(pid interface{}, opt *gitlab.DeleteGroupOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
recordedCalls = append(recordedCalls, deleteGroupCalls{Pid: pid, Opt: opt})
return &gitlab.Response{}, nil
},
},
cr: group(withExternalName("0")),
},
want: want{
cr: group(withExternalName("0")),
err: nil,
cr: group(withExternalName("0")),
calls: []deleteGroupCalls{{Pid: "0", Opt: &gitlab.DeleteGroupOptions{}}},
err: nil,
},
},
"FailedDeletion": {
args: args{
group: &fake.MockClient{
MockDeleteGroup: func(pid interface{}, opt *gitlab.DeleteGroupOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
return &gitlab.Response{}, errBoom
return nil, errBoom
},
},
cr: group(),
Expand All @@ -890,9 +907,35 @@ func TestDelete(t *testing.T) {
err: errors.Wrap(errBoom, errDeleteFailed),
},
},
"SuccessfulPermanentlyDeletion": {
args: args{
group: &fake.MockClient{
MockDeleteGroup: func(pid interface{}, opt *gitlab.DeleteGroupOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
recordedCalls = append(recordedCalls, deleteGroupCalls{Pid: pid, Opt: opt})
return &gitlab.Response{}, nil
},
},
cr: group(
withExternalName("0"),
withPermanentlyRemove(gitlab.Ptr(true)),
withFullPathToRemove(gitlab.Ptr("path/to/remove"))),
},
want: want{
cr: group(
withExternalName("0"),
withPermanentlyRemove(gitlab.Ptr(true)),
withFullPathToRemove(gitlab.Ptr("path/to/remove"))),
calls: []deleteGroupCalls{
{Pid: "0", Opt: &gitlab.DeleteGroupOptions{}},
{Pid: "0", Opt: &gitlab.DeleteGroupOptions{PermanentlyRemove: gitlab.Ptr(true), FullPath: gitlab.Ptr("path/to/remove")}},
},
err: nil,
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
recordedCalls = nil
e := &external{kube: tc.kube, client: tc.group}
err := e.Delete(context.Background(), tc.args.cr)

Expand All @@ -902,6 +945,9 @@ func TestDelete(t *testing.T) {
if diff := cmp.Diff(tc.want.cr, tc.args.cr, test.EquateConditions()); diff != "" {
t.Errorf("r: -want, +got:\n%s", diff)
}
if diff := cmp.Diff(tc.want.calls, recordedCalls, test.EquateConditions()); diff != "" {
t.Errorf("r: -want, +got:\n%s", diff)
}
})
}
}

0 comments on commit aa6ed30

Please sign in to comment.