diff --git a/api/k0smotron.io/v1beta1/k0smotroncluster_types.go b/api/k0smotron.io/v1beta1/k0smotroncluster_types.go index 1e9b83f24..dee636b96 100644 --- a/api/k0smotron.io/v1beta1/k0smotroncluster_types.go +++ b/api/k0smotron.io/v1beta1/k0smotroncluster_types.go @@ -175,6 +175,12 @@ type ServiceSpec struct { // Annotations defines extra annotations to be added to the service. //+kubebuilder:validation:Optional Annotations map[string]string `json:"annotations,omitempty"` + // Labels defines extra labels to be added to the service. + //+kubebuilder:validation:Optional + Labels map[string]string `json:"labels,omitempty"` + // LoadBalancerClass defines the load balancer class to be used for the service. Used only when service type is LoadBalancer. + //+kubebuilder:validation:Optional + LoadBalancerClass *string `json:"loadBalancerClass,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/k0smotron.io/v1beta1/zz_generated.deepcopy.go b/api/k0smotron.io/v1beta1/zz_generated.deepcopy.go index 9034831f2..b23934710 100644 --- a/api/k0smotron.io/v1beta1/zz_generated.deepcopy.go +++ b/api/k0smotron.io/v1beta1/zz_generated.deepcopy.go @@ -434,6 +434,18 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { (*out)[key] = val } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.LoadBalancerClass != nil { + in, out := &in.LoadBalancerClass, &out.LoadBalancerClass + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSpec. diff --git a/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml b/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml index 9541b905c..5bfc9bdd2 100644 --- a/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml +++ b/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml @@ -3942,6 +3942,15 @@ spec: KonnectivityPort defines the konnectivity port. If empty k0smotron will pick it automatically. type: integer + labels: + additionalProperties: + type: string + description: Labels defines extra labels to be added to the service. + type: object + loadBalancerClass: + description: LoadBalancerClass defines the load balancer class + to be used for the service. Used only when service type is LoadBalancer. + type: string type: default: ClusterIP description: Service Type string describes ingress methods for diff --git a/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml b/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml index 7b56a1dea..9d741f374 100644 --- a/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml +++ b/config/clusterapi/controlplane/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml @@ -4020,6 +4020,17 @@ spec: KonnectivityPort defines the konnectivity port. If empty k0smotron will pick it automatically. type: integer + labels: + additionalProperties: + type: string + description: Labels defines extra labels to be added to + the service. + type: object + loadBalancerClass: + description: LoadBalancerClass defines the load balancer + class to be used for the service. Used only when service + type is LoadBalancer. + type: string type: default: ClusterIP description: Service Type string describes ingress methods diff --git a/config/clusterapi/k0smotron.io/bases/k0smotron.io_clusters.yaml b/config/clusterapi/k0smotron.io/bases/k0smotron.io_clusters.yaml index c6734e459..41a61b881 100644 --- a/config/clusterapi/k0smotron.io/bases/k0smotron.io_clusters.yaml +++ b/config/clusterapi/k0smotron.io/bases/k0smotron.io_clusters.yaml @@ -3945,6 +3945,15 @@ spec: KonnectivityPort defines the konnectivity port. If empty k0smotron will pick it automatically. type: integer + labels: + additionalProperties: + type: string + description: Labels defines extra labels to be added to the service. + type: object + loadBalancerClass: + description: LoadBalancerClass defines the load balancer class + to be used for the service. Used only when service type is LoadBalancer. + type: string type: default: ClusterIP description: Service Type string describes ingress methods for diff --git a/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml b/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml index 9541b905c..5bfc9bdd2 100644 --- a/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml +++ b/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanes.yaml @@ -3942,6 +3942,15 @@ spec: KonnectivityPort defines the konnectivity port. If empty k0smotron will pick it automatically. type: integer + labels: + additionalProperties: + type: string + description: Labels defines extra labels to be added to the service. + type: object + loadBalancerClass: + description: LoadBalancerClass defines the load balancer class + to be used for the service. Used only when service type is LoadBalancer. + type: string type: default: ClusterIP description: Service Type string describes ingress methods for diff --git a/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml b/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml index 7b56a1dea..9d741f374 100644 --- a/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml +++ b/config/crd/bases/controlplane.cluster.x-k8s.io_k0smotroncontrolplanetemplates.yaml @@ -4020,6 +4020,17 @@ spec: KonnectivityPort defines the konnectivity port. If empty k0smotron will pick it automatically. type: integer + labels: + additionalProperties: + type: string + description: Labels defines extra labels to be added to + the service. + type: object + loadBalancerClass: + description: LoadBalancerClass defines the load balancer + class to be used for the service. Used only when service + type is LoadBalancer. + type: string type: default: ClusterIP description: Service Type string describes ingress methods diff --git a/config/crd/bases/k0smotron.io_clusters.yaml b/config/crd/bases/k0smotron.io_clusters.yaml index c6734e459..41a61b881 100644 --- a/config/crd/bases/k0smotron.io_clusters.yaml +++ b/config/crd/bases/k0smotron.io_clusters.yaml @@ -3945,6 +3945,15 @@ spec: KonnectivityPort defines the konnectivity port. If empty k0smotron will pick it automatically. type: integer + labels: + additionalProperties: + type: string + description: Labels defines extra labels to be added to the service. + type: object + loadBalancerClass: + description: LoadBalancerClass defines the load balancer class + to be used for the service. Used only when service type is LoadBalancer. + type: string type: default: ClusterIP description: Service Type string describes ingress methods for diff --git a/docs/resource-reference.md b/docs/resource-reference.md index ef0ff2eae..76305942d 100644 --- a/docs/resource-reference.md +++ b/docs/resource-reference.md @@ -10949,6 +10949,20 @@ will pick it automatically.
Default: 30132
false + + labels + map[string]string + + Labels defines extra labels to be added to the service.
+ + false + + loadBalancerClass + string + + LoadBalancerClass defines the load balancer class to be used for the service. Used only when service type is LoadBalancer.
+ + false @@ -19370,6 +19384,20 @@ will pick it automatically.
Default: 30132
false + + labels + map[string]string + + Labels defines extra labels to be added to the service.
+ + false + + loadBalancerClass + string + + LoadBalancerClass defines the load balancer class to be used for the service. Used only when service type is LoadBalancer.
+ + false @@ -43706,6 +43734,20 @@ will pick it automatically.
Default: 30132
false + + labels + map[string]string + + Labels defines extra labels to be added to the service.
+ + false + + loadBalancerClass + string + + LoadBalancerClass defines the load balancer class to be used for the service. Used only when service type is LoadBalancer.
+ + false diff --git a/internal/controller/k0smotron.io/k0smotroncluster_service.go b/internal/controller/k0smotron.io/k0smotroncluster_service.go index 53736d790..ea37a4530 100644 --- a/internal/controller/k0smotron.io/k0smotroncluster_service.go +++ b/internal/controller/k0smotron.io/k0smotroncluster_service.go @@ -19,9 +19,10 @@ package k0smotronio import ( "context" "fmt" - "github.com/k0sproject/k0smotron/internal/controller/util" "time" + "github.com/k0sproject/k0smotron/internal/controller/util" + km "github.com/k0sproject/k0smotron/api/k0smotron.io/v1beta1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -86,7 +87,14 @@ func (r *ClusterReconciler) generateService(kmc *km.Cluster) v1.Service { }) } - labels := labelsForCluster(kmc) + // Copy both Cluster level labels and Service labels + labels := map[string]string{} + for k, v := range labelsForCluster(kmc) { + labels[k] = v + } + for k, v := range kmc.Spec.Service.Labels { + labels[k] = v + } // Copy both Cluster level annotations and Service annotations annotations := map[string]string{} @@ -115,6 +123,10 @@ func (r *ClusterReconciler) generateService(kmc *km.Cluster) v1.Service { }, } + if kmc.Spec.Service.Type == v1.ServiceTypeLoadBalancer { + svc.Spec.LoadBalancerClass = kmc.Spec.Service.LoadBalancerClass + } + return svc } diff --git a/internal/controller/k0smotron.io/k0smotroncluster_service_test.go b/internal/controller/k0smotron.io/k0smotroncluster_service_test.go index fb40852a2..5508de2f8 100644 --- a/internal/controller/k0smotron.io/k0smotroncluster_service_test.go +++ b/internal/controller/k0smotron.io/k0smotroncluster_service_test.go @@ -21,9 +21,109 @@ import ( km "github.com/k0sproject/k0smotron/api/k0smotron.io/v1beta1" "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) +func TestClusterReconciler_serviceLabels(t *testing.T) { + tests := []struct { + name string + kmc *km.Cluster + want map[string]string + }{ + { + name: "when no labels are set on either Cluster on svc", + kmc: &km.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: km.ClusterSpec{}, + }, + want: map[string]string{ + "app": "k0smotron", + "cluster": "test", + "component": "cluster", + }, + }, + { + name: "when labels are set on only Cluster", + kmc: &km.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Labels: map[string]string{ + "test": "test", + }, + }, + Spec: km.ClusterSpec{}, + }, + want: map[string]string{ + "app": "k0smotron", + "cluster": "test", + "component": "cluster", + "test": "test", + }, + }, + { + name: "when labels are set on both Cluster and svc", + kmc: &km.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Labels: map[string]string{ + "test": "test", + }, + }, + Spec: km.ClusterSpec{ + Service: km.ServiceSpec{ + Labels: map[string]string{ + "foo": "bar", + }, + }, + }, + }, + want: map[string]string{ + "app": "k0smotron", + "cluster": "test", + "component": "cluster", + "test": "test", + "foo": "bar", + }, + }, + { + name: "when same labels is set on both Cluster and svc the svc labels wins", + kmc: &km.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Labels: map[string]string{ + "test": "test", + }, + }, + Spec: km.ClusterSpec{ + Service: km.ServiceSpec{ + Labels: map[string]string{ + "test": "foobar", + }, + }, + }, + }, + want: map[string]string{ + "app": "k0smotron", + "cluster": "test", + "component": "cluster", + "test": "foobar", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &ClusterReconciler{} + svc := r.generateService(tt.kmc) + got := svc.Labels + assert.Equal(t, tt.want, got) + }) + } +} + func TestClusterReconciler_serviceAnnotations(t *testing.T) { tests := []struct { name string @@ -108,3 +208,60 @@ func TestClusterReconciler_serviceAnnotations(t *testing.T) { }) } } + +func TestClusterReconciler_serviceLoadBalancerClass(t *testing.T) { + tests := []struct { + name string + kmc *km.Cluster + want *string + }{ + { + name: "when no loadBalancerClass is set", + kmc: &km.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: km.ClusterSpec{}, + }, + want: nil, + }, + { + name: "when loadBalancerClass is set on LoadBalancer service", + kmc: &km.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: km.ClusterSpec{ + Service: km.ServiceSpec{ + Type: v1.ServiceTypeLoadBalancer, + LoadBalancerClass: ptr.To("class1"), + }, + }, + }, + want: ptr.To("class1"), + }, + { + name: "when loadBalancerClass is set on non LoadBalancer service", + kmc: &km.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: km.ClusterSpec{ + Service: km.ServiceSpec{ + Type: v1.ServiceTypeClusterIP, + LoadBalancerClass: ptr.To("class1"), + }, + }, + }, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := &ClusterReconciler{} + svc := r.generateService(tt.kmc) + got := svc.Spec.LoadBalancerClass + assert.Equal(t, tt.want, got) + }) + } +}