controllers/keycloakrealmgroup/keycloakrealmgroup_controller.go (138 lines of code) (raw):

package keycloakrealmgroup import ( "context" "fmt" "time" "github.com/Nerzal/gocloak/v12" "github.com/pkg/errors" k8sErrors "k8s.io/apimachinery/pkg/api/errors" 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/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/epam/edp-keycloak-operator/api/common" keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" "github.com/epam/edp-keycloak-operator/controllers/helper" "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" "github.com/epam/edp-keycloak-operator/pkg/objectmeta" ) const keyCloakRealmGroupOperatorFinalizerName = "keycloak.realmgroup.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) } func NewReconcileKeycloakRealmGroup(client client.Client, helper Helper) *ReconcileKeycloakRealmGroup { return &ReconcileKeycloakRealmGroup{ client: client, helper: helper, } } type ReconcileKeycloakRealmGroup struct { client client.Client helper Helper successReconcileTimeout time.Duration } func (r *ReconcileKeycloakRealmGroup) SetupWithManager(mgr ctrl.Manager, successReconcileTimeout time.Duration) error { r.successReconcileTimeout = successReconcileTimeout pred := predicate.Funcs{ UpdateFunc: helper.IsFailuresUpdated, } err := ctrl.NewControllerManagedBy(mgr). For(&keycloakApi.KeycloakRealmGroup{}, builder.WithPredicates(pred)). Complete(r) if err != nil { return fmt.Errorf("failed to setup KeycloakRealmGroup controller: %w", err) } return nil } //+kubebuilder:rbac:groups=v1.edp.epam.com,namespace=placeholder,resources=keycloakrealmgroups,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=v1.edp.epam.com,namespace=placeholder,resources=keycloakrealmgroups/status,verbs=get;update;patch //+kubebuilder:rbac:groups=v1.edp.epam.com,namespace=placeholder,resources=keycloakrealmgroups/finalizers,verbs=update // Reconcile is a loop for reconciling KeycloakRealmGroup object. func (r *ReconcileKeycloakRealmGroup) Reconcile(ctx context.Context, request reconcile.Request) (result reconcile.Result, resultErr error) { log := ctrl.LoggerFrom(ctx) log.Info("Reconciling KeycloakRealmGroup") var instance keycloakApi.KeycloakRealmGroup if err := r.client.Get(ctx, request.NamespacedName, &instance); err != nil { if k8sErrors.IsNotFound(err) { return } resultErr = errors.Wrap(err, "unable to get keycloak realm group from k8s") return } if updated, err := r.applyDefaults(ctx, &instance); err != nil { resultErr = fmt.Errorf("unable to apply default values: %w", err) return } else if updated { return } if err := r.tryReconcile(ctx, &instance); err != nil { if errors.Is(err, helper.ErrKeycloakIsNotAvailable) { return ctrl.Result{ RequeueAfter: helper.RequeueOnKeycloakNotAvailablePeriod, }, nil } instance.Status.Value = err.Error() result.RequeueAfter = r.helper.SetFailureCount(&instance) log.Error(err, "an error has occurred while handling keycloak realm group", "name", request.Name) } else { helper.SetSuccessStatus(&instance) result.RequeueAfter = r.successReconcileTimeout } if err := r.client.Status().Update(ctx, &instance); err != nil { resultErr = errors.Wrap(err, "unable to update status") } log.Info("Reconciling done") return } func (r *ReconcileKeycloakRealmGroup) tryReconcile(ctx context.Context, keycloakRealmGroup *keycloakApi.KeycloakRealmGroup) error { err := r.helper.SetRealmOwnerRef(ctx, keycloakRealmGroup) if err != nil { return fmt.Errorf("unable to set realm owner ref: %w", err) } kClient, err := r.helper.CreateKeycloakClientFromRealmRef(ctx, keycloakRealmGroup) if err != nil { return fmt.Errorf("unable to create keycloak client from realm ref: %w", err) } realm, err := r.helper.GetKeycloakRealmFromRef(ctx, keycloakRealmGroup, kClient) if err != nil { return fmt.Errorf("unable to get keycloak realm from ref: %w", err) } deleted, err := r.helper.TryToDelete( ctx, keycloakRealmGroup, makeTerminator( kClient, gocloak.PString(realm.Realm), keycloakRealmGroup.Spec.Name, objectmeta.PreserveResourcesOnDeletion(keycloakRealmGroup), ), keyCloakRealmGroupOperatorFinalizerName, ) if err != nil { return fmt.Errorf("failed to delete keycloak realm group: %w", err) } if deleted { return nil } id, err := kClient.SyncRealmGroup(ctx, gocloak.PString(realm.Realm), &keycloakRealmGroup.Spec) if err != nil { return fmt.Errorf("unable to sync realm group: %w", err) } keycloakRealmGroup.Status.ID = id return nil } func (r *ReconcileKeycloakRealmGroup) applyDefaults(ctx context.Context, instance *keycloakApi.KeycloakRealmGroup) (bool, error) { if instance.Spec.RealmRef.Name == "" { instance.Spec.RealmRef = common.RealmRef{ Kind: keycloakApi.KeycloakRealmKind, Name: instance.Spec.Realm, } if err := r.client.Update(ctx, instance); err != nil { return false, fmt.Errorf("failed to update default values: %w", err) } return true, nil } return false, nil }