...

Source file src/edge-infra.dev/pkg/edge/api/graph/mapper/helm_release_post_renderer.go

Documentation: edge-infra.dev/pkg/edge/api/graph/mapper

     1  package mapper
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"slices"
     7  
     8  	"github.com/fluxcd/pkg/apis/kustomize"
     9  	corev1 "k8s.io/api/core/v1"
    10  	virtv1 "kubevirt.io/api/core/v1"
    11  
    12  	"edge-infra.dev/pkg/edge/api/graph/model"
    13  	persistence "edge-infra.dev/pkg/edge/apis/persistence/v1alpha1"
    14  	"edge-infra.dev/pkg/edge/capabilities"
    15  	kinformmodel "edge-infra.dev/pkg/f8n/kinform/model"
    16  	v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
    17  	"edge-infra.dev/pkg/sds/lib/jsonpatch"
    18  )
    19  
    20  type HelmReleasePostRenderer struct {
    21  	installationType model.WorkloadInstallationType
    22  	customLabels     []*model.Label
    23  	helmEdgeID       string
    24  }
    25  
    26  func (h *HelmReleasePostRenderer) GetNodeSelectorTerm() corev1.NodeSelectorTerm {
    27  	operator := corev1.NodeSelectorOpIn
    28  	key := "node.ncr.com/class"
    29  
    30  	if h.installationType == model.WorkloadInstallationTypeTouchpoint {
    31  		operator = corev1.NodeSelectorOpNotIn
    32  	}
    33  
    34  	return corev1.NodeSelectorTerm{
    35  		MatchExpressions: []corev1.NodeSelectorRequirement{
    36  			{
    37  				Key:      key,
    38  				Operator: operator,
    39  				Values:   []string{"server"},
    40  			},
    41  		},
    42  	}
    43  }
    44  
    45  func (h *HelmReleasePostRenderer) GetAffinityPatchStr() string {
    46  	affinity := h.nodeAffinity()
    47  	jsonAffinityByteArr, err := json.Marshal(affinity)
    48  	if err != nil {
    49  		fmt.Println(err)
    50  	}
    51  	return string(jsonAffinityByteArr)
    52  }
    53  
    54  func (h *HelmReleasePostRenderer) nodeAffinity() corev1.NodeAffinity {
    55  	if h.installationType == model.WorkloadInstallationTypeAdvanced && h.customLabels != nil {
    56  		return h.CreateCustomLabelsAffinity()
    57  	}
    58  	return h.CreateAffinity()
    59  }
    60  
    61  func (h *HelmReleasePostRenderer) GetNodeSelectorPatchStr() string {
    62  	var jsonNodeSelectorByteArr []byte
    63  	var err error
    64  
    65  	if h.installationType == model.WorkloadInstallationTypeAdvanced && h.customLabels != nil {
    66  		jsonNodeSelectorByteArr, err = json.Marshal(h.createCustomLabelNodeSelectorTerms())
    67  	} else {
    68  		jsonNodeSelectorByteArr, err = json.Marshal([]corev1.NodeSelectorTerm{
    69  			h.GetNodeSelectorTerm(),
    70  		})
    71  	}
    72  
    73  	if err != nil {
    74  		fmt.Println(err)
    75  	}
    76  
    77  	return string(jsonNodeSelectorByteArr)
    78  }
    79  
    80  // TODO(pa250194): Pending Investigation and fixing in Q1 2025
    81  // We will disable drift detection for the Kinds: IENPatch, StatefulSet, Daemonset
    82  // Bug Tickets:
    83  // https://github.com/ncrvoyix-swt-retail/edge-roadmap/issues/12785
    84  // https://github.com/ncrvoyix-swt-retail/edge-roadmap/issues/12747
    85  func disableDriftDetectionForProblemKinds() []kustomize.Patch {
    86  	patches := make([]kustomize.Patch, 0)
    87  	problemKinds := []string{"IENPatch", "StatefulSet", "DaemonSet"}
    88  	for _, problemKind := range problemKinds {
    89  		patch := kustomize.Patch{
    90  			Target: &kustomize.Selector{
    91  				Version: "v1",
    92  				Kind:    problemKind,
    93  			},
    94  			Patch: "- op: add\n  path: /metadata/annotations/helm.toolkit.fluxcd.io~1driftDetection\n  value: disabled",
    95  		}
    96  		patches = append(patches, patch)
    97  	}
    98  	return patches
    99  }
   100  
   101  func (h *HelmReleasePostRenderer) GetKustomizePatches() ([]kustomize.Patch, error) {
   102  	patchKindsByInstallationType := map[model.WorkloadInstallationType][]string{
   103  		model.WorkloadInstallationTypeServerPreferred: {"Deployment", "StatefulSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine"},
   104  		model.WorkloadInstallationTypeAny:             {"Deployment", "StatefulSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine"},
   105  		model.WorkloadInstallationTypeAdvanced:        {"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
   106  		model.WorkloadInstallationTypeTouchpoint:      {"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
   107  		model.WorkloadInstallationTypeServer:          {"Deployment", "StatefulSet", "DaemonSet", "ReplicaSet", "Job", "CronJob", "VirtualMachine", persistence.Kind},
   108  	}
   109  
   110  	patchesKinds := patchKindsByInstallationType[h.installationType]
   111  	jsonAffinityStr := h.GetAffinityPatchStr()
   112  	jsonNodeSelectorStr := h.GetNodeSelectorPatchStr()
   113  
   114  	var patches []kustomize.Patch
   115  	for _, patchKind := range patchesKinds {
   116  		kustomizeSelector := kustomize.Selector{
   117  			Version: "v1",
   118  			Kind:    patchKind,
   119  		}
   120  
   121  		kubePatch := kustomize.Patch{}
   122  		switch patchKind {
   123  		case persistence.Kind:
   124  			kustomizeSelector.Version = persistence.GroupVersion.Version
   125  			kustomizeSelector.Group = persistence.GroupVersion.Group
   126  			kubePatch.Patch = fmt.Sprintf("- op: replace\n  path: /spec/nodeSelectorTerms\n  value: %s", jsonNodeSelectorStr)
   127  			kubePatch.Target = &kustomizeSelector
   128  		case "CronJob":
   129  			kubePatch.Patch = fmt.Sprintf("- op: add\n  path: /spec/jobTemplate/spec/template/spec/affinity\n  value: {\"nodeAffinity\": %s }", jsonAffinityStr)
   130  			kubePatch.Target = &kustomizeSelector
   131  		case "VirtualMachine":
   132  			var err error
   133  			kubePatch, err = h.vmPatch()
   134  			if err != nil {
   135  				return nil, err
   136  			}
   137  		default:
   138  			kubePatch.Patch = fmt.Sprintf("- op: add\n  path: /spec/template/spec/affinity\n  value: {\"nodeAffinity\": %s }", jsonAffinityStr)
   139  			kubePatch.Target = &kustomizeSelector
   140  		}
   141  
   142  		patches = append(patches, kubePatch)
   143  	}
   144  
   145  	return patches, nil
   146  }
   147  
   148  func (h *HelmReleasePostRenderer) vmPatch() (kustomize.Patch, error) {
   149  	kustomizeSelector := kustomize.Selector{
   150  		Version: virtv1.SchemeGroupVersion.Version,
   151  		Group:   virtv1.SchemeGroupVersion.Group,
   152  		Kind:    "VirtualMachine",
   153  	}
   154  	ops := []jsonpatch.Operation{}
   155  	affinity := h.nodeAffinity()
   156  	ops = append(ops, jsonpatch.Operation{
   157  		Op:    jsonpatch.Add,
   158  		Path:  "/spec/template/spec/affinity",
   159  		Value: corev1.Affinity{NodeAffinity: &affinity},
   160  	})
   161  	ops = append(ops, jsonpatch.Operation{
   162  		Op:    jsonpatch.Add,
   163  		Path:  fmt.Sprintf("/spec/template/metadata/annotations/%s", kinformmodel.HelmEdgeIDAnnotation),
   164  		Value: h.helmEdgeID,
   165  	})
   166  	patchStr, err := jsonpatch.NewPatch(ops...).String()
   167  	if err != nil {
   168  		return kustomize.Patch{}, err
   169  	}
   170  	return kustomize.Patch{
   171  		Patch:  patchStr,
   172  		Target: &kustomizeSelector,
   173  	}, nil
   174  }
   175  
   176  func (h *HelmReleasePostRenderer) CreateAffinity() corev1.NodeAffinity {
   177  	affinity := corev1.NodeAffinity{}
   178  
   179  	if h.installationType == model.WorkloadInstallationTypeAny || h.installationType == model.WorkloadInstallationTypeServerPreferred {
   180  		affinity.PreferredDuringSchedulingIgnoredDuringExecution = []corev1.PreferredSchedulingTerm{
   181  			{
   182  				Weight:     1,
   183  				Preference: h.GetNodeSelectorTerm(),
   184  			},
   185  		}
   186  	} else {
   187  		affinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{
   188  			NodeSelectorTerms: []corev1.NodeSelectorTerm{
   189  				h.GetNodeSelectorTerm(),
   190  			},
   191  		}
   192  	}
   193  
   194  	return affinity
   195  }
   196  
   197  func (h *HelmReleasePostRenderer) CreateCustomLabelsAffinity() corev1.NodeAffinity {
   198  	affinity := corev1.NodeAffinity{}
   199  
   200  	nodeSelectorTerms := h.createCustomLabelNodeSelectorTerms()
   201  
   202  	affinity.RequiredDuringSchedulingIgnoredDuringExecution = &corev1.NodeSelector{
   203  		NodeSelectorTerms: nodeSelectorTerms,
   204  	}
   205  
   206  	return affinity
   207  }
   208  
   209  func (h *HelmReleasePostRenderer) createCustomLabelNodeSelectorTerms() []corev1.NodeSelectorTerm {
   210  	nodeSelectorTerm := corev1.NodeSelectorTerm{}
   211  	if h.customLabels != nil {
   212  		for _, label := range h.customLabels {
   213  			key := label.Key
   214  			if !slices.Contains(capabilities.EdgeAutomatedCapabilityLabelTypes, label.Type) {
   215  				key = fmt.Sprintf(v1ien.CustomNodeLabel, label.Key)
   216  			}
   217  			matchExpression := corev1.NodeSelectorRequirement{
   218  				Key:      key,
   219  				Operator: corev1.NodeSelectorOpExists,
   220  			}
   221  			nodeSelectorTerm.MatchExpressions = append(nodeSelectorTerm.MatchExpressions, matchExpression)
   222  		}
   223  	}
   224  	return []corev1.NodeSelectorTerm{
   225  		nodeSelectorTerm,
   226  	}
   227  }
   228  
   229  func (h *HelmReleasePostRenderer) CreatePostrender() (interface{}, error) {
   230  	return h.GetKustomizePatches()
   231  }
   232  

View as plain text