summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorming.tang <ming.tang@daocloud.io>2024-01-18 16:22:16 +0800
committerming.tang <ming.tang@daocloud.io>2024-01-19 10:28:45 +0800
commit7d6ea14a5bd4399a43c2e9ffca46c29c72b0e083 (patch)
treea93ae1dcc2e66279e123977e625620701dc8b258
parentMerge pull request #5280 from wbc6080/fix-namespace-in-mapper (diff)
downloadkubeedge-7d6ea14a5bd4399a43c2e9ffca46c29c72b0e083.tar.gz
EdgeApplication differential configuration added support for env and resources fields.
Signed-off-by: ming.tang <ming.tang@daocloud.io>
-rw-r--r--cloud/pkg/controllermanager/controllermanager.go2
-rw-r--r--cloud/pkg/controllermanager/edgeapplication/overridemanager/envoverrider.go229
-rw-r--r--cloud/pkg/controllermanager/edgeapplication/overridemanager/resourcesoverrider.go103
3 files changed, 334 insertions, 0 deletions
diff --git a/cloud/pkg/controllermanager/controllermanager.go b/cloud/pkg/controllermanager/controllermanager.go
index 52781e843..790e55a47 100644
--- a/cloud/pkg/controllermanager/controllermanager.go
+++ b/cloud/pkg/controllermanager/controllermanager.go
@@ -68,6 +68,8 @@ func setupControllers(ctx context.Context, mgr manager.Manager) error {
&overridemanager.NodeSelectorOverrider{},
&overridemanager.CommandOverrider{},
&overridemanager.ArgsOverrider{},
+ &overridemanager.EnvOverrider{},
+ &overridemanager.ResourcesOverrider{},
},
},
}
diff --git a/cloud/pkg/controllermanager/edgeapplication/overridemanager/envoverrider.go b/cloud/pkg/controllermanager/edgeapplication/overridemanager/envoverrider.go
new file mode 100644
index 000000000..c91476457
--- /dev/null
+++ b/cloud/pkg/controllermanager/edgeapplication/overridemanager/envoverrider.go
@@ -0,0 +1,229 @@
+/*
+Copyright 2024 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package overridemanager
+
+import (
+ "fmt"
+ "strings"
+
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/klog/v2"
+
+ "github.com/kubeedge/kubeedge/pkg/apis/apps/v1alpha1"
+)
+
+type EnvOverrider struct{}
+
+func (o *EnvOverrider) ApplyOverrides(rawObj *unstructured.Unstructured, overriders OverriderInfo) error {
+ envOverriders := overriders.Overriders.EnvOverrides
+ for index := range envOverriders {
+ patches, err := buildEnvPatches(rawObj, &envOverriders[index])
+ if err != nil {
+ return err
+ }
+
+ klog.V(4).Infof("Parsed JSON patches by EnvOverrider(%+v): %+v", envOverriders[index], patches)
+ if err = applyJSONPatch(rawObj, patches); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// buildEnvPatches build JSON patches for the resource object according to EnvOverrider declaration.
+func buildEnvPatches(rawObj *unstructured.Unstructured, envOverrider *v1alpha1.EnvOverrider) ([]overrideOption, error) {
+ switch rawObj.GetKind() {
+ case PodKind:
+ return buildEnvPatchesWithPath("spec/containers", rawObj, envOverrider)
+ case ReplicaSetKind:
+ fallthrough
+ case DeploymentKind:
+ fallthrough
+ case DaemonSetKind:
+ fallthrough
+ case JobKind:
+ fallthrough
+ case StatefulSetKind:
+ return buildEnvPatchesWithPath("spec/template/spec/containers", rawObj, envOverrider)
+ }
+ return nil, nil
+}
+
+func buildEnvPatchesWithPath(specContainersPath string, rawObj *unstructured.Unstructured, envOverrider *v1alpha1.EnvOverrider) ([]overrideOption, error) {
+ patches := make([]overrideOption, 0)
+ containers, ok, err := unstructured.NestedSlice(rawObj.Object, strings.Split(specContainersPath, pathSplit)...)
+ if err != nil {
+ return nil, fmt.Errorf("failed to retrieve path(%s) from rawObj, error: %v", specContainersPath, err)
+ }
+ if !ok || len(containers) == 0 {
+ return nil, nil
+ }
+ klog.V(4).Infof("buildEnvPatchesWithPath containers info (%+v)", containers)
+ for index, container := range containers {
+ if container.(map[string]interface{})["name"] == envOverrider.ContainerName {
+ envPath := fmt.Sprintf("/%s/%d/env", specContainersPath, index)
+ envValue := make([]corev1.EnvVar, 0)
+ var patch overrideOption
+ // if env is nil, to add new [env]
+ if container.(map[string]interface{})["env"] == nil {
+ patch, _ = acquireAddEnvOverrideOption(envPath, envOverrider)
+ } else {
+ env, ok := container.(map[string]interface{})["env"].([]interface{})
+ if !ok {
+ return nil, fmt.Errorf("failed to retrieve env from container")
+ }
+ for _, val := range env {
+ envVar, err := convertToEnvVar(val)
+ if err != nil {
+ return nil, err
+ }
+ envValue = append(envValue, *envVar)
+ }
+ patch, _ = acquireReplaceEnvOverrideOption(envPath, envValue, envOverrider)
+ }
+
+ klog.V(4).Infof("[buildEnvPatchesWithPath] containers patch info (%+v)", patch)
+ patches = append(patches, patch)
+ }
+ }
+ return patches, nil
+}
+
+func acquireAddEnvOverrideOption(envPath string, envOverrider *v1alpha1.EnvOverrider) (overrideOption, error) {
+ if !strings.HasPrefix(envPath, pathSplit) {
+ return overrideOption{}, fmt.Errorf("internal error: [acquireAddEnvOverrideOption] envPath should start with / character")
+ }
+ newEnv, err := overrideEnv([]corev1.EnvVar{}, envOverrider)
+ if err != nil {
+ return overrideOption{}, err
+ }
+ return overrideOption{
+ Op: string(v1alpha1.OverriderOpAdd),
+ Path: envPath,
+ Value: newEnv,
+ }, nil
+}
+
+func acquireReplaceEnvOverrideOption(envPath string, envValue []corev1.EnvVar, envOverrider *v1alpha1.EnvOverrider) (overrideOption, error) {
+ if !strings.HasPrefix(envPath, pathSplit) {
+ return overrideOption{}, fmt.Errorf("internal error: [acquireReplaceEnvOverrideOption] envPath should start with / character")
+ }
+
+ newEnv, err := overrideEnv(envValue, envOverrider)
+ if err != nil {
+ return overrideOption{}, err
+ }
+
+ return overrideOption{
+ Op: string(v1alpha1.OverriderOpReplace),
+ Path: envPath,
+ Value: newEnv,
+ }, nil
+}
+
+func overrideEnv(curEnv []corev1.EnvVar, envOverrider *v1alpha1.EnvOverrider) ([]corev1.EnvVar, error) {
+ var newEnv []corev1.EnvVar
+ switch envOverrider.Operator {
+ case v1alpha1.OverriderOpAdd:
+ newEnv = append(curEnv, envOverrider.Value...)
+ case v1alpha1.OverriderOpRemove:
+ newEnv = envRemove(curEnv, envOverrider.Value)
+ case v1alpha1.OverriderOpReplace:
+ newEnv = replaceEnv(curEnv, envOverrider.Value)
+ default:
+ newEnv = curEnv
+ klog.V(4).Infof("[overrideEnv], op: %s , op not supported, ignored.", envOverrider.Operator)
+ }
+ return newEnv, nil
+}
+
+func replaceEnv(curEnv []corev1.EnvVar, replaceValues []corev1.EnvVar) []corev1.EnvVar {
+ newEnv := make([]corev1.EnvVar, 0, len(curEnv))
+ currentMap := make(map[string]corev1.EnvVar)
+
+ // Populate current map with existing environment variables
+ for _, envVar := range curEnv {
+ currentMap[envVar.Name] = envVar
+ }
+
+ // Replace or add new environment variables
+ for _, replaceVar := range replaceValues {
+ currentMap[replaceVar.Name] = replaceVar
+ }
+
+ // Convert map back to slice
+ for _, envVar := range currentMap {
+ newEnv = append(newEnv, envVar)
+ }
+
+ return newEnv
+}
+
+func envRemove(curEnv []corev1.EnvVar, removeValues []corev1.EnvVar) []corev1.EnvVar {
+ newEnv := make([]corev1.EnvVar, 0, len(curEnv))
+ currentSet := sets.NewString()
+ for _, val := range removeValues {
+ currentSet.Insert(val.Name)
+ }
+ for _, envVar := range curEnv {
+ if !currentSet.Has(envVar.Name) {
+ newEnv = append(newEnv, envVar)
+ }
+ }
+ return newEnv
+}
+
+func convertToEnvVar(value interface{}) (*corev1.EnvVar, error) {
+ envMap, ok := value.(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("failed to convert env value to map[string]interface{}")
+ }
+ name, ok := envMap["name"].(string)
+ if !ok {
+ return nil, fmt.Errorf("failed to convert env value name to string")
+ }
+ var env corev1.EnvVar
+ env.Name = name
+ if value, ok := envMap["value"].(string); ok {
+ env.Value = value
+ }
+ if value, ok := envMap["valueFrom"].(map[string]interface{}); ok {
+ valueFrom, err := convertToEnvVarSource(value)
+ if err != nil {
+ return nil, err
+ }
+ env.ValueFrom = &valueFrom
+ }
+ return &env, nil
+}
+
+func convertToEnvVarSource(value map[string]interface{}) (corev1.EnvVarSource, error) {
+ var envVarSource corev1.EnvVarSource
+ fieldRef, ok := value["fieldRef"].(map[string]interface{})
+ if !ok {
+ return envVarSource, fmt.Errorf("failed to convert env valueFrom fieldRef to map[string]interface{}")
+ }
+ fieldPath, ok := fieldRef["fieldPath"].(string)
+ if !ok {
+ return envVarSource, fmt.Errorf("failed to convert env valueFrom fieldPath to string")
+ }
+ envVarSource.FieldRef = &corev1.ObjectFieldSelector{FieldPath: fieldPath}
+ return envVarSource, nil
+}
diff --git a/cloud/pkg/controllermanager/edgeapplication/overridemanager/resourcesoverrider.go b/cloud/pkg/controllermanager/edgeapplication/overridemanager/resourcesoverrider.go
new file mode 100644
index 000000000..05c1562cf
--- /dev/null
+++ b/cloud/pkg/controllermanager/edgeapplication/overridemanager/resourcesoverrider.go
@@ -0,0 +1,103 @@
+/*
+Copyright 2024 The KubeEdge Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package overridemanager
+
+import (
+ "fmt"
+ "strings"
+
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/klog/v2"
+
+ "github.com/kubeedge/kubeedge/pkg/apis/apps/v1alpha1"
+)
+
+type ResourcesOverrider struct{}
+
+func (o *ResourcesOverrider) ApplyOverrides(rawObj *unstructured.Unstructured, overriders OverriderInfo) error {
+ resourcesOverriders := overriders.Overriders.ResourcesOverrides
+ for index := range resourcesOverriders {
+ patches, err := buildResourcesPatches(rawObj, &resourcesOverriders[index])
+ if err != nil {
+ return err
+ }
+
+ klog.V(4).Infof("Parsed JSON patches by ResourcesOverrider(%+v): %+v", resourcesOverriders[index], patches)
+ if err = applyJSONPatch(rawObj, patches); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// buildResourcesPatches build JSON patches for the resource object according to override declaration.
+func buildResourcesPatches(rawObj *unstructured.Unstructured, resourcesOverrider *v1alpha1.ResourcesOverrider) ([]overrideOption, error) {
+ switch rawObj.GetKind() {
+ case PodKind:
+ return buildResourcesPatchesWithPath("spec/containers", rawObj, resourcesOverrider)
+ case ReplicaSetKind:
+ fallthrough
+ case DeploymentKind:
+ fallthrough
+ case DaemonSetKind:
+ fallthrough
+ case JobKind:
+ fallthrough
+ case StatefulSetKind:
+ return buildResourcesPatchesWithPath("spec/template/spec/containers", rawObj, resourcesOverrider)
+ }
+ return nil, nil
+}
+
+func buildResourcesPatchesWithPath(specContainersPath string, rawObj *unstructured.Unstructured, resourcesOverrider *v1alpha1.ResourcesOverrider) ([]overrideOption, error) {
+ patches := make([]overrideOption, 0)
+ containers, ok, err := unstructured.NestedSlice(rawObj.Object, strings.Split(specContainersPath, pathSplit)...)
+ if err != nil {
+ return nil, fmt.Errorf("failed to retrieve path(%s) from rawObj, error: %v", specContainersPath, err)
+ }
+ if !ok || len(containers) == 0 {
+ return nil, nil
+ }
+ klog.V(4).Infof("buildResourcesPatchesWithPath containers info (%+v)", containers)
+ for index, container := range containers {
+ if container.(map[string]interface{})["name"] == resourcesOverrider.ContainerName {
+ resourcesPath := fmt.Sprintf("/%s/%d/resources", specContainersPath, index)
+ resourcesValue := resourcesOverrider.Value
+ var patch overrideOption
+ // Overridden with the current value regardless of whether the template value is empty
+ patch, _ = acquireOverride(resourcesPath, resourcesValue)
+
+ klog.V(4).Infof("[buildResourcesPatchesWithPath] containers patch info (%+v)", patch)
+ patches = append(patches, patch)
+ }
+ }
+ return patches, nil
+}
+
+// acquireOverrideOption for adding resources override.
+func acquireOverride(resourcesPath string, resourcesValue corev1.ResourceRequirements) (overrideOption, error) {
+ if !strings.HasPrefix(resourcesPath, pathSplit) {
+ return overrideOption{}, fmt.Errorf("internal error: [acquireOverride] resourcesPath should start with / character")
+ }
+ return overrideOption{
+ Op: string(v1alpha1.OverriderOpReplace),
+ Path: resourcesPath,
+ Value: resourcesValue,
+ }, nil
+}