/* Copyright 2022. 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 controllers import ( "context" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" networkv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" "reflect" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "strings" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" myAppsv1 "mashibing.com/pkg/mashibing-deployment/api/v1" ) // MsbDeploymentReconciler reconciles a MsbDeployment object type MsbDeploymentReconciler struct { client.Client Scheme *runtime.Scheme } //+kubebuilder:rbac:groups=apps.mashibing.com,resources=msbdeployments,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=apps.mashibing.com,resources=msbdeployments/status,verbs=get;update;patch //+kubebuilder:rbac:groups=apps.mashibing.com,resources=msbdeployments/finalizers,verbs=update // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // TODO(user): Modify the Reconcile function to compare the state specified by // the MsbDeployment object against the actual cluster state, and then // perform operations to make the cluster state reflect the state specified by // the user. // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile func (r *MsbDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx, "MsbDployment", req.NamespacedName) logger.Info("Reconcile is started.") // 1. 获取资源对象 md := new(myAppsv1.MsbDeployment) if err := r.Client.Get(ctx, req.NamespacedName, md); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } // 防止污染缓存 mdCopy := md.DeepCopy() // ======= 处理 deployment ====== // 2. 获取deployment资源对象 deploy := new(appsv1.Deployment) if err := r.Client.Get(ctx, req.NamespacedName, deploy); err != nil { if errors.IsNotFound(err) { // 2.1 不存在对象 // 2.1.1 创建 deployment if err := r.createDeployment(ctx, mdCopy); err != nil { return ctrl.Result{}, err } } else { return ctrl.Result{}, err } } else { // 2.2 存在对象 // 2.2.1 更新 deployment if err := r.updateDeployment(ctx, mdCopy, deploy); err != nil { return ctrl.Result{}, err } } // ======= 处理 service ========= // 3. 获取 service 资源对象 svc := new(corev1.Service) if err := r.Client.Get(ctx, req.NamespacedName, svc); err != nil { if errors.IsNotFound(err) { // 3.1 不存在 // 3.1.1 mode 为 ingress if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeIngress { // 3.1.1.1 创建普通service if err := r.createService(ctx, mdCopy); err != nil { return ctrl.Result{}, err } } else if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeNodePort { // 3.1.2 mode 为 nodeport // 3.1.2.1 创建 nodeport 模式的 service if err := r.createNPService(ctx, mdCopy); err != nil { return ctrl.Result{}, err } } else { return ctrl.Result{}, myAppsv1.ErrorNotSupportMode } } else { return ctrl.Result{}, err } } else { // 3.2 存在 if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeIngress { // 3.2.1 mode 为 ingress // 3.2.1.1 更新普通的 service if err := r.updateService(ctx, mdCopy, svc); err != nil { return ctrl.Result{}, err } } else if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeNodePort { // 3.2.2 mode 为 nodeport // 3.2.2.1 更新nodeport模式的service if err := r.updateNPService(ctx, mdCopy, svc); err != nil { return ctrl.Result{}, err } } else { return ctrl.Result{}, myAppsv1.ErrorNotSupportMode } } // ======= 处理 ingress ========= // 4 获取 ingress 资源 ig := new(networkv1.Ingress) if err := r.Client.Get(ctx, req.NamespacedName, ig); err != nil { if errors.IsNotFound(err) { // 4.1 不存在 if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeIngress { // 4.1.1 mode 为 ingress // 4.1.1.1 创建 ingress if err := r.createIngress(ctx, mdCopy); err != nil { return ctrl.Result{}, err } } else if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeNodePort { // 4.1.2 mode 为 nodeport // 4.1.2.1 退出 return ctrl.Result{}, nil } } else { return ctrl.Result{}, err } } else { // 4.2 存在 if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeIngress { // 4.2.1 mode 为 ingress // 4.2.1.1更新 ingress if err := r.updateIngress(ctx, mdCopy, ig); err != nil { return ctrl.Result{}, err } } else if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeNodePort { // 4.2.2 mode 为 nodeport // 4.2.2.1 删除 ingress if err := r.deleteIngress(ctx, mdCopy); err != nil { return ctrl.Result{}, err } } } logger.Info("Reconcile is ended.") return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. func (r *MsbDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&myAppsv1.MsbDeployment{}). Owns(&appsv1.Deployment{}). // 监控 deployment 类型,变更就触发 reconciler Owns(&corev1.Service{}). // 监控 service 类型,变更就触发 reconciler Owns(&networkv1.Ingress{}). // 监控 ingress 类型,变更就触发 reconciler Complete(r) } func (r *MsbDeploymentReconciler) createDeployment(ctx context.Context, md *myAppsv1.MsbDeployment) error { deploy, err := NewDeployment(md) if err != nil { return err } // 设置 deployment 所属于 md if err := controllerutil.SetControllerReference(md, deploy, r.Scheme); err != nil { return err } return r.Client.Create(ctx, deploy) } func (r *MsbDeploymentReconciler) updateDeployment(ctx context.Context, md *myAppsv1.MsbDeployment, dp *appsv1.Deployment) error { deploy, err := NewDeployment(md) if err != nil { return err } // 设置 deployment 所属于 md if err := controllerutil.SetControllerReference(md, deploy, r.Scheme); err != nil { return err } // 预更新deployment,得到更新后的数据 if err := r.Update(ctx, deploy, client.DryRunAll); err != nil { return err } // 和之前的数据进行比较,如果相同,说明更新不需要。 if reflect.DeepEqual(dp.Spec, deploy.Spec) { return nil } return r.Client.Update(ctx, deploy) } func (r *MsbDeploymentReconciler) createService(ctx context.Context, md *myAppsv1.MsbDeployment) error { svc, err := NewService(md) if err != nil { return err } // 设置 service 所属于 md if err := controllerutil.SetControllerReference(md, svc, r.Scheme); err != nil { return err } return r.Client.Create(ctx, svc) } func (r *MsbDeploymentReconciler) createNPService(ctx context.Context, md *myAppsv1.MsbDeployment) error { svc, err := NewServiceNP(md) if err != nil { return err } // 设置 service 所属于 md if err := controllerutil.SetControllerReference(md, svc, r.Scheme); err != nil { return err } return r.Client.Create(ctx, svc) } func (r *MsbDeploymentReconciler) updateService(ctx context.Context, md *myAppsv1.MsbDeployment, service *corev1.Service) error { svc, err := NewService(md) if err != nil { return err } // 设置 service 所属于 md if err := controllerutil.SetControllerReference(md, svc, r.Scheme); err != nil { return err } // 预更新service,得到更新后的数据 if err := r.Update(ctx, svc, client.DryRunAll); err != nil { return err } // 和之前的数据进行比较,如果相同,说明更新不需要。 if reflect.DeepEqual(service.Spec, svc.Spec) { return nil } return r.Client.Update(ctx, svc) } func (r *MsbDeploymentReconciler) updateNPService(ctx context.Context, md *myAppsv1.MsbDeployment, service *corev1.Service) error { svc, err := NewServiceNP(md) if err != nil { return err } // 设置 service 所属于 md if err := controllerutil.SetControllerReference(md, svc, r.Scheme); err != nil { return err } // 预更新service,得到更新后的数据 if err := r.Update(ctx, svc, client.DryRunAll); err != nil { return err } // 和之前的数据进行比较,如果相同,说明更新不需要。 if reflect.DeepEqual(service.Spec, svc.Spec) { return nil } return r.Client.Update(ctx, svc) } func (r *MsbDeploymentReconciler) createIngress(ctx context.Context, md *myAppsv1.MsbDeployment) error { ig, err := NewIngress(md) if err != nil { return err } // 设置 ingress 所属于 md if err := controllerutil.SetControllerReference(md, ig, r.Scheme); err != nil { return err } return r.Client.Create(ctx, ig) } func (r *MsbDeploymentReconciler) updateIngress(ctx context.Context, md *myAppsv1.MsbDeployment, ingress *networkv1.Ingress) error { ig, err := NewIngress(md) if err != nil { return err } // 设置 ingress 所属于 md if err := controllerutil.SetControllerReference(md, ig, r.Scheme); err != nil { return err } // 预更新ingress,得到更新后的数据 if err := r.Update(ctx, ingress, client.DryRunAll); err != nil { return err } // 和之前的数据进行比较,如果相同,说明更新不需要。 if reflect.DeepEqual(ingress.Spec, ig.Spec) { return nil } return r.Client.Update(ctx, ig) } func (r *MsbDeploymentReconciler) deleteIngress(ctx context.Context, md *myAppsv1.MsbDeployment) error { ig, err := NewIngress(md) if err != nil { return err } return r.Client.Delete(ctx, ig) }