Skip to content

Commit

Permalink
Update placementapi to use service override
Browse files Browse the repository at this point in the history
Removes creation of routes.Those get done in the openstack-operator.
Via service overrides the service can be customized. The service operator
adds annotation to the service which needs to be exposed as a route.

Jira: OSP-26690

Depends-On: openstack-k8s-operators/lib-common#332
stuggi authored and openshift-merge-robot committed Sep 20, 2023
1 parent 7af4ec1 commit 3c99d09
Showing 16 changed files with 635 additions and 260 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -134,13 +134,16 @@ metadata:
name: placement
spec:
...
externalEndpoints:
- endpoint: internal
ipAddressPool: osp-internalapi
loadBalancerIPs:
- 172.17.0.202
sharedIP: true
sharedIPKey: ""
override:
service:
internal:
metadata:
annotations:
metallb.universe.tf/address-pool: osp-internalapi
metallb.universe.tf/allow-shared-ip: internalapi
metallb.universe.tf/loadBalancerIPs: 172.17.0.202
spec:
type: LoadBalancer
...
...
```
216 changes: 174 additions & 42 deletions api/bases/placement.openstack.org_placementapis.yaml
Original file line number Diff line number Diff line change
@@ -92,48 +92,6 @@ spec:
to add additional files. Those get added to the service config dir
in /etc/<service> . TODO: -> implement'
type: object
externalEndpoints:
description: ExternalEndpoints, expose a VIP using a pre-created IPAddressPool
items:
description: MetalLBConfig to configure the MetalLB loadbalancer
service
properties:
endpoint:
description: Endpoint, OpenStack endpoint this service maps
to
enum:
- internal
- public
type: string
ipAddressPool:
description: IPAddressPool expose VIP via MetalLB on the IPAddressPool
minLength: 1
type: string
loadBalancerIPs:
description: LoadBalancerIPs, request given IPs from the pool
if available. Using a list to allow dual stack (IPv4/IPv6)
support
items:
type: string
type: array
sharedIP:
default: true
description: SharedIP if true, VIP/VIPs get shared with multiple
services
type: boolean
sharedIPKey:
default: ""
description: SharedIPKey specifies the sharing key which gets
set as the annotation on the LoadBalancer service. Services
which share the same VIP must have the same SharedIPKey. Defaults
to the IPAddressPool if SharedIP is true, but no SharedIPKey
specified.
type: string
required:
- endpoint
- ipAddressPool
type: object
type: array
networkAttachments:
description: NetworkAttachments is a list of NetworkAttachment resource
names to expose the services to the given network
@@ -146,6 +104,180 @@ spec:
description: NodeSelector to target subset of worker nodes running
this service
type: object
override:
description: Override, provides the ability to override the generated
manifest of several child resources.
properties:
service:
additionalProperties:
description: RoutedOverrideSpec - a routed service override
configuration for the Service created to serve traffic to
the cluster. Allows for the manifest of the created Service
to be overwritten with custom configuration.
properties:
endpointURL:
type: string
metadata:
description: EmbeddedLabelsAnnotations is an embedded subset
of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta.
Only labels and annotations are included.
properties:
annotations:
additionalProperties:
type: string
description: 'Annotations is an unstructured key value
map stored with a resource that may be set by external
tools to store and retrieve arbitrary metadata. They
are not queryable and should be preserved when modifying
objects. More info: http://kubernetes.io/docs/user-guide/annotations'
type: object
labels:
additionalProperties:
type: string
description: 'Map of string keys and values that can
be used to organize and categorize (scope and select)
objects. May match selectors of replication controllers
and services. More info: http://kubernetes.io/docs/user-guide/labels'
type: object
type: object
spec:
description: OverrideServiceSpec is a subset of the fields
included in https://pkg.go.dev/k8s.io/[email protected]/core/v1#ServiceSpec
Limited to Type, SessionAffinity, LoadBalancerSourceRanges,
ExternalName, ExternalTrafficPolicy, SessionAffinityConfig,
IPFamilyPolicy, LoadBalancerClass and InternalTrafficPolicy
properties:
externalName:
description: externalName is the external reference
that discovery mechanisms will return as an alias
for this service (e.g. a DNS CNAME record). No proxying
will be involved. Must be a lowercase RFC-1123 hostname
(https://tools.ietf.org/html/rfc1123) and requires
`type` to be "ExternalName".
type: string
externalTrafficPolicy:
description: externalTrafficPolicy describes how nodes
distribute service traffic they receive on one of
the Service's "externally-facing" addresses (NodePorts,
ExternalIPs, and LoadBalancer IPs). If set to "Local",
the proxy will configure the service in a way that
assumes that external load balancers will take care
of balancing the service traffic between nodes, and
so each node will deliver traffic only to the node-local
endpoints of the service, without masquerading the
client source IP. (Traffic mistakenly sent to a node
with no endpoints will be dropped.) The default value,
"Cluster", uses the standard behavior of routing to
all endpoints evenly (possibly modified by topology
and other features). Note that traffic sent to an
External IP or LoadBalancer IP from within the cluster
will always get "Cluster" semantics, but clients sending
to a NodePort from within the cluster may need to
take traffic policy into account when picking a node.
type: string
internalTrafficPolicy:
description: InternalTrafficPolicy describes how nodes
distribute service traffic they receive on the ClusterIP.
If set to "Local", the proxy will assume that pods
only want to talk to endpoints of the service on the
same node as the pod, dropping the traffic if there
are no local endpoints. The default value, "Cluster",
uses the standard behavior of routing to all endpoints
evenly (possibly modified by topology and other features).
type: string
ipFamilyPolicy:
description: IPFamilyPolicy represents the dual-stack-ness
requested or required by this Service. If there is
no value provided, then this field will be set to
SingleStack. Services can be "SingleStack" (a single
IP family), "PreferDualStack" (two IP families on
dual-stack configured clusters or a single IP family
on single-stack clusters), or "RequireDualStack" (two
IP families on dual-stack configured clusters, otherwise
fail). The ipFamilies and clusterIPs fields depend
on the value of this field. This field will be wiped
when updating a service to type ExternalName.
type: string
loadBalancerClass:
description: loadBalancerClass is the class of the load
balancer implementation this Service belongs to. If
specified, the value of this field must be a label-style
identifier, with an optional prefix, e.g. "internal-vip"
or "example.com/internal-vip". Unprefixed names are
reserved for end-users. This field can only be set
when the Service type is 'LoadBalancer'. If not set,
the default load balancer implementation is used,
today this is typically done through the cloud provider
integration, but should apply for any default implementation.
If set, it is assumed that a load balancer implementation
is watching for Services with a matching class. Any
default load balancer implementation (e.g. cloud providers)
should ignore Services that set this field. This field
can only be set when creating or updating a Service
to type 'LoadBalancer'. Once set, it can not be changed.
This field will be wiped when a service is updated
to a non 'LoadBalancer' type.
type: string
loadBalancerSourceRanges:
description: 'If specified and supported by the platform,
this will restrict traffic through the cloud-provider
load-balancer will be restricted to the specified
client IPs. This field will be ignored if the cloud-provider
does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/'
items:
type: string
type: array
sessionAffinity:
description: 'Supports "ClientIP" and "None". Used to
maintain session affinity. Enable client IP based
session affinity. Must be ClientIP or None. Defaults
to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies'
type: string
sessionAffinityConfig:
description: sessionAffinityConfig contains the configurations
of session affinity.
properties:
clientIP:
description: clientIP contains the configurations
of Client IP based session affinity.
properties:
timeoutSeconds:
description: timeoutSeconds specifies the seconds
of ClientIP type session sticky time. The
value must be >0 && <=86400(for 1 day) if
ServiceAffinity == "ClientIP". Default value
is 10800(for 3 hours).
format: int32
type: integer
type: object
type: object
type:
description: 'type determines how the Service is exposed.
Defaults to ClusterIP. Valid options are ExternalName,
ClusterIP, NodePort, and LoadBalancer. "ClusterIP"
allocates a cluster-internal IP address for load-balancing
to endpoints. Endpoints are determined by the selector
or if that is not specified, by manual construction
of an Endpoints object or EndpointSlice objects. If
clusterIP is "None", no virtual IP is allocated and
the endpoints are published as a set of endpoints
rather than a virtual IP. "NodePort" builds on ClusterIP
and allocates a port on every node which routes to
the same endpoints as the clusterIP. "LoadBalancer"
builds on NodePort and creates an external load-balancer
(if supported in the current cloud) which routes to
the same endpoints as the clusterIP. "ExternalName"
aliases this service to the specified externalName.
Several other fields do not apply to ExternalName
services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types'
type: string
type: object
type: object
description: Override configuration for the Service created to
serve traffic to the cluster. The key must be the endpoint type
(public, internal)
type: object
type: object
passwordSelectors:
default:
database: PlacementDatabasePassword
5 changes: 1 addition & 4 deletions api/go.mod
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ module github.com/openstack-k8s-operators/placement-operator/api
go 1.19

require (
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216
k8s.io/api v0.26.9
k8s.io/apimachinery v0.26.9
sigs.k8s.io/controller-runtime v0.14.6
@@ -35,21 +35,18 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openshift/api v3.9.0+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/term v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
8 changes: 2 additions & 6 deletions api/go.sum
Original file line number Diff line number Diff line change
@@ -220,10 +220,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxCMwNRnMjhhIDOWHJowi6q8G6koI=
github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2 h1:/ez+9PSwtucQ9v1I5X72xlP5UJztTMPH4M5gDAJAatc=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2/go.mod h1:bG2JdbaO4bR4u8rtXZ7MgmMELuEseTkL2BPgk9JBYmY=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216 h1:arYbQA6bLyXJkHm+6M6gPc4YpWMFjs5qkG16Yii4UDo=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216/go.mod h1:Ge7Yf6AUmjEvJK9AIW2bT5udLzBIcK59b1QxqymncaQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -294,7 +292,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -492,7 +489,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
38 changes: 8 additions & 30 deletions api/v1beta1/placementapi_types.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ package v1beta1

import (
condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
endpoint "github.com/openstack-k8s-operators/lib-common/modules/common/endpoint"
"github.com/openstack-k8s-operators/lib-common/modules/common/service"
"github.com/openstack-k8s-operators/lib-common/modules/common/util"

corev1 "k8s.io/api/core/v1"
@@ -113,37 +113,15 @@ type PlacementAPISpec struct {
NetworkAttachments []string `json:"networkAttachments,omitempty"`

// +kubebuilder:validation:Optional
// ExternalEndpoints, expose a VIP using a pre-created IPAddressPool
ExternalEndpoints []MetalLBConfig `json:"externalEndpoints,omitempty"`
// Override, provides the ability to override the generated manifest of several child resources.
Override APIOverrideSpec `json:"override,omitempty"`
}

// MetalLBConfig to configure the MetalLB loadbalancer service
type MetalLBConfig struct {
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=internal;public
// Endpoint, OpenStack endpoint this service maps to
Endpoint endpoint.Endpoint `json:"endpoint"`

// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// IPAddressPool expose VIP via MetalLB on the IPAddressPool
IPAddressPool string `json:"ipAddressPool"`

// +kubebuilder:validation:Optional
// +kubebuilder:default=true
// SharedIP if true, VIP/VIPs get shared with multiple services
SharedIP bool `json:"sharedIP"`

// +kubebuilder:validation:Optional
// +kubebuilder:default=""
// SharedIPKey specifies the sharing key which gets set as the annotation on the LoadBalancer service.
// Services which share the same VIP must have the same SharedIPKey. Defaults to the IPAddressPool if
// SharedIP is true, but no SharedIPKey specified.
SharedIPKey string `json:"sharedIPKey"`

// +kubebuilder:validation:Optional
// LoadBalancerIPs, request given IPs from the pool if available. Using a list to allow dual stack (IPv4/IPv6) support
LoadBalancerIPs []string `json:"loadBalancerIPs,omitempty"`
// APIOverrideSpec to override the generated manifest of several child resources.
type APIOverrideSpec struct {
// Override configuration for the Service created to serve traffic to the cluster.
// The key must be the endpoint type (public, internal)
Service map[service.Endpoint]service.RoutedOverrideSpec `json:"service,omitempty"`
}

// PasswordSelector to identify the DB and AdminUser password from the Secret
27 changes: 12 additions & 15 deletions api/v1beta1/zz_generated.deepcopy.go
216 changes: 174 additions & 42 deletions config/crd/bases/placement.openstack.org_placementapis.yaml
Original file line number Diff line number Diff line change
@@ -92,48 +92,6 @@ spec:
to add additional files. Those get added to the service config dir
in /etc/<service> . TODO: -> implement'
type: object
externalEndpoints:
description: ExternalEndpoints, expose a VIP using a pre-created IPAddressPool
items:
description: MetalLBConfig to configure the MetalLB loadbalancer
service
properties:
endpoint:
description: Endpoint, OpenStack endpoint this service maps
to
enum:
- internal
- public
type: string
ipAddressPool:
description: IPAddressPool expose VIP via MetalLB on the IPAddressPool
minLength: 1
type: string
loadBalancerIPs:
description: LoadBalancerIPs, request given IPs from the pool
if available. Using a list to allow dual stack (IPv4/IPv6)
support
items:
type: string
type: array
sharedIP:
default: true
description: SharedIP if true, VIP/VIPs get shared with multiple
services
type: boolean
sharedIPKey:
default: ""
description: SharedIPKey specifies the sharing key which gets
set as the annotation on the LoadBalancer service. Services
which share the same VIP must have the same SharedIPKey. Defaults
to the IPAddressPool if SharedIP is true, but no SharedIPKey
specified.
type: string
required:
- endpoint
- ipAddressPool
type: object
type: array
networkAttachments:
description: NetworkAttachments is a list of NetworkAttachment resource
names to expose the services to the given network
@@ -146,6 +104,180 @@ spec:
description: NodeSelector to target subset of worker nodes running
this service
type: object
override:
description: Override, provides the ability to override the generated
manifest of several child resources.
properties:
service:
additionalProperties:
description: RoutedOverrideSpec - a routed service override
configuration for the Service created to serve traffic to
the cluster. Allows for the manifest of the created Service
to be overwritten with custom configuration.
properties:
endpointURL:
type: string
metadata:
description: EmbeddedLabelsAnnotations is an embedded subset
of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta.
Only labels and annotations are included.
properties:
annotations:
additionalProperties:
type: string
description: 'Annotations is an unstructured key value
map stored with a resource that may be set by external
tools to store and retrieve arbitrary metadata. They
are not queryable and should be preserved when modifying
objects. More info: http://kubernetes.io/docs/user-guide/annotations'
type: object
labels:
additionalProperties:
type: string
description: 'Map of string keys and values that can
be used to organize and categorize (scope and select)
objects. May match selectors of replication controllers
and services. More info: http://kubernetes.io/docs/user-guide/labels'
type: object
type: object
spec:
description: OverrideServiceSpec is a subset of the fields
included in https://pkg.go.dev/k8s.io/api@v0.26.6/core/v1#ServiceSpec
Limited to Type, SessionAffinity, LoadBalancerSourceRanges,
ExternalName, ExternalTrafficPolicy, SessionAffinityConfig,
IPFamilyPolicy, LoadBalancerClass and InternalTrafficPolicy
properties:
externalName:
description: externalName is the external reference
that discovery mechanisms will return as an alias
for this service (e.g. a DNS CNAME record). No proxying
will be involved. Must be a lowercase RFC-1123 hostname
(https://tools.ietf.org/html/rfc1123) and requires
`type` to be "ExternalName".
type: string
externalTrafficPolicy:
description: externalTrafficPolicy describes how nodes
distribute service traffic they receive on one of
the Service's "externally-facing" addresses (NodePorts,
ExternalIPs, and LoadBalancer IPs). If set to "Local",
the proxy will configure the service in a way that
assumes that external load balancers will take care
of balancing the service traffic between nodes, and
so each node will deliver traffic only to the node-local
endpoints of the service, without masquerading the
client source IP. (Traffic mistakenly sent to a node
with no endpoints will be dropped.) The default value,
"Cluster", uses the standard behavior of routing to
all endpoints evenly (possibly modified by topology
and other features). Note that traffic sent to an
External IP or LoadBalancer IP from within the cluster
will always get "Cluster" semantics, but clients sending
to a NodePort from within the cluster may need to
take traffic policy into account when picking a node.
type: string
internalTrafficPolicy:
description: InternalTrafficPolicy describes how nodes
distribute service traffic they receive on the ClusterIP.
If set to "Local", the proxy will assume that pods
only want to talk to endpoints of the service on the
same node as the pod, dropping the traffic if there
are no local endpoints. The default value, "Cluster",
uses the standard behavior of routing to all endpoints
evenly (possibly modified by topology and other features).
type: string
ipFamilyPolicy:
description: IPFamilyPolicy represents the dual-stack-ness
requested or required by this Service. If there is
no value provided, then this field will be set to
SingleStack. Services can be "SingleStack" (a single
IP family), "PreferDualStack" (two IP families on
dual-stack configured clusters or a single IP family
on single-stack clusters), or "RequireDualStack" (two
IP families on dual-stack configured clusters, otherwise
fail). The ipFamilies and clusterIPs fields depend
on the value of this field. This field will be wiped
when updating a service to type ExternalName.
type: string
loadBalancerClass:
description: loadBalancerClass is the class of the load
balancer implementation this Service belongs to. If
specified, the value of this field must be a label-style
identifier, with an optional prefix, e.g. "internal-vip"
or "example.com/internal-vip". Unprefixed names are
reserved for end-users. This field can only be set
when the Service type is 'LoadBalancer'. If not set,
the default load balancer implementation is used,
today this is typically done through the cloud provider
integration, but should apply for any default implementation.
If set, it is assumed that a load balancer implementation
is watching for Services with a matching class. Any
default load balancer implementation (e.g. cloud providers)
should ignore Services that set this field. This field
can only be set when creating or updating a Service
to type 'LoadBalancer'. Once set, it can not be changed.
This field will be wiped when a service is updated
to a non 'LoadBalancer' type.
type: string
loadBalancerSourceRanges:
description: 'If specified and supported by the platform,
this will restrict traffic through the cloud-provider
load-balancer will be restricted to the specified
client IPs. This field will be ignored if the cloud-provider
does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/'
items:
type: string
type: array
sessionAffinity:
description: 'Supports "ClientIP" and "None". Used to
maintain session affinity. Enable client IP based
session affinity. Must be ClientIP or None. Defaults
to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies'
type: string
sessionAffinityConfig:
description: sessionAffinityConfig contains the configurations
of session affinity.
properties:
clientIP:
description: clientIP contains the configurations
of Client IP based session affinity.
properties:
timeoutSeconds:
description: timeoutSeconds specifies the seconds
of ClientIP type session sticky time. The
value must be >0 && <=86400(for 1 day) if
ServiceAffinity == "ClientIP". Default value
is 10800(for 3 hours).
format: int32
type: integer
type: object
type: object
type:
description: 'type determines how the Service is exposed.
Defaults to ClusterIP. Valid options are ExternalName,
ClusterIP, NodePort, and LoadBalancer. "ClusterIP"
allocates a cluster-internal IP address for load-balancing
to endpoints. Endpoints are determined by the selector
or if that is not specified, by manual construction
of an Endpoints object or EndpointSlice objects. If
clusterIP is "None", no virtual IP is allocated and
the endpoints are published as a set of endpoints
rather than a virtual IP. "NodePort" builds on ClusterIP
and allocates a port on every node which routes to
the same endpoints as the clusterIP. "LoadBalancer"
builds on NodePort and creates an external load-balancer
(if supported in the current cloud) which routes to
the same endpoints as the clusterIP. "ExternalName"
aliases this service to the specified externalName.
Several other fields do not apply to ExternalName
services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types'
type: string
type: object
type: object
description: Override configuration for the Service created to
serve traffic to the cluster. The key must be the endpoint type
(public, internal)
type: object
type: object
passwordSelectors:
default:
database: PlacementDatabasePassword
12 changes: 0 additions & 12 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
@@ -192,18 +192,6 @@ rules:
- list
- update
- watch
- apiGroups:
- route.openshift.io
resources:
- routes
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- security.openshift.io
resourceNames:
129 changes: 91 additions & 38 deletions controllers/placementapi_controller.go
Original file line number Diff line number Diff line change
@@ -29,7 +29,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"

"github.com/go-logr/logr"
routev1 "github.com/openshift/api/route/v1"
keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"

common "github.com/openstack-k8s-operators/lib-common/modules/common"
@@ -44,6 +43,7 @@ import (
nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment"
common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac"
oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret"
"github.com/openstack-k8s-operators/lib-common/modules/common/service"
util "github.com/openstack-k8s-operators/lib-common/modules/common/util"

database "github.com/openstack-k8s-operators/lib-common/modules/database"
@@ -76,7 +76,6 @@ type PlacementAPIReconciler struct {
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
// +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;watch;create;update;patch;delete;
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete;
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;patch;delete;
// +kubebuilder:rbac:groups=mariadb.openstack.org,resources=mariadbdatabases,verbs=get;list;watch;create;update;patch;delete;
// +kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneapis,verbs=get;list;watch;
// +kubebuilder:rbac:groups=keystone.openstack.org,resources=keystoneservices,verbs=get;list;watch;create;update;patch;delete;
@@ -202,7 +201,6 @@ func (r *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
Owns(&corev1.Secret{}).
Owns(&corev1.ConfigMap{}).
Owns(&appsv1.Deployment{}).
Owns(&routev1.Route{}).
Owns(&corev1.ServiceAccount{}).
Owns(&rbacv1.Role{}).
Owns(&rbacv1.RoleBinding{}).
@@ -354,48 +352,103 @@ func (r *PlacementAPIReconciler) reconcileInit(
//
// expose the service (create service, route and return the created endpoint URLs)
//
var ports = map[endpoint.Endpoint]endpoint.Data{
endpoint.EndpointPublic: {Port: placement.PlacementPublicPort},
endpoint.EndpointInternal: {Port: placement.PlacementInternalPort},
var placementEndpoints = map[service.Endpoint]endpoint.Data{
service.EndpointPublic: {Port: placement.PlacementPublicPort},
service.EndpointInternal: {Port: placement.PlacementInternalPort},
}
apiEndpoints := make(map[string]string)

for _, metallbcfg := range instance.Spec.ExternalEndpoints {
portCfg := ports[metallbcfg.Endpoint]
for endpointType, data := range placementEndpoints {
endpointTypeStr := string(endpointType)
endpointName := placement.ServiceName + "-" + endpointTypeStr

portCfg.MetalLB = &endpoint.MetalLBData{
IPAddressPool: metallbcfg.IPAddressPool,
SharedIP: metallbcfg.SharedIP,
SharedIPKey: metallbcfg.SharedIPKey,
LoadBalancerIPs: metallbcfg.LoadBalancerIPs,
svcOverride := instance.Spec.Override.Service[endpointType]
if svcOverride.EmbeddedLabelsAnnotations == nil {
svcOverride.EmbeddedLabelsAnnotations = &service.EmbeddedLabelsAnnotations{}
}

ports[metallbcfg.Endpoint] = portCfg
}
exportLabels := util.MergeStringMaps(
serviceLabels,
map[string]string{
service.AnnotationEndpointKey: endpointTypeStr,
},
)

apiEndpoints, ctrlResult, err := endpoint.ExposeEndpoints(
ctx,
helper,
placement.ServiceName,
serviceLabels,
ports,
time.Duration(5)*time.Second,
)
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.ExposeServiceReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.ExposeServiceReadyErrorMessage,
err.Error()))
return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.ExposeServiceReadyCondition,
condition.RequestedReason,
condition.SeverityInfo,
condition.ExposeServiceReadyRunningMessage))
return ctrlResult, nil
// Create the service
svc, err := service.NewService(
service.GenericService(&service.GenericServiceDetails{
Name: endpointName,
Namespace: instance.Namespace,
Labels: exportLabels,
Selector: serviceLabels,
Port: service.GenericServicePort{
Name: endpointName,
Port: data.Port,
Protocol: corev1.ProtocolTCP,
},
}),
5,
&svcOverride.OverrideSpec,
)
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.ExposeServiceReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.ExposeServiceReadyErrorMessage,
err.Error()))

return ctrl.Result{}, err
}

svc.AddAnnotation(map[string]string{
service.AnnotationEndpointKey: endpointTypeStr,
})

// add Annotation to whether creating an ingress is required or not
if endpointType == service.EndpointPublic && svc.GetServiceType() == corev1.ServiceTypeClusterIP {
svc.AddAnnotation(map[string]string{
service.AnnotationIngressCreateKey: "true",
})
} else {
svc.AddAnnotation(map[string]string{
service.AnnotationIngressCreateKey: "false",
})
if svc.GetServiceType() == corev1.ServiceTypeLoadBalancer {
svc.AddAnnotation(map[string]string{
service.AnnotationHostnameKey: svc.GetServiceHostname(), // add annotation to register service name in dnsmasq
})
}
}

ctrlResult, err := svc.CreateOrPatch(ctx, helper)
if err != nil {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.ExposeServiceReadyCondition,
condition.ErrorReason,
condition.SeverityWarning,
condition.ExposeServiceReadyErrorMessage,
err.Error()))

return ctrlResult, err
} else if (ctrlResult != ctrl.Result{}) {
instance.Status.Conditions.Set(condition.FalseCondition(
condition.ExposeServiceReadyCondition,
condition.RequestedReason,
condition.SeverityInfo,
condition.ExposeServiceReadyRunningMessage))
return ctrlResult, nil
}
// create service - end

// TODO: TLS, pass in https as protocol, create TLS cert
apiEndpoints[string(endpointType)], err = svc.GetAPIEndpoint(
svcOverride.EndpointURL, data.Protocol, data.Path)
if err != nil {
return ctrl.Result{}, err
}
}

instance.Status.Conditions.MarkTrue(condition.ExposeServiceReadyCondition, condition.ExposeServiceReadyMessage)
// expose service - end

12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -8,11 +8,10 @@ require (
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.4.0
github.com/onsi/ginkgo/v2 v2.12.0
github.com/onsi/gomega v1.27.10
github.com/openshift/api v3.9.0+incompatible
github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230914163026-da9aa9de960a
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2
github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230913075424-2680ce4b6ad2
github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230913075424-2680ce4b6ad2
github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230920085319-92ae0260bbf3
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216
github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230919113507-d74c2f31d216
github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230919113507-d74c2f31d216
github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230913081601-9e4fc8aadad5
github.com/openstack-k8s-operators/placement-operator/api v0.0.0-20230602092913-53f380989946
go.uber.org/zap v1.26.0
@@ -57,7 +56,8 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230913075424-2680ce4b6ad2 //indirect
github.com/openshift/api v3.9.0+incompatible // indirect
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230919113507-d74c2f31d216 //indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
@@ -238,16 +238,16 @@ github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7 h1:rncLxJBpFGqBztyxC
github.com/openshift/api v0.0.0-20230414143018-3367bc7e6ac7/go.mod h1:ctXNyWanKEjGj8sss1KjjHQ3ENKFm33FFnS5BKaIPh4=
github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230914145253-116f307c7875 h1:DUlCjbi3XxH66oL97MFZF5wgL28HdU+r8TkBZVw7WIc=
github.com/openstack-k8s-operators/infra-operator/apis v0.1.1-0.20230914145253-116f307c7875/go.mod h1:NgrvT3CKMu6fE8Nt1H79qHx11L3I7Bb2eItniM7c9ow=
github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230914163026-da9aa9de960a h1:MFYwi2Xk9r3OMPToCSbvqYVNrm7P+aFzGDN0eVNpgu8=
github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230914163026-da9aa9de960a/go.mod h1:nxrbUOIGMJ1h2pNlawhURdt/XJ95dW2wZGedmOVo2aw=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2 h1:/ez+9PSwtucQ9v1I5X72xlP5UJztTMPH4M5gDAJAatc=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230913075424-2680ce4b6ad2/go.mod h1:bG2JdbaO4bR4u8rtXZ7MgmMELuEseTkL2BPgk9JBYmY=
github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230913075424-2680ce4b6ad2 h1:6TgG90h8i5kxcZxPSNqi5lAx/CyggHhqOcCW0US8YSA=
github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230913075424-2680ce4b6ad2/go.mod h1:MwA2qrztcNGM7X82/IvG8Th3A0q/3GE5Evxji7clFLI=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230913075424-2680ce4b6ad2 h1:4L5DRfSnomBfyRwCfAzqQwk0+osnIbyJ1VvKBy+tzyY=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230913075424-2680ce4b6ad2/go.mod h1:NZ6weu5xOAkNPTqg1luC22DO7ZbyqiilRkvrFfhjFm0=
github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230913075424-2680ce4b6ad2 h1:TJpuax2pifQbOtsPv78DjoC+f/7+/3Tw+CdXBxxDxAs=
github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230913075424-2680ce4b6ad2/go.mod h1:fuKZmOKDEx/2f1+VLQyXF6iH9FX0ynbtyuvC/XjuJzg=
github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230920085319-92ae0260bbf3 h1:6VCz/ZBTJEQJTx4+z8UxLv3WITa4Bgx5CSP237wJ5xM=
github.com/openstack-k8s-operators/keystone-operator/api v0.1.1-0.20230920085319-92ae0260bbf3/go.mod h1:ta6w/29i4WuWkQp6I4cOLwMGQ5/vJI0y8Em7u+M34jo=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216 h1:arYbQA6bLyXJkHm+6M6gPc4YpWMFjs5qkG16Yii4UDo=
github.com/openstack-k8s-operators/lib-common/modules/common v0.1.1-0.20230919113507-d74c2f31d216/go.mod h1:Ge7Yf6AUmjEvJK9AIW2bT5udLzBIcK59b1QxqymncaQ=
github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230919113507-d74c2f31d216 h1:PykqfrahtBUbhIuM0IDRyrtVCOzy7jdEZz3yVEnwpp0=
github.com/openstack-k8s-operators/lib-common/modules/database v0.1.1-0.20230919113507-d74c2f31d216/go.mod h1:gdmDHoWrDtq8gfJHl20rQwe9vfzJJkY+KnvM6aD9+08=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230919113507-d74c2f31d216 h1:h76faqi4WAXBs3D2B0GLUdlCjS0dh78wRL0d5RZUwhk=
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.1.1-0.20230919113507-d74c2f31d216/go.mod h1:GHi64tgyC75/vuT8Crda0yN5iCIYiSyS4bpzYJjX7MA=
github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230919113507-d74c2f31d216 h1:QyzzvG8iaDFwx6Lo44dCyf2tRtgk0sqniXjgJpiW32g=
github.com/openstack-k8s-operators/lib-common/modules/test v0.1.2-0.20230919113507-d74c2f31d216/go.mod h1:RfLOPJbmPzPZ4XHwwDc2tFbbw5zxZL15JFGwb5c6VaU=
github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230913081601-9e4fc8aadad5 h1:dQcSQuXfgzgOhc4v+zD0jE6WWhn6FHr5nALOjJBPxyI=
github.com/openstack-k8s-operators/mariadb-operator/api v0.1.1-0.20230913081601-9e4fc8aadad5/go.mod h1:mJyhm/YiQZaYhLvOuLng/ITpwx8HvsYVht+VotS1Ed8=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
2 changes: 0 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@ import (
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth"

routev1 "github.com/openshift/api/route/v1"
"go.uber.org/zap/zapcore"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -55,7 +54,6 @@ func init() {
utilruntime.Must(placementv1.AddToScheme(scheme))
utilruntime.Must(mariadbv1.AddToScheme(scheme))
utilruntime.Must(keystonev1.AddToScheme(scheme))
utilruntime.Must(routev1.AddToScheme(scheme))
utilruntime.Must(networkv1.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
}
142 changes: 142 additions & 0 deletions tests/functional/placementapi_controller_test.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ limitations under the License.
package functional_test

import (
"fmt"
"os"

. "github.com/onsi/ginkgo/v2"
@@ -170,4 +171,145 @@ var _ = Describe("PlacementAPI controller", func() {
ContainSubstring("www_authenticate_uri = %s", keystoneAPI.Status.APIEndpoints["public"]))
})
})

When("A PlacementAPI is created with service override", func() {
BeforeEach(func() {
DeferCleanup(k8sClient.Delete, ctx, CreatePlacementAPISecret(namespace, SecretName))
DeferCleanup(th.DeleteKeystoneAPI, th.CreateKeystoneAPI(placementApiName.Namespace))

spec := GetDefaultPlacementAPISpec()
serviceOverride := map[string]interface{}{}
serviceOverride["internal"] = map[string]interface{}{
"metadata": map[string]map[string]string{
"annotations": {
"dnsmasq.network.openstack.org/hostname": "placement-internal.openstack.svc",
"metallb.universe.tf/address-pool": "osp-internalapi",
"metallb.universe.tf/allow-shared-ip": "osp-internalapi",
"metallb.universe.tf/loadBalancerIPs": "internal-lb-ip-1,internal-lb-ip-2",
},
"labels": {
"internal": "true",
"service": "placement",
},
},
"spec": map[string]interface{}{
"type": "LoadBalancer",
},
}

spec["override"] = map[string]interface{}{
"service": serviceOverride,
}

placementAPI := CreatePlacementAPI(placementApiName, spec)
DeferCleanup(
th.DeleteDBService,
th.CreateDBService(
placementApiName.Namespace,
GetPlacementAPI(placementApiName).Spec.DatabaseInstance,
corev1.ServiceSpec{
Ports: []corev1.ServicePort{{Port: 3306}},
},
),
)

th.SimulateMariaDBDatabaseCompleted(placementApiName)
th.SimulateJobSuccess(types.NamespacedName{
Namespace: placementApiName.Namespace,
Name: fmt.Sprintf("%s-db-sync", placementApiName.Name),
})
th.SimulateDeploymentReplicaReady(placementApiName)
th.SimulateKeystoneServiceReady(placementApiName)
th.SimulateKeystoneEndpointReady(placementApiName)
DeferCleanup(th.DeleteInstance, placementAPI)
})

It("creates KeystoneEndpoint", func() {
keystoneEndpoint := th.GetKeystoneEndpoint(placementApiName)
endpoints := keystoneEndpoint.Spec.Endpoints
Expect(endpoints).To(HaveKeyWithValue("public", "http://placement-public."+placementApiName.Namespace+".svc:8778"))
Expect(endpoints).To(HaveKeyWithValue("internal", "http://placement-internal."+placementApiName.Namespace+".svc:8778"))

th.ExpectCondition(
placementApiName,
ConditionGetterFunc(PlacementConditionGetter),
condition.KeystoneEndpointReadyCondition,
corev1.ConditionTrue,
)
})

It("creates LoadBalancer service", func() {
// As the internal endpoint is configured in ExternalEndpoints it
// gets a LoadBalancer Service with MetalLB annotations
service := th.GetService(types.NamespacedName{Namespace: namespace, Name: "placement-internal"})
Expect(service.Annotations).To(
HaveKeyWithValue("dnsmasq.network.openstack.org/hostname", "placement-internal.openstack.svc"))
Expect(service.Annotations).To(
HaveKeyWithValue("metallb.universe.tf/address-pool", "osp-internalapi"))
Expect(service.Annotations).To(
HaveKeyWithValue("metallb.universe.tf/allow-shared-ip", "osp-internalapi"))
Expect(service.Annotations).To(
HaveKeyWithValue("metallb.universe.tf/loadBalancerIPs", "internal-lb-ip-1,internal-lb-ip-2"))

th.ExpectCondition(
placementApiName,
ConditionGetterFunc(PlacementConditionGetter),
condition.ReadyCondition,
corev1.ConditionTrue,
)
})
})

When("A PlacementAPI is created with service override endpointURL set", func() {
BeforeEach(func() {
DeferCleanup(k8sClient.Delete, ctx, CreatePlacementAPISecret(namespace, SecretName))
DeferCleanup(th.DeleteKeystoneAPI, th.CreateKeystoneAPI(placementApiName.Namespace))

spec := GetDefaultPlacementAPISpec()
serviceOverride := map[string]interface{}{}
serviceOverride["public"] = map[string]interface{}{
"endpointURL": "http://placement-openstack.apps-crc.testing",
}

spec["override"] = map[string]interface{}{
"service": serviceOverride,
}

placementAPI := CreatePlacementAPI(placementApiName, spec)
DeferCleanup(
th.DeleteDBService,
th.CreateDBService(
placementApiName.Namespace,
GetPlacementAPI(placementApiName).Spec.DatabaseInstance,
corev1.ServiceSpec{
Ports: []corev1.ServicePort{{Port: 3306}},
},
),
)

th.SimulateMariaDBDatabaseCompleted(placementApiName)
th.SimulateJobSuccess(types.NamespacedName{
Namespace: placementApiName.Namespace,
Name: fmt.Sprintf("%s-db-sync", placementApiName.Name),
})
th.SimulateDeploymentReplicaReady(placementApiName)
th.SimulateKeystoneServiceReady(placementApiName)
th.SimulateKeystoneEndpointReady(placementApiName)
DeferCleanup(th.DeleteInstance, placementAPI)
})

It("creates KeystoneEndpoint", func() {
keystoneEndpoint := th.GetKeystoneEndpoint(placementApiName)
endpoints := keystoneEndpoint.Spec.Endpoints
Expect(endpoints).To(HaveKeyWithValue("public", "http://placement-openstack.apps-crc.testing"))
Expect(endpoints).To(HaveKeyWithValue("internal", "http://placement-internal."+placementApiName.Namespace+".svc:8778"))

th.ExpectCondition(
placementApiName,
ConditionGetterFunc(PlacementConditionGetter),
condition.KeystoneEndpointReadyCondition,
corev1.ConditionTrue,
)
})
})
})
7 changes: 0 additions & 7 deletions tests/functional/suite_test.go
Original file line number Diff line number Diff line change
@@ -38,8 +38,6 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

routev1 "github.com/openshift/api/route/v1"

keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
test "github.com/openstack-k8s-operators/lib-common/modules/test"
mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1"
@@ -89,8 +87,6 @@ var _ = BeforeSuite(func() {
mariaDBCRDs, err := test.GetCRDDirFromModule(
"github.com/openstack-k8s-operators/mariadb-operator/api", "../../go.mod", "bases")
Expect(err).ShouldNot(HaveOccurred())
routev1CRDs, err := test.GetOpenShiftCRDDir("route/v1", "../../go.mod")
Expect(err).ShouldNot(HaveOccurred())

By("bootstrapping test environment")
testEnv = &envtest.Environment{
@@ -99,7 +95,6 @@ var _ = BeforeSuite(func() {
// NOTE(gibi): we need to list all the external CRDs our operator depends on
keystoneCRDs,
mariaDBCRDs,
routev1CRDs,
},
ErrorIfCRDPathMissing: true,
WebhookInstallOptions: envtest.WebhookInstallOptions{
@@ -126,8 +121,6 @@ var _ = BeforeSuite(func() {
Expect(err).NotTo(HaveOccurred())
err = keystonev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = routev1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
//+kubebuilder:scaffold:scheme

logger = ctrl.Log.WithName("---Test---")
25 changes: 3 additions & 22 deletions tests/kuttl/common/assert_sample_deployment.yaml
Original file line number Diff line number Diff line change
@@ -178,7 +178,7 @@ apiVersion: v1
kind: Service
metadata:
labels:
internal: "true"
endpoint: internal
service: placement
name: placement-internal
spec:
@@ -192,7 +192,7 @@ apiVersion: v1
kind: Service
metadata:
labels:
public: "true"
endpoint: public
service: placement
name: placement-public
spec:
@@ -202,25 +202,6 @@ spec:
service: placement
type: ClusterIP
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: placement-public
labels:
public: "true"
service: placement
spec:
port:
targetPort: placement-public
to:
kind: Service
name: placement-public
status:
ingress:
- conditions:
- status: "True"
type: Admitted
---
apiVersion: v1
kind: ConfigMap
metadata:
@@ -267,7 +248,7 @@ commands:
. $PLACEMENT_KUTTL_DIR/../lib/helper_functions.sh
apiEndpoints=$(oc get -n $NAMESPACE KeystoneEndpoint placement -o go-template-file=$PLACEMENT_KUTTL_DIR/../go_templates/apiEndpoints.gotemplate)
assert_regex $apiEndpoints 'http:\/\/placement-internal\..+\.svc.*'
assert_regex $apiEndpoints 'http:\/\/placement-public-.+\.apps.*'
assert_regex $apiEndpoints 'http:\/\/placement-public\..+\.svc.*'
# when using image digests the containerImage URLs are SHA's so we verify them with a script
tupleTemplate='{{ range (index .spec.template.spec.containers 1).env }}{{ .name }}{{ "#" }}{{ .value}}{{"\n"}}{{ end }}'
19 changes: 2 additions & 17 deletions tests/kuttl/common/errors_cleanup_placement.yaml
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
# No Deployment for PlacementAPI CR
# No Pods in placement Deployment
# No Placement Services
# No Placement Routes
#
apiVersion: placement.openstack.org/v1beta1
kind: PlacementAPI
@@ -46,7 +45,7 @@ apiVersion: v1
kind: Service
metadata:
labels:
internal: "true"
endpoint: internal
service: placement
name: placement-internal
spec:
@@ -60,7 +59,7 @@ apiVersion: v1
kind: Service
metadata:
labels:
public: "true"
endpoint: public
service: placement
name: placement-public
spec:
@@ -70,20 +69,6 @@ spec:
service: placement
type: ClusterIP
---
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: placement-public
labels:
public: "true"
service: placement
spec:
port:
targetPort: placement-public
to:
kind: Service
name: placement-public
---
apiVersion: v1
kind: ConfigMap
metadata:

0 comments on commit 3c99d09

Please sign in to comment.