openkruise 生命周期钩子之inPlaceUpdate Hook(二)

576 阅读1分钟

将逻辑固化到自制controller中

真正运用这个hook必须是要在这两个状态的时候做点事情的,这不是废话嘛。

那就写个controller测试一下

首先看SetupWithManager(),这里注册了监控所有pod变动事件,

显然不能放所有pod事件都进入Reconcile()跑一圈。

所以我们用predicate,在CreateUpdate的时候判断下是否满足hook的卡点逻辑

两个卡点的具体逻辑则写在isPreDeleteHooked()

// SetupWithManager sets up the controller with the Manager.
func (r *KruiseHookReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		WithOptions(controller.Options{MaxConcurrentReconciles: 3}).
		For(&v1.Pod{}).
		WithEventFilter(predicate.Funcs{
			CreateFunc: func(e event.CreateEvent) bool {
				pod := e.Object.(*v1.Pod)
				return isPreDeleteHooked(pod)
			},
			DeleteFunc:  func(_ event.DeleteEvent) bool { return false },
			GenericFunc: func(_ event.GenericEvent) bool { return false },
			UpdateFunc: func(e event.UpdateEvent) bool {
				pod := e.ObjectNew.(*v1.Pod)
				return isPreDeleteHooked(pod)
			},
		}).
		Complete(r)
}
func isPreDeleteHooked(pod *v1.Pod) bool {
	return (pod.Labels[kruiseappspub.LifecycleStateKey] == string(kruiseappspub.LifecycleStatePreparingUpdate) && pod.Labels[hookLabelKey] == "true") ||
		(pod.Labels[kruiseappspub.LifecycleStateKey] == string(kruiseappspub.LifecycleStateUpdated) && pod.Labels[hookLabelKey] == "false")
}

设置完毕,真正干活的是Reconcile()

// after hook succeeded:的两个if,分别判断下是第一个还是第二个卡点,

然后干对应的正事。这里只是简单放行。

// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;update;patch
func (r *KruiseHookReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	pod := &v1.Pod{}
	if err := r.Get(context.TODO(), req.NamespacedName, pod); err != nil {
		if errors.IsNotFound(err) {
			return ctrl.Result{}, nil
		}
		r.Log.Error(err, "failed to get", "pod", req.NamespacedName)
		return ctrl.Result{}, err
	}

	if !isPreDeleteHooked(pod) {
		return ctrl.Result{}, nil
	}

	var body string
	// after hook succeeded:
	if pod.Labels[kruiseappspub.LifecycleStateKey] == string(kruiseappspub.LifecycleStatePreparingUpdate) {
		body = fmt.Sprintf(`{"metadata":{"labels":{"%s":"false"}}}`, hookLabelKey)
		r.Log.Info(" inplace-update hook: PreparingUpdate", "pod", req.NamespacedName)
	} else if pod.Labels[kruiseappspub.LifecycleStateKey] == string(kruiseappspub.LifecycleStateUpdated) {
		body = fmt.Sprintf(`{"metadata":{"labels":{"%s":"true"}}}`, hookLabelKey)
		r.Log.Info(" inplace-update hook: Updated", "pod", req.NamespacedName)
	}

	if err := r.Patch(context.TODO(), pod, client.RawPatch(types.StrategicMergePatchType, []byte(body))); err != nil {
		r.Log.Error(err, "failed to patch", "pod", req.NamespacedName)
		return ctrl.Result{}, err
	}

	return ctrl.Result{}, nil
}

实际运行下这个controller,两个卡点自动放行。controller也有对应log放行记录。

tips

使用kubebuilder的话,由于Reconcile的是现有的pod。没有定义新的CRD

所以kubebuilder create api后问你要不要create resource [y/n],你是要拒绝的。

然后需要的是create controller这个动作。

上述一坨无用controller代码在这里

github.com/massivezh/k…