diff --git a/Dockerfile b/Dockerfile index d46809273..6fc93f1ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,8 @@ RUN if [ ! -f $CACHITO_ENV_FILE ]; then go mod download ; fi # Build manager RUN if [ -f $CACHITO_ENV_FILE ] ; then source $CACHITO_ENV_FILE ; fi ; CGO_ENABLED=0 GO111MODULE=on go build ${GO_BUILD_EXTRA_ARGS} -a -o ${DEST_ROOT}/manager main.go +RUN cp -r templates ${DEST_ROOT}/templates + # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM $OPERATOR_BASE_IMAGE @@ -55,13 +57,17 @@ LABEL com.redhat.component="${IMAGE_COMPONENT}" \ io.openshift.tags="${IMAGE_TAGS}" ### DO NOT EDIT LINES ABOVE -ENV USER_UID=$USER_ID +ENV USER_UID=$USER_ID \ + OPERATOR_TEMPLATES=/usr/share/openstack-operator/templates/ WORKDIR / # Install operator binary to WORKDIR COPY --from=builder ${DEST_ROOT}/manager . +# Install templates +COPY --from=builder ${DEST_ROOT}/templates ${OPERATOR_TEMPLATES} + USER $USER_ID ENV PATH="/:${PATH}" diff --git a/apis/bases/client.openstack.org_openstackclients.yaml b/apis/bases/client.openstack.org_openstackclients.yaml index 753c8839f..08c34b789 100644 --- a/apis/bases/client.openstack.org_openstackclients.yaml +++ b/apis/bases/client.openstack.org_openstackclients.yaml @@ -36,6 +36,8 @@ spec: type: object spec: properties: + caSecretName: + type: string containerImage: type: string nodeSelector: @@ -43,13 +45,13 @@ spec: type: string type: object openStackConfigMap: + default: openstack-config type: string openStackConfigSecret: + default: openstack-config-secret type: string required: - containerImage - - openStackConfigMap - - openStackConfigSecret type: object status: properties: diff --git a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml index 0152515a5..cd6907883 100644 --- a/apis/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/apis/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -8933,6 +8933,31 @@ spec: - secret type: object type: object + openstackclient: + properties: + enabled: + default: false + type: boolean + template: + properties: + caSecretName: + type: string + containerImage: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + openStackConfigMap: + default: openstack-config + type: string + openStackConfigSecret: + default: openstack-config-secret + type: string + required: + - containerImage + type: object + type: object ovn: properties: enabled: @@ -13742,6 +13767,25 @@ spec: - swiftStorage type: object type: object + tls: + properties: + caSecretName: + type: string + internalEndpoints: + properties: + enabled: + default: true + type: boolean + type: object + publicEndpoints: + properties: + enabled: + default: true + type: boolean + issuer: + type: string + type: object + type: object required: - secret - storageClass diff --git a/apis/client/v1beta1/openstackclient_types.go b/apis/client/v1beta1/openstackclient_types.go index d12ff693d..b04b63773 100644 --- a/apis/client/v1beta1/openstackclient_types.go +++ b/apis/client/v1beta1/openstackclient_types.go @@ -15,6 +15,7 @@ package v1beta1 import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -31,16 +32,25 @@ type OpenStackClientSpec struct { // +kubebuilder:validation:Required // ContainerImage for the the OpenstackClient container (will be set to environmental default if empty) ContainerImage string `json:"containerImage"` - // +kubebuilder:validation:Required + + // +kubebuilder:validation:Optional + // +kubebuilder:default=openstack-config // OpenStackConfigMap is the name of the ConfigMap containing the clouds.yaml OpenStackConfigMap string `json:"openStackConfigMap"` - // +kubebuilder:validation:Required + + // +kubebuilder:validation:Optional + // +kubebuilder:default=openstack-config-secret // OpenStackConfigSecret is the name of the Secret containing the secure.yaml OpenStackConfigSecret string `json:"openStackConfigSecret"` // +kubebuilder:validation:Optional // NodeSelector to target subset of worker nodes running control plane services (currently only applies to KeystoneAPI and PlacementAPI) NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Secret containing any CA certificates which should be added to deployment pods + tls.Ca `json:",inline"` } // OpenStackClientStatus defines the observed state of OpenStackClient diff --git a/apis/client/v1beta1/zz_generated.deepcopy.go b/apis/client/v1beta1/zz_generated.deepcopy.go index 6d43290bf..b662b728e 100644 --- a/apis/client/v1beta1/zz_generated.deepcopy.go +++ b/apis/client/v1beta1/zz_generated.deepcopy.go @@ -110,6 +110,7 @@ func (in *OpenStackClientSpec) DeepCopyInto(out *OpenStackClientSpec) { (*out)[key] = val } } + out.Ca = in.Ca } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackClientSpec. diff --git a/apis/core/v1beta1/conditions.go b/apis/core/v1beta1/conditions.go index 900b30888..6a06ba06f 100644 --- a/apis/core/v1beta1/conditions.go +++ b/apis/core/v1beta1/conditions.go @@ -102,6 +102,9 @@ const ( // OpenStackControlPlaneDNSReadyCondition Status=True condition which indicates if DNSMasq is configured and operational OpenStackControlPlaneDNSReadyCondition condition.Type = "OpenStackControlPlaneDNSReadyCondition" + // OpenStackControlPlaneCAReadyCondition Status=True condition which indicates if the CAs are configured and operational + OpenStackControlPlaneCAReadyCondition condition.Type = "OpenStackControlPlaneCAReadyCondition" + // OpenStackControlPlaneCeilometerReadyCondition Status=True condition which indicates if OpenStack Ceilometer service is configured and operational OpenStackControlPlaneCeilometerReadyCondition condition.Type = "OpenStackControlPlaneCeilometerReady" @@ -384,4 +387,16 @@ const ( // OpenStackControlPlaneExposeServiceReadyMessage OpenStackControlPlaneExposeServiceReadyMessage = "OpenStackControlPlane %s service exposed" + + // OpenStackControlPlaneCAReadyInitMessage + OpenStackControlPlaneCAReadyInitMessage = "OpenStackControlPlane CAs not started" + + // OpenStackControlPlaneCAReadyMessage + OpenStackControlPlaneCAReadyMessage = "OpenStackControlPlane CAs completed" + + // OpenStackControlPlaneCAReadyRunningMessage + OpenStackControlPlaneCAReadyRunningMessage = "OpenStackControlPlane CAs in progress" + + // OpenStackControlPlaneCAReadyErrorMessage + OpenStackControlPlaneCAReadyErrorMessage = "OpenStackControlPlane CAs %s %s error occured %s" ) diff --git a/apis/core/v1beta1/openstackcontrolplane_types.go b/apis/core/v1beta1/openstackcontrolplane_types.go index dde7f7e87..29344b77b 100644 --- a/apis/core/v1beta1/openstackcontrolplane_types.go +++ b/apis/core/v1beta1/openstackcontrolplane_types.go @@ -27,6 +27,7 @@ import ( keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/route" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" "github.com/openstack-k8s-operators/lib-common/modules/common/util" "github.com/openstack-k8s-operators/lib-common/modules/storage" manilav1 "github.com/openstack-k8s-operators/manila-operator/api/v1beta1" @@ -34,6 +35,7 @@ import ( neutronv1 "github.com/openstack-k8s-operators/neutron-operator/api/v1beta1" novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" octaviav1 "github.com/openstack-k8s-operators/octavia-operator/api/v1beta1" + "github.com/openstack-k8s-operators/openstack-operator/apis/client/v1beta1" ovnv1 "github.com/openstack-k8s-operators/ovn-operator/api/v1beta1" placementv1 "github.com/openstack-k8s-operators/placement-operator/api/v1beta1" swiftv1 "github.com/openstack-k8s-operators/swift-operator/api/v1beta1" @@ -69,6 +71,11 @@ type OpenStackControlPlaneSpec struct { // NodeSelector to target subset of worker nodes running control plane services (currently only applies to KeystoneAPI and PlacementAPI) NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // +kubebuilder:validation:Optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS TLSSection `json:"tls,omitempty"` + // +kubebuilder:validation:Optional //+operator-sdk:csv:customresourcedefinitions:type=spec // DNS - Parameters related to the DNSMasq service @@ -158,6 +165,11 @@ type OpenStackControlPlaneSpec struct { // Redis - Parameters related to the Redis service Redis RedisSection `json:"redis,omitempty"` + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // OpenStackClient - Parameters related to the OpenStackClient + OpenStackClient OpenStackClientSection `json:"openstackclient,omitempty"` + // ExtraMounts containing conf files and credentials that should be provided // to the underlying operators. // This struct can be defined in the top level CR and propagated to the @@ -168,6 +180,47 @@ type OpenStackControlPlaneSpec struct { ExtraMounts []OpenStackExtraVolMounts `json:"extraMounts,omitempty"` } +// TLSSection defines the desired state of TLS configuration +type TLSSection struct { + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // PublicEndpoints tls configuration + PublicEndpoints TLSPublicEndpointSection `json:"publicEndpoints,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // InternalEndpoints tls configuration + InternalEndpoints TLSInternalEndpointSection `json:"internalEndpoints,omitempty"` + + // +kubebuilder:validation:optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Secret containing any additional CA certificates, which should be added to deployment pods + tls.Ca `json:",inline"` +} + +// TLSPublicEndpointSection defines the desired state of public TLSEndpoint configuration +type TLSPublicEndpointSection struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=true + // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:booleanSwitch"} + // Enabled - Whether TLS should be enabled for endpoint type + Enabled bool `json:"enabled"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // Issuer - cert-manager issuer to be used for the endpoint type. If not specified a self signed will be created. + Issuer *string `json:"issuer,omitempty"` +} + +// TLSInternalEndpointSection defines the desired state of internal TLSEndpoint configuration +type TLSInternalEndpointSection struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=true + // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:booleanSwitch"} + // Enabled - Whether TLS should be enabled for endpoint type + Enabled bool `json:"enabled"` +} + // DNSMasqSection defines the desired state of DNSMasq service type DNSMasqSection struct { // +kubebuilder:validation:Optional @@ -561,6 +614,20 @@ type RedisSection struct { Templates map[string]redisv1.RedisSpec `json:"templates,omitempty"` } +// OpenStackClientSection defines the desired state of the OpenStackClient +type OpenStackClientSection struct { + // +kubebuilder:validation:Optional + // Enabled - Whether the Redis service should be deployed and managed + // +kubebuilder:default=false + // +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:booleanSwitch"} + Enabled bool `json:"enabled"` + + // +kubebuilder:validation:Optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Template - Overrides to use when creating the OpenStackClient Resource + Template v1beta1.OpenStackClientSpec `json:"template,omitempty"` +} + // OpenStackControlPlaneStatus defines the observed state of OpenStackControlPlane type OpenStackControlPlaneStatus struct { //+operator-sdk:csv:customresourcedefinitions:type=status,xDescriptors={"urn:alm:descriptor:io.kubernetes.conditions"} @@ -642,6 +709,7 @@ func (instance *OpenStackControlPlane) InitConditions() { condition.UnknownCondition(OpenStackControlPlaneSwiftReadyCondition, condition.InitReason, OpenStackControlPlaneSwiftReadyInitMessage), condition.UnknownCondition(OpenStackControlPlaneOctaviaReadyCondition, condition.InitReason, OpenStackControlPlaneOctaviaReadyInitMessage), condition.UnknownCondition(OpenStackControlPlaneRedisReadyCondition, condition.InitReason, OpenStackControlPlaneRedisReadyInitMessage), + condition.UnknownCondition(OpenStackControlPlaneCAReadyCondition, condition.InitReason, OpenStackControlPlaneCAReadyInitMessage), // Also add the overall status condition as Unknown condition.UnknownCondition(condition.ReadyCondition, condition.InitReason, condition.ReadyInitMessage), diff --git a/apis/core/v1beta1/zz_generated.deepcopy.go b/apis/core/v1beta1/zz_generated.deepcopy.go index bbe5b7cdc..1985cb9ce 100644 --- a/apis/core/v1beta1/zz_generated.deepcopy.go +++ b/apis/core/v1beta1/zz_generated.deepcopy.go @@ -325,6 +325,22 @@ func (in *OctaviaSection) DeepCopy() *OctaviaSection { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenStackClientSection) DeepCopyInto(out *OpenStackClientSection) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackClientSection. +func (in *OpenStackClientSection) DeepCopy() *OpenStackClientSection { + if in == nil { + return nil + } + out := new(OpenStackClientSection) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenStackControlPlane) DeepCopyInto(out *OpenStackControlPlane) { *out = *in @@ -409,6 +425,7 @@ func (in *OpenStackControlPlaneSpec) DeepCopyInto(out *OpenStackControlPlaneSpec (*out)[key] = val } } + in.TLS.DeepCopyInto(&out.TLS) in.DNS.DeepCopyInto(&out.DNS) in.Keystone.DeepCopyInto(&out.Keystone) in.Placement.DeepCopyInto(&out.Placement) @@ -429,6 +446,7 @@ func (in *OpenStackControlPlaneSpec) DeepCopyInto(out *OpenStackControlPlaneSpec in.Swift.DeepCopyInto(&out.Swift) in.Octavia.DeepCopyInto(&out.Octavia) in.Redis.DeepCopyInto(&out.Redis) + in.OpenStackClient.DeepCopyInto(&out.OpenStackClient) if in.ExtraMounts != nil { in, out := &in.ExtraMounts, &out.ExtraMounts *out = make([]OpenStackExtraVolMounts, len(*in)) @@ -645,3 +663,56 @@ func (in *SwiftSection) DeepCopy() *SwiftSection { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSInternalEndpointSection) DeepCopyInto(out *TLSInternalEndpointSection) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSInternalEndpointSection. +func (in *TLSInternalEndpointSection) DeepCopy() *TLSInternalEndpointSection { + if in == nil { + return nil + } + out := new(TLSInternalEndpointSection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSPublicEndpointSection) DeepCopyInto(out *TLSPublicEndpointSection) { + *out = *in + if in.Issuer != nil { + in, out := &in.Issuer, &out.Issuer + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSPublicEndpointSection. +func (in *TLSPublicEndpointSection) DeepCopy() *TLSPublicEndpointSection { + if in == nil { + return nil + } + out := new(TLSPublicEndpointSection) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSSection) DeepCopyInto(out *TLSSection) { + *out = *in + in.PublicEndpoints.DeepCopyInto(&out.PublicEndpoints) + out.InternalEndpoints = in.InternalEndpoints + out.Ca = in.Ca +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSSection. +func (in *TLSSection) DeepCopy() *TLSSection { + if in == nil { + return nil + } + out := new(TLSSection) + in.DeepCopyInto(out) + return out +} diff --git a/apis/go.mod b/apis/go.mod index 8a442fb9f..1a29fad21 100644 --- a/apis/go.mod +++ b/apis/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/onsi/ginkgo/v2 v2.12.1 - github.com/onsi/gomega v1.27.10 + github.com/onsi/gomega v1.28.0 github.com/openstack-k8s-operators/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401 github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8 @@ -12,7 +12,7 @@ require ( github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 - github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 + github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20231001084618-12369665b166 github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230928152002-65395552e015 github.com/openstack-k8s-operators/neutron-operator/api v0.1.1-0.20230927135156-a3209100b1f3 @@ -55,7 +55,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.1 // indirect - github.com/gophercloud/gophercloud v1.6.0 // indirect + github.com/gophercloud/gophercloud v1.7.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -65,7 +65,7 @@ require ( 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/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 //indirect + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 //indirect github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.16.0 // indirect @@ -109,3 +109,11 @@ replace ( // 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-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/lib-common/modules/certmanager => github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e + +replace github.com/openstack-k8s-operators/horizon-operator/api => github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 diff --git a/apis/go.sum b/apis/go.sum index cb97e01a4..858a10ced 100644 --- a/apis/go.sum +++ b/apis/go.sum @@ -91,8 +91,8 @@ github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHa github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gophercloud/gophercloud v1.6.0 h1:JwJN1bauRnWPba5ueWs9IluONHteXPWjjK+MvfM4krY= -github.com/gophercloud/gophercloud v1.6.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= +github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -124,8 +124,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= 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/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b h1:BpSoGmbOxwJFGZFMHM3BxYZ+1TvZOZaHTQ/z14h4D8w= @@ -134,18 +134,12 @@ github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-a github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401/go.mod h1:U+jK0QoSnON5s4rA3cnhqIrBAMYq5bhVDQOoXym7+yE= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8 h1:cbtGPROaKwVB1bHmmGsde2WIB+IqfYz2UBnaYutpvCA= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8/go.mod h1:xxjs1gg1sfw8szoIQtUjHgh/wiicduipTzs5Rsp9Gao= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01 h1:RXdu3rtbLRn+8zdDPT/SPA7NFluO1R5y8NdBwjujYn8= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e h1:J6ufJlFvx57q0VX0bDs0Qy/pNHf1VRtOzrvWP4UYMqQ= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e/go.mod h1:mREHg4cn89igZAkvaMnrv+9YFWK2tDhK/HdqJPV/+hw= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 h1:3femyaSRudGF40dUEfp/Kn/MOdKpCvPfy5j85GAKQ2Y= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79/go.mod h1:NR5xmmZQz/v1EgGfSrL/4yCbQpRbaWLoIx1CgpGiWck= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 h1:eJofMw2iWLhz8fusIjOa5ck6yHR1zHhohCRAVfX5CCY= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984/go.mod h1:8d02elP1XA66ANyD+J0MkWIO7WqFKihqV10m+gFZq0o= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 h1:n5QmZLJfPtKbNnPVqqSQkLU1X/NMmW3CbML3yjBUjyY= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 h1:d3HP0nEGNd8cQ088beQbyWzcgdMppD+Zs8HeoMdzRws= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:+iJZo5alCeOGD/524hWWdlINA6zqY+MjfWT7cDcbvBE= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 h1:zOnRGMdgq2XvOCCtF1lY4tFhKx3jXrcrtOiZZ1PR6M8= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166/go.mod h1:LOXXvTQCwhOBNd+0FTlgllpa3wqlkI6Vf3Q5QVRVPlw= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 h1:epXz35gpdGp/CbYzAT926971T9kpO6r1v2GX8I1HHGM= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kKFAr7wZw3mX83hlQVbf2hV7TGhNNxVgMSNt9YtUPzI= github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d h1:mU35EkNUuS0MzswKxSq/yKGKiy+r8Yv4Z+k65vfRByM= @@ -199,6 +193,12 @@ 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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 h1:NbFgQkSuS+1FGK7y+PgmlzoE7BqfhUwK8DtooIKG908= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e h1:TEUaan+so+oBtqoMwoLRKDtX+kGfAVzyrh8S5nVHgAM= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e/go.mod h1:5v0ngxNmFp8QsINo2bufx1/COJc0q6jm3FMhP3xIAWE= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028 h1:5YBnqpChMqNM4D8o9jGkNT482ARP8Rb/njy6JijTefk= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= 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/config/crd/bases/client.openstack.org_openstackclients.yaml b/config/crd/bases/client.openstack.org_openstackclients.yaml index 753c8839f..08c34b789 100644 --- a/config/crd/bases/client.openstack.org_openstackclients.yaml +++ b/config/crd/bases/client.openstack.org_openstackclients.yaml @@ -36,6 +36,8 @@ spec: type: object spec: properties: + caSecretName: + type: string containerImage: type: string nodeSelector: @@ -43,13 +45,13 @@ spec: type: string type: object openStackConfigMap: + default: openstack-config type: string openStackConfigSecret: + default: openstack-config-secret type: string required: - containerImage - - openStackConfigMap - - openStackConfigSecret type: object status: properties: diff --git a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml index 0152515a5..cd6907883 100644 --- a/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml +++ b/config/crd/bases/core.openstack.org_openstackcontrolplanes.yaml @@ -8933,6 +8933,31 @@ spec: - secret type: object type: object + openstackclient: + properties: + enabled: + default: false + type: boolean + template: + properties: + caSecretName: + type: string + containerImage: + type: string + nodeSelector: + additionalProperties: + type: string + type: object + openStackConfigMap: + default: openstack-config + type: string + openStackConfigSecret: + default: openstack-config-secret + type: string + required: + - containerImage + type: object + type: object ovn: properties: enabled: @@ -13742,6 +13767,25 @@ spec: - swiftStorage type: object type: object + tls: + properties: + caSecretName: + type: string + internalEndpoints: + properties: + enabled: + default: true + type: boolean + type: object + publicEndpoints: + properties: + enabled: + default: true + type: boolean + issuer: + type: string + type: object + type: object required: - secret - storageClass diff --git a/config/manifests/bases/openstack-operator.clusterserviceversion.yaml b/config/manifests/bases/openstack-operator.clusterserviceversion.yaml index f9e92873d..68323be43 100644 --- a/config/manifests/bases/openstack-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/openstack-operator.clusterserviceversion.yaml @@ -237,6 +237,18 @@ spec: - description: Template - Overrides to use when creating Octavia Resources displayName: Template path: octavia.template + - description: OpenStackClient - Parameters related to the OpenStackClient + displayName: Open Stack Client + path: openstackclient + - description: Enabled - Whether the Redis service should be deployed and managed + displayName: Enabled + path: openstackclient.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Template - Overrides to use when creating the OpenStackClient + Resource + displayName: Template + path: openstackclient.template - description: Ovn - Overrides to use when creating the OVN Services displayName: Ovn path: ovn @@ -315,6 +327,29 @@ spec: - description: Template - Overrides to use when creating Swift Resources displayName: Template path: swift.template + - description: TLS - Parameters related to the TLS + displayName: TLS + path: tls + - description: InternalEndpoints tls configuration + displayName: Internal Endpoints + path: tls.internalEndpoints + - description: Enabled - Whether TLS should be enabled for endpoint type + displayName: Enabled + path: tls.internalEndpoints.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: PublicEndpoints tls configuration + displayName: Public Endpoints + path: tls.publicEndpoints + - description: Enabled - Whether TLS should be enabled for endpoint type + displayName: Enabled + path: tls.publicEndpoints.enabled + x-descriptors: + - urn:alm:descriptor:com.tectonic.ui:booleanSwitch + - description: Issuer - cert-manager issuer to be used for the endpoint type. + If not specified a self signed will be created. + displayName: Issuer + path: tls.publicEndpoints.issuer statusDescriptors: - description: Conditions displayName: Conditions diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 6c13715fa..1d3dd158b 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -27,6 +27,30 @@ rules: - list - update - watch +- apiGroups: + - cert-manager.io + resources: + - certificates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - cert-manager.io + resources: + - issuers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - cinder.openstack.org resources: @@ -78,8 +102,12 @@ rules: resources: - secrets verbs: + - create + - delete - get - list + - patch + - update - watch - apiGroups: - "" diff --git a/config/samples/core_v1beta1_openstackcontrolplane.yaml b/config/samples/core_v1beta1_openstackcontrolplane.yaml index 8ad4d51cd..4a1623aeb 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane.yaml @@ -5,6 +5,14 @@ metadata: spec: secret: osp-secret storageClass: local-storage + tls: + internalEndpoints: + enabled: false + publicEndpoints: + enabled: true + openstackclient: + template: + containerImage: quay.io/mschuppe/openstack-openstackclient:current-podified keystone: template: databaseInstance: openstack diff --git a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml index 4e1901f91..bc42ec9d4 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation.yaml @@ -5,6 +5,14 @@ metadata: spec: secret: osp-secret storageClass: local-storage + tls: + internalEndpoints: + enabled: false + publicEndpoints: + enabled: true + openstackclient: + template: + containerImage: quay.io/mschuppe/openstack-openstackclient:current-podified dns: template: override: 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 d71d83006..2bd1623bb 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_galera_network_isolation_3replicas.yaml @@ -5,6 +5,14 @@ metadata: spec: secret: osp-secret storageClass: local-storage + tls: + internalEndpoints: + enabled: false + publicEndpoints: + enabled: true + openstackclient: + template: + containerImage: quay.io/mschuppe/openstack-openstackclient:current-podified dns: template: override: diff --git a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml index 0619f35d2..bff45b8cb 100644 --- a/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml +++ b/config/samples/core_v1beta1_openstackcontrolplane_network_isolation.yaml @@ -5,6 +5,14 @@ metadata: spec: secret: osp-secret storageClass: local-storage + tls: + internalEndpoints: + enabled: false + publicEndpoints: + enabled: true + openstackclient: + template: + containerImage: quay.io/mschuppe/openstack-openstackclient:current-podified dns: template: override: diff --git a/controllers/client/openstackclient_controller.go b/controllers/client/openstackclient_controller.go index 5ecaf3fd4..6001a54a3 100644 --- a/controllers/client/openstackclient_controller.go +++ b/controllers/client/openstackclient_controller.go @@ -30,8 +30,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/configmap" + "github.com/openstack-k8s-operators/lib-common/modules/common/env" helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper" common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac" "github.com/openstack-k8s-operators/lib-common/modules/common/secret" @@ -172,6 +174,42 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{RequeueAfter: time.Duration(5) * time.Second}, nil } + clientLabels := map[string]string{ + common.AppSelector: "openstackclient", + } + + configVars := make(map[string]env.Setter) + + cms := []util.Template{ + // ConfigMap holding kolla config + { + Name: fmt.Sprintf("%s-config-data", instance.Name), + Namespace: instance.Namespace, + Type: util.TemplateTypeConfig, + InstanceType: instance.Kind, + ConfigOptions: nil, + Labels: clientLabels, + }, + } + err = configmap.EnsureConfigMaps(ctx, helper, instance, cms, &configVars) + if err != nil { + if k8s_errors.IsNotFound(err) { + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + clientv1.OpenStackClientConfigMapWaitingMessage)) + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil + } + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + clientv1.OpenStackClientReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + _, configMapHash, err := configmap.GetConfigMapAndHashWithName(ctx, helper, instance.Spec.OpenStackConfigMap, instance.Namespace) if err != nil { if k8s_errors.IsNotFound(err) { @@ -190,6 +228,7 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ err.Error())) return ctrl.Result{}, err } + configVars[instance.Spec.OpenStackConfigMap] = env.SetValue(configMapHash) _, secretHash, err := secret.GetSecret(ctx, helper, instance.Spec.OpenStackConfigSecret, instance.Namespace) if err != nil { @@ -209,6 +248,34 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ err.Error())) return ctrl.Result{}, err } + configVars[instance.Spec.OpenStackConfigSecret] = env.SetValue(secretHash) + + if instance.Spec.CaSecretName != "" { + _, secretHash, err = secret.GetSecret(ctx, helper, instance.Spec.OpenStackConfigSecret, instance.Namespace) + if err != nil { + if k8s_errors.IsNotFound(err) { + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + clientv1.OpenStackClientSecretWaitingMessage)) + return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil + } + instance.Status.Conditions.Set(condition.FalseCondition( + clientv1.OpenStackClientReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + clientv1.OpenStackClientReadyErrorMessage, + err.Error())) + return ctrl.Result{}, err + } + configVars[instance.Spec.OpenStackConfigSecret] = env.SetValue(secretHash) + } + + configVarsHash, err := util.HashOfInputHashes(configVars) + if err != nil { + return ctrl.Result{}, err + } instance.Status.Conditions.Set(condition.FalseCondition( clientv1.OpenStackClientReadyCondition, @@ -216,10 +283,7 @@ func (r *OpenStackClientReconciler) Reconcile(ctx context.Context, req ctrl.Requ condition.SeverityInfo, clientv1.OpenStackClientInputReady)) - clientLabels := map[string]string{ - "app": "openstackclient", - } - pod := openstackclient.ClientPod(instance, clientLabels, configMapHash, secretHash) + pod := openstackclient.ClientPod(instance, clientLabels, configVarsHash) op, err := controllerutil.CreateOrPatch(ctx, r.Client, pod, func() error { pod.Spec.Containers[0].Image = instance.Spec.ContainerImage diff --git a/controllers/core/openstackcontrolplane_controller.go b/controllers/core/openstackcontrolplane_controller.go index 2ce0515f1..fc74de612 100644 --- a/controllers/core/openstackcontrolplane_controller.go +++ b/controllers/core/openstackcontrolplane_controller.go @@ -94,6 +94,9 @@ type OpenStackControlPlaneReconciler struct { //+kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete; //+kubebuilder:rbac:groups=route.openshift.io,resources=routes/custom-host,verbs=create;update;patch //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list; +//+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete; +//+kubebuilder:rbac:groups=cert-manager.io,resources=issuers,verbs=get;list;watch;create;update;patch;delete; +//+kubebuilder:rbac:groups=cert-manager.io,resources=certificates,verbs=get;list;watch;create;update;patch;delete; // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -164,7 +167,14 @@ func (r *OpenStackControlPlaneReconciler) Reconcile(ctx context.Context, req ctr func (r *OpenStackControlPlaneReconciler) reconcileNormal(ctx context.Context, instance *corev1beta1.OpenStackControlPlane, helper *helper.Helper) (ctrl.Result, error) { - ctrlResult, err := openstack.ReconcileDNSMasqs(ctx, instance, helper) + ctrlResult, err := openstack.ReconcileCAs(ctx, instance, helper) + if err != nil { + return ctrl.Result{}, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + ctrlResult, err = openstack.ReconcileDNSMasqs(ctx, instance, helper) if err != nil { return ctrl.Result{}, err } else if (ctrlResult != ctrl.Result{}) { diff --git a/go.mod b/go.mod index e1e53abd7..4fa1dbb27 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,12 @@ module github.com/openstack-k8s-operators/openstack-operator go 1.19 require ( + github.com/cert-manager/cert-manager v1.11.5 github.com/ghodss/yaml v1.0.0 github.com/go-logr/logr v1.2.4 github.com/imdario/mergo v0.3.16 github.com/onsi/ginkgo/v2 v2.12.1 - github.com/onsi/gomega v1.27.10 + github.com/onsi/gomega v1.28.0 github.com/openstack-k8s-operators/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b github.com/openstack-k8s-operators/dataplane-operator/api v0.1.1-0.20230928164440-a955424c43ef github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401 @@ -16,7 +17,8 @@ require ( github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 - github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 + github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.0.0-20231001084618-12369665b166 + github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20231001084618-12369665b166 github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230928152002-65395552e015 github.com/openstack-k8s-operators/neutron-operator/api v0.1.1-0.20230927135156-a3209100b1f3 @@ -46,6 +48,7 @@ require ( github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.2.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/tools v0.13.0 // indirect + sigs.k8s.io/gateway-api v0.6.0 // indirect ) require ( @@ -66,7 +69,7 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.1 // indirect - github.com/gophercloud/gophercloud v1.6.0 // indirect + github.com/gophercloud/gophercloud v1.7.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -75,7 +78,7 @@ require ( 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 - github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 //indirect + github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 //indirect github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 //indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.16.0 // indirect @@ -121,3 +124,11 @@ replace github.com/openstack-k8s-operators/openstack-operator/apis => ./apis // 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-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/lib-common/modules/certmanager => github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028 + +replace github.com/openstack-k8s-operators/keystone-operator/api => github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e + +replace github.com/openstack-k8s-operators/horizon-operator/api => github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 diff --git a/go.sum b/go.sum index aacff7d8c..37d120805 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cert-manager/cert-manager v1.11.5 h1:K2LurvwIE4hIhODQZnkOW6ljYe3lVMAliS/to+gI05o= +github.com/cert-manager/cert-manager v1.11.5/go.mod h1:zNOyoTEwdn9Rtj5Or2pjBY1Bqwtw4vBElP2fKSP8/g8= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -94,8 +96,8 @@ github.com/google/pprof v0.0.0-20230510103437-eeec1cb781c3/go.mod h1:79YE0hCXdHa github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gophercloud/gophercloud v1.6.0 h1:JwJN1bauRnWPba5ueWs9IluONHteXPWjjK+MvfM4krY= -github.com/gophercloud/gophercloud v1.6.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= +github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -131,8 +133,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= +github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= 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/cinder-operator/api v0.1.2-0.20230927112537-42169dd3965b h1:BpSoGmbOxwJFGZFMHM3BxYZ+1TvZOZaHTQ/z14h4D8w= @@ -143,18 +145,12 @@ github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-a github.com/openstack-k8s-operators/glance-operator/api v0.1.2-0.20230928094722-adaea00db401/go.mod h1:U+jK0QoSnON5s4rA3cnhqIrBAMYq5bhVDQOoXym7+yE= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8 h1:cbtGPROaKwVB1bHmmGsde2WIB+IqfYz2UBnaYutpvCA= github.com/openstack-k8s-operators/heat-operator/api v0.1.1-0.20230928022009-63f4c93693b8/go.mod h1:xxjs1gg1sfw8szoIQtUjHgh/wiicduipTzs5Rsp9Gao= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01 h1:RXdu3rtbLRn+8zdDPT/SPA7NFluO1R5y8NdBwjujYn8= -github.com/openstack-k8s-operators/horizon-operator/api v0.1.1-0.20230927133505-e0a30ad65a01/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e h1:J6ufJlFvx57q0VX0bDs0Qy/pNHf1VRtOzrvWP4UYMqQ= github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230927145535-98de8aa29c2e/go.mod h1:mREHg4cn89igZAkvaMnrv+9YFWK2tDhK/HdqJPV/+hw= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79 h1:3femyaSRudGF40dUEfp/Kn/MOdKpCvPfy5j85GAKQ2Y= github.com/openstack-k8s-operators/ironic-operator/api v0.1.1-0.20230927134553-8c5a9c47ca79/go.mod h1:NR5xmmZQz/v1EgGfSrL/4yCbQpRbaWLoIx1CgpGiWck= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984 h1:eJofMw2iWLhz8fusIjOa5ck6yHR1zHhohCRAVfX5CCY= -github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230927105404-11cb6a68c984/go.mod h1:8d02elP1XA66ANyD+J0MkWIO7WqFKihqV10m+gFZq0o= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17 h1:n5QmZLJfPtKbNnPVqqSQkLU1X/NMmW3CbML3yjBUjyY= -github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17 h1:d3HP0nEGNd8cQ088beQbyWzcgdMppD+Zs8HeoMdzRws= -github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:+iJZo5alCeOGD/524hWWdlINA6zqY+MjfWT7cDcbvBE= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166 h1:zOnRGMdgq2XvOCCtF1lY4tFhKx3jXrcrtOiZZ1PR6M8= +github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20231001084618-12369665b166/go.mod h1:LOXXvTQCwhOBNd+0FTlgllpa3wqlkI6Vf3Q5QVRVPlw= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17 h1:epXz35gpdGp/CbYzAT926971T9kpO6r1v2GX8I1HHGM= github.com/openstack-k8s-operators/lib-common/modules/storage v0.1.1-0.20230927082538-4f614f333d17/go.mod h1:kKFAr7wZw3mX83hlQVbf2hV7TGhNNxVgMSNt9YtUPzI= github.com/openstack-k8s-operators/manila-operator/api v0.1.1-0.20230928195132-c19d2f673d7d h1:mU35EkNUuS0MzswKxSq/yKGKiy+r8Yv4Z+k65vfRByM= @@ -216,6 +212,14 @@ 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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0 h1:NbFgQkSuS+1FGK7y+PgmlzoE7BqfhUwK8DtooIKG908= +github.com/stuggi/horizon-operator/api v0.0.0-20230929044853-2b97c45d10c0/go.mod h1:mA0trgqY20GlCn7Q5hLeRv5lozsF5Y+ZPCFTsjd5mjE= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e h1:TEUaan+so+oBtqoMwoLRKDtX+kGfAVzyrh8S5nVHgAM= +github.com/stuggi/keystone-operator/api v0.0.0-20231002064623-6a3a240e6a0e/go.mod h1:5v0ngxNmFp8QsINo2bufx1/COJc0q6jm3FMhP3xIAWE= +github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028 h1:xGMg0VEW/pocBqHvh1ThLG4MauSAkpvGCrzSY29+lfk= +github.com/stuggi/lib-common/modules/certmanager v0.0.0-20230929092549-f68a217ec028/go.mod h1:BCSnLQLasZ03YaLIvKPKjY5DZNHtQASTOSJF/79ZSLc= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028 h1:5YBnqpChMqNM4D8o9jGkNT482ARP8Rb/njy6JijTefk= +github.com/stuggi/lib-common/modules/common v0.0.0-20230929092549-f68a217ec028/go.mod h1:kZS5rqVWBZeCyYor2PeQB9IEZ19mGaeL/to3x8F9OJg= 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= @@ -383,6 +387,8 @@ k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSn k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +sigs.k8s.io/gateway-api v0.6.0 h1:v2FqrN2ROWZLrSnI2o91taHR8Sj3s+Eh3QU7gLNWIqA= +sigs.k8s.io/gateway-api v0.6.0/go.mod h1:EYJT+jlPWTeNskjV0JTki/03WX1cyAnBhwBJfYHpV/0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= diff --git a/main.go b/main.go index 8d2816934..346521765 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" + certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" cinderv1 "github.com/openstack-k8s-operators/cinder-operator/api/v1beta1" dataplanev1beta1 "github.com/openstack-k8s-operators/dataplane-operator/api/v1beta1" glancev1 "github.com/openstack-k8s-operators/glance-operator/api/v1beta1" @@ -102,6 +103,7 @@ func init() { utilruntime.Must(clientv1.AddToScheme(scheme)) utilruntime.Must(redisv1.AddToScheme(scheme)) utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(certmgrv1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/pkg/openstack/ca.go b/pkg/openstack/ca.go new file mode 100644 index 000000000..4fa69ee17 --- /dev/null +++ b/pkg/openstack/ca.go @@ -0,0 +1,275 @@ +package openstack + +import ( + "context" + "time" + + certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" + certmgrmetav1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + "github.com/openstack-k8s-operators/lib-common/modules/certmanager" + "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/secret" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" + + corev1 "github.com/openstack-k8s-operators/openstack-operator/apis/core/v1beta1" + + ctrl "sigs.k8s.io/controller-runtime" +) + +const ( + // CombinedCASecret - + CombinedCASecret = "combined-ca-bundle" + // DefaultPublicCAName - + DefaultPublicCAName = "rootca-" + string(service.EndpointPublic) + // DefaultInternalCAName - + DefaultInternalCAName = "rootca-" + string(service.EndpointInternal) +) + +// ReconcileCAs - +func ReconcileCAs(ctx context.Context, instance *corev1.OpenStackControlPlane, helper *helper.Helper) (ctrl.Result, error) { + // create selfsigned-issuer + issuerReq := certmanager.SelfSignedIssuer( + "selfsigned-issuer", + instance.GetNamespace(), + map[string]string{}, + ) + /* + // Cleanuo? + if !instance.Spec.TLS.Enabled { + if err := cert.Delete(ctx, helper); err != nil { + return ctrl.Result{}, err + } + instance.Status.Conditions.Remove(corev1beta1.OpenStackControlPlaneCAsReadyCondition) + + return ctrl.Result{}, nil + } + */ + + helper.GetLogger().Info("Reconciling CAs", "Namespace", instance.Namespace, "Name", issuerReq.Name) + + issuer := certmanager.NewIssuer(issuerReq, 5) + ctrlResult, err := issuer.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + issuerReq.Kind, + issuerReq.GetName(), + err.Error())) + + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return ctrlResult, nil + } + + caCerts := map[string]string{} + + // create RootCA cert and Issuer that uses the generated CA certificate to issue certs + if instance.Spec.TLS.PublicEndpoints.Enabled && instance.Spec.TLS.PublicEndpoints.Issuer == nil { + caCert, ctrlResult, err := createRootCACertAndIssuer(ctx, instance, helper, issuerReq, DefaultPublicCAName) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + caCerts[DefaultPublicCAName] = string(caCert) + } + if instance.Spec.TLS.InternalEndpoints.Enabled { + caCert, ctrlResult, err := createRootCACertAndIssuer(ctx, instance, helper, issuerReq, DefaultInternalCAName) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + caCerts[DefaultInternalCAName] = string(caCert) + } + instance.Status.Conditions.MarkTrue(corev1.OpenStackControlPlaneCAReadyCondition, corev1.OpenStackControlPlaneCAReadyMessage) + + // create/update combined CA secret + if instance.Spec.TLS.CaSecretName != "" { + caSecret, _, err := secret.GetSecret(ctx, helper, instance.Spec.TLS.CaSecretName, instance.Namespace) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + "secret", + instance.Spec.TLS.CaSecretName, + err.Error())) + + return ctrlResult, err + } + + for key, ca := range caSecret.Data { + key := instance.Spec.TLS.CaSecretName + "-" + key + caCerts[key] = string(ca) + } + } + + saSecretTemplate := []util.Template{ + { + Name: CombinedCASecret, + Namespace: instance.Namespace, + Type: util.TemplateTypeNone, + InstanceType: instance.Kind, + AdditionalTemplate: nil, + Annotations: map[string]string{}, + Labels: map[string]string{ + CombinedCASecret: "", + }, + ConfigOptions: nil, + CustomData: caCerts, + }, + } + + if err := secret.EnsureSecrets(ctx, helper, instance, saSecretTemplate, nil); err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + "secret", + CombinedCASecret, + err.Error())) + + return ctrlResult, err + } + + return ctrl.Result{}, nil +} + +func createRootCACertAndIssuer( + ctx context.Context, + instance *corev1.OpenStackControlPlane, + helper *helper.Helper, + selfsignedIssuerReq *certmgrv1.Issuer, + caName string, +) (string, ctrl.Result, error) { + var caCert string + // create RootCA Certificate used to sign certificates + caCertReq := certmanager.Cert( + caName, + instance.Namespace, + map[string]string{}, + certmgrv1.CertificateSpec{ + IsCA: true, + CommonName: caName, + SecretName: caName, + PrivateKey: &certmgrv1.CertificatePrivateKey{ + Algorithm: "ECDSA", + Size: 256, + }, + IssuerRef: certmgrmetav1.ObjectReference{ + Name: selfsignedIssuerReq.Name, + Kind: selfsignedIssuerReq.Kind, + Group: selfsignedIssuerReq.GroupVersionKind().Group, + }, + }) + cert := certmanager.NewCertificate(caCertReq, 5) + + ctrlResult, err := cert.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + caCertReq.Kind, + caCertReq.Name, + err.Error())) + + return caCert, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return caCert, ctrlResult, nil + } + + // create Issuer that uses the generated CA certificate to issue certs + issuerReq := certmanager.CAIssuer( + caCertReq.Name, + instance.GetNamespace(), + map[string]string{}, + caCertReq.Name, + ) + + issuer := certmanager.NewIssuer(issuerReq, 5) + ctrlResult, err = issuer.CreateOrPatch(ctx, helper) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + issuerReq.Kind, + issuerReq.GetName(), + err.Error())) + + return caCert, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return caCert, ctrlResult, nil + } + + caCert, ctrlResult, err = getCAFromSecret(ctx, instance, helper, caName) + if err != nil { + return caCert, ctrl.Result{}, err + } else if (ctrlResult != ctrl.Result{}) { + return caCert, ctrlResult, nil + } + + return caCert, ctrl.Result{}, nil +} + +func getCAFromSecret( + ctx context.Context, + instance *corev1.OpenStackControlPlane, + helper *helper.Helper, + caName string, +) (string, ctrl.Result, error) { + caSecret, ctrlResult, err := secret.GetDataFromSecret(ctx, helper, caName, time.Duration(5), "ca.crt") + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneCAReadyErrorMessage, + "secret", + caName, + err.Error())) + + return caSecret, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + instance.Status.Conditions.Set(condition.FalseCondition( + corev1.OpenStackControlPlaneCAReadyCondition, + condition.RequestedReason, + condition.SeverityInfo, + corev1.OpenStackControlPlaneCAReadyRunningMessage)) + + return caSecret, ctrlResult, nil + } + + return caSecret, ctrl.Result{}, nil +} diff --git a/pkg/openstack/cinder.go b/pkg/openstack/cinder.go index a0be02e74..547e0a9e0 100644 --- a/pkg/openstack/cinder.go +++ b/pkg/openstack/cinder.go @@ -68,7 +68,7 @@ func ReconcileCinder(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Cinder.Template.CinderAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Cinder.Template.CinderAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/common.go b/pkg/openstack/common.go index b4f9c6ffb..b761e1c24 100644 --- a/pkg/openstack/common.go +++ b/pkg/openstack/common.go @@ -6,6 +6,7 @@ import ( "time" routev1 "github.com/openshift/api/route/v1" + "github.com/openstack-k8s-operators/lib-common/modules/certmanager" "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" @@ -13,9 +14,11 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/service" "github.com/openstack-k8s-operators/lib-common/modules/common/util" corev1 "github.com/openstack-k8s-operators/openstack-operator/apis/core/v1beta1" + k8s_corev1 "k8s.io/api/core/v1" 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" "sigs.k8s.io/controller-runtime/pkg/client" @@ -52,17 +55,37 @@ func AddServiceComponentLabel(svcOverride service.RoutedOverrideSpec, value stri return svcOverride } +// EndpointDetails - endpoint details +type EndpointDetails struct { + Name string + Namespace string + Type service.Endpoint + Labels map[string]string + Service ServiceDetails + Route RouteDetails + Hostname *string + EndpointURL string + TLS TLSDetails +} + +// TLSDetails - tls settings for the endpoint +type TLSDetails struct { + Issuer *string + PublicEndpoint bool + InternalEndpoint bool + InternalCA string +} + +// ServiceDetails - service details +type ServiceDetails struct { + Spec *k8s_corev1.Service + OverrideSpec service.RoutedOverrideSpec +} + // RouteDetails - route details type RouteDetails struct { - RouteName string - Namespace string - Endpoint service.Endpoint - RouteOverrideSpec *route.OverrideSpec - ServiceLabel map[string]string - ServiceSpec *k8s_corev1.Service - endpointURL string - hostname *string - route *routev1.Route + Route *routev1.Route + OverrideSpec route.OverrideSpec } // GetRoutesListWithLabel - Get all routes in namespace of the obj matching label selector @@ -86,166 +109,267 @@ func GetRoutesListWithLabel( return routeList, nil } -// EnsureRoute - -func EnsureRoute( +// EnsureEndpointConfig - +func EnsureEndpointConfig( ctx context.Context, instance *corev1.OpenStackControlPlane, helper *helper.Helper, owner metav1.Object, svcs *k8s_corev1.ServiceList, svcOverrides map[service.Endpoint]service.RoutedOverrideSpec, - overrideSpec *route.OverrideSpec, + routeOverrideSpec *route.OverrideSpec, condType condition.Type, ) (map[service.Endpoint]service.RoutedOverrideSpec, ctrl.Result, error) { + for _, svc := range svcs.Items { + ed := EndpointDetails{ + Name: svc.Name, + Namespace: svc.Namespace, + Type: service.Endpoint(svc.Annotations[service.AnnotationEndpointKey]), + Service: ServiceDetails{ + Spec: &svc, + }, + } + if routeOverrideSpec != nil { + ed.Route.OverrideSpec = *routeOverrideSpec + } + + // check if there is a custom issuer name reference provided + if ed.Type == service.EndpointPublic && instance.Spec.TLS.PublicEndpoints.Enabled { + if instance.Spec.TLS.PublicEndpoints.Issuer != nil { + ed.TLS.Issuer = instance.Spec.TLS.PublicEndpoints.Issuer + } else { + ed.TLS.Issuer = ptr.To(DefaultPublicCAName) + } + ed.TLS.PublicEndpoint = true + } + if ed.Type == service.EndpointInternal && instance.Spec.TLS.InternalEndpoints.Enabled { + ed.TLS.Issuer = ptr.To(DefaultInternalCAName) + ed.TLS.InternalEndpoint = true + // TODO: for TLSE create TLS cert for service + } - cleanCondition := true + ed.Service.OverrideSpec = svcOverrides[ed.Type] - for _, svc := range svcs.Items { - rd := RouteDetails{ - RouteName: svc.Name, - Namespace: svc.Namespace, - Endpoint: service.Endpoint(svc.Annotations[service.AnnotationEndpointKey]), - RouteOverrideSpec: overrideSpec, - ServiceSpec: &svc, - } - svcOverride := svcOverrides[rd.Endpoint] - - // check if there is already a route with common.AppSelector from the service - if svcLabelVal, ok := svc.Labels[common.AppSelector]; ok { - routes, err := GetRoutesListWithLabel( - ctx, - helper, - instance.Namespace, - map[string]string{common.AppSelector: svcLabelVal}, - ) + if ed.Type == service.EndpointPublic { + ctrlResult, err := ed.ensureRoute(ctx, instance, helper, &svc, owner, condType) if err != nil { - return svcOverrides, ctrl.Result{}, err + return svcOverrides, ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return svcOverrides, ctrlResult, nil } + } - // check the routes if name changed where we are the owner - for _, r := range routes.Items { - instanceRef := metav1.OwnerReference{ - APIVersion: instance.APIVersion, - Kind: instance.Kind, - Name: instance.GetName(), - UID: instance.GetUID(), - BlockOwnerDeletion: ptr.To(true), - Controller: ptr.To(true), - } + // update override for the service with the endpoint url + if ed.EndpointURL != "" { + // Any trailing path will be added on the service-operator level. + ed.Service.OverrideSpec.EndpointURL = &ed.EndpointURL + instance.Status.Conditions.MarkTrue(condType, corev1.OpenStackControlPlaneExposeServiceReadyMessage, owner.GetName()) + } - owner := metav1.GetControllerOf(&r.ObjectMeta) - - // Delete the route if the service was changed not to expose a route - if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "false" && - r.Spec.To.Name == svc.Name && - owner != nil && owner.UID == instance.GetUID() { - // Delete any other owner refs from ref list to not block deletion until owners are gone - r.SetOwnerReferences([]metav1.OwnerReference{instanceRef}) - - // Delete route - err := helper.GetClient().Delete(ctx, &r) - if err != nil && !k8s_errors.IsNotFound(err) { - err = fmt.Errorf("Error deleting route %s: %w", r.Name, err) - return svcOverrides, ctrl.Result{}, err - } - - if svcOverride.EndpointURL != nil { - svcOverride.EndpointURL = nil - helper.GetLogger().Info(fmt.Sprintf("Service %s override endpointURL removed", svc.Name)) - } - } - } + svcOverrides[ed.Type] = ed.Service.OverrideSpec + } + + return svcOverrides, ctrl.Result{}, nil +} + +func (ed *EndpointDetails) ensureRoute( + ctx context.Context, + instance *corev1.OpenStackControlPlane, + helper *helper.Helper, + svc *k8s_corev1.Service, + owner metav1.Object, + condType condition.Type, +) (ctrl.Result, error) { + // check if there is already a route with common.AppSelector from the service + if svcLabelVal, ok := svc.Labels[common.AppSelector]; ok { + routes, err := GetRoutesListWithLabel( + ctx, + helper, + instance.Namespace, + map[string]string{common.AppSelector: svcLabelVal}, + ) + if err != nil { + return ctrl.Result{}, err } - // If the service has the create ingress annotation and its a default ClusterIP service -> create route - if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "true" && svc.Spec.Type == k8s_corev1.ServiceTypeClusterIP { - if instance.Status.Conditions.Get(condType) == nil { - instance.Status.Conditions.Set(condition.UnknownCondition( - condType, - condition.InitReason, - corev1.OpenStackControlPlaneExposeServiceReadyInitMessage, - owner.GetName(), - svc.Name, - )) + // check the routes if name changed where we are the owner + for _, r := range routes.Items { + instanceRef := metav1.OwnerReference{ + APIVersion: instance.APIVersion, + Kind: instance.Kind, + Name: instance.GetName(), + UID: instance.GetUID(), + BlockOwnerDeletion: ptr.To(true), + Controller: ptr.To(true), } - if svcOverride.EmbeddedLabelsAnnotations == nil { - svcOverride.EmbeddedLabelsAnnotations = &service.EmbeddedLabelsAnnotations{} - } + owner := metav1.GetControllerOf(&r.ObjectMeta) - if labelVal, ok := svcOverride.EmbeddedLabelsAnnotations.Labels[common.AppSelector]; ok { - rd.ServiceLabel = map[string]string{common.AppSelector: labelVal} - } + // Delete the route if the service was changed not to expose a route + if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "false" && + r.Spec.To.Name == ed.Name && + owner != nil && owner.UID == instance.GetUID() { + // Delete any other owner refs from ref list to not block deletion until owners are gone + r.SetOwnerReferences([]metav1.OwnerReference{instanceRef}) - ctrlResult, err := rd.CreateRoute(ctx, helper, owner) - if err != nil { - instance.Status.Conditions.Set(condition.FalseCondition( - condType, - condition.ErrorReason, - condition.SeverityWarning, - corev1.OpenStackControlPlaneExposeServiceReadyErrorMessage, - owner.GetName(), - rd.RouteName, - err.Error())) + // Delete route + err := helper.GetClient().Delete(ctx, &r) + if err != nil && !k8s_errors.IsNotFound(err) { + err = fmt.Errorf("Error deleting route %s: %w", r.Name, err) + return ctrl.Result{}, err + } - return svcOverrides, ctrlResult, err - } else if (ctrlResult != ctrl.Result{}) { - return svcOverrides, ctrlResult, nil + if ed.Service.OverrideSpec.EndpointURL != nil { + ed.Service.OverrideSpec.EndpointURL = nil + helper.GetLogger().Info(fmt.Sprintf("Service %s override endpointURL removed", svc.Name)) + } } + } + } - cleanCondition = false + // If the service has the create ingress annotation and its a default ClusterIP service -> create route + if svc.ObjectMeta.Annotations[service.AnnotationIngressCreateKey] == "true" && svc.Spec.Type == k8s_corev1.ServiceTypeClusterIP { + if instance.Status.Conditions.Get(condType) == nil { + instance.Status.Conditions.Set(condition.UnknownCondition( + condType, + condition.InitReason, + corev1.OpenStackControlPlaneExposeServiceReadyInitMessage, + owner.GetName(), + svc.Name, + )) + } + if ed.Service.OverrideSpec.EmbeddedLabelsAnnotations == nil { + ed.Service.OverrideSpec.EmbeddedLabelsAnnotations = &service.EmbeddedLabelsAnnotations{} + } - // update override for the service with the route endpoint url - if rd.endpointURL != "" { - // Any trailing path will be added on the service-operator level. - svcOverride.EndpointURL = &rd.endpointURL - instance.Status.Conditions.MarkTrue(condType, corev1.OpenStackControlPlaneExposeServiceReadyMessage, owner.GetName()) - } + if labelVal, ok := ed.Service.OverrideSpec.EmbeddedLabelsAnnotations.Labels[common.AppSelector]; ok { + ed.Labels = map[string]string{common.AppSelector: labelVal} } - svcOverrides[rd.Endpoint] = svcOverride - } + ctrlResult, err := ed.CreateRoute(ctx, helper, owner) + if err != nil { + instance.Status.Conditions.Set(condition.FalseCondition( + condType, + condition.ErrorReason, + condition.SeverityWarning, + corev1.OpenStackControlPlaneExposeServiceReadyErrorMessage, + owner.GetName(), + ed.Name, + err.Error())) + + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } - if cleanCondition { + return ctrl.Result{}, nil + } else { instance.Status.Conditions.Remove(condType) } - return svcOverrides, ctrl.Result{}, nil + return ctrl.Result{}, nil } // CreateRoute - -func (rd *RouteDetails) CreateRoute( +func (ed *EndpointDetails) CreateRoute( ctx context.Context, helper *helper.Helper, owner metav1.Object, ) (ctrl.Result, error) { - // TODO TLS - route, err := route.NewRoute( + // initialize the route with any custom provided route override + enptRoute, err := route.NewRoute( route.GenericRoute(&route.GenericRouteDetails{ - Name: rd.RouteName, - Namespace: rd.Namespace, - Labels: rd.ServiceLabel, - ServiceName: rd.ServiceSpec.Name, - TargetPortName: rd.ServiceSpec.Name, + Name: ed.Name, + Namespace: ed.Namespace, + Labels: ed.Labels, + ServiceName: ed.Service.Spec.Name, + TargetPortName: ed.Service.Spec.Name, }), time.Duration(5)*time.Second, - rd.RouteOverrideSpec, + []route.OverrideSpec{ed.Route.OverrideSpec}, ) if err != nil { return ctrl.Result{}, err } - route.OwnerReferences = append(route.OwnerReferences, owner) + enptRoute.OwnerReferences = append(enptRoute.OwnerReferences, owner) + + // if route TLS is disabled -> create the route + // if TLS is enabled and the route does not yet exist -> create the route + // to get the hostname for creating the cert + serviceRoute := &routev1.Route{} + err = helper.GetClient().Get(ctx, types.NamespacedName{Name: ed.Name, Namespace: ed.Namespace}, serviceRoute) + if (ed.TLS.PublicEndpoint && err != nil && k8s_errors.IsNotFound(err)) || !ed.TLS.PublicEndpoint { + ctrlResult, err := enptRoute.CreateOrPatch(ctx, helper) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } - ctrlResult, err := route.CreateOrPatch(ctx, helper) - if err != nil { - return ctrlResult, err - } else if (ctrlResult != ctrl.Result{}) { - return ctrlResult, nil + ed.Hostname = ptr.To(enptRoute.GetHostname()) + } else if err != nil { + return ctrl.Result{}, err + } else { + ed.Hostname = &serviceRoute.Spec.Host } - rd.hostname = ptr.To(route.GetHostname()) - rd.endpointURL = "http://" + *rd.hostname - rd.route = route.GetRoute() + // if the issuer is provided TLS is enabled + if ed.TLS.Issuer != nil { + //create the cert + certSecret, ctrlResult, err := certmanager.EnsureCert( + ctx, + helper, + *ed.TLS.Issuer, + ed.Name, + nil, + []string{*ed.Hostname}, ed.Labels) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + // create default TLS route override + tlsConfig := &routev1.TLSConfig{ + Termination: routev1.TLSTerminationEdge, + Certificate: string(certSecret.Data["tls.crt"]), + Key: string(certSecret.Data["tls.key"]), + CACertificate: string(certSecret.Data["ca.crt"]), + InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyRedirect, + } + // for TLSE use routev1.TLSTerminationReencrypt + if ed.TLS.InternalEndpoint { + tlsConfig.Termination = routev1.TLSTerminationReencrypt + tlsConfig.DestinationCACertificate = ed.TLS.InternalCA + } + + enptRoute, err = route.NewRoute( + enptRoute.GetRoute(), + time.Duration(5)*time.Second, + []route.OverrideSpec{ + { + Spec: &route.Spec{ + TLS: tlsConfig, + }, + }, + ed.Route.OverrideSpec, + }, + ) + if err != nil { + return ctrl.Result{}, err + } + + ctrlResult, err = enptRoute.CreateOrPatch(ctx, helper) + if err != nil { + return ctrlResult, err + } else if (ctrlResult != ctrl.Result{}) { + return ctrlResult, nil + } + + ed.EndpointURL = "https://" + *ed.Hostname + } else { + ed.EndpointURL = "http://" + *ed.Hostname + } return ctrl.Result{}, nil } diff --git a/pkg/openstack/glance.go b/pkg/openstack/glance.go index 447235f64..eec84b8d9 100644 --- a/pkg/openstack/glance.go +++ b/pkg/openstack/glance.go @@ -72,7 +72,7 @@ func ReconcileGlance(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - serviceOverrides, ctrlResult, err = EnsureRoute( + serviceOverrides, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/heat.go b/pkg/openstack/heat.go index 033699b2d..5b46ec379 100644 --- a/pkg/openstack/heat.go +++ b/pkg/openstack/heat.go @@ -77,7 +77,7 @@ func ReconcileHeat(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - instance.Spec.Heat.Template.HeatAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Heat.Template.HeatAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, @@ -107,7 +107,7 @@ func ReconcileHeat(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - instance.Spec.Heat.Template.HeatCfnAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Heat.Template.HeatCfnAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/horizon.go b/pkg/openstack/horizon.go index ce61ed5d7..00002e758 100644 --- a/pkg/openstack/horizon.go +++ b/pkg/openstack/horizon.go @@ -79,7 +79,7 @@ func ReconcileHorizon(ctx context.Context, instance *corev1beta1.OpenStackContro } var ctrlResult reconcile.Result - serviceOverrides, ctrlResult, err = EnsureRoute( + serviceOverrides, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/ironic.go b/pkg/openstack/ironic.go index 44d0f5d74..1acd5e2c6 100644 --- a/pkg/openstack/ironic.go +++ b/pkg/openstack/ironic.go @@ -77,7 +77,7 @@ func ReconcileIronic(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Ironic.Template.IronicAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Ironic.Template.IronicAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, @@ -107,7 +107,7 @@ func ReconcileIronic(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Ironic.Template.IronicInspector.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Ironic.Template.IronicInspector.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/keystone.go b/pkg/openstack/keystone.go index 4aa507c12..c4b9dadad 100644 --- a/pkg/openstack/keystone.go +++ b/pkg/openstack/keystone.go @@ -68,7 +68,7 @@ func ReconcileKeystoneAPI(ctx context.Context, instance *corev1beta1.OpenStackCo } var ctrlResult reconcile.Result - instance.Spec.Keystone.Template.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Keystone.Template.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/manila.go b/pkg/openstack/manila.go index 735a804fc..5d570ebec 100644 --- a/pkg/openstack/manila.go +++ b/pkg/openstack/manila.go @@ -69,7 +69,7 @@ func ReconcileManila(ctx context.Context, instance *corev1beta1.OpenStackControl } var ctrlResult reconcile.Result - instance.Spec.Manila.Template.ManilaAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Manila.Template.ManilaAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/neutron.go b/pkg/openstack/neutron.go index 7a15f0e26..0825f496d 100644 --- a/pkg/openstack/neutron.go +++ b/pkg/openstack/neutron.go @@ -68,7 +68,7 @@ func ReconcileNeutron(ctx context.Context, instance *corev1beta1.OpenStackContro } var ctrlResult reconcile.Result - instance.Spec.Neutron.Template.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Neutron.Template.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/nova.go b/pkg/openstack/nova.go index 58ebf5e1f..701022b88 100644 --- a/pkg/openstack/nova.go +++ b/pkg/openstack/nova.go @@ -105,7 +105,7 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - instance.Spec.Nova.Template.APIServiceTemplate.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Nova.Template.APIServiceTemplate.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, @@ -147,7 +147,7 @@ func ReconcileNova(ctx context.Context, instance *corev1beta1.OpenStackControlPl } var ctrlResult reconcile.Result - routedOverrideSpec, ctrlResult, err := EnsureRoute( + routedOverrideSpec, ctrlResult, err := EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/octavia.go b/pkg/openstack/octavia.go index 6715197f1..5ebded59b 100644 --- a/pkg/openstack/octavia.go +++ b/pkg/openstack/octavia.go @@ -84,7 +84,7 @@ func ReconcileOctavia(ctx context.Context, instance *corev1beta1.OpenStackContro } var ctrlResult reconcile.Result - instance.Spec.Octavia.Template.OctaviaAPI.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Octavia.Template.OctaviaAPI.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/openstackclient.go b/pkg/openstack/openstackclient.go index 2481daab4..588027149 100644 --- a/pkg/openstack/openstackclient.go +++ b/pkg/openstack/openstackclient.go @@ -43,10 +43,11 @@ func ReconcileOpenStackClient(ctx context.Context, instance *corev1.OpenStackCon helper.GetLogger().Info("Reconciling OpenStackClient", "OpenStackClient.Namespace", instance.Namespace, "OpenStackClient.Name", openstackclient.Name) op, err := controllerutil.CreateOrPatch(ctx, helper.GetClient(), openstackclient, func() error { - // the following are created/owned by keystoneclient - openstackclient.Spec.OpenStackConfigMap = "openstack-config" - openstackclient.Spec.OpenStackConfigSecret = "openstack-config-secret" - openstackclient.Spec.NodeSelector = instance.Spec.NodeSelector + instance.Spec.OpenStackClient.Template.DeepCopyInto(&openstackclient.Spec) + + if instance.Spec.TLS.PublicEndpoints.Enabled || instance.Spec.TLS.InternalEndpoints.Enabled { + openstackclient.Spec.Ca.CaSecretName = CombinedCASecret + } err := controllerutil.SetControllerReference(helper.GetBeforeObject(), openstackclient, helper.GetScheme()) if err != nil { diff --git a/pkg/openstack/placement.go b/pkg/openstack/placement.go index 388d5a1dd..1c66971e9 100644 --- a/pkg/openstack/placement.go +++ b/pkg/openstack/placement.go @@ -67,7 +67,7 @@ func ReconcilePlacementAPI(ctx context.Context, instance *corev1beta1.OpenStackC } var ctrlResult reconcile.Result - instance.Spec.Placement.Template.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Placement.Template.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstack/swift.go b/pkg/openstack/swift.go index 006c3a50c..b4d98b262 100644 --- a/pkg/openstack/swift.go +++ b/pkg/openstack/swift.go @@ -68,7 +68,7 @@ func ReconcileSwift(ctx context.Context, instance *corev1beta1.OpenStackControlP } var ctrlResult reconcile.Result - instance.Spec.Swift.Template.SwiftProxy.Override.Service, ctrlResult, err = EnsureRoute( + instance.Spec.Swift.Template.SwiftProxy.Override.Service, ctrlResult, err = EnsureEndpointConfig( ctx, instance, helper, diff --git a/pkg/openstackclient/funcs.go b/pkg/openstackclient/funcs.go index cf9fd7044..2bbe45132 100644 --- a/pkg/openstackclient/funcs.go +++ b/pkg/openstackclient/funcs.go @@ -13,10 +13,17 @@ limitations under the License. package openstackclient import ( + env "github.com/openstack-k8s-operators/lib-common/modules/common/env" clientv1 "github.com/openstack-k8s-operators/openstack-operator/apis/client/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +const ( + // ServiceCommand - + ServiceCommand = "sudo -E /usr/local/bin/kolla_set_configs && sudo -E /usr/local/bin/kolla_start" ) // ClientPod func @@ -24,7 +31,6 @@ func ClientPod( instance *clientv1.OpenStackClient, labels map[string]string, configHash string, - secretHash string, ) *corev1.Pod { clientPod := &corev1.Pod{ @@ -34,55 +40,60 @@ func ClientPod( }, } - var terminationGracePeriodSeconds int64 = 0 - runAsUser := int64(42401) - runAsGroup := int64(42401) + envVars := map[string]env.Setter{} + envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS") + envVars["OS_CLOUD"] = env.SetValue("default") + envVars["CONFIG_HASH"] = env.SetValue(configHash) + clientPod.ObjectMeta = metav1.ObjectMeta{ Name: instance.Name, Namespace: instance.Namespace, Labels: labels, } - clientPod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds + clientPod.Spec.TerminationGracePeriodSeconds = ptr.To[int64](0) clientPod.Spec.ServiceAccountName = instance.RbacResourceName() - clientPod.Spec.Containers = []corev1.Container{ - { - Name: "openstackclient", - Image: instance.Spec.ContainerImage, - Command: []string{"/bin/sleep"}, - Args: []string{"infinity"}, - SecurityContext: &corev1.SecurityContext{ - RunAsUser: &runAsUser, - RunAsGroup: &runAsGroup, + clientContainer := corev1.Container{ + Name: "openstackclient", + Image: instance.Spec.ContainerImage, + Command: []string{ + "/bin/bash", + }, + Args: []string{"-c", ServiceCommand}, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: ptr.To[int64](42401), + RunAsGroup: ptr.To[int64](42401), + }, + Env: env.MergeEnvs([]corev1.EnvVar{}, envVars), + VolumeMounts: []corev1.VolumeMount{ + { + Name: "openstack-config", + MountPath: "/var/lib/config-data/clouds.yaml", + SubPath: "clouds.yaml", }, - Env: []corev1.EnvVar{ - { - Name: "OS_CLOUD", - Value: "default", - }, - { - Name: "CONFIG_HASH", - Value: configHash, - }, - { - Name: "SECRET_HASH", - Value: secretHash, - }, + { + Name: "openstack-config-secret", + MountPath: "/var/lib/config-data/secure.yaml", + SubPath: "secure.yaml", }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "openstack-config", - MountPath: "/etc/openstack/clouds.yaml", - SubPath: "clouds.yaml", - }, - { - Name: "openstack-config-secret", - MountPath: "/etc/openstack/secure.yaml", - SubPath: "secure.yaml", - }, + { + Name: "config-data", + MountPath: "/var/lib/kolla/config_files/config.json", + SubPath: "config.json", + ReadOnly: true, }, }, } - clientPod.Spec.Volumes = clientPodVolumes(instance, labels, configHash, secretHash) + if instance.Spec.CaSecretName != "" { + clientContainer.VolumeMounts = append(clientContainer.VolumeMounts, + corev1.VolumeMount{ + Name: "ca", + MountPath: "/var/lib/config-data/ca-certificates", + }) + } + + clientPod.Spec.Containers = []corev1.Container{clientContainer} + + clientPod.Spec.Volumes = clientPodVolumes(instance, labels) if instance.Spec.NodeSelector != nil && len(instance.Spec.NodeSelector) > 0 { clientPod.Spec.NodeSelector = instance.Spec.NodeSelector } @@ -93,11 +104,8 @@ func ClientPod( func clientPodVolumes( instance *clientv1.OpenStackClient, labels map[string]string, - configHash string, - secretHash string, ) []corev1.Volume { - - return []corev1.Volume{ + volumes := []corev1.Volume{ { Name: "openstack-config", VolumeSource: corev1.VolumeSource{ @@ -116,6 +124,29 @@ func clientPodVolumes( }, }, }, + { + Name: "config-data", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: instance.Name + "-config-data", + }, + }, + }, + }, + } + + if instance.Spec.CaSecretName != "" { + volumes = append(volumes, + corev1.Volume{ + Name: "ca", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: instance.Spec.CaSecretName, + }, + }, + }) } + return volumes } diff --git a/templates/openstackclient/config/config.json b/templates/openstackclient/config/config.json new file mode 100644 index 000000000..4f818bc34 --- /dev/null +++ b/templates/openstackclient/config/config.json @@ -0,0 +1,25 @@ +{ + "command": "/bin/sleep infinity", + "config_files": [ + { + "source": "/var/lib/config-data/clouds.yaml", + "dest": "/home/cloud-admin/.config/openstack/clouds.yaml", + "owner": "cloud-admin", + "perm": "0644" + }, + { + "source": "/var/lib/config-data/secure.yaml", + "dest": "/home/cloud-admin/.config/openstack/secure.yaml", + "owner": "cloud-admin", + "perm": "0644" + }, + { + "source": "/var/lib/config-data/ca/*", + "dest": "/etc/pki/ca-trust/source/anchors/", + "owner": "root", + "perm": "0644", + "merge": true, + "optional": true + } + ] +}