Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add compensation logic in service-available calculation #120

Merged
merged 6 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 80 additions & 4 deletions pkg/controllers/podopslifecycle/podopslifecycle_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -193,19 +194,94 @@ func (r *ReconcilePodOpsLifecycle) addServiceAvailable(pod *corev1.Pod) (bool, e
return false, nil
}

satisfied, _, err := controllerutils.SatisfyExpectedFinalizers(pod) // whether all expected finalizers are satisfied
if err != nil || !satisfied {
satisfied, notSatisfiedFinalizers, err := controllerutils.SatisfyExpectedFinalizers(pod) // whether all expected finalizers are satisfied
if err != nil {
return false, err
}

if !satisfied {
notDirtyExist, err := r.removeDirtyExpectedFinalizer(pod, notSatisfiedFinalizers)
if err != nil {
return false, err
}
if notDirtyExist {
return false, nil
}
// all not satisfied expected finalizers are dirty, so actually the pod satisfied expected finalizer now
}

if !controllerutils.IsPodReady(pod) {
return false, nil
}

labels := map[string]string{
podLabelsToAdd := map[string]string{
v1alpha1.PodServiceAvailableLabel: strconv.FormatInt(time.Now().Unix(), 10),
}
return true, r.addLabels(context.Background(), pod, labels)
return true, r.addLabels(context.Background(), pod, podLabelsToAdd)
}

func (r *ReconcilePodOpsLifecycle) removeDirtyExpectedFinalizer(pod *corev1.Pod, notSatisfiedFinalizers map[string]string) (bool, error) {
var notDirtyExist bool
WeichengWang1 marked this conversation as resolved.
Show resolved Hide resolved
dirtyExpectedFinalizer := make(map[string]string)
// expectedFinalizerKey is generated under the format(defined in kusionstack.io/resourceconsist):
// fmt.Sprintf("%s/%s/%s", employer.GetObjectKind().GroupVersionKind().Kind, employer.GetNamespace(), employer.GetName())
// in kusionstack.io/operating, just check Service since we can't determine how a CR selecting pod
for expectedFinalizerKey, finalizer := range notSatisfiedFinalizers {
keySplits := strings.Split(expectedFinalizerKey, "/")
WeichengWang1 marked this conversation as resolved.
Show resolved Hide resolved
if len(keySplits) != 3 {
notDirtyExist = true
break
}
if keySplits[0] != "Service" {
notDirtyExist = true
break
}

var svc corev1.Service
err := r.Client.Get(context.Background(), types.NamespacedName{
Namespace: keySplits[1],
Name: keySplits[2],
}, &svc)
if err != nil {
if errors.IsNotFound(err) {
dirtyExpectedFinalizer[expectedFinalizerKey] = finalizer
continue
}
return notDirtyExist, err
}

if !labels.Set(svc.Spec.Selector).AsSelector().Matches(labels.Set(pod.GetLabels())) {
dirtyExpectedFinalizer[expectedFinalizerKey] = finalizer
continue
}
notDirtyExist = true
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wu8685 shall we make it break the for loop here as long as notDirtyExist?
pros: no need to get all service and check if selected
cons: dirty expected finalizer might not be cleaned in time

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, looks better

break
}

if len(dirtyExpectedFinalizer) > 0 {
podAvailableConditions, err := controllerutils.PodAvailableConditions(pod)
if err != nil {
return notDirtyExist, err
}
for dirtyExpectedFinalizerKey := range dirtyExpectedFinalizer {
delete(podAvailableConditions.ExpectedFinalizers, dirtyExpectedFinalizerKey)
}
err = r.updateAvailableConditions(pod, podAvailableConditions)
if err != nil {
return notDirtyExist, err
}
}

return notDirtyExist, nil
}

func (r *ReconcilePodOpsLifecycle) updateAvailableConditions(pod *corev1.Pod, conditions *v1alpha1.PodAvailableConditions) error {
newAvailableConditions := utils.DumpJSON(conditions)
if pod.Annotations == nil {
pod.Annotations = make(map[string]string)
}
pod.Annotations[v1alpha1.PodAvailableConditionsAnnotation] = newAvailableConditions
return r.Client.Update(context.Background(), pod)
}

func (r *ReconcilePodOpsLifecycle) updateServiceReadiness(ctx context.Context, pod *corev1.Pod, isReady bool) (bool, error) {
Expand Down
12 changes: 6 additions & 6 deletions pkg/controllers/utils/pod_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,13 @@ func IsPodUpdatedRevision(pod *corev1.Pod, revision string) bool {
return pod.Labels[appsv1.ControllerRevisionHashLabelKey] == revision
}

func SatisfyExpectedFinalizers(pod *corev1.Pod) (bool, []string, error) {
func SatisfyExpectedFinalizers(pod *corev1.Pod) (bool, map[string]string, error) {
satisfied := true
var expectedFinalizers []string // expected finalizers that are not satisfied
notSatisfiedFinalizers := make(map[string]string) // expected finalizers that are not satisfied

availableConditions, err := PodAvailableConditions(pod)
if err != nil {
return satisfied, expectedFinalizers, err
return true, notSatisfiedFinalizers, err
}

if availableConditions != nil && len(availableConditions.ExpectedFinalizers) != 0 {
Expand All @@ -293,15 +293,15 @@ func SatisfyExpectedFinalizers(pod *corev1.Pod) (bool, []string, error) {
existFinalizers.Insert(finalizer)
}

for _, finalizer := range availableConditions.ExpectedFinalizers {
for expectedFlzKey, finalizer := range availableConditions.ExpectedFinalizers {
WeichengWang1 marked this conversation as resolved.
Show resolved Hide resolved
if !existFinalizers.Has(finalizer) {
satisfied = false
expectedFinalizers = append(expectedFinalizers, finalizer)
notSatisfiedFinalizers[expectedFlzKey] = finalizer
}
}
}

return satisfied, expectedFinalizers, nil
return satisfied, notSatisfiedFinalizers, nil
}

func PodAvailableConditions(pod *corev1.Pod) (*v1alpha1.PodAvailableConditions, error) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/webhook/server/generic/pod/opslifecycle/mutating.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ func (lc *OpsLifecycle) Mutating(ctx context.Context, c client.Client, oldPod, n
}

if completeCount == numOfIDs { // all operations are completed
satisfied, expectedFinalizers, err := controllerutils.SatisfyExpectedFinalizers(newPod) // whether all expected finalizers are satisfied
satisfied, notSatisfiedFinalizers, err := controllerutils.SatisfyExpectedFinalizers(newPod) // whether all expected finalizers are satisfied
if err != nil || !satisfied {
klog.Infof("pod: %s/%s, satisfied: %v, expectedFinalizer: %v, err: %v", newPod.Namespace, newPod.Name, satisfied, expectedFinalizers, err)
klog.Infof("pod: %s/%s, satisfied: %v, expectedFinalizer: %v, err: %v", newPod.Namespace, newPod.Name, satisfied, notSatisfiedFinalizers, err)
return err
}

Expand Down
Loading