From fd4ed88bd3b07ad9f8540aa2732c564af269d324 Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Mon, 7 Aug 2023 15:37:48 +0200 Subject: [PATCH] Update neutronapi to use service override Removes creation of routes.Those get done in the openstack-operator. Via service overrides the service can be customized. The service operator adds annotation to the service which needs to be exposed as a route. Jira: OSP-26690 Depends-On: openstack-k8s-operators/lib-common#332 --- .../neutron.openstack.org_neutronapis.yaml | 214 ++++++++++++++---- api/go.mod | 5 +- api/go.sum | 6 +- api/v1beta1/neutronapi_types.go | 40 +--- api/v1beta1/zz_generated.deepcopy.go | 27 +-- .../neutron.openstack.org_neutronapis.yaml | 214 ++++++++++++++---- config/rbac/role.yaml | 12 - controllers/neutronapi_controller.go | 127 +++++++---- go.mod | 8 +- go.sum | 12 +- main.go | 2 - test/functional/neutronapi_controller_test.go | 4 - test/functional/suite_test.go | 6 - .../common/assert_sample_deployment.yaml | 15 -- test/kuttl/common/errors_cleanup_neutron.yaml | 10 +- 15 files changed, 472 insertions(+), 230 deletions(-) diff --git a/api/bases/neutron.openstack.org_neutronapis.yaml b/api/bases/neutron.openstack.org_neutronapis.yaml index 19dbc674..bec1a716 100644 --- a/api/bases/neutron.openstack.org_neutronapis.yaml +++ b/api/bases/neutron.openstack.org_neutronapis.yaml @@ -96,48 +96,6 @@ spec: to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object - externalEndpoints: - description: ExternalEndpoints, expose a VIP using a pre-created IPAddressPool - items: - description: MetalLBConfig to configure the MetalLB loadbalancer - service - properties: - endpoint: - description: Endpoint, OpenStack endpoint this service maps - to - enum: - - internal - - public - type: string - ipAddressPool: - description: IPAddressPool expose VIP via MetalLB on the IPAddressPool - minLength: 1 - type: string - loadBalancerIPs: - description: LoadBalancerIPs, request given IPs from the pool - if available. Using a list to allow dual stack (IPv4/IPv6) - support - items: - type: string - type: array - sharedIP: - default: true - description: SharedIP if true, VIP/VIPs get shared with multiple - services - type: boolean - sharedIPKey: - default: "" - description: SharedIPKey specifies the sharing key which gets - set as the annotation on the LoadBalancer service. Services - which share the same VIP must have the same SharedIPKey. Defaults - to the IPAddressPool if SharedIP is true, but no SharedIPKey - specified. - type: string - required: - - endpoint - - ipAddressPool - type: object - type: array extraMounts: description: ExtraMounts containing conf files items: @@ -2008,6 +1966,178 @@ spec: description: NodeSelector to target subset of worker nodes running this service type: object + override: + description: Override, provides the ability to override the generated + manifest of several child resources. + properties: + service: + additionalProperties: + description: OverrideSpec - service override configuration for + the Service created to serve traffic to the cluster. Allows + for the manifest of the created Service to be overwritten + with custom configuration. + properties: + endpointURL: + description: EndpointURL to be used to register the service + in keystone. + type: string + metadata: + description: EmbeddedLabelsAnnotations is an embedded subset + of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta. + Only labels and annotations are included. + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value + map stored with a resource that may be set by external + tools to store and retrieve arbitrary metadata. They + are not queryable and should be preserved when modifying + objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can + be used to organize and categorize (scope and select) + objects. May match selectors of replication controllers + and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + type: object + spec: + description: Spec defines the behavior of a Service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + externalName: + description: externalName is the external reference + that discovery mechanisms will return as an alias + for this service (e.g. a DNS CNAME record). No proxying + will be involved. Must be a lowercase RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires + `type` to be "ExternalName". + type: string + externalTrafficPolicy: + description: externalTrafficPolicy describes how nodes + distribute service traffic they receive on one of + the Service's "externally-facing" addresses (NodePorts, + ExternalIPs, and LoadBalancer IPs). If set to "Local", + the proxy will configure the service in a way that + assumes that external load balancers will take care + of balancing the service traffic between nodes, and + so each node will deliver traffic only to the node-local + endpoints of the service, without masquerading the + client source IP. (Traffic mistakenly sent to a node + with no endpoints will be dropped.) The default value, + "Cluster", uses the standard behavior of routing to + all endpoints evenly (possibly modified by topology + and other features). Note that traffic sent to an + External IP or LoadBalancer IP from within the cluster + will always get "Cluster" semantics, but clients sending + to a NodePort from within the cluster may need to + take traffic policy into account when picking a node. + type: string + internalTrafficPolicy: + description: InternalTrafficPolicy describes how nodes + distribute service traffic they receive on the ClusterIP. + If set to "Local", the proxy will assume that pods + only want to talk to endpoints of the service on the + same node as the pod, dropping the traffic if there + are no local endpoints. The default value, "Cluster", + uses the standard behavior of routing to all endpoints + evenly (possibly modified by topology and other features). + type: string + ipFamilyPolicy: + description: IPFamilyPolicy represents the dual-stack-ness + requested or required by this Service. If there is + no value provided, then this field will be set to + SingleStack. Services can be "SingleStack" (a single + IP family), "PreferDualStack" (two IP families on + dual-stack configured clusters or a single IP family + on single-stack clusters), or "RequireDualStack" (two + IP families on dual-stack configured clusters, otherwise + fail). The ipFamilies and clusterIPs fields depend + on the value of this field. This field will be wiped + when updating a service to type ExternalName. + type: string + loadBalancerClass: + description: loadBalancerClass is the class of the load + balancer implementation this Service belongs to. If + specified, the value of this field must be a label-style + identifier, with an optional prefix, e.g. "internal-vip" + or "example.com/internal-vip". Unprefixed names are + reserved for end-users. This field can only be set + when the Service type is 'LoadBalancer'. If not set, + the default load balancer implementation is used, + today this is typically done through the cloud provider + integration, but should apply for any default implementation. + If set, it is assumed that a load balancer implementation + is watching for Services with a matching class. Any + default load balancer implementation (e.g. cloud providers) + should ignore Services that set this field. This field + can only be set when creating or updating a Service + to type 'LoadBalancer'. Once set, it can not be changed. + This field will be wiped when a service is updated + to a non 'LoadBalancer' type. + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, + this will restrict traffic through the cloud-provider + load-balancer will be restricted to the specified + client IPs. This field will be ignored if the cloud-provider + does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/' + items: + type: string + type: array + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to + maintain session affinity. Enable client IP based + session affinity. Must be ClientIP or None. Defaults + to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds + of ClientIP type session sticky time. The + value must be >0 && <=86400(for 1 day) if + ServiceAffinity == "ClientIP". Default value + is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. + Defaults to ClusterIP. Valid options are ExternalName, + ClusterIP, NodePort, and LoadBalancer. "ClusterIP" + allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector + or if that is not specified, by manual construction + of an Endpoints object or EndpointSlice objects. If + clusterIP is "None", no virtual IP is allocated and + the endpoints are published as a set of endpoints + rather than a virtual IP. "NodePort" builds on ClusterIP + and allocates a port on every node which routes to + the same endpoints as the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to + the same endpoints as the clusterIP. "ExternalName" + aliases this service to the specified externalName. + Several other fields do not apply to ExternalName + services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + type: object + description: Override configuration for the Service created to + serve traffic to the cluster. The key must be the endpoint type + (public, internal) + type: object + type: object passwordSelectors: default: database: NeutronDatabasePassword diff --git a/api/go.mod b/api/go.mod index e787010e..253f6062 100644 --- a/api/go.mod +++ b/api/go.mod @@ -42,7 +42,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/openshift/api v3.9.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -77,3 +76,7 @@ require ( // mschuppert: map to latest commit from release-4.13 tag // must consistent within modules and service operators replace github.com/openshift/api => github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 //allow-merging + +replace github.com/openstack-k8s-operators/lib-common/modules/common => github.com/stuggi/lib-common/modules/common v0.0.0-20230907061102-0705359fc344 + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/stuggi/keystone-operator/api v0.0.0-20230907062239-b15a4b6cfdb6 diff --git a/api/go.sum b/api/go.sum index 5c4002c3..b356ebb8 100644 --- a/api/go.sum +++ b/api/go.sum @@ -229,10 +229,6 @@ github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxCMwNRnMjhhIDOWHJowi6q8G6koI= -github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230824094610-976b18ca2875 h1:pj22n6PQy/XAmV5m6XaarMY6X1lvxAh16oVT5ZSVoNI= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230824094610-976b18ca2875/go.mod h1:Mqg9hyHpWPda62750vqmk5TajxP3zbYPDP1rtSH7mg0= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230824094610-976b18ca2875 h1:lC8Nw4PF2Lcqc7BJAdlBvYPyLqyaKa9R1e15dM9b3BY= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230824094610-976b18ca2875/go.mod h1:lazDTPD8BYde2yyzZ3HbOfG51Sf87vSr4KXwpF57hDs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -291,6 +287,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stuggi/lib-common/modules/common v0.0.0-20230907061102-0705359fc344 h1:NZDTBuxxyFn6JJN1nHHknhzD6k/m2AwDdv0z/lRaKWQ= +github.com/stuggi/lib-common/modules/common v0.0.0-20230907061102-0705359fc344/go.mod h1:Mqg9hyHpWPda62750vqmk5TajxP3zbYPDP1rtSH7mg0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= diff --git a/api/v1beta1/neutronapi_types.go b/api/v1beta1/neutronapi_types.go index b3abbdf3..3a3e6201 100644 --- a/api/v1beta1/neutronapi_types.go +++ b/api/v1beta1/neutronapi_types.go @@ -18,7 +18,7 @@ package v1beta1 import ( "github.com/openstack-k8s-operators/lib-common/modules/common/condition" - "github.com/openstack-k8s-operators/lib-common/modules/common/endpoint" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" "github.com/openstack-k8s-operators/lib-common/modules/common/util" "github.com/openstack-k8s-operators/lib-common/modules/storage" @@ -119,42 +119,20 @@ type NeutronAPISpec struct { // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network NetworkAttachments []string `json:"networkAttachments,omitempty"` - // +kubebuilder:validation:Optional - // ExternalEndpoints, expose a VIP using a pre-created IPAddressPool - ExternalEndpoints []MetalLBConfig `json:"externalEndpoints,omitempty"` - // ExtraMounts containing conf files // +kubebuilder:validation:Optional ExtraMounts []NeutronExtraVolMounts `json:"extraMounts,omitempty"` -} - -// MetalLBConfig to configure the MetalLB loadbalancer service -type MetalLBConfig struct { - // +kubebuilder:validation:Required - // +kubebuilder:validation:Enum=internal;public - // Endpoint, OpenStack endpoint this service maps to - Endpoint endpoint.Endpoint `json:"endpoint"` - - // +kubebuilder:validation:Required - // +kubebuilder:validation:MinLength=1 - // IPAddressPool expose VIP via MetalLB on the IPAddressPool - IPAddressPool string `json:"ipAddressPool"` - - // +kubebuilder:validation:Optional - // +kubebuilder:default=true - // SharedIP if true, VIP/VIPs get shared with multiple services - SharedIP bool `json:"sharedIP"` // +kubebuilder:validation:Optional - // +kubebuilder:default="" - // SharedIPKey specifies the sharing key which gets set as the annotation on the LoadBalancer service. - // Services which share the same VIP must have the same SharedIPKey. Defaults to the IPAddressPool if - // SharedIP is true, but no SharedIPKey specified. - SharedIPKey string `json:"sharedIPKey"` + // Override, provides the ability to override the generated manifest of several child resources. + Override APIOverrideSpec `json:"override,omitempty"` +} - // +kubebuilder:validation:Optional - // LoadBalancerIPs, request given IPs from the pool if available. Using a list to allow dual stack (IPv4/IPv6) support - LoadBalancerIPs []string `json:"loadBalancerIPs,omitempty"` +// APIOverrideSpec to override the generated manifest of several child resources. +type APIOverrideSpec struct { + // Override configuration for the Service created to serve traffic to the cluster. + // The key must be the endpoint type (public, internal) + Service map[string]service.OverrideSpec `json:"service,omitempty"` } // PasswordSelector to identify the DB and AdminUser password from the Secret diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 93e66555..97f21bf2 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -23,26 +23,29 @@ package v1beta1 import ( "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" "github.com/openstack-k8s-operators/lib-common/modules/storage" "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MetalLBConfig) DeepCopyInto(out *MetalLBConfig) { +func (in *APIOverrideSpec) DeepCopyInto(out *APIOverrideSpec) { *out = *in - if in.LoadBalancerIPs != nil { - in, out := &in.LoadBalancerIPs, &out.LoadBalancerIPs - *out = make([]string, len(*in)) - copy(*out, *in) + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = make(map[string]service.OverrideSpec, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetalLBConfig. -func (in *MetalLBConfig) DeepCopy() *MetalLBConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIOverrideSpec. +func (in *APIOverrideSpec) DeepCopy() *APIOverrideSpec { if in == nil { return nil } - out := new(MetalLBConfig) + out := new(APIOverrideSpec) in.DeepCopyInto(out) return out } @@ -166,13 +169,6 @@ func (in *NeutronAPISpec) DeepCopyInto(out *NeutronAPISpec) { *out = make([]string, len(*in)) copy(*out, *in) } - if in.ExternalEndpoints != nil { - in, out := &in.ExternalEndpoints, &out.ExternalEndpoints - *out = make([]MetalLBConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } if in.ExtraMounts != nil { in, out := &in.ExtraMounts, &out.ExtraMounts *out = make([]NeutronExtraVolMounts, len(*in)) @@ -180,6 +176,7 @@ func (in *NeutronAPISpec) DeepCopyInto(out *NeutronAPISpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.Override.DeepCopyInto(&out.Override) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NeutronAPISpec. diff --git a/config/crd/bases/neutron.openstack.org_neutronapis.yaml b/config/crd/bases/neutron.openstack.org_neutronapis.yaml index 19dbc674..bec1a716 100644 --- a/config/crd/bases/neutron.openstack.org_neutronapis.yaml +++ b/config/crd/bases/neutron.openstack.org_neutronapis.yaml @@ -96,48 +96,6 @@ spec: to add additional files. Those get added to the service config dir in /etc/ . TODO: -> implement' type: object - externalEndpoints: - description: ExternalEndpoints, expose a VIP using a pre-created IPAddressPool - items: - description: MetalLBConfig to configure the MetalLB loadbalancer - service - properties: - endpoint: - description: Endpoint, OpenStack endpoint this service maps - to - enum: - - internal - - public - type: string - ipAddressPool: - description: IPAddressPool expose VIP via MetalLB on the IPAddressPool - minLength: 1 - type: string - loadBalancerIPs: - description: LoadBalancerIPs, request given IPs from the pool - if available. Using a list to allow dual stack (IPv4/IPv6) - support - items: - type: string - type: array - sharedIP: - default: true - description: SharedIP if true, VIP/VIPs get shared with multiple - services - type: boolean - sharedIPKey: - default: "" - description: SharedIPKey specifies the sharing key which gets - set as the annotation on the LoadBalancer service. Services - which share the same VIP must have the same SharedIPKey. Defaults - to the IPAddressPool if SharedIP is true, but no SharedIPKey - specified. - type: string - required: - - endpoint - - ipAddressPool - type: object - type: array extraMounts: description: ExtraMounts containing conf files items: @@ -2008,6 +1966,178 @@ spec: description: NodeSelector to target subset of worker nodes running this service type: object + override: + description: Override, provides the ability to override the generated + manifest of several child resources. + properties: + service: + additionalProperties: + description: OverrideSpec - service override configuration for + the Service created to serve traffic to the cluster. Allows + for the manifest of the created Service to be overwritten + with custom configuration. + properties: + endpointURL: + description: EndpointURL to be used to register the service + in keystone. + type: string + metadata: + description: EmbeddedLabelsAnnotations is an embedded subset + of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta. + Only labels and annotations are included. + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value + map stored with a resource that may be set by external + tools to store and retrieve arbitrary metadata. They + are not queryable and should be preserved when modifying + objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can + be used to organize and categorize (scope and select) + objects. May match selectors of replication controllers + and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + type: object + spec: + description: Spec defines the behavior of a Service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + externalName: + description: externalName is the external reference + that discovery mechanisms will return as an alias + for this service (e.g. a DNS CNAME record). No proxying + will be involved. Must be a lowercase RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires + `type` to be "ExternalName". + type: string + externalTrafficPolicy: + description: externalTrafficPolicy describes how nodes + distribute service traffic they receive on one of + the Service's "externally-facing" addresses (NodePorts, + ExternalIPs, and LoadBalancer IPs). If set to "Local", + the proxy will configure the service in a way that + assumes that external load balancers will take care + of balancing the service traffic between nodes, and + so each node will deliver traffic only to the node-local + endpoints of the service, without masquerading the + client source IP. (Traffic mistakenly sent to a node + with no endpoints will be dropped.) The default value, + "Cluster", uses the standard behavior of routing to + all endpoints evenly (possibly modified by topology + and other features). Note that traffic sent to an + External IP or LoadBalancer IP from within the cluster + will always get "Cluster" semantics, but clients sending + to a NodePort from within the cluster may need to + take traffic policy into account when picking a node. + type: string + internalTrafficPolicy: + description: InternalTrafficPolicy describes how nodes + distribute service traffic they receive on the ClusterIP. + If set to "Local", the proxy will assume that pods + only want to talk to endpoints of the service on the + same node as the pod, dropping the traffic if there + are no local endpoints. The default value, "Cluster", + uses the standard behavior of routing to all endpoints + evenly (possibly modified by topology and other features). + type: string + ipFamilyPolicy: + description: IPFamilyPolicy represents the dual-stack-ness + requested or required by this Service. If there is + no value provided, then this field will be set to + SingleStack. Services can be "SingleStack" (a single + IP family), "PreferDualStack" (two IP families on + dual-stack configured clusters or a single IP family + on single-stack clusters), or "RequireDualStack" (two + IP families on dual-stack configured clusters, otherwise + fail). The ipFamilies and clusterIPs fields depend + on the value of this field. This field will be wiped + when updating a service to type ExternalName. + type: string + loadBalancerClass: + description: loadBalancerClass is the class of the load + balancer implementation this Service belongs to. If + specified, the value of this field must be a label-style + identifier, with an optional prefix, e.g. "internal-vip" + or "example.com/internal-vip". Unprefixed names are + reserved for end-users. This field can only be set + when the Service type is 'LoadBalancer'. If not set, + the default load balancer implementation is used, + today this is typically done through the cloud provider + integration, but should apply for any default implementation. + If set, it is assumed that a load balancer implementation + is watching for Services with a matching class. Any + default load balancer implementation (e.g. cloud providers) + should ignore Services that set this field. This field + can only be set when creating or updating a Service + to type 'LoadBalancer'. Once set, it can not be changed. + This field will be wiped when a service is updated + to a non 'LoadBalancer' type. + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, + this will restrict traffic through the cloud-provider + load-balancer will be restricted to the specified + client IPs. This field will be ignored if the cloud-provider + does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/' + items: + type: string + type: array + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to + maintain session affinity. Enable client IP based + session affinity. Must be ClientIP or None. Defaults + to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds + of ClientIP type session sticky time. The + value must be >0 && <=86400(for 1 day) if + ServiceAffinity == "ClientIP". Default value + is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. + Defaults to ClusterIP. Valid options are ExternalName, + ClusterIP, NodePort, and LoadBalancer. "ClusterIP" + allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector + or if that is not specified, by manual construction + of an Endpoints object or EndpointSlice objects. If + clusterIP is "None", no virtual IP is allocated and + the endpoints are published as a set of endpoints + rather than a virtual IP. "NodePort" builds on ClusterIP + and allocates a port on every node which routes to + the same endpoints as the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to + the same endpoints as the clusterIP. "ExternalName" + aliases this service to the specified externalName. + Several other fields do not apply to ExternalName + services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + type: object + description: Override configuration for the Service created to + serve traffic to the cluster. The key must be the endpoint type + (public, internal) + type: object + type: object passwordSelectors: default: database: NeutronDatabasePassword diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 48065c25..c2235be2 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -212,18 +212,6 @@ rules: - list - update - watch -- apiGroups: - - route.openshift.io - resources: - - routes - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - security.openshift.io resourceNames: diff --git a/controllers/neutronapi_controller.go b/controllers/neutronapi_controller.go index d3899852..4c02f3fe 100644 --- a/controllers/neutronapi_controller.go +++ b/controllers/neutronapi_controller.go @@ -22,7 +22,6 @@ import ( "time" "github.com/go-logr/logr" - routev1 "github.com/openshift/api/route/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" ctrl "sigs.k8s.io/controller-runtime" @@ -45,6 +44,7 @@ import ( nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment" common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" "github.com/openstack-k8s-operators/lib-common/modules/common/util" "github.com/openstack-k8s-operators/lib-common/modules/database" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" @@ -76,7 +76,6 @@ type NeutronAPIReconciler struct { // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list; // +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;patch;update;delete; // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;patch;update;delete; -// +kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete; // +kubebuilder:rbac:groups=mariadb.openstack.org,resources=mariadbdatabases,verbs=get;list;watch;create;update;patch;delete; // +kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneapis,verbs=get;list;watch; // +kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneservices,verbs=get;list;watch;create;update;patch;delete; @@ -204,7 +203,6 @@ func (r *NeutronAPIReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.Secret{}). Owns(&corev1.ConfigMap{}). Owns(&appsv1.Deployment{}). - Owns(&routev1.Route{}). Owns(&keystonev1.KeystoneService{}). Owns(&keystonev1.KeystoneEndpoint{}). Owns(&rabbitmqv1.TransportURL{}). @@ -381,53 +379,104 @@ func (r *NeutronAPIReconciler) reconcileInit( // run Neutron db sync - end // - // expose the service (create service, route and return the created endpoint URLs) + // expose the service (create service and return the created endpoint URLs) // - var ports = map[endpoint.Endpoint]endpoint.Data{ - endpoint.EndpointPublic: { + var neutronEndpoints = map[service.Endpoint]endpoint.Data{ + service.EndpointPublic: { Port: neutronapi.NeutronPublicPort, }, - endpoint.EndpointInternal: { + service.EndpointInternal: { Port: neutronapi.NeutronInternalPort, }, } + apiEndpoints := make(map[string]string) - for _, metallbcfg := range instance.Spec.ExternalEndpoints { - portCfg := ports[metallbcfg.Endpoint] - portCfg.MetalLB = &endpoint.MetalLBData{ - IPAddressPool: metallbcfg.IPAddressPool, - SharedIP: metallbcfg.SharedIP, - SharedIPKey: metallbcfg.SharedIPKey, - LoadBalancerIPs: metallbcfg.LoadBalancerIPs, + for endpointType, data := range neutronEndpoints { + endpointTypeStr := string(endpointType) + endpointName := neutronapi.ServiceName + "-" + endpointTypeStr + + svcOverride := instance.Spec.Override.Service[endpointTypeStr] + + exportLabels := util.MergeStringMaps( + serviceLabels, + map[string]string{ + service.AnnotationEndpointKey: endpointTypeStr, + }, + ) + + // Create the service + svc, err := service.NewService( + service.GenericService(&service.GenericServiceDetails{ + Name: endpointName, + Namespace: instance.Namespace, + Labels: exportLabels, + Selector: serviceLabels, + Port: service.GenericServicePort{ + Name: endpointName, + Port: data.Port, + Protocol: corev1.ProtocolTCP, + }, + }), + 5, + &svcOverride, + ) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ExposeServiceReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.ExposeServiceReadyErrorMessage, + err.Error())) + + return ctrl.Result{}, err } - ports[metallbcfg.Endpoint] = portCfg - } + svc.AddAnnotation(map[string]string{ + service.AnnotationEndpointKey: endpointTypeStr, + }) + + // add Annotation to whether creating an ingress is required or not + if endpointType == service.EndpointPublic && svc.GetServiceType() == corev1.ServiceTypeClusterIP { + svc.AddAnnotation(map[string]string{ + service.AnnotationIngressCreateKey: "true", + }) + svc.AddAnnotation(map[string]string{ + service.AnnotationIngressNameKey: neutronapi.ServiceName, + }) + } else { + svc.AddAnnotation(map[string]string{ + service.AnnotationIngressCreateKey: "false", + }) + } - apiEndpoints, ctrlResult, err := endpoint.ExposeEndpoints( - ctx, - helper, - neutronapi.ServiceName, - serviceLabels, - ports, - time.Duration(5)*time.Second, - ) - if err != nil { - instance.Status.Conditions.Set(condition.FalseCondition( - condition.ExposeServiceReadyCondition, - condition.ErrorReason, - condition.SeverityWarning, - condition.ExposeServiceReadyErrorMessage, - err.Error())) - return ctrlResult, err - } else if (ctrlResult != ctrl.Result{}) { - instance.Status.Conditions.Set(condition.FalseCondition( - condition.ExposeServiceReadyCondition, - condition.RequestedReason, - condition.SeverityInfo, - condition.ExposeServiceReadyRunningMessage)) - return ctrlResult, nil + ctrlResult, err := svc.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ExposeServiceReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + condition.ExposeServiceReadyErrorMessage, + err.Error())) + + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + condition.ExposeServiceReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + condition.ExposeServiceReadyRunningMessage)) + return ctrlResult, nil + } + // create service - end + + // TODO: TLS, pass in https as protocol, create TLS cert + apiEndpoints[string(endpointType)], err = svc.GetAPIEndpoint( + &svcOverride, data.Protocol, data.Path) + if err != nil { + return ctrl.Result{}, err + } } + instance.Status.Conditions.MarkTrue(condition.ExposeServiceReadyCondition, condition.ExposeServiceReadyMessage) // expose service - end diff --git a/go.mod b/go.mod index 29c3e254..54ea6bf5 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 github.com/onsi/ginkgo/v2 v2.12.0 github.com/onsi/gomega v1.27.10 - github.com/openshift/api v3.9.0+incompatible github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230829092744-a3dd5edba332 github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230823144632-e4b8db8a76bc github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230824094610-976b18ca2875 @@ -54,7 +53,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.0 // indirect + github.com/openshift/api v3.9.0+incompatible // indirect + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230824094610-976b18ca2875 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect @@ -97,3 +97,7 @@ replace github.com/openshift/api => github.com/openshift/api v0.0.0-202304141430 // needed to cert-manager v1.11.4 see https://github.com/cert-manager/cert-manager/blob/v1.11.4/go.mod#L263C1-L264C104 // remove this once we bump to cert-manager v1.12.x replace github.com/Venafi/vcert/v4 => github.com/jetstack/vcert/v4 v4.9.6-0.20230519122548-219f317ae107 //allow-merging + +replace github.com/openstack-k8s-operators/lib-common/modules/common => github.com/stuggi/lib-common/modules/common v0.0.0-20230907061102-0705359fc344 + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/stuggi/keystone-operator/api v0.0.0-20230907062239-b15a4b6cfdb6 diff --git a/go.sum b/go.sum index 1ed98b09..1f0ed4f8 100644 --- a/go.sum +++ b/go.sum @@ -239,14 +239,10 @@ github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxC github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230829092744-a3dd5edba332 h1:OxhwqONfFHQjJuJjK3HKTXTSo2oQwKa97s+T+vKspkY= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230829092744-a3dd5edba332/go.mod h1:t1xmsiZDqM3wXcLMqgHp7/iilK8ozuOkydV4Vi2Qibk= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230823144632-e4b8db8a76bc h1:78MQTRkSM5lMqdYmvjzQZGUlD6BHwYb5PSpm9m3Z310= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230823144632-e4b8db8a76bc/go.mod h1:LNJJdteQG4E2fhWDerE+f8S2/ephEJg8yBkH1eqYYOo= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230824094610-976b18ca2875 h1:pj22n6PQy/XAmV5m6XaarMY6X1lvxAh16oVT5ZSVoNI= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230824094610-976b18ca2875/go.mod h1:Mqg9hyHpWPda62750vqmk5TajxP3zbYPDP1rtSH7mg0= github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230824094610-976b18ca2875 h1:Hqzy5HbNS98ZyVxNmLnOgoPjWPMm0eOHx1lHIoN/5UM= github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230824094610-976b18ca2875/go.mod h1:Z5qA6F9H/4iYPjcqehlFJ1I68icwBKIVMB00ruNLKaM= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.0 h1:mMeJvCQfZmakssvMyHjzp/ngxKysETDj9GJYhRwydzg= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.0/go.mod h1:+paEFOL5IlJzhg9fy7/1+HSErVkWUgUj1ORLFwgvxnI= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230824094610-976b18ca2875 h1:aUlwELsLYWQ3FL+/nRG/1uGVNW86c3MhtLrHNVDd57k= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230824094610-976b18ca2875/go.mod h1:Vng+vqdTJUuZ+AEzSAaU0I7bn3qwYMMFEUHHhiH0440= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230824094610-976b18ca2875 h1:lC8Nw4PF2Lcqc7BJAdlBvYPyLqyaKa9R1e15dM9b3BY= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230824094610-976b18ca2875/go.mod h1:lazDTPD8BYde2yyzZ3HbOfG51Sf87vSr4KXwpF57hDs= github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230824094610-976b18ca2875 h1:Q23dbdnbcL5A54Ixb7oKVTRE24IcxiqizrZX7X43XOI= @@ -311,6 +307,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stuggi/keystone-operator/api v0.0.0-20230907062239-b15a4b6cfdb6 h1:5VrV9yQtmT7Bj9XM7TFMDgfZprgqPfw2AqK+khEwjjs= +github.com/stuggi/keystone-operator/api v0.0.0-20230907062239-b15a4b6cfdb6/go.mod h1:xVGf/an6HspP2PmYvTxJG07kq4hOX/f2nRx9tMK2k7A= +github.com/stuggi/lib-common/modules/common v0.0.0-20230907061102-0705359fc344 h1:NZDTBuxxyFn6JJN1nHHknhzD6k/m2AwDdv0z/lRaKWQ= +github.com/stuggi/lib-common/modules/common v0.0.0-20230907061102-0705359fc344/go.mod h1:Mqg9hyHpWPda62750vqmk5TajxP3zbYPDP1rtSH7mg0= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= diff --git a/main.go b/main.go index c55f8d44..22c55b1d 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,6 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" - routev1 "github.com/openshift/api/route/v1" "go.uber.org/zap/zapcore" "k8s.io/apimachinery/pkg/runtime" @@ -62,7 +61,6 @@ func init() { utilruntime.Must(ovnv1alpha1.AddToScheme(scheme)) utilruntime.Must(neutronv1beta1.AddToScheme(scheme)) - utilruntime.Must(routev1.AddToScheme(scheme)) utilruntime.Must(networkv1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/test/functional/neutronapi_controller_test.go b/test/functional/neutronapi_controller_test.go index 1002c4b6..fa272d37 100644 --- a/test/functional/neutronapi_controller_test.go +++ b/test/functional/neutronapi_controller_test.go @@ -414,10 +414,6 @@ var _ = Describe("NeutronAPI controller", func() { th.AssertServiceExists(types.NamespacedName{Namespace: namespace, Name: "neutron-internal"}) }) - It("Assert Routes are created", func() { - th.AssertRouteExists(types.NamespacedName{Namespace: namespace, Name: "neutron-public"}) - }) - It("Endpoints are created", func() { th.ExpectCondition( diff --git a/test/functional/suite_test.go b/test/functional/suite_test.go index e439295e..7ae657ab 100644 --- a/test/functional/suite_test.go +++ b/test/functional/suite_test.go @@ -33,7 +33,6 @@ import ( logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - routev1 "github.com/openshift/api/route/v1" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -86,8 +85,6 @@ var _ = BeforeSuite(func() { transportCRDs, err := test.GetCRDDirFromModule( "github.com/openstack-k8s-operators/infra-operator/apis", "../../go.mod", "bases") Expect(err).ShouldNot(HaveOccurred()) - routev1CRDs, err := test.GetOpenShiftCRDDir("route/v1", "../../go.mod") - Expect(err).ShouldNot(HaveOccurred()) By("bootstrapping test environment") testEnv = &envtest.Environment{ @@ -98,7 +95,6 @@ var _ = BeforeSuite(func() { mariaDBCRDs, ovnCRDs, transportCRDs, - routev1CRDs, }, ErrorIfCRDPathMissing: true, } @@ -128,8 +124,6 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) err = appsv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = routev1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) //+kubebuilder:scaffold:scheme logger = ctrl.Log.WithName("---Test---") diff --git a/test/kuttl/common/assert_sample_deployment.yaml b/test/kuttl/common/assert_sample_deployment.yaml index 32850fb0..b2c61745 100644 --- a/test/kuttl/common/assert_sample_deployment.yaml +++ b/test/kuttl/common/assert_sample_deployment.yaml @@ -5,7 +5,6 @@ # - Deployment with 1 Pod for NeutronAPI CR # - Neutron-internal Service # - Neutron-public Service -# - Neutron-public Route apiVersion: neutron.openstack.org/v1beta1 kind: NeutronAPI @@ -203,20 +202,6 @@ spec: sessionAffinity: None type: ClusterIP --- -apiVersion: route.openshift.io/v1 -kind: Route -metadata: - name: neutron-public - labels: - public: "true" - service: neutron -spec: - port: - targetPort: neutron-public - to: - kind: Service - name: neutron-public ---- apiVersion: keystone.openstack.org/v1beta1 kind: KeystoneEndpoint metadata: diff --git a/test/kuttl/common/errors_cleanup_neutron.yaml b/test/kuttl/common/errors_cleanup_neutron.yaml index ed7cad24..b6f34227 100644 --- a/test/kuttl/common/errors_cleanup_neutron.yaml +++ b/test/kuttl/common/errors_cleanup_neutron.yaml @@ -5,7 +5,6 @@ # No Deployment for NeutronAPI CR # No Pods in neutron Deployment # No Neutron Services -# No Neutron Routes # apiVersion: neutron.openstack.org/v1beta1 kind: NeutronAPI @@ -43,11 +42,4 @@ metadata: public: "true" service: neutron name: neutron-public ---- -apiVersion: route.openshift.io/v1 -kind: Route -metadata: - name: neutron-public - labels: - public: "true" - service: neutron + namespace: openstack