diff --git a/pkg/helm/frrk8s.go b/pkg/helm/frrk8s.go index b51bf34a5..a57697221 100644 --- a/pkg/helm/frrk8s.go +++ b/pkg/helm/frrk8s.go @@ -14,10 +14,13 @@ import ( "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli/values" "helm.sh/helm/v3/pkg/getter" + appsv1 "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" ) const ( + frrk8sDaemonsetName = "frr-k8s" frrk8sWebhookDeploymentName = "frr-k8s-webhook-server" frrk8sWebhookServiceName = "frr-k8s-webhook-service" frrk8sWebhookSecretName = "frr-k8s-webhook-server-cert" @@ -84,6 +87,13 @@ func (h *FRRK8SChart) Objects(envConfig params.EnvConfig, crdConfig *metallbv1be obj.SetNamespace(envConfig.Namespace) } + if isFRRK8SDaemonset(obj) && crdConfig.Spec.SpeakerConfig != nil { + obj, err = overrideFRRK8SDaemonParameters(crdConfig, obj) + if err != nil { + return nil, err + } + } + if isFRRK8SWebhookSecret(obj) && envConfig.IsOpenshift { // We want to skip creating the secret on OpenShift since it is created and managed // via the serving-cert-secret-name annotation on the service. @@ -175,6 +185,10 @@ func frrk8sValues(envConfig params.EnvConfig, crdConfig *metallbv1beta1.MetalLB) if crdConfig.Spec.SpeakerTolerations != nil { frrk8sValueMap["tolerations"] = crdConfig.Spec.SpeakerTolerations } + if crdConfig.Spec.SpeakerConfig != nil { + frrk8sValueMap["priorityClassName"] = crdConfig.Spec.SpeakerConfig.PriorityClassName + frrk8sValueMap["runtimeClassName"] = crdConfig.Spec.SpeakerConfig.RuntimeClassName + } if crdConfig.Spec.FRRK8SConfig != nil { var err error @@ -235,6 +249,32 @@ func prometheusValues(envConfig params.EnvConfig) map[string]interface{} { } } +func overrideFRRK8SDaemonParameters(crdConfig *metallbv1beta1.MetalLB, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + config := crdConfig.Spec.SpeakerConfig + var daemon *appsv1.DaemonSet + err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &daemon) + if err != nil { + return nil, err + } + if config.Affinity != nil { + daemon.Spec.Template.Spec.Affinity = config.Affinity + } + for j, container := range daemon.Spec.Template.Spec.Containers { + if container.Name == "controller" && config.Resources != nil { + daemon.Spec.Template.Spec.Containers[j].Resources = *config.Resources + } + } + objMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(daemon) + if err != nil { + return nil, err + } + return &unstructured.Unstructured{Object: objMap}, nil +} + +func isFRRK8SDaemonset(obj *unstructured.Unstructured) bool { + return obj.GetKind() == "DaemonSet" && obj.GetName() == frrk8sDaemonsetName +} + func isFRRK8SWebhookDeployment(obj *unstructured.Unstructured) bool { return obj.GetKind() == "Deployment" && obj.GetName() == frrk8sWebhookDeploymentName } diff --git a/pkg/helm/frrk8s_test.go b/pkg/helm/frrk8s_test.go index 4f2ff702d..4e97a0775 100644 --- a/pkg/helm/frrk8s_test.go +++ b/pkg/helm/frrk8s_test.go @@ -7,6 +7,7 @@ import ( . "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -48,6 +49,15 @@ func TestParseFRRK8SChartWithCustomValues(t *testing.T) { LogLevel: metallbv1beta1.LogLevelDebug, SpeakerNodeSelector: nodeSelector, SpeakerTolerations: tolerations, + SpeakerConfig: &metallbv1beta1.Config{ + PriorityClassName: "high-priority", + RuntimeClassName: "cri-o", + Resources: &corev1.ResourceRequirements{Limits: map[corev1.ResourceName]resource.Quantity{corev1.ResourceCPU: *resource.NewMilliQuantity(200, resource.DecimalSI)}}, + Affinity: &corev1.Affinity{PodAffinity: &corev1.PodAffinity{RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "metallb", + }}}}}}, + }, FRRK8SConfig: &metallbv1beta1.FRRK8SConfig{ AlwaysBlock: []string{"192.168.1.0/24", "2001:db8::/32", @@ -67,6 +77,9 @@ func TestParseFRRK8SChartWithCustomValues(t *testing.T) { err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &frrk8s) g.Expect(err).To(BeNil()) g.Expect(frrk8s.GetName()).To(Equal(frrk8sDaemonSetName)) + g.Expect(frrk8s.Spec.Template.Spec.PriorityClassName).To(Equal("high-priority")) + g.Expect(*frrk8s.Spec.Template.Spec.RuntimeClassName).To(Equal("cri-o")) + g.Expect(frrk8s.Spec.Template.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector.MatchLabels["app"]).To(Equal("metallb")) var frrk8sControllerFound bool for _, container := range frrk8s.Spec.Template.Spec.Containers { if container.Name == "controller" { @@ -84,6 +97,8 @@ func TestParseFRRK8SChartWithCustomValues(t *testing.T) { } g.Expect(logLevelChanged).To(BeTrue()) g.Expect(alwaysBlockChanged).To(BeTrue()) + g.Expect(container.Resources).NotTo(BeNil()) + g.Expect(container.Resources.Limits.Cpu().MilliValue()).To(Equal(int64(200))) } } g.Expect(frrk8sControllerFound).To(BeTrue())