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

Commit

Permalink
Merge pull request #12 from kadisi/out_ud_patch
Browse files Browse the repository at this point in the history
[Feature] UnitedDeployment support patch for pool
  • Loading branch information
Fei-Guo authored Mar 24, 2021
2 parents 5bd431a + fedb13b commit 8207677
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ spec:
distributed across multiple groups of nodes. A pool's nodeSelectorTerm
is not allowed to be updated.
type: object
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
under this pool.
Expand Down
10 changes: 9 additions & 1 deletion pkg/yurtappmanager/apis/apps/v1alpha1/uniteddeployment_types.go
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 All @@ -25,6 +25,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

type TemplateType string
Expand Down Expand Up @@ -124,6 +125,13 @@ type Pool struct {
// Indicates the number of the pod to be created under this pool.
// +required
Replicas *int32 `json:"replicas,omitempty"`

// 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
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 All @@ -20,7 +20,7 @@ change some const value

package v1alpha1

// UnitedDeployment related labels
// UnitedDeployment related labels and annotations
const (
// ControllerRevisionHashLabelKey is used to record the controller revision of current resource.
ControllerRevisionHashLabelKey = "apps.openyurt.io/controller-revision-hash"
Expand All @@ -30,6 +30,9 @@ const (

// SpecifiedDeleteKey indicates this object should be deleted, and the value could be the deletion option.
SpecifiedDeleteKey = "apps.openyurt.io/specified-delete"

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

// NodePool related labels and annotations
Expand All @@ -43,12 +46,6 @@ const (

AnnotationPrevAttrs = "nodepool.openyurt.io/previous-attributes"

// Note !!!!
// Can not change this const name , because go build -ldflags will change this values
// @kadisi
// LabelEdgeWorker indicates if the node is an edge node
LabelEdgeWorker = "alibabacloud.com/is-edge-worker"

// DefaultCloudNodePoolName defines the name of the default cloud nodepool
DefaultCloudNodePoolName = "default-nodepool"

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 @@ -18,12 +18,16 @@ limitations under the License.
package adapter

import (
"encoding/json"
"fmt"

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 @@ -34,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 @@ -68,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 @@ -89,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 @@ -103,3 +107,56 @@ func getCurrentPartition(pods []*corev1.Pod, revision string) *int32 {

return &partition
}

func StrategicMergeByPatches(oldobj interface{}, patch *runtime.RawExtension, newPatched interface{}) error {
patchMap := make(map[string]interface{})
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("ToUnstructured error %v", err)
return err
}

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

if err := runtime.DefaultUnstructuredConverter.FromUnstructured(patchedObjMap, newPatched); err != nil {
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 @@ -19,13 +19,14 @@ package adapter
import (
"fmt"

"k8s.io/klog"

alpha1 "github.com/openyurtio/yurt-app-manager/pkg/yurtappmanager/apis/apps/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

alpha1 "github.com/openyurtio/yurt-app-manager/pkg/yurtappmanager/apis/apps/v1alpha1"
)

type DeploymentAdapter struct {
Expand Down Expand Up @@ -78,9 +79,9 @@ func (a *DeploymentAdapter) ApplyPoolTemplate(ud *alpha1.UnitedDeployment, poolN
set := obj.(*appsv1.Deployment)

var poolConfig *alpha1.Pool
for _, pool := range ud.Spec.Topology.Pools {
for i, pool := range ud.Spec.Topology.Pools {
if pool.Name == poolName {
poolConfig = &pool
poolConfig = &(ud.Spec.Topology.Pools[i])
break
}
}
Expand Down Expand Up @@ -136,6 +137,23 @@ func (a *DeploymentAdapter) ApplyPoolTemplate(ud *alpha1.UnitedDeployment, poolN
set.Spec.ProgressDeadlineSeconds = ud.Spec.WorkloadTemplate.DeploymentTemplate.Spec.ProgressDeadlineSeconds

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

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 := 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.Patch.Raw), err)
return err
}
patched.DeepCopyInto(set)

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

Expand Down
Loading

0 comments on commit 8207677

Please sign in to comment.