diff --git a/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml b/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml index 38b1d35b..196cd0c4 100644 --- a/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml +++ b/operator/v1/0000_00_operator.open-cluster-management.io_klusterlets.crd.yaml @@ -49,8 +49,10 @@ spec: such as the bind ports of their health probe and metrics endpoints. properties: bindConfiguration: - description: BindConfiguration represents the server bind configuration - of the klusterlet agents. + description: |- + BindConfiguration represents the server bind configuration of the klusterlet agents. + It applies to the klusterlet-agent in Singleton mode. In Default mode, it applies to + the registration and work agents that do not set their own bindConfiguration. properties: healthProbePort: default: 8443 @@ -74,6 +76,76 @@ spec: maximum: 65535 type: integer type: object + registrationAgentConfiguration: + description: |- + RegistrationAgentConfiguration represents the configuration of the registration agent + in Default mode. It is ignored in Singleton mode. + properties: + bindConfiguration: + description: |- + BindConfiguration represents the server bind configuration of the agent. + In Default mode the registration and work agents run as separate pods. When both + use hostNetwork and can be scheduled on the same node, set a different + healthProbePort for each of them. + properties: + healthProbePort: + default: 8443 + description: |- + HealthProbePort represents the bind port of the agents' health check endpoint. The default value is 8443. + Agents run as non-root, so privileged ports below 1024 are not allowed. + format: int32 + maximum: 65535 + minimum: 1024 + type: integer + hostNetwork: + description: HostNetwork enables running the agent pods + in host networking mode. + type: boolean + metricsPort: + default: 8080 + description: |- + MetricsPort represents the bind port of the agents' metrics endpoint. The default value is 8080. + Metrics may be disabled by setting a value less than or equal to 0. + format: int32 + maximum: 65535 + type: integer + type: object + type: object + workAgentConfiguration: + description: |- + WorkAgentConfiguration represents the configuration of the work agent in Default mode. + It is ignored in Singleton mode. + properties: + bindConfiguration: + description: |- + BindConfiguration represents the server bind configuration of the agent. + In Default mode the registration and work agents run as separate pods. When both + use hostNetwork and can be scheduled on the same node, set a different + healthProbePort for each of them. + properties: + healthProbePort: + default: 8443 + description: |- + HealthProbePort represents the bind port of the agents' health check endpoint. The default value is 8443. + Agents run as non-root, so privileged ports below 1024 are not allowed. + format: int32 + maximum: 65535 + minimum: 1024 + type: integer + hostNetwork: + description: HostNetwork enables running the agent pods + in host networking mode. + type: boolean + metricsPort: + default: 8080 + description: |- + MetricsPort represents the bind port of the agents' metrics endpoint. The default value is 8080. + Metrics may be disabled by setting a value less than or equal to 0. + format: int32 + maximum: 65535 + type: integer + type: object + type: object type: object clusterName: description: |- diff --git a/operator/v1/types_klusterlet.go b/operator/v1/types_klusterlet.go index 04449577..13ee998b 100644 --- a/operator/v1/types_klusterlet.go +++ b/operator/v1/types_klusterlet.go @@ -110,6 +110,28 @@ type KlusterletSpec struct { // AgentConfiguration represents customization of the klusterlet agents. type AgentConfiguration struct { // BindConfiguration represents the server bind configuration of the klusterlet agents. + // It applies to the klusterlet-agent in Singleton mode. In Default mode, it applies to + // the registration and work agents that do not set their own bindConfiguration. + // +optional + BindConfiguration *AgentBindConfiguration `json:"bindConfiguration,omitempty"` + + // RegistrationAgentConfiguration represents the configuration of the registration agent + // in Default mode. It is ignored in Singleton mode. + // +optional + RegistrationAgentConfiguration *AgentComponentConfiguration `json:"registrationAgentConfiguration,omitempty"` + + // WorkAgentConfiguration represents the configuration of the work agent in Default mode. + // It is ignored in Singleton mode. + // +optional + WorkAgentConfiguration *AgentComponentConfiguration `json:"workAgentConfiguration,omitempty"` +} + +// AgentComponentConfiguration represents customization of a single klusterlet agent. +type AgentComponentConfiguration struct { + // BindConfiguration represents the server bind configuration of the agent. + // In Default mode the registration and work agents run as separate pods. When both + // use hostNetwork and can be scheduled on the same node, set a different + // healthProbePort for each of them. // +optional BindConfiguration *AgentBindConfiguration `json:"bindConfiguration,omitempty"` } diff --git a/operator/v1/zz_generated.deepcopy.go b/operator/v1/zz_generated.deepcopy.go index 14431e86..1225ddf5 100644 --- a/operator/v1/zz_generated.deepcopy.go +++ b/operator/v1/zz_generated.deepcopy.go @@ -70,6 +70,27 @@ func (in *AgentBindConfiguration) DeepCopy() *AgentBindConfiguration { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AgentComponentConfiguration) DeepCopyInto(out *AgentComponentConfiguration) { + *out = *in + if in.BindConfiguration != nil { + in, out := &in.BindConfiguration, &out.BindConfiguration + *out = new(AgentBindConfiguration) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgentComponentConfiguration. +func (in *AgentComponentConfiguration) DeepCopy() *AgentComponentConfiguration { + if in == nil { + return nil + } + out := new(AgentComponentConfiguration) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AgentConfiguration) DeepCopyInto(out *AgentConfiguration) { *out = *in @@ -78,6 +99,16 @@ func (in *AgentConfiguration) DeepCopyInto(out *AgentConfiguration) { *out = new(AgentBindConfiguration) **out = **in } + if in.RegistrationAgentConfiguration != nil { + in, out := &in.RegistrationAgentConfiguration, &out.RegistrationAgentConfiguration + *out = new(AgentComponentConfiguration) + (*in).DeepCopyInto(*out) + } + if in.WorkAgentConfiguration != nil { + in, out := &in.WorkAgentConfiguration, &out.WorkAgentConfiguration + *out = new(AgentComponentConfiguration) + (*in).DeepCopyInto(*out) + } return } diff --git a/operator/v1/zz_generated.model_name.go b/operator/v1/zz_generated.model_name.go index 1ececfaf..31345b4e 100644 --- a/operator/v1/zz_generated.model_name.go +++ b/operator/v1/zz_generated.model_name.go @@ -21,6 +21,11 @@ func (in AgentBindConfiguration) OpenAPIModelName() string { return "io.open-cluster-management.api.operator.v1.AgentBindConfiguration" } +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in AgentComponentConfiguration) OpenAPIModelName() string { + return "io.open-cluster-management.api.operator.v1.AgentComponentConfiguration" +} + // OpenAPIModelName returns the OpenAPI model name for this type. func (in AgentConfiguration) OpenAPIModelName() string { return "io.open-cluster-management.api.operator.v1.AgentConfiguration" diff --git a/test/integration/api/klusterlet_test.go b/test/integration/api/klusterlet_test.go index f78e9a5b..a1d585d5 100644 --- a/test/integration/api/klusterlet_test.go +++ b/test/integration/api/klusterlet_test.go @@ -336,6 +336,49 @@ var _ = Describe("Klusterlet API test with AgentConfiguration", func() { _, err := operatorClient.OperatorV1().Klusterlets().Create(context.TODO(), klusterlet, metav1.CreateOptions{}) Expect(err).To(HaveOccurred()) }) + + It("should accept a separate bind configuration per agent", func() { + klusterlet.Spec.AgentConfiguration = &operatorv1.AgentConfiguration{ + RegistrationAgentConfiguration: &operatorv1.AgentComponentConfiguration{ + BindConfiguration: &operatorv1.AgentBindConfiguration{HealthProbePort: 9443, HostNetwork: true}, + }, + WorkAgentConfiguration: &operatorv1.AgentComponentConfiguration{ + BindConfiguration: &operatorv1.AgentBindConfiguration{HealthProbePort: 9444, HostNetwork: true}, + }, + } + created, err := operatorClient.OperatorV1().Klusterlets().Create(context.TODO(), klusterlet, metav1.CreateOptions{}) + Expect(err).ToNot(HaveOccurred()) + Expect(created.Spec.AgentConfiguration.BindConfiguration).Should(BeNil()) + + registration := created.Spec.AgentConfiguration.RegistrationAgentConfiguration.BindConfiguration + Expect(registration.HealthProbePort).Should(Equal(int32(9443))) + Expect(registration.HostNetwork).Should(BeTrue()) + + work := created.Spec.AgentConfiguration.WorkAgentConfiguration.BindConfiguration + Expect(work.HealthProbePort).Should(Equal(int32(9444))) + Expect(work.HostNetwork).Should(BeTrue()) + }) + + It("should default the health probe port of a per-agent bind configuration", func() { + klusterlet.Spec.AgentConfiguration = &operatorv1.AgentConfiguration{ + WorkAgentConfiguration: &operatorv1.AgentComponentConfiguration{ + BindConfiguration: &operatorv1.AgentBindConfiguration{}, + }, + } + created, err := operatorClient.OperatorV1().Klusterlets().Create(context.TODO(), klusterlet, metav1.CreateOptions{}) + Expect(err).ToNot(HaveOccurred()) + Expect(created.Spec.AgentConfiguration.WorkAgentConfiguration.BindConfiguration.HealthProbePort).Should(Equal(int32(8443))) + }) + + It("should reject a privileged health probe port in a per-agent bind configuration", func() { + klusterlet.Spec.AgentConfiguration = &operatorv1.AgentConfiguration{ + RegistrationAgentConfiguration: &operatorv1.AgentComponentConfiguration{ + BindConfiguration: &operatorv1.AgentBindConfiguration{HealthProbePort: 80}, + }, + } + _, err := operatorClient.OperatorV1().Klusterlets().Create(context.TODO(), klusterlet, metav1.CreateOptions{}) + Expect(err).To(HaveOccurred()) + }) }) var _ = Describe("Klusterlet v1 Enhanced API test", func() {