controllers/clusterkeycloakrealm/clusterkeycloakrealm_controller.go (108 lines of code) (raw):

package clusterkeycloakrealm import ( "context" "errors" "fmt" "time" k8sErrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" keycloakAlpha "github.com/epam/edp-keycloak-operator/api/v1alpha1" "github.com/epam/edp-keycloak-operator/controllers/clusterkeycloakrealm/chain" "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" ) 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 } // ClusterKeycloakRealmReconciler reconciles a ClusterKeycloakRealm object. type ClusterKeycloakRealmReconciler struct { client client.Client scheme *runtime.Scheme helper Helper operatorNamespace string } func NewClusterKeycloakRealmReconciler(client client.Client, scheme *runtime.Scheme, helper Helper, operatorNamespace string) *ClusterKeycloakRealmReconciler { return &ClusterKeycloakRealmReconciler{client: client, scheme: scheme, helper: helper, operatorNamespace: operatorNamespace} } const ( keyCloakRealmOperatorFinalizerName = "keycloak.realm.operator.finalizer.name" successConnectionRetryPeriod = time.Minute * 30 ) //+kubebuilder:rbac:groups=v1.edp.epam.com,resources=clusterkeycloakrealms,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=v1.edp.epam.com,resources=clusterkeycloakrealms/status,verbs=get;update;patch //+kubebuilder:rbac:groups=v1.edp.epam.com,resources=clusterkeycloakrealms/finalizers,verbs=update // Reconcile is loop for reconciling ClusterKeycloakRealm object. func (r *ClusterKeycloakRealmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := ctrl.LoggerFrom(ctx) log.Info("Reconciling ClusterKeycloakRealm") clusterRealm := &keycloakAlpha.ClusterKeycloakRealm{} if err := r.client.Get(ctx, req.NamespacedName, clusterRealm); err != nil { if k8sErrors.IsNotFound(err) { return ctrl.Result{}, nil } 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) { return ctrl.Result{ RequeueAfter: helper.RequeueOnKeycloakNotAvailablePeriod, }, nil } return ctrl.Result{}, fmt.Errorf("failed to create keycloak client for realm: %w", err) } if deleted, err := r.helper.TryToDelete( ctx, clusterRealm, makeTerminator(clusterRealm.Spec.RealmName, kClient, objectmeta.PreserveResourcesOnDeletion(clusterRealm)), keyCloakRealmOperatorFinalizerName, ); err != nil { return ctrl.Result{}, fmt.Errorf("failed to delete realm %w", err) } else if deleted { return reconcile.Result{}, nil } if err := chain.MakeChain(r.client, r.operatorNamespace).ServeRequest(ctx, clusterRealm, kClient); err != nil { clusterRealm.Status.Available = false clusterRealm.Status.Value = err.Error() requeue := r.helper.SetFailureCount(clusterRealm) if updateErr := r.client.Status().Update(ctx, clusterRealm); updateErr != nil { return ctrl.Result{}, fmt.Errorf("unable to update cluster realm status: %w", updateErr) } return ctrl.Result{ RequeueAfter: requeue, }, fmt.Errorf("error during ClusterRealm chain: %w", err) } if err := r.updateSuccessStatus(ctx, clusterRealm); err != nil { return ctrl.Result{}, err } return ctrl.Result{ RequeueAfter: successConnectionRetryPeriod, }, nil } func (r *ClusterKeycloakRealmReconciler) updateSuccessStatus(ctx context.Context, clusterRealm *keycloakAlpha.ClusterKeycloakRealm) error { if clusterRealm.Status.Available { return nil } clusterRealm.Status.Available = true clusterRealm.Status.Value = helper.StatusOK clusterRealm.Status.FailureCount = 0 if err := r.client.Status().Update(ctx, clusterRealm); err != nil { return fmt.Errorf("unable to update cluster realm status: %w", err) } return nil } // SetupWithManager sets up the controller with the Manager. func (r *ClusterKeycloakRealmReconciler) SetupWithManager(mgr ctrl.Manager) error { err := ctrl.NewControllerManagedBy(mgr). For(&keycloakAlpha.ClusterKeycloakRealm{}). Complete(r) if err != nil { return fmt.Errorf("unable to create ClusterKeycloakRealm controller: %w", err) } return nil }