diff --git a/pkg/agent/controller/managedclusterrole_controller.go b/pkg/agent/controller/managedclusterrole_controller.go index 4f1ec8af..0fbf0082 100644 --- a/pkg/agent/controller/managedclusterrole_controller.go +++ b/pkg/agent/controller/managedclusterrole_controller.go @@ -53,7 +53,7 @@ func (r *ManagedClusterRoleReconciler) Reconcile(ctx context.Context, req ctrl.R managedClusterRole := &authorizationv1alpha1.ManagedClusterRole{} if err := r.HubClient.Get(ctx, req.NamespacedName, managedClusterRole); err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } // create clusterRole in spoke cluster diff --git a/pkg/agent/controller/managedclusterrolebinding_controller.go b/pkg/agent/controller/managedclusterrolebinding_controller.go index 76e86ef4..2568bdef 100644 --- a/pkg/agent/controller/managedclusterrolebinding_controller.go +++ b/pkg/agent/controller/managedclusterrolebinding_controller.go @@ -22,7 +22,6 @@ import ( authzv1alpah1 "github.com/kluster-manager/cluster-auth/apis/authorization/v1alpha1" "github.com/kluster-manager/cluster-auth/pkg/common" - "github.com/kluster-manager/cluster-auth/pkg/utils" rbac "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -62,9 +61,9 @@ func (r *ManagedClusterRoleBindingReconciler) Reconcile(ctx context.Context, req var managedCRB authzv1alpah1.ManagedClusterRoleBinding if err := r.HubClient.Get(ctx, req.NamespacedName, &managedCRB); err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } - _, hubOwnerID := utils.GetUserIDAndHubOwnerIDFromLabelValues(&managedCRB) + userName := managedCRB.Subjects[0].Name // Check if the managedCRB is marked for deletion @@ -91,7 +90,7 @@ func (r *ManagedClusterRoleBindingReconciler) Reconcile(ctx context.Context, req // impersonate clusterRole cr := rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("impersonate-%s-%s-%s", userName, hubOwnerID, rand.String(7)), + Name: fmt.Sprintf("ace.%s.impersonate.%s", userName, rand.String(10)), Labels: managedCRB.Labels, }, Rules: []rbac.PolicyRule{ diff --git a/pkg/common/constants.go b/pkg/common/constants.go index 80b26206..e4cbd9da 100644 --- a/pkg/common/constants.go +++ b/pkg/common/constants.go @@ -28,4 +28,6 @@ const ( UserAuthLabel = "authentication.k8s.appscode.com/user" HubOwnerLabel = "authentication.k8s.appscode.com/hub-owner" HubClusterIdLabel = "cluster.k8s.appscode.com/cluster-id" + + ServiceAccountPrefix = "system:serviceaccount:" ) diff --git a/pkg/manager/agent-manifests/cluster-auth/templates/deployment.yaml b/pkg/manager/agent-manifests/cluster-auth/templates/deployment.yaml index 49013ee9..2b1c8e39 100644 --- a/pkg/manager/agent-manifests/cluster-auth/templates/deployment.yaml +++ b/pkg/manager/agent-manifests/cluster-auth/templates/deployment.yaml @@ -32,10 +32,8 @@ spec: - name: agent securityContext: {{- toYaml .Values.image.securityContext | nindent 10 }} -{{/* image: {{ include "image.registry" . }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}*/}} -{{/* imagePullPolicy: {{ .Values.imagePullPolicy }}*/}} - image: rokibulhasan114/cluster-auth:version-update_linux_amd64 - imagePullPolicy: Always + image: {{ include "image.registry" . }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }} + imagePullPolicy: {{ .Values.imagePullPolicy }} args: - agent - --v={{ .Values.logLevel }} diff --git a/pkg/manager/controller/authentication/account_controller.go b/pkg/manager/controller/authentication/account_controller.go index fce4a202..574133b7 100644 --- a/pkg/manager/controller/authentication/account_controller.go +++ b/pkg/manager/controller/authentication/account_controller.go @@ -1,21 +1,39 @@ +/* +Copyright AppsCode Inc. and Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package authentication import ( "context" "fmt" + "strings" authenticationv1alpha1 "github.com/kluster-manager/cluster-auth/apis/authentication/v1alpha1" "github.com/kluster-manager/cluster-auth/pkg/common" core "k8s.io/api/core/v1" rbac "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + kmapi "kmodules.xyz/client-go/api/v1" cu "kmodules.xyz/client-go/client" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -32,19 +50,34 @@ func (r *AccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct acc := &authenticationv1alpha1.Account{} err := r.Client.Get(ctx, req.NamespacedName, acc) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } + // Update status to InProgress only if not already set + if acc.Status.Phase == "" { + acc.Status.Phase = authenticationv1alpha1.AccountPhaseInProgress + setAccountType(acc) + if err := r.Client.Status().Update(ctx, acc); err != nil { + return reconcile.Result{}, err + } + } if err = r.createServiceAccount(ctx, acc); err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, r.setStatusFailed(ctx, acc, err) } if err = r.createGatewayClusterRoleBindingForUser(ctx, acc); err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, r.setStatusFailed(ctx, acc, err) } if err = r.createClusterRoleAndClusterRoleBindingToImpersonate(ctx, acc); err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, r.setStatusFailed(ctx, acc, err) + } + + // Set the status to success after successful reconciliation + if acc.Status.Phase != authenticationv1alpha1.AccountPhaseCurrent { + if err := r.setStatusSuccess(ctx, acc, "Reconciliation completed successfully."); err != nil { + return reconcile.Result{}, err + } } return reconcile.Result{}, nil @@ -104,7 +137,7 @@ func (r *AccountReconciler) createGatewayClusterRoleBindingForUser(ctx context.C }, } - if acc.Spec.Type == authenticationv1alpha1.AccountTypeServiceAccount { + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { sub = []rbac.Subject{ { APIGroup: "", @@ -130,7 +163,7 @@ func (r *AccountReconciler) createGatewayClusterRoleBindingForUser(ctx context.C }, } - if acc.Spec.Type == authenticationv1alpha1.AccountTypeServiceAccount { + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { crb.Name = fmt.Sprintf("ace.%s.proxy", acc.Spec.Username) } @@ -165,7 +198,7 @@ func (r *AccountReconciler) createClusterRoleAndClusterRoleBindingToImpersonate( }, } - if acc.Spec.Type == authenticationv1alpha1.AccountTypeServiceAccount { + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { cr = rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("ace.%s.impersonate", acc.Spec.Username), @@ -232,9 +265,75 @@ func (r *AccountReconciler) createClusterRoleAndClusterRoleBindingToImpersonate( return nil } +// updateConditions adds or updates a condition in the conditions array. +func (r *AccountReconciler) updateConditions(conditions []kmapi.Condition, conditionType kmapi.ConditionType, message string) []kmapi.Condition { + now := metav1.Now() + for i, condition := range conditions { + if condition.Type == conditionType { + conditions[i].Status = metav1.ConditionStatus(core.ConditionTrue) + conditions[i].LastTransitionTime = now + conditions[i].Message = message + return conditions + } + } + + return append(conditions, kmapi.Condition{ + Type: conditionType, + Status: metav1.ConditionStatus(core.ConditionTrue), + LastTransitionTime: now, + Reason: string(conditionType), + Message: message, + }) +} + +func (r *AccountReconciler) setStatusFailed(ctx context.Context, acc *authenticationv1alpha1.Account, err error) error { + // Re-fetch the latest version of the Account object + if err := r.Client.Get(ctx, client.ObjectKeyFromObject(acc), acc); err != nil && !errors.IsNotFound(err) { + return fmt.Errorf("failed to get latest account object: %w", err) + } + + acc.Status.Phase = authenticationv1alpha1.AccountPhaseFailed + setAccountType(acc) + + acc.Status.Conditions = r.updateConditions(acc.Status.Conditions, "ReconciliationFailed", err.Error()) + if updateErr := r.Client.Status().Update(ctx, acc); updateErr != nil { + return fmt.Errorf("failed to update status to Failed: %w", updateErr) + } + return err +} + +func (r *AccountReconciler) setStatusSuccess(ctx context.Context, acc *authenticationv1alpha1.Account, message string) error { + // Re-fetch the latest version of the Account object + if err := r.Client.Get(ctx, client.ObjectKeyFromObject(acc), acc); err != nil && !errors.IsNotFound(err) { + return fmt.Errorf("failed to get latest account object: %w", err) + } + + acc.Status.Phase = authenticationv1alpha1.AccountPhaseCurrent + acc.Status.Conditions = r.updateConditions(acc.Status.Conditions, "ReconciliationSuccessful", message) + setAccountType(acc) + acc.Status.ServiceAccountRef = &core.LocalObjectReference{ + Name: acc.Name, + } + + // Update or add a successful condition + acc.Status.Conditions = r.updateConditions(acc.Status.Conditions, "ReconciliationSuccessful", message) + if updateErr := r.Client.Status().Update(ctx, acc); updateErr != nil { + return fmt.Errorf("failed to update status to Current: %w", updateErr) + } + return nil +} + +func setAccountType(acc *authenticationv1alpha1.Account) { + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { + acc.Status.Type = authenticationv1alpha1.AccountTypeServiceAccount + } else { + acc.Status.Type = authenticationv1alpha1.AccountTypeUser + } +} + // SetupWithManager sets up the controller with the Manager. func (r *AccountReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&authenticationv1alpha1.Account{}).Watches(&authenticationv1alpha1.Account{}, &handler.EnqueueRequestForObject{}). + For(&authenticationv1alpha1.Account{}). Complete(r) } diff --git a/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go b/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go index b1c6af2e..4e88e9a6 100644 --- a/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go +++ b/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go @@ -59,7 +59,7 @@ func (r *ManagedClusterSetRoleBindingReconciler) Reconcile(ctx context.Context, managedCSRB := &authorizationv1alpha1.ManagedClusterSetRoleBinding{} err := r.Client.Get(ctx, req.NamespacedName, managedCSRB) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } // Check if the managedCRB is marked for deletion