|
|
|
@ -18,13 +18,16 @@ package controllers
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
|
networkv1 "k8s.io/api/networking/v1"
|
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
|
"reflect"
|
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
|
|
@ -34,6 +37,8 @@ import (
|
|
|
|
|
myAppsv1 "mashibing.com/pkg/mashibing-deployment/api/v1"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var WaitRequeue = 10 * time.Second
|
|
|
|
|
|
|
|
|
|
// MsbDeploymentReconciler reconciles a MsbDeployment object
|
|
|
|
|
type MsbDeploymentReconciler struct {
|
|
|
|
|
client.Client
|
|
|
|
@ -54,6 +59,14 @@ type MsbDeploymentReconciler struct {
|
|
|
|
|
// 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) {
|
|
|
|
|
// 状态更新策略
|
|
|
|
|
// 创建的时候
|
|
|
|
|
// 更新为创建
|
|
|
|
|
// 更新的时候
|
|
|
|
|
// 根据获取的状态来判断时候更新status
|
|
|
|
|
// 删除的时候
|
|
|
|
|
// 只有在操作 ingress 的时候,并且 mode 为 nodeport 的时候
|
|
|
|
|
|
|
|
|
|
logger := log.FromContext(ctx, "MsbDployment", req.NamespacedName)
|
|
|
|
|
|
|
|
|
|
logger.Info("Reconcile is started.")
|
|
|
|
@ -74,10 +87,26 @@ func (r *MsbDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|
|
|
|
if errors.IsNotFound(err) {
|
|
|
|
|
// 2.1 不存在对象
|
|
|
|
|
// 2.1.1 创建 deployment
|
|
|
|
|
if err := r.createDeployment(ctx, mdCopy); err != nil {
|
|
|
|
|
return ctrl.Result{}, err
|
|
|
|
|
if errCreate := r.createDeployment(ctx, mdCopy); err != nil {
|
|
|
|
|
return ctrl.Result{}, errCreate
|
|
|
|
|
}
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeDeployment,
|
|
|
|
|
fmt.Sprintf(myAppsv1.ConditionMessageDeploymentNotFmt, req.Name),
|
|
|
|
|
myAppsv1.ConditonStatusFalse,
|
|
|
|
|
myAppsv1.ConditionReasonDeploymentNotReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeDeployment,
|
|
|
|
|
fmt.Sprintf("Deployment %s, err: %s", req.Name, err.Error()),
|
|
|
|
|
myAppsv1.ConditonStatusFalse,
|
|
|
|
|
myAppsv1.ConditionReasonDeploymentNotReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
return ctrl.Result{}, err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -86,6 +115,25 @@ func (r *MsbDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|
|
|
|
if err := r.updateDeployment(ctx, mdCopy, deploy); err != nil {
|
|
|
|
|
return ctrl.Result{}, err
|
|
|
|
|
}
|
|
|
|
|
if deploy.Status.AvailableReplicas == mdCopy.Spec.Replicas {
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeDeployment,
|
|
|
|
|
fmt.Sprintf(myAppsv1.ConditionMessageDeploymentOKFmt, req.Name),
|
|
|
|
|
myAppsv1.ConditonStatusTrue,
|
|
|
|
|
myAppsv1.ConditionReasonDeploymentReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeDeployment,
|
|
|
|
|
fmt.Sprintf(myAppsv1.ConditionMessageDeploymentNotFmt, req.Name),
|
|
|
|
|
myAppsv1.ConditonStatusFalse,
|
|
|
|
|
myAppsv1.ConditionReasonDeploymentNotReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ======= 处理 service =========
|
|
|
|
@ -109,7 +157,23 @@ func (r *MsbDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|
|
|
|
} else {
|
|
|
|
|
return ctrl.Result{}, myAppsv1.ErrorNotSupportMode
|
|
|
|
|
}
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeService,
|
|
|
|
|
fmt.Sprintf(myAppsv1.ConditionMessageServiceNotFmt, req.Name),
|
|
|
|
|
myAppsv1.ConditonStatusFalse,
|
|
|
|
|
myAppsv1.ConditionReasonServiceNotReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeService,
|
|
|
|
|
fmt.Sprintf("Service %s, err: %s", req.Name, err.Error()),
|
|
|
|
|
myAppsv1.ConditonStatusFalse,
|
|
|
|
|
myAppsv1.ConditionReasonServiceNotReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
return ctrl.Result{}, err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -129,6 +193,14 @@ func (r *MsbDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|
|
|
|
} else {
|
|
|
|
|
return ctrl.Result{}, myAppsv1.ErrorNotSupportMode
|
|
|
|
|
}
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeService,
|
|
|
|
|
fmt.Sprintf(myAppsv1.ConditionMessageServiceOKFmt, req.Name),
|
|
|
|
|
myAppsv1.ConditonStatusTrue,
|
|
|
|
|
myAppsv1.ConditionReasonServiceReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ======= 处理 ingress =========
|
|
|
|
@ -143,12 +215,28 @@ func (r *MsbDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|
|
|
|
if err := r.createIngress(ctx, mdCopy); err != nil {
|
|
|
|
|
return ctrl.Result{}, err
|
|
|
|
|
}
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeIngress,
|
|
|
|
|
fmt.Sprintf(myAppsv1.ConditionMessageIngressNotFmt, req.Name),
|
|
|
|
|
myAppsv1.ConditonStatusFalse,
|
|
|
|
|
myAppsv1.ConditionReasonIngressNotReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
} else if strings.ToLower(mdCopy.Spec.Expose.Mode) == myAppsv1.ModeNodePort {
|
|
|
|
|
// 4.1.2 mode 为 nodeport
|
|
|
|
|
// 4.1.2.1 退出
|
|
|
|
|
return ctrl.Result{}, nil
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeIngress,
|
|
|
|
|
fmt.Sprintf("Ingress %s, err: %s", req.Name, err.Error()),
|
|
|
|
|
myAppsv1.ConditonStatusFalse,
|
|
|
|
|
myAppsv1.ConditionReasonIngressNotReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
return ctrl.Result{}, err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -159,15 +247,37 @@ func (r *MsbDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|
|
|
|
if err := r.updateIngress(ctx, mdCopy, ig); err != nil {
|
|
|
|
|
return ctrl.Result{}, err
|
|
|
|
|
}
|
|
|
|
|
if _, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
myAppsv1.ConditionTypeIngress,
|
|
|
|
|
fmt.Sprintf(myAppsv1.ConditionMessageIngressOKFmt, req.Name),
|
|
|
|
|
myAppsv1.ConditonStatusTrue,
|
|
|
|
|
myAppsv1.ConditionReasonIngressReady); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
}
|
|
|
|
|
} 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
|
|
|
|
|
}
|
|
|
|
|
r.deleteStatus(mdCopy, myAppsv1.ConditionTypeIngress)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最后检查状态时候最终完成
|
|
|
|
|
if sus, errStatus := r.updateStatus(ctx,
|
|
|
|
|
mdCopy,
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
""); errStatus != nil {
|
|
|
|
|
return ctrl.Result{}, errStatus
|
|
|
|
|
} else if !sus {
|
|
|
|
|
logger.Info("Reconcile is ended.")
|
|
|
|
|
return ctrl.Result{RequeueAfter: WaitRequeue}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Info("Reconcile is ended.")
|
|
|
|
|
return ctrl.Result{}, nil
|
|
|
|
|
}
|
|
|
|
@ -341,3 +451,133 @@ func (r *MsbDeploymentReconciler) deleteIngress(ctx context.Context, md *myAppsv
|
|
|
|
|
}
|
|
|
|
|
return r.Client.Delete(ctx, ig)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理status
|
|
|
|
|
// return:
|
|
|
|
|
//
|
|
|
|
|
// bool: 资源是否完成,时候需要等待。如果是 true,表示资源已经完成没不需要再次reconcile
|
|
|
|
|
// 如果是 false,表示资源还未完成,需要重新入队
|
|
|
|
|
// error:执行 update 的状态
|
|
|
|
|
func (r *MsbDeploymentReconciler) updateStatus(ctx context.Context, md *myAppsv1.MsbDeployment, conditionType, message, status, reason string) (bool, error) {
|
|
|
|
|
if conditionType != "" {
|
|
|
|
|
// 1. 获取 status
|
|
|
|
|
//status := md.Status
|
|
|
|
|
// 2. 获取 conditions 字段
|
|
|
|
|
//conditions := status.Conditions
|
|
|
|
|
// 3. 根据当前的需求,获取指定的 condition
|
|
|
|
|
var condition *myAppsv1.Condition
|
|
|
|
|
for i := range md.Status.Conditions {
|
|
|
|
|
// 4. 是否获取到
|
|
|
|
|
if md.Status.Conditions[i].Type == conditionType {
|
|
|
|
|
// 4.1 获取到了
|
|
|
|
|
condition = &md.Status.Conditions[i]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if condition != nil {
|
|
|
|
|
// 4.1.1 获取当前线上的 conditon 状态,与存储的condition进行比较,如果相同,跳过。不同,替换
|
|
|
|
|
if condition.Status != status ||
|
|
|
|
|
condition.Message != message ||
|
|
|
|
|
condition.Reason != reason {
|
|
|
|
|
condition.Status = status
|
|
|
|
|
condition.Message = message
|
|
|
|
|
condition.Reason = reason
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 4.2 没获取到,创建这个conditon,更新到conditons中
|
|
|
|
|
md.Status.Conditions = append(md.Status.Conditions,
|
|
|
|
|
createCondition(conditionType, message, status, reason))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 继续处理其他的conditions
|
|
|
|
|
m, re, p, sus := isSuccess(md.Status.Conditions)
|
|
|
|
|
if sus {
|
|
|
|
|
// 6.1 如果所有conditions的状态都为成功,则更新总的 status 为成功。
|
|
|
|
|
md.Status.Message = myAppsv1.StatusMessageSuccess
|
|
|
|
|
md.Status.Reason = myAppsv1.StatusReasonSuccess
|
|
|
|
|
md.Status.Phase = myAppsv1.StatusPhaseComplete
|
|
|
|
|
} else {
|
|
|
|
|
// 6.2 遍历所有的conditons 状态,如果有任意一个condition不是完成的状态,则将这个状态更新到总的 status 中。更待一定时间再次入队。
|
|
|
|
|
md.Status.Message = m
|
|
|
|
|
md.Status.Reason = re
|
|
|
|
|
md.Status.Phase = p
|
|
|
|
|
}
|
|
|
|
|
// 7. 执行更新
|
|
|
|
|
return sus, r.Client.Status().Update(ctx, md)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isSuccess(conditions []myAppsv1.Condition) (message, reason, phase string, sus bool) {
|
|
|
|
|
if len(conditions) == 0 {
|
|
|
|
|
return "", "", "", false
|
|
|
|
|
}
|
|
|
|
|
for i := range conditions {
|
|
|
|
|
if conditions[i].Status == myAppsv1.ConditonStatusFalse {
|
|
|
|
|
return conditions[i].Message, conditions[i].Reason, conditions[i].Type, false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "", "", "", true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func createCondition(conditionType, message, status, reason string) myAppsv1.Condition {
|
|
|
|
|
return myAppsv1.Condition{
|
|
|
|
|
Type: conditionType,
|
|
|
|
|
Message: message,
|
|
|
|
|
Status: status,
|
|
|
|
|
Reason: reason,
|
|
|
|
|
LastTransitionTime: metav1.NewTime(time.Now()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 需要是幂等的,可以多次执行,不管是否存在。如果存在就删除,不存在就什么也不做
|
|
|
|
|
// 只是删除对应的Condition不做更多的操作
|
|
|
|
|
func (r *MsbDeploymentReconciler) deleteStatus(md *myAppsv1.MsbDeployment, conditionType string) {
|
|
|
|
|
// 1. 遍历conditions
|
|
|
|
|
for i := range md.Status.Conditions {
|
|
|
|
|
// 2. 找到要删除的对象
|
|
|
|
|
if md.Status.Conditions[i].Type == conditionType {
|
|
|
|
|
// 3. 执行删除
|
|
|
|
|
md.Status.Conditions = deleteCondition(md.Status.Conditions, i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// a := struct {
|
|
|
|
|
// len int
|
|
|
|
|
// cap int
|
|
|
|
|
// [cap]int [1,2,3,4]
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// b = a
|
|
|
|
|
//
|
|
|
|
|
// b
|
|
|
|
|
//
|
|
|
|
|
// a
|
|
|
|
|
// a = append(a, "b")
|
|
|
|
|
// conditions = deleteCondition(conditions, 2)
|
|
|
|
|
// a = [1,2,3,4,5]
|
|
|
|
|
// f(a)
|
|
|
|
|
//
|
|
|
|
|
// f(b []int) {
|
|
|
|
|
// b = n[0:4]
|
|
|
|
|
// }
|
|
|
|
|
func deleteCondition(conditions []myAppsv1.Condition, i int) []myAppsv1.Condition {
|
|
|
|
|
// 前提:切片中的元素顺序不敏感
|
|
|
|
|
// 1. 要删除的元素的索引值不能大于切片长度
|
|
|
|
|
if i >= len(conditions) {
|
|
|
|
|
return []myAppsv1.Condition{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 如果切片长度为1,且索引值为0,直接清空
|
|
|
|
|
if len(conditions) == 1 && i == 0 {
|
|
|
|
|
return conditions[:0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. 如果长度-1等于索引值,删除最后一个元素
|
|
|
|
|
if len(conditions)-1 == i {
|
|
|
|
|
return conditions[:len(conditions)-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 交换索引位置的元素和最后一个元素,删除最后一个元素
|
|
|
|
|
conditions[i], conditions[len(conditions)-1] = conditions[len(conditions)-1], conditions[i]
|
|
|
|
|
return conditions[:len(conditions)-1]
|
|
|
|
|
}
|
|
|
|
|