diff --git a/README.md b/README.md index b67e629f..01db016c 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,6 @@ template: preserveJobs: false replicas: 1 resources: {} - route: - routeName: horizon secret: osp-secret ``` @@ -110,8 +108,6 @@ template: preserveJobs: false replicas: 1 resources: {} - route: - routeName: horizon secret: osp-secret memcachedInstance: my-custom-memcached #<<-- Custom memcached instance supplied here. ``` diff --git a/api/bases/horizon.openstack.org_horizons.yaml b/api/bases/horizon.openstack.org_horizons.yaml index d7bb6b54..8656484b 100644 --- a/api/bases/horizon.openstack.org_horizons.yaml +++ b/api/bases/horizon.openstack.org_horizons.yaml @@ -83,6 +83,172 @@ 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: + description: Override configuration for the Service created to + serve traffic to the cluster. + properties: + endpointURL: + 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: OverrideServiceSpec is a subset of the fields + included in https://pkg.go.dev/k8s.io/api@v0.26.6/core/v1#ServiceSpec + Limited to Type, SessionAffinity, LoadBalancerSourceRanges, + ExternalName, ExternalTrafficPolicy, SessionAffinityConfig, + IPFamilyPolicy, LoadBalancerClass and InternalTrafficPolicy + 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 + type: object preserveJobs: default: false description: PreserveJobs - do not delete jobs after they finished @@ -143,32 +309,6 @@ spec: to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object - route: - description: HorizonRoute holds all of the necessary options for configuring - the Horizon Route object. This can be used to configure TLS TODO(bshephar) - Implement everything about this. It's just a placeholder at the - moment. - properties: - routeLocation: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - routeName: - default: horizon - type: string - routeTLSCA: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - routeTLSEnabled: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - routeTLSKey: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - type: object secret: description: Secret containing OpenStack password information for Horizon Secret Key diff --git a/api/go.mod b/api/go.mod index a47269a7..f02c6095 100644 --- a/api/go.mod +++ b/api/go.mod @@ -3,17 +3,12 @@ module github.com/openstack-k8s-operators/horizon-operator/api go 1.19 require ( - github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2 + github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216 k8s.io/api v0.26.9 k8s.io/apimachinery v0.26.9 sigs.k8s.io/controller-runtime v0.14.6 ) -require ( - go.uber.org/zap v1.26.0 // indirect - golang.org/x/tools v0.13.0 // indirect -) - require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/api/go.sum b/api/go.sum index 0ae19d77..742ec4dc 100644 --- a/api/go.sum +++ b/api/go.sum @@ -221,8 +221,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2 h1:/ez+9PSwtucQ9v1I5X72xlP5UJztTMPH4M5gDAJAatc= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2/go.mod h1:bG2JdbaO4bR4u8rtXZ7MgmMELuEseTkL2BPgk9JBYmY= +github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216 h1:arYbQA6bLyXJkHm+6M6gPc4YpWMFjs5qkG16Yii4UDo= +github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216/go.mod h1:Ge7Yf6AUmjEvJK9AIW2bT5udLzBIcK59b1QxqymncaQ= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -490,7 +490,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/api/v1beta1/horizon_types.go b/api/v1beta1/horizon_types.go index a87810ee..fb825ba2 100644 --- a/api/v1beta1/horizon_types.go +++ b/api/v1beta1/horizon_types.go @@ -20,6 +20,7 @@ import ( "fmt" condition "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/common/util" corev1 "k8s.io/api/core/v1" @@ -80,16 +81,20 @@ type HorizonSpec struct { // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ Resources corev1.ResourceRequirements `json:"resources,omitempty"` - // +kubebuilder:validation:Optional - // HorizonRoute holds all of the necessary options for configuring the Horizon Route object. - // This can be used to configure TLS - //TODO(bshephar) Implement everything about this. It's just a placeholder at the moment. - Route HorizonRoute `json:"route,omitempty"` - // +kubebuilder:validation:Required // +kubebuilder:default=memcached // Memcached instance name. MemcachedInstance string `json:"memcachedInstance"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override HorizionOverrideSpec `json:"override,omitempty"` +} + +// HorizionOverrideSpec to override the generated manifest of several child resources. +type HorizionOverrideSpec struct { + // Override configuration for the Service created to serve traffic to the cluster. + Service *service.RoutedOverrideSpec `json:"service,omitempty"` } // HorizonDebug can be used to enable debug in the Horizon service @@ -100,30 +105,6 @@ type HorizonDebug struct { Service bool `json:"service"` } -// HorizonRoute is used to define all of the information for the OpenShift route -// todo(bshephar) implement -type HorizonRoute struct { - // +kubebuilder:validation:Optional - // +kubebuilder:default=horizon - RouteName string `json:"routeName"` - - //TODO(bshephar) We need to implement TLS handling here to secure the route - // +kubebuilder:validation:Optional - RouteTLSEnabled string `json:"routeTLSEnabled,omitempty"` - - //TODO(bshephar) We need to implement TLS handling here to secure the route - // +kubebuilder:validation:Optional - RouteTLSCA string `json:"routeTLSCA,omitempty"` - - //TODO(bshephar) We need to implement TLS handling here to secure the route - // +kubebuilder:validation:Optional - RouteTLSKey string `json:"routeTLSKey,omitempty"` - - //TODO(bshephar) We need to implement TLS handling here to secure the route - // +kubebuilder:validation:Optional - RouteLocation string `json:"routeLocation,omitempty"` -} - // HorizonStatus defines the observed state of Horizon type HorizonStatus struct { // ReadyCount of Horizon instances diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index b9d9dd43..d78311cb 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -23,9 +23,30 @@ package v1beta1 import ( "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HorizionOverrideSpec) DeepCopyInto(out *HorizionOverrideSpec) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(service.RoutedOverrideSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizionOverrideSpec. +func (in *HorizionOverrideSpec) DeepCopy() *HorizionOverrideSpec { + if in == nil { + return nil + } + out := new(HorizionOverrideSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Horizon) DeepCopyInto(out *Horizon) { *out = *in @@ -115,21 +136,6 @@ func (in *HorizonList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HorizonRoute) DeepCopyInto(out *HorizonRoute) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizonRoute. -func (in *HorizonRoute) DeepCopy() *HorizonRoute { - if in == nil { - return nil - } - out := new(HorizonRoute) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HorizonSpec) DeepCopyInto(out *HorizonSpec) { *out = *in @@ -154,7 +160,7 @@ func (in *HorizonSpec) DeepCopyInto(out *HorizonSpec) { } } in.Resources.DeepCopyInto(&out.Resources) - out.Route = in.Route + in.Override.DeepCopyInto(&out.Override) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizonSpec. diff --git a/config/crd/bases/horizon.openstack.org_horizons.yaml b/config/crd/bases/horizon.openstack.org_horizons.yaml index d7bb6b54..8656484b 100644 --- a/config/crd/bases/horizon.openstack.org_horizons.yaml +++ b/config/crd/bases/horizon.openstack.org_horizons.yaml @@ -83,6 +83,172 @@ 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: + description: Override configuration for the Service created to + serve traffic to the cluster. + properties: + endpointURL: + 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: OverrideServiceSpec is a subset of the fields + included in https://pkg.go.dev/k8s.io/api@v0.26.6/core/v1#ServiceSpec + Limited to Type, SessionAffinity, LoadBalancerSourceRanges, + ExternalName, ExternalTrafficPolicy, SessionAffinityConfig, + IPFamilyPolicy, LoadBalancerClass and InternalTrafficPolicy + 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 + type: object preserveJobs: default: false description: PreserveJobs - do not delete jobs after they finished @@ -143,32 +309,6 @@ spec: to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object - route: - description: HorizonRoute holds all of the necessary options for configuring - the Horizon Route object. This can be used to configure TLS TODO(bshephar) - Implement everything about this. It's just a placeholder at the - moment. - properties: - routeLocation: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - routeName: - default: horizon - type: string - routeTLSCA: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - routeTLSEnabled: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - routeTLSKey: - description: TODO(bshephar) We need to implement TLS handling - here to secure the route - type: string - type: object secret: description: Secret containing OpenStack password information for Horizon Secret Key diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index cbf28dc3..30bdcd96 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -145,18 +145,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/horizon_controller.go b/controllers/horizon_controller.go index 8b6bed57..782e9fae 100644 --- a/controllers/horizon_controller.go +++ b/controllers/horizon_controller.go @@ -23,7 +23,6 @@ import ( "time" "github.com/go-logr/logr" - routev1 "github.com/openshift/api/route/v1" horizonv1beta1 "github.com/openstack-k8s-operators/horizon-operator/api/v1beta1" horizon "github.com/openstack-k8s-operators/horizon-operator/pkg/horizon" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" @@ -38,6 +37,7 @@ import ( labels "github.com/openstack-k8s-operators/lib-common/modules/common/labels" common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac" oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" util "github.com/openstack-k8s-operators/lib-common/modules/common/util" appsv1 "k8s.io/api/apps/v1" @@ -48,6 +48,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/rand" "k8s.io/client-go/kubernetes" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -91,7 +92,6 @@ type HorizonReconciler struct { //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete; -//+kubebuilder:rbac:groups=route.openshift.io,resources=routes,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=keystoneendpoints,verbs=get;list;watch; //+kubebuilder:rbac:groups=memcached.openstack.org,resources=memcacheds,verbs=get;list;watch; @@ -237,7 +237,6 @@ func (r *HorizonReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.Secret{}). Owns(&corev1.ConfigMap{}). Owns(&appsv1.Deployment{}). - Owns(&routev1.Route{}). Owns(&corev1.ServiceAccount{}). Owns(&rbacv1.Role{}). Owns(&rbacv1.RoleBinding{}). @@ -267,21 +266,33 @@ func (r *HorizonReconciler) reconcileInit( l.Info("Reconciling Service init") // - // expose the service (create service, route and return the created endpoint URLs) + // expose the service (create service and return the created endpoint URL) // - var horizonPorts = map[endpoint.Endpoint]endpoint.Data{ - endpoint.EndpointPublic: { - Port: horizon.HorizonPublicPort, - }, + endpointName := horizon.ServiceName + + svcOverride := instance.Spec.Override.Service + if svcOverride == nil { + svcOverride = &service.RoutedOverrideSpec{} + } + if svcOverride.EmbeddedLabelsAnnotations == nil { + svcOverride.EmbeddedLabelsAnnotations = &service.EmbeddedLabelsAnnotations{} } - apiEndpoints, ctrlResult, err := endpoint.ExposeEndpoints( - ctx, - helper, - horizon.ServiceName, - serviceLabels, - horizonPorts, - time.Second * 5, + // Create the service + svc, err := service.NewService( + service.GenericService(&service.GenericServiceDetails{ + Name: endpointName, + Namespace: instance.Namespace, + Labels: serviceLabels, + Selector: serviceLabels, + Port: service.GenericServicePort{ + Name: endpointName, + Port: horizon.HorizonPublicPort, + Protocol: corev1.ProtocolTCP, + }, + }), + 5, + &svcOverride.OverrideSpec, ) if err != nil { instance.Status.Conditions.Set(condition.FalseCondition( @@ -290,9 +301,37 @@ func (r *HorizonReconciler) reconcileInit( condition.SeverityWarning, condition.ExposeServiceReadyErrorMessage, err.Error())) - return ctrlResult, err + + return ctrl.Result{}, err } - if (ctrlResult != ctrl.Result{}) { + + // add Annotation to whether creating an ingress is required or not + if svc.GetServiceType() == corev1.ServiceTypeClusterIP { + svc.AddAnnotation(map[string]string{ + service.AnnotationIngressCreateKey: "true", + }) + } else { + svc.AddAnnotation(map[string]string{ + service.AnnotationIngressCreateKey: "false", + }) + if svc.GetServiceType() == corev1.ServiceTypeLoadBalancer { + svc.AddAnnotation(map[string]string{ + service.AnnotationHostnameKey: svc.GetServiceHostname(), // add annotation to register service name in dnsmasq + }) + } + } + + 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, @@ -300,12 +339,20 @@ func (r *HorizonReconciler) reconcileInit( condition.ExposeServiceReadyRunningMessage)) return ctrlResult, nil } + // create service - end + + // TODO: TLS, pass in https as protocol + apiEndpoint, err := svc.GetAPIEndpoint( + svcOverride.EndpointURL, ptr.To(service.ProtocolHTTP), "") + if err != nil { + return ctrl.Result{}, err + } instance.Status.Conditions.MarkTrue(condition.ExposeServiceReadyCondition, condition.ExposeServiceReadyMessage) // - // Update instance status with service endpoint url from route host information + // Update instance status with service endpoint url information // - instance.Status.Endpoint = apiEndpoints[string(endpoint.EndpointPublic)] + instance.Status.Endpoint = apiEndpoint // expose service - end @@ -489,7 +536,7 @@ func (r *HorizonReconciler) reconcileNormal(ctx context.Context, instance *horiz depl := deployment.NewDeployment( deplDef, - time.Second * 5, + time.Second*5, ) ctrlResult, err = depl.CreateOrPatch(ctx, helper) diff --git a/go.mod b/go.mod index 54cddb69..5ce89854 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,11 @@ require ( github.com/google/uuid v1.3.1 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/horizon-operator/api v0.1.1-0.20230828060631-f5678c16313e github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230914145253-116f307c7875 - github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230914163026-da9aa9de960a - github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2 - github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230913075424-2680ce4b6ad2 + github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230920085319-92ae0260bbf3 + github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216 + github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230919113507-d74c2f31d216 k8s.io/api v0.26.9 k8s.io/apimachinery v0.26.9 k8s.io/client-go v0.26.9 @@ -24,7 +23,7 @@ require ( require ( github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0 // indirect - github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230912174650-9fb4c4a76e55 // indirect + github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230913081601-9e4fc8aadad5 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect ) @@ -57,7 +56,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.1-0.20230913075424-2680ce4b6ad2 // indirect + github.com/openshift/api v3.9.0+incompatible // indirect + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230919113507-d74c2f31d216 // 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 diff --git a/go.sum b/go.sum index c935c13d..acb54c3d 100644 --- a/go.sum +++ b/go.sum @@ -237,16 +237,16 @@ 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.20230914145253-116f307c7875 h1:DUlCjbi3XxH66oL97MFZF5wgL28HdU+r8TkBZVw7WIc= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230914145253-116f307c7875/go.mod h1:NgrvT3CKMu6fE8Nt1H79qHx11L3I7Bb2eItniM7c9ow= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230914163026-da9aa9de960a h1:MFYwi2Xk9r3OMPToCSbvqYVNrm7P+aFzGDN0eVNpgu8= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230914163026-da9aa9de960a/go.mod h1:nxrbUOIGMJ1h2pNlawhURdt/XJ95dW2wZGedmOVo2aw= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2 h1:/ez+9PSwtucQ9v1I5X72xlP5UJztTMPH4M5gDAJAatc= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2/go.mod h1:bG2JdbaO4bR4u8rtXZ7MgmMELuEseTkL2BPgk9JBYmY= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230913075424-2680ce4b6ad2 h1:4L5DRfSnomBfyRwCfAzqQwk0+osnIbyJ1VvKBy+tzyY= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230913075424-2680ce4b6ad2/go.mod h1:NZ6weu5xOAkNPTqg1luC22DO7ZbyqiilRkvrFfhjFm0= -github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230913075424-2680ce4b6ad2 h1:TJpuax2pifQbOtsPv78DjoC+f/7+/3Tw+CdXBxxDxAs= -github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230913075424-2680ce4b6ad2/go.mod h1:fuKZmOKDEx/2f1+VLQyXF6iH9FX0ynbtyuvC/XjuJzg= -github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230912174650-9fb4c4a76e55 h1:iwa4MpjFHCl+qfiimlUwVm1BV+LGjbGYtXIwLe/QHwk= -github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230912174650-9fb4c4a76e55/go.mod h1:rh0jKCJYeHXVWfJnXl22AuaTRkYfHwmKuK6O3IeTG4A= +github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230920085319-92ae0260bbf3 h1:6VCz/ZBTJEQJTx4+z8UxLv3WITa4Bgx5CSP237wJ5xM= +github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230920085319-92ae0260bbf3/go.mod h1:ta6w/29i4WuWkQp6I4cOLwMGQ5/vJI0y8Em7u+M34jo= +github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216 h1:arYbQA6bLyXJkHm+6M6gPc4YpWMFjs5qkG16Yii4UDo= +github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216/go.mod h1:Ge7Yf6AUmjEvJK9AIW2bT5udLzBIcK59b1QxqymncaQ= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230919113507-d74c2f31d216 h1:h76faqi4WAXBs3D2B0GLUdlCjS0dh78wRL0d5RZUwhk= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230919113507-d74c2f31d216/go.mod h1:GHi64tgyC75/vuT8Crda0yN5iCIYiSyS4bpzYJjX7MA= +github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230919113507-d74c2f31d216 h1:QyzzvG8iaDFwx6Lo44dCyf2tRtgk0sqniXjgJpiW32g= +github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230919113507-d74c2f31d216/go.mod h1:RfLOPJbmPzPZ4XHwwDc2tFbbw5zxZL15JFGwb5c6VaU= +github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230913081601-9e4fc8aadad5 h1:dQcSQuXfgzgOhc4v+zD0jE6WWhn6FHr5nALOjJBPxyI= +github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230913081601-9e4fc8aadad5/go.mod h1:mJyhm/YiQZaYhLvOuLng/ITpwx8HvsYVht+VotS1Ed8= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/main.go b/main.go index 0f4585c2..4654c406 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" - routev1 "github.com/openshift/api/route/v1" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" @@ -52,13 +51,9 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(horizonv1beta1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme - - // As we are creating routes, it's necessary to register the routev1.Route - // type with the scheme - utilruntime.Must(routev1.AddToScheme(scheme)) utilruntime.Must(keystonev1.AddToScheme(scheme)) utilruntime.Must(memcachedv1.AddToScheme(scheme)) + //+kubebuilder:scaffold:scheme } func main() { diff --git a/tests/functional/horizon_controller_test.go b/tests/functional/horizon_controller_test.go index e86d72d6..418e3fe0 100644 --- a/tests/functional/horizon_controller_test.go +++ b/tests/functional/horizon_controller_test.go @@ -212,10 +212,6 @@ var _ = Describe("Horizon controller", func() { ContainSubstring("OPENSTACK_KEYSTONE_URL = \"http://keystone-public-openstack.testing/v3\"")) Expect(cm.Data["local_settings.py"]).Should( ContainSubstring("'LOCATION': [ 'memcached-0.memcached:11211', 'memcached-1.memcached:11211', 'memcached-2.memcached:11211' ]")) - th.AssertRouteExists(types.NamespacedName{ - Name: "horizon-public", - Namespace: horizonName.Namespace, - }) }) }) diff --git a/tests/functional/suite_test.go b/tests/functional/suite_test.go index b1e6fe4e..dcf53aaa 100644 --- a/tests/functional/suite_test.go +++ b/tests/functional/suite_test.go @@ -37,8 +37,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" - horizonv1 "github.com/openstack-k8s-operators/horizon-operator/api/v1beta1" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" @@ -88,8 +86,6 @@ var _ = BeforeSuite(func() { memcachedCRDs, 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{ @@ -97,7 +93,6 @@ var _ = BeforeSuite(func() { filepath.Join("..", "..", "config", "crd", "bases"), keystoneCRDs, memcachedCRDs, - routev1CRDs, }, ErrorIfCRDPathMissing: true, WebhookInstallOptions: envtest.WebhookInstallOptions{ @@ -120,8 +115,6 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) err = memcachedv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - err = routev1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) //+kubebuilder:scaffold:scheme diff --git a/tests/kuttl/common/assert-sample-deployment.yaml b/tests/kuttl/common/assert-sample-deployment.yaml index 9a3453e6..9a23242b 100644 --- a/tests/kuttl/common/assert-sample-deployment.yaml +++ b/tests/kuttl/common/assert-sample-deployment.yaml @@ -10,19 +10,3 @@ spec: LOGGING['handlers']['console']['level'] = 'DEBUG' status: readyCount: 1 ---- -# Test the status code is correct for each endpoint -# This test is for heat endpoints -apiVersion: kuttl.dev/v1beta1 -kind: TestAssert -commands: - - script: | - set -x - RETURN_CODE=0 - PUBLIC_URL=$(oc get -n $NAMESPACE horizon horizon -o jsonpath='{.status.endpoint}') - STATUSCODE=$(curl --silent --output /dev/stderr --head --write-out "%{http_code}" "$PUBLIC_URL/dashboard/auth/login/?next=/dashboard/") - if test $STATUSCODE -ne 200; then - RETURN_CODE=1 - echo "${PUBLIC_URL} status code expected is 200 but was ${STATUSCODE}" - fi - exit $RETURN_CODE