diff --git a/pkg/metallb/metallb.go b/pkg/metallb/metallb.go index ead59f351..a0d7b0aee 100644 --- a/pkg/metallb/metallb.go +++ b/pkg/metallb/metallb.go @@ -8,6 +8,7 @@ import ( "github.com/openshift-kni/eco-goinfra/pkg/clients" "github.com/openshift-kni/eco-goinfra/pkg/metallb/mlbtypes" "github.com/openshift-kni/eco-goinfra/pkg/msg" + corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -326,6 +327,195 @@ func (builder *Builder) WithSpeakerNodeSelector(label map[string]string) *Builde return builder } +// WithControllerPriorityClassName adds a priority class name tp the metallb controller config. +func (builder *Builder) WithControllerPriorityClassName(priorityClassName string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + // if len(priorityClassName) == 0 { + // glog.V(100).Infof(" Controller Config parameter priorityClassName cannot be an empty field") + // + // return builder + //} + + builder.Definition.Spec.ControllerConfig.PriorityClassName = priorityClassName + + return builder +} + +// WithControllerRuntimeClassName adds a runtime class name tp the metallb controller config. +func (builder *Builder) WithControllerRuntimeClassName(runtimeClassName string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + if len(runtimeClassName) == 0 { + glog.V(100).Infof("Controller Config parameter runtimeClassName cannot be an empty field") + + return builder + } + + builder.Definition.Spec.ControllerConfig.RuntimeClassName = runtimeClassName + + return builder +} + +// WithControllerAnnotationMapString adds a runtime class name tp the metallb controller config. +func (builder *Builder) WithControllerAnnotationMapString(annotationMapString map[string]string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + if len(annotationMapString) == 0 { + glog.V(100).Infof(" Controller Config parameter annotationMapString cannot be an empty field") + + return builder + } + + builder.Definition.Spec.ControllerConfig.Annotations = annotationMapString + + return builder +} + +// WithControllerPodAffinityLabel adds a runtime class name tp the metallb controller config. +func (builder *Builder) WithControllerPodAffinityLabel(controllerPodAffinityLabel string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + if len(controllerPodAffinityLabel) == 0 { + glog.V(100).Infof("Controller Config parameter runtimeClassName cannot be an empty field") + + return builder + } + + builder.Definition.Spec = mlbtypes.MetalLBSpec{ + ControllerConfig: &mlbtypes.Config{ + Affinity: &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{ + {LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"component": controllerPodAffinityLabel}, + }, + TopologyKey: "kubernetes.io/hostname", + }, + }, + }, + }, + }, + } + + return builder +} + +// WithControllerTolerations adds a toleration configuration inside the speaker on . +func (builder *Builder) WithControllerTolerations(tolerationKey, tolerationOperator, tolerationEffect string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + operator := corev1.TolerationOperator(tolerationOperator) + effect := corev1.TaintEffect(tolerationEffect) + toleration := corev1.Toleration{Key: tolerationKey, Operator: operator, Effect: effect} + + glog.V(100).Infof("Updating pod %s with toleration %v", builder.Definition.Name, toleration) + + if builder.errorMsg != "" { + return builder + } + + builder.Definition.Spec.ControllerTolerations = append(builder.Definition.Spec.ControllerTolerations, toleration) + + return builder +} + +// WithSpeakerPriorityClassName adds a priority class name tp the metallb controller config. +func (builder *Builder) WithSpeakerPriorityClassName(priorityClassName string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + if len(priorityClassName) == 0 { + glog.V(100).Infof(" Controller Config parameter priorityClassName cannot be an empty field") + + return builder + } + + builder.Definition.Spec.SpeakerConfig.PriorityClassName = priorityClassName + + return builder +} + +// WithSpeakerRuntimeClassName adds a runtime class name tp the metallb controller config. +func (builder *Builder) WithSpeakerRuntimeClassName(runtimeClassName string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + if len(runtimeClassName) == 0 { + glog.V(100).Infof("Controller Config parameter runtimeClassName cannot be an empty field") + + return builder + } + + builder.Definition.Spec.SpeakerConfig.RuntimeClassName = runtimeClassName + + return builder +} + +// WithSpeakerPodAffinityLabel adds a runtime class name tp the metallb controller config. +func (builder *Builder) WithSpeakerPodAffinityLabel(controllerPodAffinityLabel string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + if len(controllerPodAffinityLabel) == 0 { + glog.V(100).Infof("Speaker Config parameter runtimeClassName cannot be an empty field") + + return builder + } + + builder.Definition.Spec = mlbtypes.MetalLBSpec{ + SpeakerConfig: &mlbtypes.Config{ + Affinity: &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{ + {LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"component": controllerPodAffinityLabel}, + }, + TopologyKey: "kubernetes.io/hostname", + }, + }, + }, + }, + }, + } + + return builder +} + +// WithSpeakerTolerations adds a toleration configuration inside the speaker configuration . +func (builder *Builder) WithSpeakerTolerations(tolerationKey, tolerationOperator, tolerationEffect string) *Builder { + if valid, _ := builder.validate(); !valid { + return builder + } + + operator := corev1.TolerationOperator(tolerationOperator) + effect := corev1.TaintEffect(tolerationEffect) + toleration := corev1.Toleration{Key: tolerationKey, Operator: operator, Effect: effect} + + glog.V(100).Infof("Updating pod %s with toleration %v", builder.Definition.Name, toleration) + + if builder.errorMsg != "" { + return builder + } + + builder.Definition.Spec.SpeakerTolerations = append(builder.Definition.Spec.SpeakerTolerations, toleration) + + return builder +} + // WithOptions creates metallb with generic mutation options. func (builder *Builder) WithOptions(options ...AdditionalOptions) *Builder { if valid, _ := builder.validate(); !valid { diff --git a/pkg/metallb/metallb_test.go b/pkg/metallb/metallb_test.go index 66b41d621..8350f3310 100644 --- a/pkg/metallb/metallb_test.go +++ b/pkg/metallb/metallb_test.go @@ -353,6 +353,228 @@ func TestMetalLbWithSpeakerNodeSelector(t *testing.T) { } } +func TestMetalLbWithControllerPriorityClassName(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + priorityClassName string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + priorityClassName: "test-name", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithControllerPriorityClassName(testCase.priorityClassName) + assert.Equal(t, netBuilder.Definition.Spec.ControllerConfig.PriorityClassName, testCase.priorityClassName) + } +} + +func TestMetalLbWithControllerRuntimeClassName(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + runtimeClassName string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + runtimeClassName: "myclass", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithControllerRuntimeClassName(testCase.runtimeClassName) + assert.Equal(t, netBuilder.Definition.Spec.ControllerConfig.RuntimeClassName, testCase.runtimeClassName) + } +} + +func TestMetalLbWithControllerAnnotationMapString(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + annotationMapString map[string]string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + annotationMapString: map[string]string{"component": "controller-test"}, + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithControllerAnnotationMapString(testCase.annotationMapString) + assert.Equal(t, netBuilder.Definition.Spec.ControllerConfig.Annotations, testCase.annotationMapString) + } +} + +func TestMetalLbWithControllerPodAffinityLabel(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + controllerPodAffinityLabel string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + controllerPodAffinityLabel: "controller-test", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings). + WithControllerPodAffinityLabel(testCase.controllerPodAffinityLabel) + assert.Equal(t, netBuilder.Definition.Spec.ControllerConfig.Affinity.PodAffinity. + RequiredDuringSchedulingIgnoredDuringExecution, testCase.controllerPodAffinityLabel) + } +} + +func TestMetalLbWithControllerTolerations(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + tolerationKey string + tolerationOperator string + tolerationEffect string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + tolerationKey: "example", + }, + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + tolerationOperator: "Exists", + }, + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + tolerationEffect: "NoExecute", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithControllerTolerations(testCase.tolerationKey, + testCase.tolerationOperator, testCase.tolerationEffect) + assert.Equal(t, netBuilder.Definition.Spec.ControllerTolerations, testCase.tolerationKey, + testCase.tolerationOperator, testCase.tolerationEffect) + } +} + +func TestMetalLbWithSpeakerPriorityClassName(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + priorityClassName string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + priorityClassName: "high-priority", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithSpeakerPriorityClassName(testCase.priorityClassName) + assert.Equal(t, netBuilder.Definition.Spec.SpeakerConfig.PriorityClassName, testCase.priorityClassName) + } +} + +func TestMetalLbWithSpeakerRuntimeClassName(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + runtimeClassName string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + runtimeClassName: "myclass", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithSpeakerRuntimeClassName(testCase.runtimeClassName) + assert.Equal(t, netBuilder.Definition.Spec.SpeakerConfig.RuntimeClassName, testCase.runtimeClassName) + } +} + +func TestMetalLbWithSpeakerAnnotationMapString(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + annotationMapString map[string]string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + annotationMapString: map[string]string{"component": "controller-test"}, + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithControllerAnnotationMapString(testCase.annotationMapString) + assert.Equal(t, netBuilder.Definition.Spec.ControllerConfig.Annotations, testCase.annotationMapString) + } +} + +func TestMetalLbWithSpeakerPodAffinityLabel(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + controllerPodAffinityLabel string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + controllerPodAffinityLabel: "controller-test", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings). + WithControllerPodAffinityLabel(testCase.controllerPodAffinityLabel) + assert.Equal(t, netBuilder.Definition.Spec.ControllerConfig.Affinity.PodAffinity. + RequiredDuringSchedulingIgnoredDuringExecution, testCase.controllerPodAffinityLabel) + } +} + +func TestMetalLbWithSpeakerTolerations(t *testing.T) { + testCases := []struct { + testMetalLb *Builder + tolerationKey string + tolerationOperator string + tolerationEffect string + expectedError string + }{ + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + tolerationKey: "example", + }, + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + tolerationOperator: "Exists", + }, + { + testMetalLb: buildValidMetalLbBuilder(buildMetalLbTestClientWithDummyObject()), + expectedError: "", + tolerationEffect: "NoExecute", + }, + } + for _, testCase := range testCases { + testSettings := buildTestClientWithDummyObject() + netBuilder := buildValidMetalLbBuilder(testSettings).WithControllerTolerations(testCase.tolerationKey, + testCase.tolerationOperator, testCase.tolerationEffect) + assert.Equal(t, netBuilder.Definition.Spec.ControllerTolerations, testCase.tolerationKey, + testCase.tolerationOperator, testCase.tolerationEffect) + } +} + func TestMetalLbWithOptions(t *testing.T) { testSettings := buildMetalLbTestClientWithDummyObject() testBuilder := buildValidMetalLbBuilder(testSettings).WithOptions(