diff --git a/internal/controller/higresscontroller/deployment.go b/internal/controller/higresscontroller/deployment.go index 17db5b6..b928953 100644 --- a/internal/controller/higresscontroller/deployment.go +++ b/internal/controller/higresscontroller/deployment.go @@ -34,33 +34,36 @@ func initDeployment(deploy *appsv1.Deployment, instance *operatorv1alpha1.Higres } func updateDeploymentSpec(deploy *appsv1.Deployment, instance *operatorv1alpha1.HigressController) { - deploy.Spec.Selector = &metav1.LabelSelector{ - MatchLabels: instance.Spec.SelectorLabels, - } + deploy.Spec.Selector = &metav1.LabelSelector{MatchLabels: instance.Spec.SelectorLabels} + deploy.Spec.Replicas = instance.Spec.Replicas - deploy.Spec.Template = apiv1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Name: instance.Name, - Namespace: instance.Namespace, - Labels: instance.Spec.SelectorLabels, - }, - Spec: apiv1.PodSpec{ - ServiceAccountName: getServiceAccount(instance), - Containers: []apiv1.Container{ - { - Name: genControllerName(instance), - Image: genImage(instance.Spec.Controller.Image.Repository, instance.Spec.Controller.Image.Tag), - ImagePullPolicy: instance.Spec.Controller.Image.ImagePullPolicy, - Args: genControllerArgs(instance), - Ports: genControllerPorts(instance), - SecurityContext: genControllerSecurityContext(instance), - Env: genControllerEnv(instance), - VolumeMounts: genControllerVolumeMounts(instance), - }, - }, - Volumes: genVolumes(instance), - }, + + controller.UpdateObjectMeta(&deploy.Spec.Template.ObjectMeta, instance, instance.Spec.SelectorLabels) + + deploy.Spec.Template.Spec.ServiceAccountName = getServiceAccount(instance) + + exist := false + for _, c := range deploy.Spec.Template.Spec.Containers { + if c.Name == genControllerName(instance) { + exist = true + break + } + } + if !exist { + deploy.Spec.Template.Spec.Containers = append(deploy.Spec.Template.Spec.Containers, apiv1.Container{ + Name: genControllerName(instance), + Image: genImage(instance.Spec.Controller.Image.Repository, instance.Spec.Controller.Image.Tag), + ImagePullPolicy: instance.Spec.Controller.Image.ImagePullPolicy, + Args: genControllerArgs(instance), + Ports: genControllerPorts(instance), + SecurityContext: genControllerSecurityContext(instance), + Env: genControllerEnv(instance), + VolumeMounts: genControllerVolumeMounts(instance), + }) } + + deploy.Spec.Template.Spec.Volumes = genVolumes(instance) + if !instance.Spec.EnableHigressIstio { pilot := apiv1.Container{ Name: genPilotName(instance), @@ -73,7 +76,16 @@ func updateDeploymentSpec(deploy *appsv1.Deployment, instance *operatorv1alpha1. VolumeMounts: genPilotVolumeMounts(instance), } - deploy.Spec.Template.Spec.Containers = append(deploy.Spec.Template.Spec.Containers, pilot) + exist = false + for _, c := range deploy.Spec.Template.Spec.Containers { + if c.Name == genPilotName(instance) { + exist = true + break + } + } + if !exist { + deploy.Spec.Template.Spec.Containers = append(deploy.Spec.Template.Spec.Containers, pilot) + } } } @@ -420,6 +432,7 @@ func genControllerVolumeMounts(instance *operatorv1alpha1.HigressController) []a func genVolumes(instance *operatorv1alpha1.HigressController) []apiv1.Volume { optional := true + defaultMode := int32(420) volumes := []apiv1.Volume{ { Name: "log", @@ -441,6 +454,7 @@ func genVolumes(instance *operatorv1alpha1.HigressController) []apiv1.Volume { Secret: &apiv1.SecretVolumeSource{ SecretName: "cacerts", Optional: &optional, + DefaultMode: &defaultMode, }, }, }, @@ -450,6 +464,7 @@ func genVolumes(instance *operatorv1alpha1.HigressController) []apiv1.Volume { Secret: &apiv1.SecretVolumeSource{ SecretName: "istio-kubeconfig", Optional: &optional, + DefaultMode: &defaultMode, }, }, }, diff --git a/internal/controller/higresscontroller/rbac.go b/internal/controller/higresscontroller/rbac.go index a65f9f6..94f037f 100644 --- a/internal/controller/higresscontroller/rbac.go +++ b/internal/controller/higresscontroller/rbac.go @@ -1,6 +1,8 @@ package higresscontroller import ( + "reflect" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -9,6 +11,7 @@ import ( ) const ( + role = "higress-controller" clusterRole = "higress-controller" ) @@ -152,7 +155,6 @@ func initClusterRole(cr *rbacv1.ClusterRole, instance *operatorv1alpha1.HigressC func muteClusterRole(cr *rbacv1.ClusterRole, instance *operatorv1alpha1.HigressController) controllerutil.MutateFn { return func() error { - cr.Name = clusterRole cr.Rules = defaultRules() return nil } @@ -167,26 +169,37 @@ func initClusterRoleBinding(crb *rbacv1.ClusterRoleBinding, instance *operatorv1 ObjectMeta: metav1.ObjectMeta{ Name: getServiceAccount(instance), }, - RoleRef: rbacv1.RoleRef{ - Kind: "ClusterRole", - Name: clusterRole, - APIGroup: "rbac.authorization.k8s.io", - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: getServiceAccount(instance), - Namespace: instance.Namespace, - }, - }, } + updateClusterRoleBinding(crb, instance) return crb } +func updateClusterRoleBinding(crb *rbacv1.ClusterRoleBinding, instance *operatorv1alpha1.HigressController) { + crb.RoleRef = rbacv1.RoleRef{ + Kind: "ClusterRole", + Name: clusterRole, + APIGroup: "rbac.authorization.k8s.io", + } + + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: getServiceAccount(instance), + Namespace: instance.Namespace, + } + + for _, sub := range crb.Subjects { + if reflect.DeepEqual(sub, subject) { + return + } + } + + crb.Subjects = append(crb.Subjects, subject) +} + func muteClusterRoleBinding(crb *rbacv1.ClusterRoleBinding, instance *operatorv1alpha1.HigressController) controllerutil.MutateFn { return func() error { - crb = initClusterRoleBinding(crb, instance) + updateClusterRoleBinding(crb, instance) return nil } } @@ -197,39 +210,51 @@ func initRoleBinding(rb *rbacv1.RoleBinding, instance *operatorv1alpha1.HigressC Name: getServiceAccount(instance), Namespace: instance.Namespace, }, - RoleRef: rbacv1.RoleRef{ - Kind: "Role", - Name: getServiceAccount(instance), - APIGroup: "rbac.authorization.k8s.io", - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: getServiceAccount(instance), - Namespace: instance.Namespace, - }, - }, } + + updateRoleBinding(rb, instance) return rb } +func updateRoleBinding(rb *rbacv1.RoleBinding, instance *operatorv1alpha1.HigressController) { + rb.RoleRef = rbacv1.RoleRef{ + Kind: "Role", + Name: role, + APIGroup: "rbac.authorization.k8s.io", + } + + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: getServiceAccount(instance), + Namespace: instance.Namespace, + } + + for _, sub := range rb.Subjects { + if reflect.DeepEqual(sub, subject) { + return + } + } + + rb.Subjects = append(rb.Subjects, subject) +} + func muteRoleBinding(rb *rbacv1.RoleBinding, instance *operatorv1alpha1.HigressController) controllerutil.MutateFn { return func() error { - initRoleBinding(rb, instance) + updateRoleBinding(rb, instance) return nil } } -func initRole(role *rbacv1.Role, instance *operatorv1alpha1.HigressController) *rbacv1.Role { - *role = rbacv1.Role{ +func initRole(r *rbacv1.Role, instance *operatorv1alpha1.HigressController) *rbacv1.Role { + *r = rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{ - Name: getServiceAccount(instance), + Name: role, Namespace: instance.Namespace, }, Rules: defaultRules(), } - return role + return r } func muteRole(role *rbacv1.Role, instance *operatorv1alpha1.HigressController) controllerutil.MutateFn { diff --git a/internal/controller/higresscontroller/service.go b/internal/controller/higresscontroller/service.go index 5f0f512..532a5e9 100644 --- a/internal/controller/higresscontroller/service.go +++ b/internal/controller/higresscontroller/service.go @@ -58,13 +58,21 @@ func updateServiceSpec(svc *apiv1.Service, instance *operatorv1alpha1.HigressCon Port: 15014, }, } - svc.Spec.Ports = append(svc.Spec.Ports, ports...) + set := make(map[string]struct{}) + for _, port := range svc.Spec.Ports { + set[port.Name] = struct{}{} + } + for _, port := range ports { + if _, ok := set[port.Name]; !ok { + svc.Spec.Ports = append(svc.Spec.Ports, port) + } + } } } func muteService(svc *apiv1.Service, instance *operatorv1alpha1.HigressController) controllerutil.MutateFn { return func() error { - initService(svc, instance) + updateServiceSpec(svc, instance) return nil } } diff --git a/internal/controller/higressgateway/rbac.go b/internal/controller/higressgateway/rbac.go index de3de6d..f36555c 100644 --- a/internal/controller/higressgateway/rbac.go +++ b/internal/controller/higressgateway/rbac.go @@ -1,6 +1,8 @@ package higressgateway import ( + "reflect" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -47,26 +49,37 @@ func initClusterRoleBinding(crb *rbacv1.ClusterRoleBinding, instance *operatorv1 ObjectMeta: metav1.ObjectMeta{ Name: getServiceAccount(instance), }, - RoleRef: rbacv1.RoleRef{ - Kind: "ClusterRole", - Name: clusterRole, - APIGroup: "rbac.authorization.k8s.io", - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: getServiceAccount(instance), - Namespace: instance.Namespace, - }, - }, } + updateClusterRoleBinding(crb, instance) return crb } +func updateClusterRoleBinding(crb *rbacv1.ClusterRoleBinding, instance *operatorv1alpha1.HigressGateway) { + crb.RoleRef = rbacv1.RoleRef{ + Kind: "ClusterRole", + Name: clusterRole, + APIGroup: "rbac.authorization.k8s.io", + } + + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: getServiceAccount(instance), + Namespace: instance.Namespace, + } + + for _, sub := range crb.Subjects { + if reflect.DeepEqual(sub, subject) { + return + } + } + + crb.Subjects = append(crb.Subjects, subject) +} + func muteClusterRoleBinding(crb *rbacv1.ClusterRoleBinding, instance *operatorv1alpha1.HigressGateway) controllerutil.MutateFn { return func() error { - initClusterRoleBinding(crb, instance) + updateClusterRoleBinding(crb, instance) return nil } } @@ -93,9 +106,31 @@ func initRoleBinding(rb *rbacv1.RoleBinding, instance *operatorv1alpha1.HigressG return rb } +func updateRoleBinding(rb *rbacv1.RoleBinding, instance *operatorv1alpha1.HigressGateway) { + rb.RoleRef = rbacv1.RoleRef{ + Kind: "Role", + Name: role, + APIGroup: "rbac.authorization.k8s.io", + } + + subject := rbacv1.Subject{ + Kind: "ServiceAccount", + Name: getServiceAccount(instance), + Namespace: instance.Namespace, + } + + for _, sub := range rb.Subjects { + if reflect.DeepEqual(sub, subject) { + return + } + } + + rb.Subjects = append(rb.Subjects, subject) +} + func muteRoleBinding(rb *rbacv1.RoleBinding, instance *operatorv1alpha1.HigressGateway) controllerutil.MutateFn { return func() error { - initRoleBinding(rb, instance) + updateRoleBinding(rb, instance) return nil } } diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 397d2dd..bdddba9 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -5,7 +5,10 @@ import ( "fmt" "github.com/go-logr/logr" + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -32,3 +35,51 @@ func CreateIfNotExits(ctx context.Context, cli client.Client, object client.Obje return false, err } + +func createOrUpdate(ctx context.Context, c client.Client, obj client.Object, f controllerutil.MutateFn, logger logr.Logger) (controllerutil.OperationResult, error) { + key := client.ObjectKeyFromObject(obj) + if err := c.Get(ctx, key, obj); err != nil { + if !errors.IsNotFound(err) { + return controllerutil.OperationResultNone, err + } + if err := mutate(f, key, obj); err != nil { + return controllerutil.OperationResultNone, err + } + if err := c.Create(ctx, obj); err != nil { + return controllerutil.OperationResultNone, err + } + return controllerutil.OperationResultCreated, nil + } + + existing := obj.DeepCopyObject() + if err := mutate(f, key, obj); err != nil { + return controllerutil.OperationResultNone, err + } + + if equality.Semantic.DeepEqual(existing, obj) { + return controllerutil.OperationResultNone, nil + } + + logger.Info(fmt.Sprintf("the diff of %v is %v", key, cmp.Diff(obj, existing))) + + if err := c.Update(ctx, obj); err != nil { + return controllerutil.OperationResultNone, err + } + return controllerutil.OperationResultUpdated, nil +} + +func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object) error { + if err := f(); err != nil { + return err + } + if newKey := client.ObjectKeyFromObject(obj); key != newKey { + return fmt.Errorf("MutateFn cannot mutate object name and/or object namespace") + } + return nil +} + +func UpdateObjectMeta(obj *metav1.ObjectMeta, instance metav1.Object, labels map[string]string) { + obj.Name = instance.GetName() + obj.Namespace = instance.GetNamespace() + obj.Labels = labels +}