Skip to content
This repository has been archived by the owner on Nov 2, 2023. It is now read-only.

Commit

Permalink
statefulset support patches features
Browse files Browse the repository at this point in the history
Signed-off-by: kadisi <iamkadisi@163.com>
  • Loading branch information
kadisi committed Mar 23, 2021
1 parent 2707026 commit fedb13b
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,12 @@ spec:
distributed across multiple groups of nodes. A pool's nodeSelectorTerm
is not allowed to be updated.
type: object
patches:
description: Indicates the patches for the templateSpec
patch:
description: Indicates the patch for the templateSpec Now
support strategic merge path :https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#notes-on-the-strategic-merge-patch
Patch takes precedence over Replicas fields If the Patch
also modifies the Replicas, use the Replicas value in the
Patch
type: object
replicas:
description: Indicates the number of the pod to be created
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020 The OpenYurt Authors.
Copyright 2021 The OpenYurt Authors.
Copyright 2020 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -126,9 +126,12 @@ type Pool struct {
// +required
Replicas *int32 `json:"replicas,omitempty"`

// Indicates the patches for the templateSpec
// Indicates the patch for the templateSpec
// Now support strategic merge path :https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#notes-on-the-strategic-merge-patch
// Patch takes precedence over Replicas fields
// If the Patch also modifies the Replicas, use the Replicas value in the Patch
// +optional
Patches *runtime.RawExtension `json:"patches,omitempty"`
Patch *runtime.RawExtension `json:"patch,omitempty"`
}

// UnitedDeploymentStatus defines the observed state of UnitedDeployment.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020 The OpenYurt Authors.
Copyright 2021 The OpenYurt Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,8 +31,8 @@ const (
// SpecifiedDeleteKey indicates this object should be deleted, and the value could be the deletion option.
SpecifiedDeleteKey = "apps.openyurt.io/specified-delete"

// AnnotationPatchesKey indicates the patches for every sub pool
AnnotationPatchesKey = "apps.openyurt.io/patches"
// AnnotationPatchKey indicates the patch for every sub pool
AnnotationPatchKey = "apps.openyurt.io/patch"
)

// NodePool related labels and annotations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020 The OpenYurt Authors.
Copyright 2021 The OpenYurt Authors.
Copyright 2019 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -21,14 +21,13 @@ import (
"encoding/json"
"fmt"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/klog"

unitv1alpha1 "github.com/openyurtio/yurt-app-manager/pkg/yurtappmanager/apis/apps/v1alpha1"
appsv1alpha1 "github.com/openyurtio/yurt-app-manager/pkg/yurtappmanager/apis/apps/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/klog"
)

func getPoolPrefix(controllerName, poolName string) string {
Expand All @@ -39,12 +38,12 @@ func getPoolPrefix(controllerName, poolName string) string {
return prefix
}

func attachNodeAffinityAndTolerations(podSpec *corev1.PodSpec, pool *unitv1alpha1.Pool) {
func attachNodeAffinityAndTolerations(podSpec *corev1.PodSpec, pool *appsv1alpha1.Pool) {
attachNodeAffinity(podSpec, pool)
attachTolerations(podSpec, pool)
}

func attachNodeAffinity(podSpec *corev1.PodSpec, pool *unitv1alpha1.Pool) {
func attachNodeAffinity(podSpec *corev1.PodSpec, pool *appsv1alpha1.Pool) {
if podSpec.Affinity == nil {
podSpec.Affinity = &corev1.Affinity{}
}
Expand Down Expand Up @@ -73,7 +72,7 @@ func attachNodeAffinity(podSpec *corev1.PodSpec, pool *unitv1alpha1.Pool) {
}
}

func attachTolerations(podSpec *corev1.PodSpec, poolConfig *unitv1alpha1.Pool) {
func attachTolerations(podSpec *corev1.PodSpec, poolConfig *appsv1alpha1.Pool) {

if poolConfig.Tolerations == nil {
return
Expand All @@ -94,7 +93,7 @@ func getRevision(objMeta metav1.Object) string {
if objMeta.GetLabels() == nil {
return ""
}
return objMeta.GetLabels()[unitv1alpha1.ControllerRevisionHashLabelKey]
return objMeta.GetLabels()[appsv1alpha1.ControllerRevisionHashLabelKey]
}

// getCurrentPartition calculates current partition by counting the pods not having the updated revision
Expand All @@ -109,28 +108,55 @@ func getCurrentPartition(pods []*corev1.Pod, revision string) *int32 {
return &partition
}

func StrategicMergeByPatches(patchKind string, oldobj interface{}, patches *runtime.RawExtension, newPatched interface{}) error {
func StrategicMergeByPatches(oldobj interface{}, patch *runtime.RawExtension, newPatched interface{}) error {
patchMap := make(map[string]interface{})
if err := json.Unmarshal(patches.Raw, &patchMap); err != nil {
klog.Errorf("For %s Unmarshal Pool Patches error %v, Patches Raw %v", patchKind, err,string(patches.Raw))
return err
if err := json.Unmarshal(patch.Raw, &patchMap); err != nil {
klog.Errorf("Unmarshal pool patch error %v, patch Raw %v", err, string(patch.Raw))
return err
}

originalObjMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(oldobj)
if err != nil {
klog.Errorf("For %s obj ToUnstructured error %v", patchKind, err)
return err
klog.Errorf("ToUnstructured error %v", err)
return err
}

patchedObjMap, err := strategicpatch.StrategicMergeMapPatch(originalObjMap, patchMap, newPatched)
if err != nil {
klog.Errorf("For %s obj StartegicMergeMapPatch error %v", patchKind, err)
klog.Errorf("StartegicMergeMapPatch error %v", err)
return err
}

if err := runtime.DefaultUnstructuredConverter.FromUnstructured(patchedObjMap, newPatched); err != nil {
klog.Errorf("For %s FromUnstructured error %v", patchKind, err)
klog.Errorf("FromUnstructured error %v", err)
return err
}
return nil
}

func PoolHasPatch(poolConfig *appsv1alpha1.Pool, set metav1.Object) bool {
if poolConfig.Patch == nil {
// If No Patches, Must Set patches annotation to ""
if anno := set.GetAnnotations(); anno != nil {
anno[appsv1alpha1.AnnotationPatchKey] = ""
}
return false
}
return true
}

func CreateNewPatchedObject(patchInfo *runtime.RawExtension, set metav1.Object, newPatched metav1.Object) error {

if err := StrategicMergeByPatches(set, patchInfo, newPatched); err != nil {
return err
}

if anno := newPatched.GetAnnotations(); anno == nil {
newPatched.SetAnnotations(map[string]string{
appsv1alpha1.AnnotationPatchKey: string(patchInfo.Raw),
})
} else {
anno[appsv1alpha1.AnnotationPatchKey] = string(patchInfo.Raw)
}
return nil
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020 The OpenYurt Authors.
Copyright 2021 The OpenYurt Authors.
Copyright 2019 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -21,6 +21,10 @@ import (
"fmt"
"testing"

appsv1 "k8s.io/api/apps/v1"

"k8s.io/apimachinery/pkg/runtime"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand Down Expand Up @@ -86,3 +90,84 @@ func buildPodList(ordinals []int, revisions []string, t *testing.T) []*corev1.Po

return pods
}

func TestCreateNewPatchedObject(t *testing.T) {
cases := []struct {
Name string
PatchInfo *runtime.RawExtension
OldObj *appsv1.Deployment
EqualFuntion func(new *appsv1.Deployment) bool
}{
{
Name: "replace image",
PatchInfo: &runtime.RawExtension{Raw: []byte(`{"spec":{"template":{"spec":{"containers":[{"image":"nginx:1.18.0","name":"nginx"}]}}}}`)},
OldObj: &appsv1.Deployment{
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx:1.19.0",
},
},
},
},
},
},
EqualFuntion: func(new *appsv1.Deployment) bool {
return new.Spec.Template.Spec.Containers[0].Image == "nginx:1.18.0"
},
},
{
Name: "add other image",
PatchInfo: &runtime.RawExtension{Raw: []byte(`{"spec":{"template":{"spec":{"containers":[{"image":"nginx:1.18.0","name":"nginx111"}]}}}}`)},
OldObj: &appsv1.Deployment{
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx:1.19.0",
},
},
},
},
},
},
EqualFuntion: func(new *appsv1.Deployment) bool {
if len(new.Spec.Template.Spec.Containers) != 2 {
return false
}
containerMap := make(map[string]string)
for _, container := range new.Spec.Template.Spec.Containers {
containerMap[container.Name] = container.Image
}
image, ok := containerMap["nginx"]
if !ok {
return false
}

image1, ok := containerMap["nginx111"]
if !ok {
return false
}
return image == "nginx:1.19.0" && image1 == "nginx:1.18.0"
},
},
}

for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
newObj := &appsv1.Deployment{}
if err := CreateNewPatchedObject(c.PatchInfo, c.OldObj, newObj); err != nil {
t.Fatalf("%s CreateNewPatchedObject error %v", c.Name, err)
}
if !c.EqualFuntion(newObj) {
t.Fatalf("%s Not Expect equal funtion", c.Name)
}
})
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2019 The OpenYurt Authors.
Copyright 2021 The OpenYurt Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,7 @@ package adapter

import (
"fmt"

"k8s.io/klog"

alpha1 "github.com/openyurtio/yurt-app-manager/pkg/yurtappmanager/apis/apps/v1alpha1"
Expand Down Expand Up @@ -110,7 +111,6 @@ func (a *DeploymentAdapter) ApplyPoolTemplate(ud *alpha1.UnitedDeployment, poolN
set.Annotations[k] = v
}


set.GenerateName = getPoolPrefix(ud.Name, poolName)

selectors := ud.Spec.Selector.DeepCopy()
Expand Down Expand Up @@ -138,31 +138,22 @@ func (a *DeploymentAdapter) ApplyPoolTemplate(ud *alpha1.UnitedDeployment, poolN

attachNodeAffinityAndTolerations(&set.Spec.Template.Spec, poolConfig)

if poolConfig.Patches == nil {
// If No Patches, Must Set patches annotation
set.Annotations[alpha1.AnnotationPatchesKey]=""
if !PoolHasPatch(poolConfig, set) {
klog.Infof("Deployment[%s/%s-] has no patches, do not need strategicmerge", set.Namespace,
set.GenerateName)
return nil
}

patched := &appsv1.Deployment{}
if err := StrategicMergeByPatches(set.Kind,set,poolConfig.Patches,patched); err != nil {
if err := CreateNewPatchedObject(poolConfig.Patch, set, patched); err != nil {
klog.Errorf("Deployment[%s/%s-] strategic merge by patch %s error %v", set.Namespace,
set.GenerateName, string(poolConfig.Patches.Raw), err)
set.GenerateName, string(poolConfig.Patch.Raw), err)
return err
}
klog.Infof("Deployment [%s/%s-] has patches configure successfully:%v", set.Namespace,
set.GenerateName, string(poolConfig.Patches.Raw))
patched.DeepCopyInto(set)

*set = *patched

// Must Set Annotations, and judge whether is nil
if set.Annotations == nil {
set.Annotations = map[string]string{}
}

set.Annotations[alpha1.AnnotationPatchesKey]=string(poolConfig.Patches.Raw)
klog.Infof("Deployment [%s/%s-] has patches configure successfully:%v", set.Namespace,
set.GenerateName, string(poolConfig.Patch.Raw))
return nil
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020 The OpenYurt Authors.
Copyright 2021 The OpenYurt Authors.
Copyright 2019 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -149,6 +149,21 @@ func (a *StatefulSetAdapter) ApplyPoolTemplate(ud *alpha1.UnitedDeployment, pool

attachNodeAffinityAndTolerations(&set.Spec.Template.Spec, poolConfig)

if !PoolHasPatch(poolConfig, set) {
klog.Infof("StatefulSet[%s/%s-] has no patches, do not need strategicmerge", set.Namespace,
set.GenerateName)
return nil
}

patched := &appsv1.StatefulSet{}
if err := CreateNewPatchedObject(poolConfig.Patch, set, patched); err != nil {
klog.Errorf("StatefulSet[%s/%s-] strategic merge by patch %s error %v", set.Namespace,
set.GenerateName, string(poolConfig.Patch.Raw), err)
return err
}
patched.DeepCopyInto(set)
klog.Infof("Statefulset [%s/%s-] has patches configure successfully:%v", set.Namespace,
set.GenerateName, string(poolConfig.Patch.Raw))
return nil
}

Expand Down
Loading

0 comments on commit fedb13b

Please sign in to comment.