Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ To prevent the operator from deleting resources from Keycloak, add the `edp.epam
kind: Keycloak
```

#### Resources deletion

To avoid resources getting stuck during deletion, it is important to delete them in the correct order:

1. **First**, remove realm resources `KeycloakClient`, `KeycloakRealmUser`, etc.
2. **Then**, remove `KeycloakRealm`/`ClusterKeycloakRealm`.
3. **Finally**, remove `Keycloak`/`ClusterKeycloak`.

## Local Development

To develop the operator, first set up a local environment, and refer to the [Local Development](https://epam.github.io/edp-install/developer-guide/local-development/) page.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ type Helper interface {
SetFailureCount(fc helper.FailureCountable) time.Duration
TryToDelete(ctx context.Context, obj client.Object, terminator helper.Terminator, finalizer string) (isDeleted bool, resultErr error)
CreateKeycloakClientFromClusterRealm(ctx context.Context, realm *keycloakAlpha.ClusterKeycloakRealm) (keycloak.Client, error)
SetKeycloakOwnerRef(ctx context.Context, object helper.ObjectWithKeycloakRef) error
InvalidateKeycloakClientTokenSecret(ctx context.Context, namespace, rootKeycloakName string) error
}

Expand Down Expand Up @@ -61,10 +60,6 @@ func (r *ClusterKeycloakRealmReconciler) Reconcile(ctx context.Context, req ctrl
return ctrl.Result{}, fmt.Errorf("unable to get cluster realm: %w", err)
}

if err := r.helper.SetKeycloakOwnerRef(ctx, clusterRealm); err != nil {
return ctrl.Result{}, fmt.Errorf("unable to set keycloak owner ref: %w", err)
}

kClient, err := r.helper.CreateKeycloakClientFromClusterRealm(ctx, clusterRealm)
if err != nil {
if errors.Is(err, helper.ErrKeycloakIsNotAvailable) {
Expand Down
111 changes: 0 additions & 111 deletions controllers/helper/controller_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/go-logr/logr"
"github.com/go-resty/resty/v2"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -61,8 +60,6 @@ type adapterBuilder func(
//
//go:generate mockery --name ControllerHelper --filename helper_mock.go
type ControllerHelper interface {
SetKeycloakOwnerRef(ctx context.Context, object ObjectWithKeycloakRef) error
SetRealmOwnerRef(ctx context.Context, object ObjectWithRealmRef) error
SetFailureCount(fc FailureCountable) time.Duration
TryToDelete(ctx context.Context, obj client.Object, terminator Terminator, finalizer string) (isDeleted bool, resultErr error)
GetKeycloakRealmFromRef(ctx context.Context, object ObjectWithRealmRef, kcClient keycloak.Client) (*gocloak.RealmRepresentation, error)
Expand Down Expand Up @@ -115,114 +112,6 @@ func MakeHelper(client client.Client, scheme *runtime.Scheme, operatorNamespace
}
}

// SetKeycloakOwnerRef sets owner reference for object.
//
//nolint:dupl,cyclop
func (h *Helper) SetKeycloakOwnerRef(ctx context.Context, object ObjectWithKeycloakRef) error {
if metav1.GetControllerOf(object) != nil {
return nil
}

kind := object.GetKeycloakRef().Kind
name := object.GetKeycloakRef().Name

switch kind {
case keycloakApi.KeycloakKind:
kc := &keycloakApi.Keycloak{}
if err := h.client.Get(ctx, types.NamespacedName{
Namespace: object.GetNamespace(),
Name: name,
}, kc); err != nil {
return fmt.Errorf("failed to get Keycloak: %w", err)
}

if err := controllerutil.SetControllerReference(kc, object, h.scheme); err != nil {
return fmt.Errorf("failed to set controller reference for %s: %w", object.GetName(), err)
}

if err := h.client.Update(ctx, object); err != nil {
return fmt.Errorf("failed to update keycloak owner reference %s: %w", kc.GetName(), err)
}

return nil

case keycloakAlpha.ClusterKeycloakKind:
clusterKc := &keycloakAlpha.ClusterKeycloak{}
if err := h.client.Get(ctx, types.NamespacedName{
Name: name,
}, clusterKc); err != nil {
return fmt.Errorf("failed to get ClusterKeycloak: %w", err)
}

if err := controllerutil.SetControllerReference(clusterKc, object, h.scheme); err != nil {
return fmt.Errorf("failed to set controller reference for %s: %w", object.GetName(), err)
}

if err := h.client.Update(ctx, object); err != nil {
return fmt.Errorf("failed to update keycloak owner reference %s: %w", clusterKc.GetName(), err)
}

return nil

default:
return fmt.Errorf("unknown keycloak kind: %s", kind)
}
}

// SetRealmOwnerRef sets owner reference for object.
//
//nolint:dupl,cyclop
func (h *Helper) SetRealmOwnerRef(ctx context.Context, object ObjectWithRealmRef) error {
if metav1.GetControllerOf(object) != nil {
return nil
}

kind := object.GetRealmRef().Kind
name := object.GetRealmRef().Name

switch kind {
case keycloakApi.KeycloakRealmKind:
realm := &keycloakApi.KeycloakRealm{}
if err := h.client.Get(ctx, types.NamespacedName{
Namespace: object.GetNamespace(),
Name: name,
}, realm); err != nil {
return fmt.Errorf("failed to get KeycloakRealm: %w", err)
}

if err := controllerutil.SetControllerReference(realm, object, h.scheme); err != nil {
return fmt.Errorf("failed to set controller reference for %s: %w", object.GetName(), err)
}

if err := h.client.Update(ctx, object); err != nil {
return fmt.Errorf("failed to update realm owner reference %s: %w", realm.GetName(), err)
}

return nil

case keycloakAlpha.ClusterKeycloakRealmKind:
clusterRealm := &keycloakAlpha.ClusterKeycloakRealm{}
if err := h.client.Get(ctx, types.NamespacedName{
Name: name,
}, clusterRealm); err != nil {
return fmt.Errorf("failed to get ClusterKeycloakRealm: %w", err)
}

if err := controllerutil.SetControllerReference(clusterRealm, object, h.scheme); err != nil {
return fmt.Errorf("unable to set controller reference for %s: %w", object.GetName(), err)
}

if err := h.client.Update(ctx, object); err != nil {
return fmt.Errorf("failed to update realm owner reference %s: %w", clusterRealm.GetName(), err)
}

return nil

default:
return fmt.Errorf("unknown realm kind: %s", kind)
}
}

func (h *Helper) TryToDelete(ctx context.Context, obj client.Object, terminator Terminator, finalizer string) (isDeleted bool, resultErr error) {
logger := ctrl.LoggerFrom(ctx)

Expand Down
122 changes: 0 additions & 122 deletions controllers/helper/controller_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,139 +8,17 @@ import (
"github.com/go-logr/logr"
"github.com/go-resty/resty/v2"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
testifymock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/controller-runtime/pkg/client/fake"

"github.com/epam/edp-keycloak-operator/api/common"
keycloakApi "github.com/epam/edp-keycloak-operator/api/v1"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak/adapter"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mock"
"github.com/epam/edp-keycloak-operator/pkg/fakehttp"
)

func TestHelper_GetOrCreateRealmOwnerRef(t *testing.T) {
mc := K8SClientMock{}

sch := runtime.NewScheme()
utilruntime.Must(keycloakApi.AddToScheme(sch))

helper := MakeHelper(&mc, sch, "default")

kcGroup := keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test",
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: "realm",
},
},
}

mc.On("Get", types.NamespacedName{
Namespace: "test",
Name: "realm",
}, &keycloakApi.KeycloakRealm{}).Return(nil)
mc.On("Update", testifymock.Anything, testifymock.Anything).Return(nil)

err := helper.SetRealmOwnerRef(context.Background(), &kcGroup)
require.NoError(t, err)

kcGroup = keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test",
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
Realm: "foo13",
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: "realm",
},
},
}

mc.On("Get", types.NamespacedName{
Namespace: "test",
Name: "foo13",
}, &keycloakApi.KeycloakRealm{}).Return(nil)

err = helper.SetRealmOwnerRef(context.Background(), &kcGroup)
require.NoError(t, err)
}

func TestHelper_GetOrCreateRealmOwnerRef_Failure(t *testing.T) {
mc := K8SClientMock{}

sch := runtime.NewScheme()
utilruntime.Must(keycloakApi.AddToScheme(sch))

helper := MakeHelper(&mc, sch, "default")

kcGroup := keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test",
OwnerReferences: []metav1.OwnerReference{
{
Name: "foo",
Kind: "KeycloakRealm",
},
},
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: "realm",
},
},
}

mockErr := errors.New("mock error")

mc.On("Get", types.NamespacedName{
Namespace: "test",
Name: kcGroup.Spec.RealmRef.Name,
}, &keycloakApi.KeycloakRealm{}).Return(mockErr)

err := helper.SetRealmOwnerRef(context.Background(), &kcGroup)
if err == nil {
t.Fatal("no error on k8s client get fatal")
}

assert.ErrorIs(t, err, mockErr)

kcGroup = keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test",
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: "realm",
},
},
}

mc.On("Get", types.NamespacedName{
Namespace: "test",
Name: kcGroup.Spec.RealmRef.Name,
}, &keycloakApi.KeycloakRealm{}).Return(mockErr)

err = helper.SetRealmOwnerRef(context.Background(), &kcGroup)
if err == nil {
t.Fatal("no error on k8s client get fatal")
}

assert.ErrorIs(t, err, mockErr)
}

func TestMakeHelper(t *testing.T) {
rCl := resty.New()

Expand Down
5 changes: 0 additions & 5 deletions controllers/keycloakauthflow/keycloakauthflow_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ type Helper interface {
SetFailureCount(fc helper.FailureCountable) time.Duration
TryToDelete(ctx context.Context, obj client.Object, terminator helper.Terminator, finalizer string) (isDeleted bool, resultErr error)
CreateKeycloakClientFromRealmRef(ctx context.Context, object helper.ObjectWithRealmRef) (keycloak.Client, error)
SetRealmOwnerRef(ctx context.Context, object helper.ObjectWithRealmRef) error
GetKeycloakRealmFromRef(ctx context.Context, object helper.ObjectWithRealmRef, kcClient keycloak.Client) (*gocloak.RealmRepresentation, error)
}

Expand Down Expand Up @@ -127,10 +126,6 @@ func (r *Reconcile) Reconcile(ctx context.Context, request reconcile.Request) (r
}

func (r *Reconcile) tryReconcile(ctx context.Context, instance *keycloakApi.KeycloakAuthFlow) error {
if err := r.helper.SetRealmOwnerRef(ctx, instance); err != nil {
return fmt.Errorf("unable to set realm owner ref: %w", err)
}

kClient, err := r.helper.CreateKeycloakClientFromRealmRef(ctx, instance)
if err != nil {
return fmt.Errorf("unable to create keycloak client from realm ref: %w", err)
Expand Down
6 changes: 0 additions & 6 deletions controllers/keycloakclient/keycloakclient_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
type Helper interface {
SetFailureCount(fc helper.FailureCountable) time.Duration
TryToDelete(ctx context.Context, obj client.Object, terminator helper.Terminator, finalizer string) (isDeleted bool, resultErr error)
SetRealmOwnerRef(ctx context.Context, object helper.ObjectWithRealmRef) error
CreateKeycloakClientFromRealmRef(ctx context.Context, object helper.ObjectWithRealmRef) (keycloak.Client, error)
GetKeycloakRealmFromRef(ctx context.Context, object helper.ObjectWithRealmRef, kcClient keycloak.Client) (*gocloak.RealmRepresentation, error)
}
Expand Down Expand Up @@ -118,11 +117,6 @@ func (r *ReconcileKeycloakClient) Reconcile(ctx context.Context, request reconci
}

func (r *ReconcileKeycloakClient) tryReconcile(ctx context.Context, keycloakClient *keycloakApi.KeycloakClient) error {
err := r.helper.SetRealmOwnerRef(ctx, keycloakClient)
if err != nil {
return fmt.Errorf("unable to set realm owner ref: %w", err)
}

kClient, err := r.helper.CreateKeycloakClientFromRealmRef(ctx, keycloakClient)
if err != nil {
return fmt.Errorf("unable to create keycloak client from realm ref: %w", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const finalizerName = "keycloak.clientscope.operator.finalizer.name"
type Helper interface {
SetFailureCount(fc helper.FailureCountable) time.Duration
TryToDelete(ctx context.Context, obj client.Object, terminator helper.Terminator, finalizer string) (isDeleted bool, resultErr error)
SetRealmOwnerRef(ctx context.Context, object helper.ObjectWithRealmRef) error
GetKeycloakRealmFromRef(ctx context.Context, object helper.ObjectWithRealmRef, kcClient keycloak.Client) (*gocloak.RealmRepresentation, error)
CreateKeycloakClientFromRealmRef(ctx context.Context, object helper.ObjectWithRealmRef) (keycloak.Client, error)
}
Expand Down Expand Up @@ -131,11 +130,6 @@ func (r *Reconcile) Reconcile(ctx context.Context, request reconcile.Request) (r
}

func (r *Reconcile) tryReconcile(ctx context.Context, instance *keycloakApi.KeycloakClientScope) (string, error) {
err := r.helper.SetRealmOwnerRef(ctx, instance)
if err != nil {
return "", fmt.Errorf("unable to set realm owner ref: %w", err)
}

cl, err := r.helper.CreateKeycloakClientFromRealmRef(ctx, instance)
if err != nil {
return "", fmt.Errorf("unable to create keycloak client from realm ref: %w", err)
Expand Down
5 changes: 0 additions & 5 deletions controllers/keycloakrealm/keycloakrealm_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ type Helper interface {
SetFailureCount(fc helper.FailureCountable) time.Duration
TryToDelete(ctx context.Context, obj client.Object, terminator helper.Terminator, finalizer string) (isDeleted bool, resultErr error)
CreateKeycloakClientFromRealm(ctx context.Context, realm *keycloakApi.KeycloakRealm) (keycloak.Client, error)
SetKeycloakOwnerRef(ctx context.Context, object helper.ObjectWithKeycloakRef) error
InvalidateKeycloakClientTokenSecret(ctx context.Context, namespace, rootKeycloakName string) error
}

Expand Down Expand Up @@ -123,10 +122,6 @@ func (r *ReconcileKeycloakRealm) Reconcile(ctx context.Context, request reconcil
}

func (r *ReconcileKeycloakRealm) tryReconcile(ctx context.Context, realm *keycloakApi.KeycloakRealm) error {
if err := r.helper.SetKeycloakOwnerRef(ctx, realm); err != nil {
return fmt.Errorf("failed to set keycloak owner reference: %w", err)
}

kClient, err := r.helper.CreateKeycloakClientFromRealm(ctx, realm)
if err != nil {
return fmt.Errorf("failed to create keycloak client for realm: %w", err)
Expand Down
Loading
Loading