From 14a8771000139a0eb92c95b1441b5300af45203c Mon Sep 17 00:00:00 2001 From: Martin Schuppert Date: Fri, 11 Aug 2023 12:00:49 +0200 Subject: [PATCH] Create horizon route and svc overrides Creates the route for the horizon, also allows to customize the route via override. Generats the service override for the env with what is configured in the externalEndpoints, or specified in the service template override. Depends-On: https://github.com/openstack-k8s-operators/lib-common/pull/313 Depends-On: https://github.com/openstack-k8s-operators/keystone-operator/pull/289 Depends-On: https://github.com/openstack-k8s-operators/horizon-operator/pull/202 Jira: OSP-26690 --- ....openstack.org_openstackcontrolplanes.yaml | 105 +++++++++++++++++- apis/core/v1beta1/conditions.go | 3 + .../v1beta1/openstackcontrolplane_types.go | 12 ++ apis/core/v1beta1/zz_generated.deepcopy.go | 21 ++++ ....openstack.org_openstackcontrolplanes.yaml | 105 +++++++++++++++++- ...controlplane_galera_network_isolation.yaml | 2 + ...ne_galera_network_isolation_3replicas.yaml | 2 + ...enstackcontrolplane_network_isolation.yaml | 2 + ...ckcontrolplane_network_isolation_ceph.yaml | 2 + pkg/openstack/horizon.go | 55 +++++++++ 10 files changed, 305 insertions(+), 4 deletions(-) diff --git a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml index b841c6b94..87fc4c1b6 100644 --- a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -3943,6 +3943,107 @@ spec: type: object horizon: properties: + apiOverride: + properties: + route: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + alternateBackends: + items: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + maxItems: 3 + type: array + host: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + path: + pattern: ^/ + type: string + port: + properties: + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - targetPort + type: object + subdomain: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + tls: + properties: + caCertificate: + type: string + certificate: + type: string + destinationCACertificate: + type: string + insecureEdgeTerminationPolicy: + type: string + key: + type: string + termination: + enum: + - edge + - reencrypt + - passthrough + type: string + required: + - termination + type: object + to: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + wildcardPolicy: + enum: + - None + - Subdomain + - "" + type: string + type: object + type: object + type: object enabled: default: false type: boolean @@ -3973,7 +4074,7 @@ spec: override: properties: service: - items: + additionalProperties: properties: endpointURL: type: string @@ -4019,7 +4120,7 @@ spec: type: string type: object type: object - type: array + type: object type: object preserveJobs: default: false diff --git a/apis/core/v1beta1/conditions.go b/apis/core/v1beta1/conditions.go index 31bb79e91..b2cf3c6a0 100644 --- a/apis/core/v1beta1/conditions.go +++ b/apis/core/v1beta1/conditions.go @@ -81,6 +81,9 @@ const ( // OpenStackControlPlaneHorizonReadyCondition Status=True condition which indicates if Horizon is configured and operational OpenStackControlPlaneHorizonReadyCondition condition.Type = "OpenStackControlPlaneHorizonReady" + // OpenStackControlPlaneExposeHorizonReadyCondition Status=True condition which indicates if Horizon is exposed via a route + OpenStackControlPlaneExposeHorizonReadyCondition condition.Type = "OpenStackControlPlaneExposeHorizonReady" + // OpenStackControlPlaneClientReadyCondition Status=True condition which indicates if OpenStackClient is configured and operational OpenStackControlPlaneClientReadyCondition condition.Type = "OpenStackControlPlaneClientReady" diff --git a/apis/core/v1beta1/openstackcontrolplane_types.go b/apis/core/v1beta1/openstackcontrolplane_types.go index abbc3c06f..d1ba8a491 100644 --- a/apis/core/v1beta1/openstackcontrolplane_types.go +++ b/apis/core/v1beta1/openstackcontrolplane_types.go @@ -508,6 +508,18 @@ type HorizonSection struct { // +kubebuilder:validation:Optional // Template - Overrides to use when creating the Horizon services Template horizonv1.HorizonSpec `json:"template,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // APIOverride, provides the ability to override the generated manifest of several child resources. + APIOverride HorizonOverride `json:"apiOverride,omitempty"` +} + +// HorizonOverride to override the generated manifest of several child resources. +type HorizonOverride struct { + // +kubebuilder:validation:Optional + // Route overrides to use when creating the public service endpoint + Route *route.OverrideSpec `json:"route,omitempty"` } // CeilometerSection defines the desired state of OpenStack Telemetry services diff --git a/apis/core/v1beta1/zz_generated.deepcopy.go b/apis/core/v1beta1/zz_generated.deepcopy.go index aa52bca90..0e49e1f36 100644 --- a/apis/core/v1beta1/zz_generated.deepcopy.go +++ b/apis/core/v1beta1/zz_generated.deepcopy.go @@ -137,10 +137,31 @@ func (in *HeatSection) DeepCopy() *HeatSection { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HorizonOverride) DeepCopyInto(out *HorizonOverride) { + *out = *in + if in.Route != nil { + in, out := &in.Route, &out.Route + *out = new(route.OverrideSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizonOverride. +func (in *HorizonOverride) DeepCopy() *HorizonOverride { + if in == nil { + return nil + } + out := new(HorizonOverride) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HorizonSection) DeepCopyInto(out *HorizonSection) { *out = *in in.Template.DeepCopyInto(&out.Template) + in.APIOverride.DeepCopyInto(&out.APIOverride) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HorizonSection. diff --git a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml index b841c6b94..87fc4c1b6 100644 --- a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -3943,6 +3943,107 @@ spec: type: object horizon: properties: + apiOverride: + properties: + route: + properties: + metadata: + properties: + annotations: + additionalProperties: + type: string + type: object + labels: + additionalProperties: + type: string + type: object + type: object + spec: + properties: + alternateBackends: + items: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + maxItems: 3 + type: array + host: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + path: + pattern: ^/ + type: string + port: + properties: + targetPort: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + required: + - targetPort + type: object + subdomain: + maxLength: 253 + pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$ + type: string + tls: + properties: + caCertificate: + type: string + certificate: + type: string + destinationCACertificate: + type: string + insecureEdgeTerminationPolicy: + type: string + key: + type: string + termination: + enum: + - edge + - reencrypt + - passthrough + type: string + required: + - termination + type: object + to: + properties: + kind: + enum: + - Service + - "" + type: string + name: + type: string + weight: + format: int32 + maximum: 256 + minimum: 0 + type: integer + type: object + wildcardPolicy: + enum: + - None + - Subdomain + - "" + type: string + type: object + type: object + type: object enabled: default: false type: boolean @@ -3973,7 +4074,7 @@ spec: override: properties: service: - items: + additionalProperties: properties: endpointURL: type: string @@ -4019,7 +4120,7 @@ spec: type: string type: object type: object - type: array + type: object type: object preserveJobs: default: false diff --git a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml index 9b97f83e4..f9bd0bccb 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml @@ -123,6 +123,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml index c5d77c406..b4a14d96c 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml @@ -123,6 +123,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml index f46d5d3a3..4ec6204a4 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml @@ -111,6 +111,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml index 847b58656..9c6950fe2 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation_ceph.yaml @@ -155,6 +155,8 @@ spec: networkAttachments: - internalapi horizon: + apiOverride: + route: {} template: replicas: 1 secret: osp-secret diff --git a/pkg/openstack/horizon.go b/pkg/openstack/horizon.go index 84f7a6ed8..ddc3941fd 100644 --- a/pkg/openstack/horizon.go +++ b/pkg/openstack/horizon.go @@ -4,14 +4,20 @@ import ( "context" "fmt" + "github.com/openstack-k8s-operators/lib-common/modules/common" "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/helper" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" horizonv1 "github.com/openstack-k8s-operators/horizon-operator/api/v1beta1" corev1beta1 "github.com/openstack-k8s-operators/openstack-operator/apis/core/v1beta1" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" ) @@ -31,9 +37,58 @@ func ReconcileHorizon(ctx context.Context, instance *corev1beta1.OpenStackContro instance.Status.Conditions.Remove(corev1beta1.OpenStackControlPlaneHorizonReadyCondition) return ctrl.Result{}, nil } + + // add selector to service overrides + for _, endpointType := range []service.Endpoint{service.EndpointPublic, service.EndpointInternal} { + if instance.Spec.Horizon.Template.Override.Service == nil { + instance.Spec.Horizon.Template.Override.Service = map[string]service.OverrideSpec{} + } + instance.Spec.Horizon.Template.Override.Service[string(endpointType)] = + AddServiceComponentLabel( + ptr.To(instance.Spec.Horizon.Template.Override.Service[string(endpointType)]), + horizon.Name) + } + + // When component services got created check if there is the need to create a route + if err := helper.GetClient().Get(ctx, types.NamespacedName{Name: "horizon", Namespace: instance.Namespace}, horizon); err != nil { + if !k8s_errors.IsNotFound(err) { + return ctrl.Result{}, err + } + } + + if horizon.Status.Conditions.IsTrue(condition.ExposeServiceReadyCondition) { + svcs, err := service.GetServicesListWithLabel( + ctx, + helper, + instance.Namespace, + map[string]string{common.AppSelector: horizon.Name}, + ) + if err != nil { + return ctrl.Result{}, err + } + + var ctrlResult reconcile.Result + instance.Spec.Horizon.Template.Override.Service, ctrlResult, err = EnsureRoute( + ctx, + instance, + helper, + horizon, + svcs, + instance.Spec.Horizon.Template.Override.Service, + instance.Spec.Horizon.APIOverride.Route, + corev1beta1.OpenStackControlPlaneExposeHorizonReadyCondition, + ) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + } + helper.GetLogger().Info("Reconcile Horizon", "horizon.Namespace", instance.Namespace, "horizon.Name", "horizon") op, err := controllerutil.CreateOrPatch(ctx, helper.GetClient(), horizon, func() error { instance.Spec.Horizon.Template.DeepCopyInto(&horizon.Spec) + err := controllerutil.SetControllerReference(helper.GetBeforeObject(), horizon, helper.GetScheme()) if err != nil { return err