174 lines
5.8 KiB
Go
174 lines
5.8 KiB
Go
package overridemanager
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
|
"github.com/karmada-io/karmada/pkg/util"
|
|
"github.com/karmada-io/karmada/pkg/util/imageparser"
|
|
)
|
|
|
|
const (
|
|
pathSplit = "/"
|
|
imageString = "image"
|
|
)
|
|
|
|
// buildPatches parse JSON patches from resource object by imageOverriders
|
|
func buildPatches(rawObj *unstructured.Unstructured, imageOverrider *policyv1alpha1.ImageOverrider) ([]overrideOption, error) {
|
|
if imageOverrider.Predicate == nil {
|
|
return buildPatchesWithEmptyPredicate(rawObj, imageOverrider)
|
|
}
|
|
|
|
return buildPatchesWithPredicate(rawObj, imageOverrider)
|
|
}
|
|
|
|
func buildPatchesWithEmptyPredicate(rawObj *unstructured.Unstructured, imageOverrider *policyv1alpha1.ImageOverrider) ([]overrideOption, error) {
|
|
switch rawObj.GetKind() {
|
|
case util.PodKind:
|
|
return buildPatchesWithPath("spec/containers", rawObj, imageOverrider)
|
|
case util.ReplicaSetKind:
|
|
fallthrough
|
|
case util.DeploymentKind:
|
|
fallthrough
|
|
case util.StatefulSetKind:
|
|
return buildPatchesWithPath("spec/template/spec/containers", rawObj, imageOverrider)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func buildPatchesWithPath(specContainersPath string, rawObj *unstructured.Unstructured, imageOverrider *policyv1alpha1.ImageOverrider) ([]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 retrieves path(%s) from rawObj, error: %v", specContainersPath, err)
|
|
}
|
|
if !ok || len(containers) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
for index := range containers {
|
|
imagePath := fmt.Sprintf("/%s/%d/image", specContainersPath, index)
|
|
imageValue := containers[index].(map[string]interface{})[imageString].(string)
|
|
patch, err := acquireOverrideOption(imagePath, imageValue, imageOverrider)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
patches = append(patches, patch)
|
|
}
|
|
|
|
return patches, nil
|
|
}
|
|
|
|
func buildPatchesWithPredicate(rawObj *unstructured.Unstructured, imageOverrider *policyv1alpha1.ImageOverrider) ([]overrideOption, error) {
|
|
patches := make([]overrideOption, 0)
|
|
|
|
imageValue, err := obtainImageValue(rawObj, imageOverrider.Predicate.Path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to obtain imageValue with predicate path(%s), error: %v", imageOverrider.Predicate.Path, err)
|
|
}
|
|
|
|
patch, err := acquireOverrideOption(imageOverrider.Predicate.Path, imageValue, imageOverrider)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
patches = append(patches, patch)
|
|
return patches, nil
|
|
}
|
|
|
|
func obtainImageValue(rawObj *unstructured.Unstructured, predicatePath string) (string, error) {
|
|
pathSegments := strings.Split(strings.Trim(predicatePath, pathSplit), pathSplit)
|
|
imageValue := ""
|
|
currentObj := rawObj.Object
|
|
ok := false
|
|
for index := 0; index < len(pathSegments)-1; index++ {
|
|
switch currentObj[pathSegments[index]].(type) {
|
|
case map[string]interface{}:
|
|
currentObj = currentObj[pathSegments[index]].(map[string]interface{})
|
|
case []interface{}:
|
|
tmpSlice := currentObj[pathSegments[index]].([]interface{})
|
|
sliceIndex, err := strconv.ParseInt(pathSegments[index+1], 10, 32)
|
|
if err != nil {
|
|
return "", fmt.Errorf("path(%s) of rawObj's is not number", pathSegments[index+1])
|
|
}
|
|
currentObj = tmpSlice[sliceIndex].(map[string]interface{})
|
|
index++
|
|
default:
|
|
return "", fmt.Errorf("path(%s) of rawObj's type is not map[string]interface{} and []interface{}", pathSegments[index])
|
|
}
|
|
}
|
|
|
|
imageValue, ok = currentObj[pathSegments[len(pathSegments)-1]].(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("failed to convert path(%s) to string", pathSegments[len(pathSegments)-1])
|
|
}
|
|
|
|
return imageValue, nil
|
|
}
|
|
|
|
func acquireOverrideOption(imagePath, curImage string, imageOverrider *policyv1alpha1.ImageOverrider) (overrideOption, error) {
|
|
if !strings.HasPrefix(imagePath, pathSplit) {
|
|
return overrideOption{}, fmt.Errorf("imagePath should be start with / character")
|
|
}
|
|
|
|
newImage, err := overrideImage(curImage, imageOverrider)
|
|
if err != nil {
|
|
return overrideOption{}, err
|
|
}
|
|
|
|
return overrideOption{
|
|
Op: string(policyv1alpha1.OverriderOpReplace),
|
|
Path: imagePath,
|
|
Value: newImage,
|
|
}, nil
|
|
}
|
|
|
|
func overrideImage(curImage string, imageOverrider *policyv1alpha1.ImageOverrider) (string, error) {
|
|
imageComponent, err := imageparser.Parse(curImage)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to parse image value(%s), error: %v", curImage, err)
|
|
}
|
|
|
|
switch imageOverrider.Component {
|
|
case policyv1alpha1.Registry:
|
|
switch imageOverrider.Operator {
|
|
case policyv1alpha1.OverriderOpAdd:
|
|
imageComponent.SetHostname(imageComponent.Hostname() + imageOverrider.Value)
|
|
case policyv1alpha1.OverriderOpReplace:
|
|
imageComponent.SetHostname(imageOverrider.Value)
|
|
case policyv1alpha1.OverriderOpRemove:
|
|
imageComponent.RemoveHostname()
|
|
}
|
|
return imageComponent.String(), nil
|
|
case policyv1alpha1.Repository:
|
|
switch imageOverrider.Operator {
|
|
case policyv1alpha1.OverriderOpAdd:
|
|
imageComponent.SetRepository(imageComponent.Repository() + imageOverrider.Value)
|
|
case policyv1alpha1.OverriderOpReplace:
|
|
imageComponent.SetRepository(imageOverrider.Value)
|
|
case policyv1alpha1.OverriderOpRemove:
|
|
imageComponent.RemoveRepository()
|
|
}
|
|
return imageComponent.String(), nil
|
|
case policyv1alpha1.Tag:
|
|
switch imageOverrider.Operator {
|
|
case policyv1alpha1.OverriderOpAdd:
|
|
imageComponent.SetTagOrDigest(imageComponent.TagOrDigest() + imageOverrider.Value)
|
|
case policyv1alpha1.OverriderOpReplace:
|
|
imageComponent.SetTagOrDigest(imageOverrider.Value)
|
|
case policyv1alpha1.OverriderOpRemove:
|
|
imageComponent.RemoveTagOrDigest()
|
|
}
|
|
return imageComponent.String(), nil
|
|
}
|
|
|
|
// should never reach to here
|
|
return "", fmt.Errorf("unsupported image component(%s)", imageOverrider.Component)
|
|
}
|