diff --git a/api/bases/nova.openstack.org_nova.yaml b/.api/bases/nova.openstack.org_nova.yaml similarity index 100% rename from api/bases/nova.openstack.org_nova.yaml rename to .api/bases/nova.openstack.org_nova.yaml diff --git a/api/bases/nova.openstack.org_novaapis.yaml b/.api/bases/nova.openstack.org_novaapis.yaml similarity index 100% rename from api/bases/nova.openstack.org_novaapis.yaml rename to .api/bases/nova.openstack.org_novaapis.yaml diff --git a/api/bases/nova.openstack.org_novacells.yaml b/.api/bases/nova.openstack.org_novacells.yaml similarity index 100% rename from api/bases/nova.openstack.org_novacells.yaml rename to .api/bases/nova.openstack.org_novacells.yaml diff --git a/api/bases/nova.openstack.org_novacomputes.yaml b/.api/bases/nova.openstack.org_novacomputes.yaml similarity index 100% rename from api/bases/nova.openstack.org_novacomputes.yaml rename to .api/bases/nova.openstack.org_novacomputes.yaml diff --git a/api/bases/nova.openstack.org_novaconductors.yaml b/.api/bases/nova.openstack.org_novaconductors.yaml similarity index 100% rename from api/bases/nova.openstack.org_novaconductors.yaml rename to .api/bases/nova.openstack.org_novaconductors.yaml diff --git a/api/bases/nova.openstack.org_novametadata.yaml b/.api/bases/nova.openstack.org_novametadata.yaml similarity index 100% rename from api/bases/nova.openstack.org_novametadata.yaml rename to .api/bases/nova.openstack.org_novametadata.yaml diff --git a/api/bases/nova.openstack.org_novanovncproxies.yaml b/.api/bases/nova.openstack.org_novanovncproxies.yaml similarity index 100% rename from api/bases/nova.openstack.org_novanovncproxies.yaml rename to .api/bases/nova.openstack.org_novanovncproxies.yaml diff --git a/api/bases/nova.openstack.org_novaschedulers.yaml b/.api/bases/nova.openstack.org_novaschedulers.yaml similarity index 100% rename from api/bases/nova.openstack.org_novaschedulers.yaml rename to .api/bases/nova.openstack.org_novaschedulers.yaml diff --git a/api/go.mod b/.api/go.mod similarity index 100% rename from api/go.mod rename to .api/go.mod diff --git a/api/go.sum b/.api/go.sum similarity index 100% rename from api/go.sum rename to .api/go.sum diff --git a/api/v1beta1/common_types.go b/.api/v1beta1/common_types.go similarity index 100% rename from api/v1beta1/common_types.go rename to .api/v1beta1/common_types.go diff --git a/api/v1beta1/common_webhook.go b/.api/v1beta1/common_webhook.go similarity index 100% rename from api/v1beta1/common_webhook.go rename to .api/v1beta1/common_webhook.go diff --git a/api/v1beta1/conditions.go b/.api/v1beta1/conditions.go similarity index 100% rename from api/v1beta1/conditions.go rename to .api/v1beta1/conditions.go diff --git a/api/v1beta1/groupversion_info.go b/.api/v1beta1/groupversion_info.go similarity index 100% rename from api/v1beta1/groupversion_info.go rename to .api/v1beta1/groupversion_info.go diff --git a/api/v1beta1/nova_types.go b/.api/v1beta1/nova_types.go similarity index 100% rename from api/v1beta1/nova_types.go rename to .api/v1beta1/nova_types.go diff --git a/api/v1beta1/nova_webhook.go b/.api/v1beta1/nova_webhook.go similarity index 100% rename from api/v1beta1/nova_webhook.go rename to .api/v1beta1/nova_webhook.go diff --git a/api/v1beta1/novaapi_types.go b/.api/v1beta1/novaapi_types.go similarity index 100% rename from api/v1beta1/novaapi_types.go rename to .api/v1beta1/novaapi_types.go diff --git a/api/v1beta1/novaapi_webhook.go b/.api/v1beta1/novaapi_webhook.go similarity index 100% rename from api/v1beta1/novaapi_webhook.go rename to .api/v1beta1/novaapi_webhook.go diff --git a/api/v1beta1/novacell_types.go b/.api/v1beta1/novacell_types.go similarity index 100% rename from api/v1beta1/novacell_types.go rename to .api/v1beta1/novacell_types.go diff --git a/api/v1beta1/novacell_webhook.go b/.api/v1beta1/novacell_webhook.go similarity index 100% rename from api/v1beta1/novacell_webhook.go rename to .api/v1beta1/novacell_webhook.go diff --git a/api/v1beta1/novacompute_types.go b/.api/v1beta1/novacompute_types.go similarity index 100% rename from api/v1beta1/novacompute_types.go rename to .api/v1beta1/novacompute_types.go diff --git a/api/v1beta1/novacompute_webhook.go b/.api/v1beta1/novacompute_webhook.go similarity index 100% rename from api/v1beta1/novacompute_webhook.go rename to .api/v1beta1/novacompute_webhook.go diff --git a/api/v1beta1/novaconductor_types.go b/.api/v1beta1/novaconductor_types.go similarity index 100% rename from api/v1beta1/novaconductor_types.go rename to .api/v1beta1/novaconductor_types.go diff --git a/api/v1beta1/novaconductor_webhook.go b/.api/v1beta1/novaconductor_webhook.go similarity index 100% rename from api/v1beta1/novaconductor_webhook.go rename to .api/v1beta1/novaconductor_webhook.go diff --git a/api/v1beta1/novametadata_types.go b/.api/v1beta1/novametadata_types.go similarity index 100% rename from api/v1beta1/novametadata_types.go rename to .api/v1beta1/novametadata_types.go diff --git a/api/v1beta1/novametadata_webhook.go b/.api/v1beta1/novametadata_webhook.go similarity index 100% rename from api/v1beta1/novametadata_webhook.go rename to .api/v1beta1/novametadata_webhook.go diff --git a/api/v1beta1/novanovncproxy_types.go b/.api/v1beta1/novanovncproxy_types.go similarity index 100% rename from api/v1beta1/novanovncproxy_types.go rename to .api/v1beta1/novanovncproxy_types.go diff --git a/api/v1beta1/novanovncproxy_webhook.go b/.api/v1beta1/novanovncproxy_webhook.go similarity index 100% rename from api/v1beta1/novanovncproxy_webhook.go rename to .api/v1beta1/novanovncproxy_webhook.go diff --git a/api/v1beta1/novascheduler_types.go b/.api/v1beta1/novascheduler_types.go similarity index 100% rename from api/v1beta1/novascheduler_types.go rename to .api/v1beta1/novascheduler_types.go diff --git a/api/v1beta1/novascheduler_webhook.go b/.api/v1beta1/novascheduler_webhook.go similarity index 100% rename from api/v1beta1/novascheduler_webhook.go rename to .api/v1beta1/novascheduler_webhook.go diff --git a/api/v1beta1/zz_generated.deepcopy.go b/.api/v1beta1/zz_generated.deepcopy.go similarity index 100% rename from api/v1beta1/zz_generated.deepcopy.go rename to .api/v1beta1/zz_generated.deepcopy.go diff --git a/Makefile b/Makefile index bb5a59cc1..c8f1a2f5b 100644 --- a/Makefile +++ b/Makefile @@ -112,14 +112,23 @@ docker-buildx: ## Build and push docker image for the manager for cross-platform ##@ Development +# .PHONY: manifests +# manifests: gowork controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. +# $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases && \ +# rm -f apis/bases/* && cp -a config/crd/bases apis/ + .PHONY: manifests manifests: gowork controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases && \ - rm -f api/bases/* && cp -a config/crd/bases api/ + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./apis/..." paths="./internal/..." paths="./cmd/..." output:crd:artifacts:config=config/crd/bases && \ + rm -f apis/bases/* && cp -a config/crd/bases apis/ + +# .PHONY: generate +# generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +# $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./apis/..." .PHONY: fmt fmt: ## Run go fmt against code. @@ -128,7 +137,7 @@ fmt: ## Run go fmt against code. .PHONY: vet vet: gowork ## Run go vet against code. go vet ./... - go vet ./api/... + go vet ./apis/... .PHONY: tidy @@ -150,7 +159,7 @@ PROC_CMD = --procs ${PROCS} test: manifests generate fmt vet envtest ginkgo ## Run tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) -v debug --bin-dir $(LOCALBIN) use $(ENVTEST_K8S_VERSION) -p path)" \ OPERATOR_TEMPLATES="$(PWD)/templates" \ - $(GINKGO) --trace --cover --coverpkg=../../internal/...,../../api/v1beta1 --coverprofile cover.out --covermode=atomic --randomize-all ${PROC_CMD} $(GINKGO_ARGS) ./test/... + $(GINKGO) --trace --cover --coverpkg=../../internal/...,../../apis/nova/v1beta1 --coverprofile cover.out --covermode=atomic --randomize-all ${PROC_CMD} $(GINKGO_ARGS) ./test/... ##@ Build @@ -359,13 +368,13 @@ golint: get-ci-tools .PHONY: operator-lint operator-lint: $(LOCALBIN) gowork ## Runs operator-lint GOBIN=$(LOCALBIN) go install github.com/gibizer/operator-lint@v0.5.0 - go vet -vettool=$(LOCALBIN)/operator-lint ./... ./api/... + go vet -vettool=$(LOCALBIN)/operator-lint ./... ./apis/... .PHONY: gowork gowork: ## Generate go.work file test -f go.work || GOTOOLCHAIN=$(GOTOOLCHAIN_VERSION) go work init go work use . - go work use ./api + go work use ./apis go work sync OPERATOR_NAMESPACE ?= openstack-operators @@ -435,7 +444,7 @@ force-bump: ## Force bump operator and lib-common dependencies for dep in $$(cat go.mod | grep openstack-k8s-operators | grep -vE -- 'indirect|nova-operator|^replace' | awk '{print $$1}'); do \ go get $$dep@$(BRANCH) ; \ done - for dep in $$(cat api/go.mod | grep openstack-k8s-operators | grep -vE -- 'indirect|nova-operator|^replace' | awk '{print $$1}'); do \ + for dep in $$(cat apis/go.mod | grep openstack-k8s-operators | grep -vE -- 'indirect|nova-operator|^replace' | awk '{print $$1}'); do \ cd ./api && go get $$dep@$(BRANCH) && cd .. ; \ done diff --git a/PROJECT b/PROJECT index 237f3fd3d..b74f2787f 100644 --- a/PROJECT +++ b/PROJECT @@ -18,7 +18,7 @@ resources: domain: openstack.org group: nova kind: NovaAPI - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 version: v1beta1 webhooks: defaulting: true @@ -31,7 +31,7 @@ resources: domain: openstack.org group: nova kind: NovaScheduler - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 version: v1beta1 webhooks: defaulting: true @@ -44,7 +44,7 @@ resources: domain: openstack.org group: nova kind: NovaConductor - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 version: v1beta1 webhooks: defaulting: true @@ -57,7 +57,7 @@ resources: domain: openstack.org group: nova kind: NovaMetadata - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 version: v1beta1 webhooks: defaulting: true @@ -70,7 +70,7 @@ resources: domain: openstack.org group: nova kind: NovaNoVNCProxy - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 version: v1beta1 webhooks: defaulting: true @@ -83,7 +83,7 @@ resources: domain: openstack.org group: nova kind: NovaCell - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 version: v1beta1 webhooks: defaulting: true @@ -96,7 +96,7 @@ resources: domain: openstack.org group: nova kind: Nova - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 version: v1beta1 webhooks: defaulting: true @@ -109,7 +109,20 @@ resources: domain: openstack.org group: nova kind: NovaCompute - path: github.com/openstack-k8s-operators/nova-operator/api/v1beta1 + path: github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1 + version: v1beta1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: openstack.org + group: placement + kind: PlacementAPI + path: github.com/openstack-k8s-operators/nova-operator/apis/placement/v1beta1 version: v1beta1 webhooks: defaulting: true diff --git a/apis/bases/nova.openstack.org_nova.yaml b/apis/bases/nova.openstack.org_nova.yaml new file mode 100644 index 000000000..322c6ee19 --- /dev/null +++ b/apis/bases/nova.openstack.org_nova.yaml @@ -0,0 +1,1996 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: nova.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: Nova + listKind: NovaList + plural: nova + singular: nova + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: Nova is the Schema for the nova API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaSpec defines the desired state of Nova + properties: + apiContainerImageURL: + description: APIContainerImageURL + type: string + apiDatabaseAccount: + default: nova-api + description: APIDatabaseAccount - MariaDBAccount to use when accessing + the API DB + type: string + apiDatabaseInstance: + default: openstack + description: |- + APIDatabaseInstance is the name of the MariaDB CR to select the DB + Service instance used for the Nova API DB. + type: string + apiMessageBusInstance: + description: |- + APIMessageBusInstance is the name of the RabbitMqCluster CR to select + the Message Bus Service instance used by the Nova top level services to + communicate. + Deprecated: Use MessagingBus.Cluster instead + type: string + apiServiceTemplate: + default: + replicas: 1 + description: APIServiceTemplate - define the nova-api service + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like e.g. api-paste.ini or policy.yaml. + type: object + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + 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 + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret + for the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for + the service + type: string + type: object + public: + description: Public GenericService - holds the secret + for the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for + the service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + apiTimeout: + default: 60 + description: APITimeout for Route and Apache + minimum: 10 + type: integer + auth: + description: Auth - Parameters related to authentication (shared by + all Nova services) + properties: + applicationCredentialSecret: + description: |- + ApplicationCredentialSecret - the name of the k8s Secret that contains the + application credential data used for authentication + type: string + type: object + cellTemplates: + additionalProperties: + description: |- + NovaCellTemplate defines the input parameters specified by the user to + create a NovaCell via higher level CRDs. + properties: + cellDatabaseAccount: + description: CellDatabaseAccount - MariaDBAccount to use when + accessing the give cell DB + type: string + cellDatabaseInstance: + default: openstack + description: |- + CellDatabaseInstance is the name of the MariaDB CR to select the DB + Service instance used as the DB of this cell. + type: string + cellMessageBusInstance: + description: |- + CellMessageBusInstance is the name of the RabbitMqCluster CR to select + the Message Bus Service instance used by the nova services to + communicate in this cell. For cell0 it is unused. + Deprecated: Use MessagingBus.Cluster instead + type: string + conductorServiceTemplate: + description: ConductorServiceTemplate - defines the cell conductor + deployment for the cell. + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + dbPurge: + description: DBPurge defines the parameters for the DB archiving + and purging cron job + properties: + archiveAge: + default: 30 + description: |- + ArchiveAge defines the minimum age of the records in days that can be + moved to the shadow tables. + minimum: 1 + type: integer + purgeAge: + default: 90 + description: |- + PurgeAge defines the minimum age of the records in days that can be + deleted from the shadow tables + minimum: 1 + type: integer + schedule: + default: 0 0 * * * + description: |- + Schedule defines when to run the DB maintenance job in a cron format. + By default it runs every midnight. + type: string + type: object + hasAPIAccess: + description: |- + HasAPIAccess defines if this Cell is configured to have access to the + API DB and message bus. + type: boolean + memcachedInstance: + description: |- + MemcachedInstance is the name of the Memcached CR that the services in the cell will use. + If defined then this takes precedence over Nova.Spec.MemcachedInstance for this cel + type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and + cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object + metadataServiceTemplate: + description: |- + MetadataServiceTemplate - defines the metadata service dedicated for the + cell. Note that for cell0 metadata service should not be deployed. Also + if metadata service needs to be deployed per cell here then it should + not be enabled to be deployed on the top level via the Nova CR at the + same time. By default Nova CR deploys the metadata service at the top + level and disables it on the cell level. + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite + default config files like e.g. api-paste.ini. + type: object + enabled: + description: |- + Enabled - Whether NovaMetadata services should be deployed and managed. + If it is set to false then the related NovaMetadata CR will be deleted + if exists and owned by a higher level nova CR (Nova or NovaCell). If it + exist but not owned by a higher level nova CR then the NovaMetadata CR + will not be touched. + If it is set to true the a NovaMetadata CR will be created. + If there is already a manually created NovaMetadata CR with the relevant + name then this operator will not try to update that CR, instead + the higher level nova CR will be in error state until the manually + create NovaMetadata CR is deleted manually. + type: boolean + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + override: + description: Override, provides the ability to override + the generated manifest of several child resources. + properties: + service: + description: |- + Override configuration for the Service created to serve traffic to the cluster for internal + communication. + properties: + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains + the configurations of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs + in a pre-created bundle file + type: string + secretName: + description: SecretName - holding the cert, key for + the service + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + noVNCProxyServiceTemplate: + description: |- + NoVNCProxyServiceTemplate - defines the novncproxy service dedicated for + the cell. Note that for cell0 novncproxy should not be deployed so + the enabled field of this template is defaulted to false in cell0 but + defaulted to true in other cells. + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + enabled: + description: |- + Enabled - Whether NovaNoVNCProxy services should be deployed and managed. + If it is set to false then the related NovaNoVNCProxy CR will be deleted + if exists and owned by the NovaCell. If it exist but not owned by the + NovaCell then the NovaNoVNCProxy will not be touched. + If it is set to true the a NovaNoVNCProxy CR will be created. + If there is already a manually created NovaNoVNCProxy CR with the + relevant name then the cell will not try to update that CR, instead the + NovaCell be in error state until the manually create NovaNoVNCProxy CR + is deleted by the operator. + type: boolean + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes + running this service + type: object + override: + description: Override, provides the ability to override + the generated manifest of several child resources. + properties: + service: + description: Override configuration for the Service + created to serve traffic to the cluster. + properties: + endpointURL: + type: string + metadata: + description: |- + EmbeddedLabelsAnnotations is an embedded subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta. + Only labels and annotations are included. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains + the configurations of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs + in a pre-created bundle file + type: string + service: + description: Service - Cert secret used for the nova + novnc service endpoint + properties: + secretName: + description: SecretName - holding the cert, key + for the service + type: string + type: object + vencrypt: + description: |- + Vencrypt - cert secret containing the x509 certificate to be presented to the VNC server. + The CommonName field should match the primary hostname of the controller node. If using a HA deployment, + the Organization field can also be configured to a value that is common across all console proxy instances in the deployment. + https://docs.openstack.org/nova/latest/admin/remote-console-access.html#novnc-proxy-server-configuration + properties: + secretName: + description: SecretName - holding the cert, key + for the service + type: string + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + cell. + type: object + novaComputeTemplates: + additionalProperties: + description: |- + NovaComputeTemplate defines the input parameters specified by the user to + create a NovaCompute via higher level CRDs. + properties: + computeDriver: + description: ComputeDriver - defines which driver to use + for controlling virtualization + enum: + - ironic.IronicDriver + - fake.FakeDriver + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite + default config files like e.g. provider.yaml + type: object + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + replicas: + default: 1 + description: Replicas of the service to run. For ironic.IronicDriver + the max replica is 1 + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry + in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the + Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - computeDriver + type: object + description: |- + NovaComputeTemplates - map of nova computes template with selected drivers in format + compute_name: compute_template. Key from map is arbitrary name for the compute with + a limit of 20 characters. + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - cellDatabaseAccount + - hasAPIAccess + type: object + default: + cell0: + cellDatabaseAccount: nova-cell0 + hasAPIAccess: true + cell1: + cellDatabaseAccount: nova-cell1 + cellDatabaseInstance: openstack-cell1 + hasAPIAccess: true + messagingBus: + cluster: rabbitmq-cell1 + description: |- + Cells is a mapping of cell names to NovaCellTemplate objects defining + the cells in the deployment. The "cell0" cell is a mandatory cell in + every deployment. Moreover any real deployment needs at least one + additional normal cell as "cell0" cannot have any computes. + type: object + computeContainerImageURL: + description: NovaComputeContainerImageURL + type: string + conductorContainerImageURL: + description: ConductorContainerImageURL + type: string + keystoneInstance: + default: keystone + description: |- + KeystoneInstance to name of the KeystoneAPI CR to select the Service + instance used by the Nova services to authenticate. + type: string + memcachedInstance: + default: memcached + description: MemcachedInstance is the name of the Memcached CR that + all nova service will use. + type: string + messagingBus: + description: MessagingBus configuration (username, vhost, and cluster) + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object + metadataContainerImageURL: + description: MetadataContainerImageURL + type: string + metadataServiceTemplate: + default: + enabled: true + description: |- + MetadataServiceTemplate - defines the metadata service that is global + for the deployment serving all the cells. Note that if you want to + deploy metadata per cell then the metadata service should be disabled + here and enabled in the cellTemplates instead. + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like e.g. api-paste.ini. + type: object + enabled: + description: |- + Enabled - Whether NovaMetadata services should be deployed and managed. + If it is set to false then the related NovaMetadata CR will be deleted + if exists and owned by a higher level nova CR (Nova or NovaCell). If it + exist but not owned by a higher level nova CR then the NovaMetadata CR + will not be touched. + If it is set to true the a NovaMetadata CR will be created. + If there is already a manually created NovaMetadata CR with the relevant + name then this operator will not try to update that CR, instead + the higher level nova CR will be in error state until the manually + create NovaMetadata CR is deleted manually. + type: boolean + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + override: + description: Override, provides the ability to override the generated + manifest of several child resources. + properties: + service: + description: |- + Override configuration for the Service created to serve traffic to the cluster for internal + communication. + properties: + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + secretName: + description: SecretName - holding the cert, key for the service + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting + NodeSelector here acts as a default value and can be overridden by service + specific NodeSelector Settings. + type: object + notificationsBus: + description: NotificationsBus configuration (username, vhost, and + cluster) for notifications + properties: + cluster: + description: Name of the cluster + minLength: 1 + type: string + user: + description: User - RabbitMQ username + type: string + vhost: + description: Vhost - RabbitMQ vhost name + type: string + required: + - cluster + type: object + notificationsBusInstance: + description: |- + NotificationsBusInstance is the name of the RabbitMqCluster CR to select + the Message Bus Service instance used by the Nova top level services and all cells to publish notifications. + If undefined, the value will be inherited from OpenStackControlPlane. + An empty value "" leaves the notification drivers unconfigured and emitting no notifications at all. + Avoid colocating it with RabbitMqClusterName, APIMessageBusInstance or CellMessageBusInstance used for RPC. + For particular Nova cells, notifications cannot be disabled, nor configured differently. + type: string + novncproxyContainerImageURL: + description: NoVNCContainerImageURL + type: string + passwordSelectors: + default: + service: NovaPassword + description: |- + PasswordSelectors - Selectors to identify the DB and ServiceUser + passwords from the Secret + properties: + metadataSecret: + default: MetadataSecret + description: |- + MetadataSecret - the name of the field to get the metadata secret from the + Secret + type: string + prefixMetadataCellsSecret: + default: MetadataCellsSecret + description: |- + prefixMetadataCellsSecret - the prefix name of the field to get the metadata secret from the + Secret for cells. Vale of metadata_proxy_shared_secret + information for the nova-metadata service. This secret is shared + between nova and neutron ovn-metadata inside selected cell + and if this is not defined the global metadata_proxy_shared_secret + secret will be used + type: string + service: + default: NovaPassword + description: |- + Service - Selector to get the keystone service user password from the + Secret + type: string + type: object + preserveJobs: + default: false + description: PreserveJobs - do not delete jobs after they finished + e.g. to check logs + type: boolean + schedulerContainerImageURL: + description: SchedulerContainerImageURL + type: string + schedulerServiceTemplate: + default: + replicas: 1 + description: SchedulerServiceTemplate- define the nova-scheduler service + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + secret: + description: |- + Secret is the name of the Secret instance containing password + information for nova like the keystone service password and DB passwords + type: string + serviceUser: + default: nova + description: ServiceUser - optional username used for this service + to register in keystone + type: string + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - apiContainerImageURL + - computeContainerImageURL + - conductorContainerImageURL + - metadataContainerImageURL + - novncproxyContainerImageURL + - schedulerContainerImageURL + - secret + type: object + status: + description: NovaStatus defines the observed state of Nova + properties: + apiServiceReadyCount: + description: APIServiceReadyCount defines the number or replicas ready + from nova-api + format: int32 + type: integer + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + discoveredCells: + additionalProperties: + type: string + description: |- + DiscoveredCells is a map keyed by cell names that have discovered all kubernetes managed + computes in cell value is a hash of config from all kubernetes managed computes in cell + type: object + metadataServiceReadyCount: + description: |- + MetadataReadyCount defines the number of replicas ready from + nova-metadata service + format: int32 + type: integer + observedGeneration: + description: ObservedGeneration - the most recent generation observed + for this service. If the observed generation is less than the spec + generation, then the controller has not processed the latest changes. + format: int64 + type: integer + registeredCells: + additionalProperties: + type: string + description: |- + RegisteredCells is a map keyed by cell names that are registered in the + nova_api database with a value that is the hash of the given cell + configuration. + type: object + schedulerServiceReadyCount: + description: SchedulerServiceReadyCount defines the number or replicas + ready from nova-scheduler + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/nova.openstack.org_novaapis.yaml b/apis/bases/nova.openstack.org_novaapis.yaml new file mode 100644 index 000000000..da1464453 --- /dev/null +++ b/apis/bases/nova.openstack.org_novaapis.yaml @@ -0,0 +1,528 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: novaapis.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: NovaAPI + listKind: NovaAPIList + plural: novaapis + singular: novaapi + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: NovaAPI is the Schema for the novaapis API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaAPISpec defines the desired state of NovaAPI + properties: + apiDatabaseAccount: + default: nova-api + description: APIDatabaseAccount - MariaDBAccount to use when accessing + the API DB + type: string + apiDatabaseHostname: + description: APIDatabaseHostname - hostname to use when accessing + the API DB + type: string + apiTimeout: + default: 60 + description: APITimeout for Route and Apache + minimum: 10 + type: integer + cell0DatabaseAccount: + default: nova-cell0 + description: APIDatabaseAccount - MariaDBAccount to use when accessing + the cell0 DB + type: string + cell0DatabaseHostname: + description: APIDatabaseHostname - hostname to use when accessing + the cell0 DB + type: string + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like e.g. api-paste.ini or policy.yaml. + type: object + keystoneAuthURL: + description: |- + KeystoneAuthURL configures the keystone API endpoint to be used + by the service for authentication and authorization + type: string + keystonePublicAuthURL: + description: |- + KeystonePublicAuthURL configures the public keystone API endpoint. This + can be different from KeystoneAuthURL. The service uses this value + to redirect unauthenticated users. + type: string + memcachedInstance: + description: MemcachedInstance is the name of the Memcached CR that + all nova service will use. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + 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 + region: + default: regionOne + description: Region - the region name to use for service endpoint + discovery + type: string + registeredCells: + additionalProperties: + type: string + description: |- + RegisteredCells is a map keyed by cell names that are registered in the + nova_api database with a value that is the hash of the given cell + configuration. + This is used to detect when a new cell is added or an existing cell is + reconfigured to trigger refresh of the in memory cell caches of the + service. + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: |- + Secret is the name of the Secret instance containing password + information for the nova-api service. This secret is expected to be + generated by the nova-operator based on the information passed to the + Nova CR. + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Nova services the default SA name + type: string + serviceUser: + default: nova + description: |- + ServiceUser - optional username used for this service to register in + keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret for + the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + public: + description: Public GenericService - holds the secret for + the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - apiDatabaseHostname + - cell0DatabaseHostname + - keystoneAuthURL + - keystonePublicAuthURL + - memcachedInstance + - registeredCells + - secret + - serviceAccount + type: object + status: + description: NovaAPIStatus defines the observed state of NovaAPI + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: |- + ObservedGeneration - the most recent generation observed for this + service. If the observed generation is less than the spec generation, + then the controller has not processed the latest changes injected by + the openstack-operator in the top-level CR (e.g. the ContainerImage) + format: int64 + type: integer + readyCount: + description: ReadyCount defines the number of replicas ready from + nova-api + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/nova.openstack.org_novacells.yaml b/apis/bases/nova.openstack.org_novacells.yaml new file mode 100644 index 000000000..45a27f8b4 --- /dev/null +++ b/apis/bases/nova.openstack.org_novacells.yaml @@ -0,0 +1,1141 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: novacells.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: NovaCell + listKind: NovaCellList + plural: novacells + singular: novacell + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: NovaCell is the Schema for the novacells API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaCellSpec defines the desired state of NovaCell + properties: + apiDatabaseAccount: + default: nova + description: APIDatabaseAccount - MariaDBAccount to use when accessing + the API DB + type: string + apiDatabaseHostname: + description: |- + APIDatabaseHostname - hostname to use when accessing the API DB. If not + provided then up-calls will be disabled. This filed is Required for + cell0. + type: string + apiTimeout: + default: 60 + description: APITimeout for Route and Apache + minimum: 10 + type: integer + cellDatabaseAccount: + default: nova + description: CellDatabaseAccount - MariaDBAccount to use when accessing + the cell DB + type: string + cellDatabaseHostname: + description: CellDatabaseHostname - hostname to use when accessing + the cell DB + type: string + cellName: + description: |- + CellName is the name of the Nova Cell. The value "cell0" has a special + meaning. The "cell0" Cell cannot have compute nodes associated and the + conductor in this cell acts as the super conductor for all the cells in + the deployment. + type: string + computeContainerImageURL: + description: NovaComputeContainerImageURL + type: string + conductorContainerImageURL: + description: ConductorContainerImageURL + type: string + conductorServiceTemplate: + description: ConductorServiceTemplate - defines the cell conductor + deployment for the cell + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + dbPurge: + description: DBPurge defines the parameters for the DB archiving and + purging cron job + properties: + archiveAge: + default: 30 + description: |- + ArchiveAge defines the minimum age of the records in days that can be + moved to the shadow tables. + minimum: 1 + type: integer + purgeAge: + default: 90 + description: |- + PurgeAge defines the minimum age of the records in days that can be + deleted from the shadow tables + minimum: 1 + type: integer + schedule: + default: 0 0 * * * + description: |- + Schedule defines when to run the DB maintenance job in a cron format. + By default it runs every midnight. + type: string + type: object + keystoneAuthURL: + description: |- + KeystoneAuthURL - the URL that the service in the cell can use to talk + to keystone + type: string + memcachedInstance: + description: MemcachedInstance is the name of the Memcached CR that + all nova service will use. + type: string + metadataContainerImageURL: + description: MetadataContainerImageURL + type: string + metadataServiceTemplate: + description: MetadataServiceTemplate - defines the metadata service + dedicated for the cell. + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like e.g. api-paste.ini. + type: object + enabled: + description: |- + Enabled - Whether NovaMetadata services should be deployed and managed. + If it is set to false then the related NovaMetadata CR will be deleted + if exists and owned by a higher level nova CR (Nova or NovaCell). If it + exist but not owned by a higher level nova CR then the NovaMetadata CR + will not be touched. + If it is set to true the a NovaMetadata CR will be created. + If there is already a manually created NovaMetadata CR with the relevant + name then this operator will not try to update that CR, instead + the higher level nova CR will be in error state until the manually + create NovaMetadata CR is deleted manually. + type: boolean + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + override: + description: Override, provides the ability to override the generated + manifest of several child resources. + properties: + service: + description: |- + Override configuration for the Service created to serve traffic to the cluster for internal + communication. + properties: + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + secretName: + description: SecretName - holding the cert, key for the service + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + noVNCProxyServiceTemplate: + description: |- + NoVNCProxyServiceTemplate - defines the novncproxy service dedicated for + the cell. + properties: + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + enabled: + description: |- + Enabled - Whether NovaNoVNCProxy services should be deployed and managed. + If it is set to false then the related NovaNoVNCProxy CR will be deleted + if exists and owned by the NovaCell. If it exist but not owned by the + NovaCell then the NovaNoVNCProxy will not be touched. + If it is set to true the a NovaNoVNCProxy CR will be created. + If there is already a manually created NovaNoVNCProxy CR with the + relevant name then the cell will not try to update that CR, instead the + NovaCell be in error state until the manually create NovaNoVNCProxy CR + is deleted by the operator. + type: boolean + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this service + type: object + override: + description: Override, provides the ability to override the generated + manifest of several child resources. + properties: + service: + description: Override configuration for the Service created + to serve traffic to the cluster. + properties: + endpointURL: + type: string + metadata: + description: |- + EmbeddedLabelsAnnotations is an embedded subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta. + Only labels and annotations are included. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in + a pre-created bundle file + type: string + service: + description: Service - Cert secret used for the nova novnc + service endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + vencrypt: + description: |- + Vencrypt - cert secret containing the x509 certificate to be presented to the VNC server. + The CommonName field should match the primary hostname of the controller node. If using a HA deployment, + the Organization field can also be configured to a value that is common across all console proxy instances in the deployment. + https://docs.openstack.org/nova/latest/admin/remote-console-access.html#novnc-proxy-server-configuration + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + type: object + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this services. + type: object + novaComputeTemplates: + additionalProperties: + description: |- + NovaComputeTemplate defines the input parameters specified by the user to + create a NovaCompute via higher level CRDs. + properties: + computeDriver: + description: ComputeDriver - defines which driver to use for + controlling virtualization + enum: + - ironic.IronicDriver + - fake.FakeDriver + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite + default config files like e.g. provider.yaml + type: object + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment + resource names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: |- + NodeSelector to target subset of worker nodes running this service. Setting here overrides + any global NodeSelector settings within the Nova CR. + type: object + replicas: + default: 1 + description: Replicas of the service to run. For ironic.IronicDriver + the max replica is 1 + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service + references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - computeDriver + type: object + description: |- + NovaComputeTemplates - map of nova computes template with selected drivers in format + compute_name: compute_template. Key from map is arbitrary name for the compute. + because of that there is a 20 character limit on the compute name. + type: object + novncproxyContainerImageURL: + description: NoVNCContainerImageURL + type: string + preserveJobs: + default: false + description: PreserveJobs - do not delete jobs after they finished + e.g. to check logs + type: boolean + region: + default: regionOne + description: Region - the region name to use for service endpoint + discovery + type: string + secret: + description: |- + Secret is the name of the Secret instance containing password + information for the nova cell. This secret is expected to be + generated by the nova-operator based on the information passed to the + Nova CR. + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Nova services the default SA name + type: string + serviceUser: + default: nova + description: |- + ServiceUser - optional username used for this service to register in + keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - cellDatabaseHostname + - cellName + - computeContainerImageURL + - conductorContainerImageURL + - conductorServiceTemplate + - keystoneAuthURL + - memcachedInstance + - metadataContainerImageURL + - noVNCProxyServiceTemplate + - novncproxyContainerImageURL + - secret + - serviceAccount + type: object + status: + description: NovaCellStatus defines the observed state of NovaCell + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + conductorServiceReadyCount: + description: |- + ConductorServiceReadyCount defines the number of replicas ready from + nova-conductor service in the cell + format: int32 + type: integer + hash: + additionalProperties: + type: string + description: |- + INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + Important: Run "make" to regenerate code after modifying this file + Map of hashes to track e.g. job status + type: object + metadataServiceReadyCount: + description: |- + MetadataServiceReadyCount defines the number of replicas ready from + nova-metadata service in the cell + format: int32 + type: integer + noVNCProxyServiceReadyCount: + description: |- + NoVNCPRoxyServiceReadyCount defines the number of replicas ready from + nova-novncproxy service in the cell + format: int32 + type: integer + novaComputesStatus: + additionalProperties: + description: NovaComputeCellStatus defines state of NovaCompute + in cell + properties: + deployed: + description: 'Deployed value: true means that the compute is + deployed but can still be undiscovered' + type: boolean + errors: + description: Errors value True means that during deployment, + errors appear, and the user needs to check the compute for + problems + type: boolean + required: + - deployed + - errors + type: object + description: |- + NovaComputesStatus is a map with format cell_name: NovaComputeCellStatus + where NovaComputeCellStatus tell if compute with selected name deployed successfully + and indicates if the compute is successfully mapped to the cell in + the nova_api database. + When a compute is removed from the Spec the operator will delete the + related NovaCompute CR and then remove the compute from this Status field. + type: object + observedGeneration: + description: |- + ObservedGeneration - the most recent generation observed for this + service. If the observed generation is less than the spec generation, + then the controller has not processed the latest changes injected by + the openstack-operator in the top-level CR (e.g. the ContainerImage) + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/nova.openstack.org_novacomputes.yaml b/apis/bases/nova.openstack.org_novacomputes.yaml new file mode 100644 index 000000000..cf4cebcf4 --- /dev/null +++ b/apis/bases/nova.openstack.org_novacomputes.yaml @@ -0,0 +1,312 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: novacomputes.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: NovaCompute + listKind: NovaComputeList + plural: novacomputes + singular: novacompute + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: NovaCompute is the Schema for the NovaCompute + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaComputeSpec defines the desired state of NovaCompute + properties: + cellName: + description: CellName is the name of the Nova Cell this NovaCompute + belongs to. + type: string + computeDriver: + description: ComputeDriver defines which driver to use for controlling + virtualization + enum: + - ironic.IronicDriver + - fake.FakeDriver + type: string + computeName: + description: ComputeName - compute name. + type: string + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like e.g. provider.yaml + type: object + keystoneAuthURL: + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this service + type: object + region: + default: regionOne + description: Region - the region name to use for service endpoint + discovery + type: string + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: |- + Secret is the name of the Secret instance containing password + information for the NovaCompute service. This secret is expected to be + generated by the nova-operator based on the information passed to the + Nova CR. + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Nova services the default SA name + type: string + serviceUser: + default: nova + description: |- + ServiceUser - optional username used for this service to register in + keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - cellName + - computeDriver + - computeName + - keystoneAuthURL + - secret + - serviceAccount + type: object + status: + description: NovaComputeStatus defines the observed state of NovaCompute + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: |- + ObservedGeneration - the most recent generation observed for this + service. If the observed generation is less than the spec generation, + then the controller has not processed the latest changes injected by + the openstack-operator in the top-level CR (e.g. the ContainerImage) + format: int64 + type: integer + readyCount: + description: ReadyCount defines the number of replicas ready from + NovaCompute + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/nova.openstack.org_novaconductors.yaml b/apis/bases/nova.openstack.org_novaconductors.yaml new file mode 100644 index 000000000..bf1311254 --- /dev/null +++ b/apis/bases/nova.openstack.org_novaconductors.yaml @@ -0,0 +1,353 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: novaconductors.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: NovaConductor + listKind: NovaConductorList + plural: novaconductors + singular: novaconductor + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: NovaConductor is the Schema for the novaconductors API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaConductorSpec defines the desired state of NovaConductor + properties: + apiDatabaseAccount: + default: nova + description: APIDatabaseAccount - MariaDBAccount to use when accessing + the API DB + type: string + apiDatabaseHostname: + description: |- + APIDatabaseHostname - hostname to use when accessing the API DB. If not + provided then up-calls will be disabled. This filed is Required for + cell0. + type: string + cellDatabaseAccount: + default: nova + description: CellDatabaseAccount - MariaDBAccount to use when accessing + the cell DB + type: string + cellDatabaseHostname: + description: |- + NOTE(gibi): This should be Required, see notes in KeystoneAuthURL + CellDatabaseHostname - hostname to use when accessing the cell DB + type: string + cellName: + description: CellName is the name of the Nova Cell this conductor + belongs to. + type: string + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + dbPurge: + description: DBPurge defines the parameters for the DB archiving and + purging cron job + properties: + archiveAge: + default: 30 + description: |- + ArchiveAge defines the minimum age of the records in days that can be + moved to the shadow tables. + minimum: 1 + type: integer + purgeAge: + default: 90 + description: |- + PurgeAge defines the minimum age of the records in days that can be + deleted from the shadow tables + minimum: 1 + type: integer + schedule: + default: 0 0 * * * + description: |- + Schedule defines when to run the DB maintenance job in a cron format. + By default it runs every midnight. + type: string + type: object + keystoneAuthURL: + description: |- + KeystoneAuthURL - the URL that the nova-conductor service can use to + talk to keystone + type: string + memcachedInstance: + description: MemcachedInstance is the name of the Memcached CR that + all nova service will use. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this service + type: object + preserveJobs: + default: false + description: PreserveJobs - do not delete jobs after they finished + e.g. to check logs + type: boolean + region: + default: regionOne + description: Region - the region name to use for service endpoint + discovery + type: string + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: |- + Secret is the name of the Secret instance containing password + information for the nova-conductor service. This secret is expected to + be generated by the nova-operator based on the information passed to the + Nova CR. + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Nova services the default SA name + type: string + serviceUser: + default: nova + description: |- + ServiceUser - optional username used for this service to register in + keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - cellName + - keystoneAuthURL + - memcachedInstance + - secret + - serviceAccount + type: object + status: + description: NovaConductorStatus defines the observed state of NovaConductor + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: |- + ObservedGeneration - the most recent generation observed for this + service. If the observed generation is less than the spec generation, + then the controller has not processed the latest changes injected by + the openstack-operator in the top-level CR (e.g. the ContainerImage) + format: int64 + type: integer + readyCount: + description: ReadyCount defines the number of replicas ready from + nova-conductor + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/nova.openstack.org_novametadata.yaml b/apis/bases/nova.openstack.org_novametadata.yaml new file mode 100644 index 000000000..e113ff0c0 --- /dev/null +++ b/apis/bases/nova.openstack.org_novametadata.yaml @@ -0,0 +1,501 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: novametadata.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: NovaMetadata + listKind: NovaMetadataList + plural: novametadata + singular: novametadata + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: NovaMetadata is the Schema for the novametadata API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaMetadataSpec defines the desired state of NovaMetadata + properties: + apiDatabaseAccount: + default: nova-api + description: APIDatabaseAccount - MariaDBAccount to use when accessing + the API DB + type: string + apiDatabaseHostname: + description: |- + APIDatabaseHostname - hostname to use when accessing the API DB. + This filed is Required if the CellName is not provided + type: string + apiTimeout: + default: 60 + description: APITimeout for Route and Apache + minimum: 10 + type: integer + cellDatabaseAccount: + default: nova + description: CellDatabaseAccount - MariaDBAccount to use when accessing + the cell DB + type: string + cellDatabaseHostname: + description: |- + CellDatabaseHostname - hostname to use when accessing the cell DB + This is unused if CellName is not provided. But if it is provided then + CellDatabaseHostName is also Required. + type: string + cellName: + description: |- + CellName is the name of the Nova Cell this metadata service belongs to. + If not provided then the metadata serving every cells in the deployment + type: string + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like e.g. api-paste.ini. + type: object + keystoneAuthURL: + description: |- + KeystoneAuthURL - the URL that the nova-metadata service can use to talk + to keystone + type: string + memcachedInstance: + description: MemcachedInstance is the name of the Memcached CR that + all nova service will use. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this service + type: object + override: + description: Override, provides the ability to override the generated + manifest of several child resources. + properties: + service: + description: |- + Override configuration for the Service created to serve traffic to the cluster for internal + communication. + properties: + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object + type: object + region: + default: regionOne + description: Region - the region name to use for service endpoint + discovery + type: string + registeredCells: + additionalProperties: + type: string + description: |- + RegisteredCells is a map keyed by cell names that are registered in the + nova_api database with a value that is the hash of the given cell + configuration. + This is used to detect when a new cell is added or an existing cell is + reconfigured to trigger refresh of the in memory cell caches of the + service. + This is empty for the case when nova-metadata runs within the cell. + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: |- + Secret is the name of the Secret instance containing password + information for the nova-conductor service. This secret is expected to + be generated by the nova-operator based on the information passed to the + Nova CR. + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Nova services the default SA name + type: string + serviceUser: + default: nova + description: |- + ServiceUser - optional username used for this service to register in + keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + secretName: + description: SecretName - holding the cert, key for the service + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - keystoneAuthURL + - memcachedInstance + - secret + - serviceAccount + type: object + status: + description: NovaMetadataStatus defines the observed state of NovaMetadata + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: |- + ObservedGeneration - the most recent generation observed for this + service. If the observed generation is less than the spec generation, + then the controller has not processed the latest changes injected by + the openstack-operator in the top-level CR (e.g. the ContainerImage) + format: int64 + type: integer + readyCount: + description: ReadyCount defines the number of replicas ready from + nova-metadata + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/nova.openstack.org_novanovncproxies.yaml b/apis/bases/nova.openstack.org_novanovncproxies.yaml new file mode 100644 index 000000000..77b96fd57 --- /dev/null +++ b/apis/bases/nova.openstack.org_novanovncproxies.yaml @@ -0,0 +1,484 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: novanovncproxies.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: NovaNoVNCProxy + listKind: NovaNoVNCProxyList + plural: novanovncproxies + singular: novanovncproxy + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: NovaNoVNCProxy is the Schema for the novanovncproxies API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaNoVNCProxySpec defines the desired state of NovaNoVNCProxy + properties: + cellDatabaseAccount: + default: nova + description: CellDatabaseAccount - MariaDBAccount to use when accessing + the cell DB + type: string + cellDatabaseHostname: + description: CellDatabaseHostname - hostname to use when accessing + the cell DB + type: string + cellName: + description: CellName is the name of the Nova Cell this novncproxy + belongs to. + type: string + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + keystoneAuthURL: + description: |- + KeystoneAuthURL - the URL that the nova-novncproxy service can use to + talk to keystone + type: string + memcachedInstance: + description: MemcachedInstance is the name of the Memcached CR that + all nova service will use. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this service + type: object + override: + description: Override, provides the ability to override the generated + manifest of several child resources. + properties: + service: + description: Override configuration for the Service created to + serve traffic to the cluster. + properties: + endpointURL: + type: string + metadata: + description: |- + EmbeddedLabelsAnnotations is an embedded subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta. + Only labels and annotations are included. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations is an unstructured key value map stored with a resource that may be + set by external tools to store and retrieve arbitrary metadata. They are not + queryable and should be preserved when modifying objects. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + sessionAffinity: + description: |- + Supports "ClientIP" and "None". Used to maintain session affinity. + Enable client IP based session affinity. + Must be ClientIP or None. + Defaults to None. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations + of Client IP based session affinity. + properties: + timeoutSeconds: + description: |- + timeoutSeconds specifies the seconds of ClientIP type session sticky time. + The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP". + Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: |- + type determines how the Service is exposed. Defaults to ClusterIP. Valid + options are ExternalName, ClusterIP, NodePort, and LoadBalancer. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or if that is not + specified, by manual construction of an Endpoints object or + EndpointSlice objects. If clusterIP is "None", no virtual IP is + allocated and the endpoints are published as a set of endpoints rather + than a virtual IP. + "NodePort" builds on ClusterIP and allocates a port on every node which + routes to the same endpoints as the clusterIP. + "LoadBalancer" builds on NodePort and creates an external load-balancer + (if supported in the current cloud) which routes to the same endpoints + as the clusterIP. + "ExternalName" aliases this service to the specified externalName. + Several other fields do not apply to ExternalName services. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: string + type: object + type: object + type: object + region: + default: regionOne + description: Region - the region name to use for service endpoint + discovery + type: string + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: |- + Secret is the name of the Secret instance containing password + information for the nova-novncproxy service. This secret is expected to + be generated by the nova-operator based on the information passed to the + Nova CR. + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Nova services the default SA name + type: string + serviceUser: + default: nova + description: |- + ServiceUser - optional username used for this service to register in + keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + service: + description: Service - Cert secret used for the nova novnc service + endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the service + type: string + type: object + vencrypt: + description: |- + Vencrypt - cert secret containing the x509 certificate to be presented to the VNC server. + The CommonName field should match the primary hostname of the controller node. If using a HA deployment, + the Organization field can also be configured to a value that is common across all console proxy instances in the deployment. + https://docs.openstack.org/nova/latest/admin/remote-console-access.html#novnc-proxy-server-configuration + properties: + secretName: + description: SecretName - holding the cert, key for the service + type: string + type: object + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - cellDatabaseHostname + - cellName + - keystoneAuthURL + - memcachedInstance + - secret + - serviceAccount + type: object + status: + description: NovaNoVNCProxyStatus defines the observed state of NovaNoVNCProxy + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: |- + ObservedGeneration - the most recent generation observed for this + service. If the observed generation is less than the spec generation, + then the controller has not processed the latest changes injected by + the openstack-operator in the top-level CR (e.g. the ContainerImage) + format: int64 + type: integer + readyCount: + description: ReadyCount defines the number of replicas ready from + nova-novncproxy + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/nova.openstack.org_novaschedulers.yaml b/apis/bases/nova.openstack.org_novaschedulers.yaml new file mode 100644 index 000000000..3bb5934cb --- /dev/null +++ b/apis/bases/nova.openstack.org_novaschedulers.yaml @@ -0,0 +1,332 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: novaschedulers.nova.openstack.org +spec: + group: nova.openstack.org + names: + kind: NovaScheduler + listKind: NovaSchedulerList + plural: novaschedulers + singular: novascheduler + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: NovaScheduler is the Schema for the novaschedulers API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: NovaSchedulerSpec defines the desired state of NovaScheduler + properties: + apiDatabaseAccount: + default: nova-api + description: APIDatabaseAccount - MariaDBAccount to use when accessing + the API DB + type: string + apiDatabaseHostname: + description: APIDatabaseHostname - hostname to use when accessing + the API DB + type: string + cell0DatabaseAccount: + default: nova-cell0 + description: Cell0DatabaseAccount - MariaDBAccount to use when accessing + the cell0 DB + type: string + cell0DatabaseHostname: + description: Cell0DatabaseHostname - hostname to use when accessing + the cell0 DB + type: string + containerImage: + description: The service specific Container Image URL (will be set + to environmental default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + keystoneAuthURL: + description: |- + KeystoneAuthURL - the URL that the nova-scheduler service can use to + talk to keystone + type: string + memcachedInstance: + description: MemcachedInstance is the name of the Memcached CR that + all nova service will use. + type: string + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to target subset of worker nodes running + this service + type: object + region: + default: regionOne + description: Region - the region name to use for service endpoint + discovery + type: string + registeredCells: + additionalProperties: + type: string + description: |- + RegisteredCells is a map keyed by cell names that are registered in the + nova_api database with a value that is the hash of the given cell + configuration. + This is used to detect when a new cell is added or an existing cell is + reconfigured to trigger refresh of the in memory cell caches of the + service. + type: object + replicas: + default: 1 + description: Replicas of the service to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: |- + Secret is the name of the Secret instance containing password + information for the nova-scheduler service. This secret is expected to + be generated by the nova-operator based on the information passed to the + Nova CR. + type: string + serviceAccount: + description: ServiceAccount - service account name used internally + to provide Nova services the default SA name + type: string + serviceUser: + default: nova + description: |- + ServiceUser - optional username used for this service to register in + keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - apiDatabaseHostname + - cell0DatabaseHostname + - keystoneAuthURL + - memcachedInstance + - registeredCells + - secret + - serviceAccount + type: object + status: + description: NovaSchedulerStatus defines the observed state of NovaScheduler + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + hash: + additionalProperties: + type: string + description: |- + INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + Important: Run "make" to regenerate code after modifying this file + Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: |- + ObservedGeneration - the most recent generation observed for this + service. If the observed generation is less than the spec generation, + then the controller has not processed the latest changes injected by + the openstack-operator in the top-level CR (e.g. the ContainerImage) + format: int64 + type: integer + readyCount: + description: ReadyCount defines the number of replicas ready from + nova-scheduler + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/bases/placement.openstack.org_placementapis.yaml b/apis/bases/placement.openstack.org_placementapis.yaml new file mode 100644 index 000000000..73ef0098b --- /dev/null +++ b/apis/bases/placement.openstack.org_placementapis.yaml @@ -0,0 +1,502 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: placementapis.placement.openstack.org +spec: + group: placement.openstack.org + names: + kind: PlacementAPI + listKind: PlacementAPIList + plural: placementapis + singular: placementapi + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: PlacementAPI is the Schema for the placementapis API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PlacementAPISpec defines the desired state of PlacementAPI + properties: + apiTimeout: + default: 60 + description: APITimeout for HAProxy, Apache + minimum: 10 + type: integer + auth: + description: Auth - Parameters related to authentication + properties: + applicationCredentialSecret: + description: ApplicationCredentialSecret - Secret containing Application + Credential ID and Secret + type: string + type: object + containerImage: + description: PlacementAPI Container Image URL (will be set to environmental + default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + databaseAccount: + default: placement + description: DatabaseAccount - name of MariaDBAccount which will be + used to connect. + type: string + databaseInstance: + description: |- + MariaDB instance name + Right now required by the maridb-operator to get the credentials from the instance to create the DB + Might not be required in future + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like policy.yaml. + type: object + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + 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: + service: PlacementPassword + description: PasswordSelectors - Selectors to identify the DB and + ServiceUser password from the Secret + properties: + service: + default: PlacementPassword + description: Service - Selector to get the service user password + from the Secret + type: string + type: object + preserveJobs: + default: false + description: PreserveJobs - do not delete jobs after they finished + e.g. to check logs + type: boolean + replicas: + default: 1 + description: Replicas of placement API to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: Secret containing OpenStack password information for + placement PlacementPassword + type: string + serviceUser: + default: placement + description: ServiceUser - optional username used for this service + to register in keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret for + the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + public: + description: Public GenericService - holds the secret for + the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - containerImage + - databaseInstance + - secret + type: object + status: + description: PlacementAPIStatus defines the observed state of PlacementAPI + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + databaseHostname: + description: Placement Database Hostname + type: string + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: ObservedGeneration - the most recent generation observed + for this service. If the observed generation is less than the spec + generation, then the controller has not processed the latest changes. + format: int64 + type: integer + readyCount: + description: ReadyCount of placement API instances + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/apis/go.mod b/apis/go.mod new file mode 100644 index 000000000..f8be13197 --- /dev/null +++ b/apis/go.mod @@ -0,0 +1,99 @@ +module github.com/openstack-k8s-operators/nova-operator/apis +// module github.com/openstack-k8s-operators/nova-operator/api + + +go 1.24.4 + +require ( + github.com/google/go-cmp v0.7.0 + github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260126091827-7758173fbb09 + github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260128142552-e2c25eccae5a + github.com/robfig/cron/v3 v3.0.1 + k8s.io/api v0.31.14 + k8s.io/apimachinery v0.31.14 + k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d + sigs.k8s.io/controller-runtime v0.19.7 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.21.1 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.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 + github.com/mailru/easyjson v0.9.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.27.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/rabbitmq/cluster-operator/v2 v2.16.0 // indirect + github.com/spf13/pflag v1.0.7 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/time v0.12.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/protobuf v1.36.7 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.33.2 // indirect + k8s.io/client-go v0.31.14 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect +) + +// mschuppert: map to latest commit from release-4.16 tag +// must consistent within modules and service operators +replace github.com/openshift/api => github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e //allow-merging + +// pin these to avoid later versions pulled by rabbitmq +replace k8s.io/apimachinery => k8s.io/apimachinery v0.31.14 //allow-merging + +replace k8s.io/api => k8s.io/api v0.31.14 //allow-merging + +replace k8s.io/apiserver => k8s.io/apiserver v0.31.14 //allow-merging + +replace k8s.io/client-go => k8s.io/client-go v0.31.14 //allow-merging + +replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.14 //allow-merging + +replace k8s.io/cli-runtime => k8s.io/cli-runtime v0.31.14 //allow-merging + +replace k8s.io/code-generator => k8s.io/code-generator v0.31.14 //allow-merging + +replace k8s.io/component-base => k8s.io/component-base v0.31.14 //allow-merging + +// custom RabbitmqClusterSpecCore for OpenStackControlplane (v2.16.0_patches) +replace github.com/rabbitmq/cluster-operator/v2 => github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec //allow-merging + +replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging diff --git a/apis/go.sum b/apis/go.sum new file mode 100644 index 000000000..5cbe305ee --- /dev/null +++ b/apis/go.sum @@ -0,0 +1,208 @@ +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= +github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= +github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.27.5 h1:ZeVgZMx2PDMdJm/+w5fE/OyG6ILo1Y3e+QX4zSR0zTE= +github.com/onsi/ginkgo/v2 v2.27.5/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q= +github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260126091827-7758173fbb09 h1:vhAGLKZitJIffj7ONiPpKmOX7Tmt/LGJpaY0Z2LeyfQ= +github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260126091827-7758173fbb09/go.mod h1:ZXwFlspJCdZEUjMbmaf61t5AMB4u2vMyAMMoe/vJroE= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260128142552-e2c25eccae5a h1:97OfmmJgoIKTfbED2SfyjoPkivoiMHg4jfbrTuwSGQw= +github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260128142552-e2c25eccae5a/go.mod h1:ndqfy1KbVorHH6+zlUFPIrCRhMSxO3ImYJUGaooE0x0= +github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec h1:saovr368HPAKHN0aRPh8h8n9s9dn3d8Frmfua0UYRlc= +github.com/openstack-k8s-operators/rabbitmq-cluster-operator/v2 v2.6.1-0.20250929174222-a0d328fa4dec/go.mod h1:Nh2NEePLjovUQof2krTAg4JaAoLacqtPTZQXK6izNfg= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.31.14 h1:xYn/S/WFJsksI7dk/5uBRd3Umm/D8W5g7sRnd4csotA= +k8s.io/api v0.31.14/go.mod h1:K8fvRey4z73RAuxBZCma7WtY8WFvkViYhfFLCMT4xgA= +k8s.io/apiextensions-apiserver v0.31.14 h1:1KupD0PyU7CgiT/PiZPSgZhTCL2KGwvXd1ejGcxjEfg= +k8s.io/apiextensions-apiserver v0.31.14/go.mod h1:Odk14fSl/zaciI8DRUSPMSH74UXtz4gfinw7zY7YHvE= +k8s.io/apimachinery v0.31.14 h1:/eMIwjv+GFm6A/sSGlB1NupBU6wTDPhEWsju0Fj69kY= +k8s.io/apimachinery v0.31.14/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.14 h1:d4/G0xfksNIbMWH7ghjzOwC5bTAwQ20gABTjZw7fLlQ= +k8s.io/client-go v0.31.14/go.mod h1:0uRpRB7r5QwtsbxEngZPkbcIVoNdAQAPIcopgiXjhQc= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e h1:UGI9rv1A2cV87NhXr4s+AUBxIuoo/SME/IyJ3b6KztE= +k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e/go.mod h1:GLOk5B+hDbRROvt0X2+hqX64v/zO3vXN7J78OUmBSKw= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.19.7 h1:DLABZfMr20A+AwCZOHhcbcu+TqBXnJZaVBri9K3EO48= +sigs.k8s.io/controller-runtime v0.19.7/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/apis/nova/v1beta1/common_types.go b/apis/nova/v1beta1/common_types.go new file mode 100644 index 000000000..a41792196 --- /dev/null +++ b/apis/nova/v1beta1/common_types.go @@ -0,0 +1,238 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + corev1 "k8s.io/api/core/v1" + + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" +) + +// Container image fall-back defaults +const ( + NovaAPIContainerImage = "quay.io/podified-antelope-centos9/openstack-nova-api:current-podified" + NovaConductorContainerImage = "quay.io/podified-antelope-centos9/openstack-nova-conductor:current-podified" + NovaMetadataContainerImage = "quay.io/podified-antelope-centos9/openstack-nova-api:current-podified" + NovaNoVNCContainerImage = "quay.io/podified-antelope-centos9/openstack-nova-novncproxy:current-podified" + NovaSchedulerContainerImage = "quay.io/podified-antelope-centos9/openstack-nova-scheduler:current-podified" + NovaComputeContainerImage = "quay.io/podified-antelope-centos9/openstack-nova-compute:current-podified" +) + +// Compute drivers names +const ( + IronicDriver = "ironic.IronicDriver" +) + +const ( + // ComputeDiscoverHashKey is the key to hash of compute discovery job based on compute templates for cell + ComputeDiscoverHashKey = "nova-compute-discovery" +) + +// NovaServiceBase contains the fields that are needed for each nova service CRD +type NovaServiceBase struct { + // +kubebuilder:validation:Optional + // The service specific Container Image URL (will be set to environmental default if empty) + ContainerImage string `json:"containerImage"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of the service to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` +} + +// PasswordSelector to identify the DB and AdminUser password from the Secret +type PasswordSelector struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default="NovaPassword" + // Service - Selector to get the keystone service user password from the + // Secret + Service string `json:"service"` + // +kubebuilder:validation:Optional + // +kubebuilder:default="MetadataSecret" + // MetadataSecret - the name of the field to get the metadata secret from the + // Secret + MetadataSecret string `json:"metadataSecret"` + // +kubebuilder:validation:Optional + // +kubebuilder:default="MetadataCellsSecret" + // prefixMetadataCellsSecret - the prefix name of the field to get the metadata secret from the + // Secret for cells. Vale of metadata_proxy_shared_secret + // information for the nova-metadata service. This secret is shared + // between nova and neutron ovn-metadata inside selected cell + // and if this is not defined the global metadata_proxy_shared_secret + // secret will be used + PrefixMetadataCellsSecret string `json:"prefixMetadataCellsSecret"` +} + +// AuthSpec defines authentication parameters for Nova services +type AuthSpec struct { + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // ApplicationCredentialSecret - the name of the k8s Secret that contains the + // application credential data used for authentication + ApplicationCredentialSecret string `json:"applicationCredentialSecret,omitempty"` +} + +// NovaImages defines container images used by top level Nova CR +type NovaImages struct { + // +kubebuilder:validation:Required + // APIContainerImageURL + APIContainerImageURL string `json:"apiContainerImageURL"` + + // +kubebuilder:validation:Required + // SchedulerContainerImageURL + SchedulerContainerImageURL string `json:"schedulerContainerImageURL"` + + NovaCellImages `json:",inline"` +} + +// Default sets default image URLs for NovaImages +func (r *NovaImages) Default(defaults NovaDefaults) { + r.NovaCellImages.Default(defaults.NovaCellDefaults) + if r.APIContainerImageURL == "" { + r.APIContainerImageURL = defaults.APIContainerImageURL + } + if r.SchedulerContainerImageURL == "" { + r.SchedulerContainerImageURL = defaults.SchedulerContainerImageURL + } +} + +// NovaCellImages defines container images used by NovaCell services +type NovaCellImages struct { + + // +kubebuilder:validation:Required + // ConductorContainerImageURL + ConductorContainerImageURL string `json:"conductorContainerImageURL"` + + // +kubebuilder:validation:Required + // MetadataContainerImageURL + MetadataContainerImageURL string `json:"metadataContainerImageURL"` + + // +kubebuilder:validation:Required + // NoVNCContainerImageURL + NoVNCContainerImageURL string `json:"novncproxyContainerImageURL"` + + // +kubebuilder:validation:Required + // NovaComputeContainerImageURL + NovaComputeContainerImageURL string `json:"computeContainerImageURL"` +} + +// Default sets default image URLs for NovaCellImages +func (r *NovaCellImages) Default(defaults NovaCellDefaults) { + if r.ConductorContainerImageURL == "" { + r.ConductorContainerImageURL = defaults.ConductorContainerImageURL + } + if r.MetadataContainerImageURL == "" { + r.MetadataContainerImageURL = defaults.MetadataContainerImageURL + } + if r.NoVNCContainerImageURL == "" { + r.NoVNCContainerImageURL = defaults.NoVNCContainerImageURL + } + if r.NovaComputeContainerImageURL == "" { + r.NovaComputeContainerImageURL = defaults.NovaComputeContainerImageURL + } +} + +// SetupDefaults - initializes any CRD field defaults based on environment variables (the defaulting mechanism itself is implemented via webhooks) +func SetupDefaults() { + // Acquire environmental defaults and initialize NovaCell defaults with them + novaCellDefaults := NovaCellDefaults{ + ConductorContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_CONDUCTOR_IMAGE_URL_DEFAULT", NovaConductorContainerImage), + MetadataContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_API_IMAGE_URL_DEFAULT", NovaMetadataContainerImage), + NoVNCContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_NOVNC_IMAGE_URL_DEFAULT", NovaNoVNCContainerImage), + NovaComputeContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_COMPUTE_IMAGE_URL_DEFAULT", NovaComputeContainerImage), + } + + SetupNovaCellDefaults(novaCellDefaults) + + // Acquire environmental defaults and initialize Nova defaults with them + novaDefaults := NovaDefaults{ + APIContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_API_IMAGE_URL_DEFAULT", NovaAPIContainerImage), + SchedulerContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_SCHEDULER_IMAGE_URL_DEFAULT", NovaSchedulerContainerImage), + NovaCellDefaults: novaCellDefaults, + APITimeout: 60, + } + + SetupNovaDefaults(novaDefaults) + + // Acquire environmental defaults and initialize NovaAPI defaults with them + novaAPIDefaults := NovaAPIDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_API_IMAGE_URL_DEFAULT", NovaAPIContainerImage), + } + + SetupNovaAPIDefaults(novaAPIDefaults) + + // Acquire environmental defaults and initialize NovaConductor defaults with them + novaConductorDefaults := NovaConductorDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_CONDUCTOR_IMAGE_URL_DEFAULT", NovaConductorContainerImage), + } + + SetupNovaConductorDefaults(novaConductorDefaults) + + // Acquire environmental defaults and initialize NovaMetadata defaults with them + novaMetadataDefaults := NovaMetadataDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_API_IMAGE_URL_DEFAULT", NovaMetadataContainerImage), + } + + SetupNovaMetadataDefaults(novaMetadataDefaults) + + // Acquire environmental defaults and initialize NovaNoVNCProxy defaults with them + novaNoVNCProxyDefaults := NovaNoVNCProxyDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_NOVNC_IMAGE_URL_DEFAULT", NovaNoVNCContainerImage), + } + + SetupNovaNoVNCProxyDefaults(novaNoVNCProxyDefaults) + + // Acquire environmental defaults and initialize NovaScheduler defaults with them + novaSchedulerDefaults := NovaSchedulerDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_SCHEDULER_IMAGE_URL_DEFAULT", NovaSchedulerContainerImage), + } + SetupNovaSchedulerDefaults(novaSchedulerDefaults) + + // Acquire environmental defaults and initialize NovaCompute defaults with them + novaComputeDefaults := NovaComputeDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_NOVA_COMPUTE_IMAGE_URL_DEFAULT", NovaComputeContainerImage), + } + + SetupNovaComputeDefaults(novaComputeDefaults) +} diff --git a/apis/nova/v1beta1/common_webhook.go b/apis/nova/v1beta1/common_webhook.go new file mode 100644 index 000000000..7bbc52681 --- /dev/null +++ b/apis/nova/v1beta1/common_webhook.go @@ -0,0 +1,186 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "fmt" + "path/filepath" + "strings" + + common_webhook "github.com/openstack-k8s-operators/lib-common/modules/common/webhook" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +// ValidateDefaultConfigOverwrite checks if the file names in the overwrite map +// are allowed and return an error for each unsupported files. The allowedKeys +// list supports direct string match and globs like provider*.yaml +func ValidateDefaultConfigOverwrite( + basePath *field.Path, + defaultConfigOverwrite map[string]string, + allowedKeys []string, +) field.ErrorList { + var errors field.ErrorList + for requested := range defaultConfigOverwrite { + if !matchAny(requested, allowedKeys) { + errors = append( + errors, + field.Invalid( + basePath, + requested, + fmt.Sprintf( + "Only the following keys are valid: %s", + strings.Join(allowedKeys, ", ")), + ), + ) + } + } + return errors +} + +func matchAny(requested string, allowed []string) bool { + for _, a := range allowed { + if matched, _ := filepath.Match(a, requested); matched { + return true + } + } + return false +} + +// getDeprecatedFields returns the centralized list of deprecated fields for NovaSpecCore +func (spec *NovaSpecCore) getDeprecatedFields(old *NovaSpecCore) []common_webhook.DeprecatedFieldUpdate { + // Get new field value (handle nil NotificationsBus) + var newNotifBusCluster *string + if spec.NotificationsBus != nil { + newNotifBusCluster = &spec.NotificationsBus.Cluster + } + + deprecatedFields := []common_webhook.DeprecatedFieldUpdate{ + { + DeprecatedFieldName: "apiMessageBusInstance", + NewFieldPath: []string{"messagingBus", "cluster"}, + NewDeprecatedValue: &spec.APIMessageBusInstance, + NewValue: &spec.MessagingBus.Cluster, + }, + { + DeprecatedFieldName: "notificationsBusInstance", + NewFieldPath: []string{"notificationsBus", "cluster"}, + NewDeprecatedValue: spec.NotificationsBusInstance, + NewValue: newNotifBusCluster, + }, + } + + // If old spec is provided (UPDATE operation), add old values + if old != nil { + deprecatedFields[0].OldDeprecatedValue = &old.APIMessageBusInstance + deprecatedFields[1].OldDeprecatedValue = old.NotificationsBusInstance + } + + return deprecatedFields +} + +// validateDeprecatedFieldsCreate validates deprecated fields during CREATE operations +func (spec *NovaSpecCore) validateDeprecatedFieldsCreate(basePath *field.Path) ([]string, field.ErrorList) { + // Get deprecated fields list (without old values for CREATE) + deprecatedFieldsUpdate := spec.getDeprecatedFields(nil) + + // Convert to DeprecatedField list for CREATE validation + deprecatedFields := make([]common_webhook.DeprecatedField, len(deprecatedFieldsUpdate)) + for i, df := range deprecatedFieldsUpdate { + deprecatedFields[i] = common_webhook.DeprecatedField{ + DeprecatedFieldName: df.DeprecatedFieldName, + NewFieldPath: df.NewFieldPath, + DeprecatedValue: df.NewDeprecatedValue, + NewValue: df.NewValue, + } + } + + // Validate top-level NovaSpecCore fields + warnings := common_webhook.ValidateDeprecatedFieldsCreate(deprecatedFields, basePath) + + // Validate deprecated fields in cell templates + for cellName, cellTemplate := range spec.CellTemplates { + cellPath := basePath.Child("cellTemplates").Key(cellName) + cellWarnings := cellTemplate.validateDeprecatedFieldsCreate(cellPath) + warnings = append(warnings, cellWarnings...) + } + + return warnings, nil +} + +// validateDeprecatedFieldsUpdate validates deprecated fields during UPDATE operations +func (spec *NovaSpecCore) validateDeprecatedFieldsUpdate(old NovaSpecCore, basePath *field.Path) ([]string, field.ErrorList) { + // Get deprecated fields list with old values + deprecatedFields := spec.getDeprecatedFields(&old) + warnings, errors := common_webhook.ValidateDeprecatedFieldsUpdate(deprecatedFields, basePath) + + // Validate deprecated fields in cell templates + for cellName, cellTemplate := range spec.CellTemplates { + if oldCell, exists := old.CellTemplates[cellName]; exists { + cellPath := basePath.Child("cellTemplates").Key(cellName) + cellWarnings, cellErrors := cellTemplate.validateDeprecatedFieldsUpdate(oldCell, cellPath) + warnings = append(warnings, cellWarnings...) + errors = append(errors, cellErrors...) + } + } + + return warnings, errors +} + +// getDeprecatedFields returns the centralized list of deprecated fields for NovaCellTemplate +func (spec *NovaCellTemplate) getDeprecatedFields(old *NovaCellTemplate) []common_webhook.DeprecatedFieldUpdate { + deprecatedFields := []common_webhook.DeprecatedFieldUpdate{ + { + DeprecatedFieldName: "cellMessageBusInstance", + NewFieldPath: []string{"messagingBus", "cluster"}, + NewDeprecatedValue: &spec.CellMessageBusInstance, + NewValue: &spec.MessagingBus.Cluster, + }, + } + + // If old spec is provided (UPDATE operation), add old values + if old != nil { + deprecatedFields[0].OldDeprecatedValue = &old.CellMessageBusInstance + } + + return deprecatedFields +} + +// validateDeprecatedFieldsCreate validates deprecated fields during CREATE operations for NovaCellTemplate +func (spec *NovaCellTemplate) validateDeprecatedFieldsCreate(basePath *field.Path) []string { + // Get deprecated fields list (without old values for CREATE) + deprecatedFieldsUpdate := spec.getDeprecatedFields(nil) + + // Convert to DeprecatedField list for CREATE validation + deprecatedFields := make([]common_webhook.DeprecatedField, len(deprecatedFieldsUpdate)) + for i, df := range deprecatedFieldsUpdate { + deprecatedFields[i] = common_webhook.DeprecatedField{ + DeprecatedFieldName: df.DeprecatedFieldName, + NewFieldPath: df.NewFieldPath, + DeprecatedValue: df.NewDeprecatedValue, + NewValue: df.NewValue, + } + } + + return common_webhook.ValidateDeprecatedFieldsCreate(deprecatedFields, basePath) +} + +// validateDeprecatedFieldsUpdate validates deprecated fields during UPDATE operations for NovaCellTemplate +func (spec *NovaCellTemplate) validateDeprecatedFieldsUpdate(old NovaCellTemplate, basePath *field.Path) ([]string, field.ErrorList) { + // Get deprecated fields list with old values + deprecatedFields := spec.getDeprecatedFields(&old) + return common_webhook.ValidateDeprecatedFieldsUpdate(deprecatedFields, basePath) +} diff --git a/apis/nova/v1beta1/conditions.go b/apis/nova/v1beta1/conditions.go new file mode 100644 index 000000000..bceb8886e --- /dev/null +++ b/apis/nova/v1beta1/conditions.go @@ -0,0 +1,205 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" +) + +// Nova Condition Types used by API objects. +const ( + // NovaAPIReadyCondition indicates if the NovaAPI is operational + NovaAPIReadyCondition condition.Type = "NovaAPIReady" + // NovaConductorReadyCondition indicates if the NovaConductor is ready + // in a given cell. + NovaConductorReadyCondition condition.Type = "NovaConductorReady" + // NovaAPIDBReadyCondition indicates if the nova_api DB is created + NovaAPIDBReadyCondition condition.Type = "NovaAPIDBReady" + // NovaAllCellsDBReadyCondition indicates that the DB for each configured + // Cell is created successfully + NovaAllCellsDBReadyCondition condition.Type = "NovaAllCellDBReady" + // NovaAllCellsReadyCondition indicates that every defined Cell is ready + NovaAllCellsReadyCondition condition.Type = "NovaAllCellReady" + // NovaAPIMQReadyCondition indicated that the top level message bus is ready + NovaAPIMQReadyCondition condition.Type = "NovaAPIMQReady" + // NovaAllCellsMQReadyCondition indicates that the message bus for each + // configured Cell is created successfully + NovaAllCellsMQReadyCondition condition.Type = "NovaAllCellsMQReady" + // NovaSchedulerReadyCondition indicates if the NovaScheduler is operational + NovaSchedulerReadyCondition condition.Type = "NovaSchedulerReady" + // NovaCellReadyCondition indicates when the given NovaCell instance is Ready + NovaCellReadyCondition condition.Type = "NovaCellReady" + // NovaMetadataReadyCondition indicates when the given NovaMetadata instance is Ready + NovaMetadataReadyCondition condition.Type = "NovaMetadataReady" + // NovaNoVNCProxyReadyCondition indicates when the given NoVNCProxy instance is Ready + NovaNoVNCProxyReadyCondition condition.Type = "NovaNoVNCProxyReady" + // NovaComputeServiceConfigReady indicates when the compute service config + // is ready for the given NovaCell + NovaComputeServiceConfigReady condition.Type = "NovaComputeServiceConfigReady" + // NovaAllControlPlaneComputesReadyCondition indicates that every defined NovaCompute is ready but undiscovered + NovaAllControlPlaneComputesReadyCondition condition.Type = "NovaAllControlPlaneComputesReady" + //NovaCellsDeletionCondition indicates that the NovaCells deletion is in progress + NovaCellsDeletionCondition condition.Type = "NovaCellsDeletion" + + // notifications + // NovaNotificationMQReadyCondition indicated that the top level notification message bus is ready + NovaNotificationMQReadyCondition condition.Type = "NovaNotificationMQReady" +) + +// Common Messages used by API objects. +const ( + // NovaAPIReadyInitMessage + NovaAPIReadyInitMessage = "NovaAPI not started" + + // NovaAPIReadyErrorMessage + NovaAPIReadyErrorMessage = "NovaAPI error occurred %s" + + // NovaConductorReadyInitMessage + NovaConductorReadyInitMessage = "NovaConductor not started" + + // NovaConductorReadyErrorMessage + NovaConductorReadyErrorMessage = "NovaConductor error occurred %s" + + // NovaAllCellsDBReadyInitMessage + NovaAllCellsDBReadyInitMessage = "DB creation not started" + + // NovaAllCellsDBReadyCreatingMessage + NovaAllCellsDBReadyCreatingMessage = "DB creation ongoing for %s" + + // NovaAllCellsDBReadyErrorMessage + NovaAllCellsDBReadyErrorMessage = "DB creation failed for %s" + + // NovaAllCellsReadyMessage + NovaAllCellsDBReadyMessage = "All DBs created successfully" + + // NovaAllCellsReadyInitMessage + NovaAllCellsReadyInitMessage = "NovaCells are not started" + + // NovaAllCellsReadyCreatingMessage + NovaAllCellsReadyNotReadyMessage = "NovaCell %s is not Ready" + + // NovaAllCellsReadyWaitingMessage + NovaAllCellsReadyWaitingMessage = "NovaCell creation waits for DB creation for %s" + + // NovaAllCellsReadyErrorMessage + NovaAllCellsReadyErrorMessage = "NovaCell creation failed for %s" + + // NovaAllCellsReadyMessage + NovaAllCellsReadyMessage = "All NovaCells are ready" + + // NovaAPIMQReadyInitMessage + NovaAPIMQReadyInitMessage = "API message bus not started" + + // NovaAPIMQReadyErrorMessage + NovaAPIMQReadyErrorMessage = "API message bus creation failed: %s" + + // NovaAPIMQReadyMessage + NovaAPIMQReadyMessage = "API message bus creation successfully" + + // NovaAPIMQReadyCreatingMessage + NovaAPIMQReadyCreatingMessage = "API message bus creation ongoing" + + // NovaAllCellsMQReadyInitMessage + NovaAllCellsMQReadyInitMessage = "Message bus creation not started" + + // NovaAllCellsMQReadyErrorMessage + NovaAllCellsMQReadyCreatingMessage = "Message bus creation ongoing for %s" + + // NovaAllCellsMQReadyErrorMessage + NovaAllCellsMQReadyErrorMessage = "Message bus creation failed for %s" + + // NovaAllCellsMQReadyMessage + NovaAllCellsMQReadyMessage = "All message buses created successfully" + + // NovaSchedulerReadyInitMessage + NovaSchedulerReadyInitMessage = "NovaScheduler not started" + + // NovaSchedulerReadyErrorMessage + NovaSchedulerReadyErrorMessage = "NovaScheduler error occurred %s" + + // InputReadyWaitingMessage + InputReadyWaitingMessage = "Input data resources missing: %s" + + // NovaApplicationCredentialSecretErrorMessage + NovaApplicationCredentialSecretErrorMessage = "Error with application credential secret" + + // NovaCellReadyInitMessage + NovaCellReadyInitMessage = "The status of NovaCell %s is unknown" + + // NovaCellReadyNotExistsMessage + NovaCellReadyNotExistsMessage = "Waiting for NovaCell %s to exists" + + // NovaCellReadyNotReadyMessage + NovaCellReadyNotReadyMessage = "Waiting for NovaCell %s to become Ready" + + //NovaCellReadyErrorMessage + NovaCellReadyErrorMessage = "Error occurred while querying NovaCell %s: %s" + + //NovaCellReadyMessage + NovaCellReadyMessage = "NovaCell %s is Ready" + + //NovaMetadataReadyInitMessage + NovaMetadataReadyInitMessage = "NovaMetadata not started" + + //NovaMetadataReadyErrorMessage + NovaMetadataReadyErrorMessage = "NovaMetadata error occurred %s" + + //NovaNoVNCProxyReadyInitMessage + NovaNoVNCProxyReadyInitMessage = "NovaNoVNCProxy not started" + + //NovaNoVNCProxyReadyErrorMessage + NovaNoVNCProxyReadyErrorMessage = "NovaNoVNCProxy error occurred %s" + + //NovaComputeServiceConfigInitMessage + NovaComputeServiceConfigInitMessage = "Compute service config generation is not started" + + //NovaComputeServiceConfigErrorMessage + NovaComputeServiceConfigErrorMessage = "Compute service config generation error occurred %s" + + //NovaComputeReadyInitMessage + NovaComputeReadyInitMessage = "NovaCompute not started" + + //NovaComputeReadyErrorMessage + NovaComputeReadyErrorMessage = "NovaCompute error occurred %s" + + //CellHostDiscoverInitMessage + CellHostDiscoverInitMessage = "Not all host discovered in cell" + + //CellHostDiscoverErrorMessage + CellHostDiscoverErrorMessage = "CellHostDiscover error occurred %s" + + // NovaCellsDeletionConditionInitMessage + NovaCellsDeletionConditionInitMessage = "NovaCells deletion not started" + + // NovaCellsDeletionMessage + NovaCellsDeletionMessage = "NovaCells deletion in progress: %s" + + // NovaCellsDeletionConditionReadyMessage + NovaCellsDeletionConditionReadyMessage = "There is no more NovaCells to delete" + // notifications + // NovaNotificationMQReadyInitMessage + NovaNotificationMQReadyInitMessage = "Notification message bus not started" + + // NovaNotificationMQReadyErrorMessage + NovaNotificationMQReadyErrorMessage = "Notification message bus creation failed: %s" + + // NovaNotificationMQReadyCreatingMessage + NovaNotificationMQReadyCreatingMessage = "Notification message bus creation ongoing" + + // NovaNotificationMQReadyMessage + NovaNotificationMQReadyMessage = "Notification message bus created successfully" +) diff --git a/apis/nova/v1beta1/groupversion_info.go b/apis/nova/v1beta1/groupversion_info.go new file mode 100644 index 000000000..7ee40c982 --- /dev/null +++ b/apis/nova/v1beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains API Schema definitions for the nova v1beta1 API group +// +kubebuilder:object:generate=true +// +groupName=nova.openstack.org +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "nova.openstack.org", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/nova/v1beta1/nova_types.go b/apis/nova/v1beta1/nova_types.go new file mode 100644 index 000000000..39d4a5c11 --- /dev/null +++ b/apis/nova/v1beta1/nova_types.go @@ -0,0 +1,243 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NovaSpecCore defines the template for NovaSpec used in OpenStackControlPlane +type NovaSpecCore struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Optional + // +kubebuilder:default=keystone + // KeystoneInstance to name of the KeystoneAPI CR to select the Service + // instance used by the Nova services to authenticate. + KeystoneInstance string `json:"keystoneInstance"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=openstack + // APIDatabaseInstance is the name of the MariaDB CR to select the DB + // Service instance used for the Nova API DB. + APIDatabaseInstance string `json:"apiDatabaseInstance"` + + // +kubebuilder:validation:Optional + // APIMessageBusInstance is the name of the RabbitMqCluster CR to select + // the Message Bus Service instance used by the Nova top level services to + // communicate. + // Deprecated: Use MessagingBus.Cluster instead + APIMessageBusInstance string `json:"apiMessageBusInstance,omitempty"` + + // +kubebuilder:validation:Optional + // MessagingBus configuration (username, vhost, and cluster) + MessagingBus rabbitmqv1.RabbitMqConfig `json:"messagingBus,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={cell0: {cellDatabaseAccount: nova-cell0, hasAPIAccess: true}, cell1: {cellDatabaseAccount: nova-cell1, cellDatabaseInstance: openstack-cell1, messagingBus: {cluster: rabbitmq-cell1}, hasAPIAccess: true}} + // Cells is a mapping of cell names to NovaCellTemplate objects defining + // the cells in the deployment. The "cell0" cell is a mandatory cell in + // every deployment. Moreover any real deployment needs at least one + // additional normal cell as "cell0" cannot have any computes. + CellTemplates map[string]NovaCellTemplate `json:"cellTemplates"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova" + // ServiceUser - optional username used for this service to register in keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova-api" + // APIDatabaseAccount - MariaDBAccount to use when accessing the API DB + APIDatabaseAccount string `json:"apiDatabaseAccount"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=60 + // +kubebuilder:validation:Minimum=10 + // APITimeout for Route and Apache + APITimeout int `json:"apiTimeout"` + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for nova like the keystone service password and DB passwords + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={service: NovaPassword} + // PasswordSelectors - Selectors to identify the DB and ServiceUser + // passwords from the Secret + PasswordSelectors PasswordSelector `json:"passwordSelectors"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service. Setting + // NodeSelector here acts as a default value and can be overridden by service + // specific NodeSelector Settings. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + // PreserveJobs - do not delete jobs after they finished e.g. to check logs + PreserveJobs bool `json:"preserveJobs"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={replicas:1} + // APIServiceTemplate - define the nova-api service + APIServiceTemplate NovaAPITemplate `json:"apiServiceTemplate"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={replicas:1} + // SchedulerServiceTemplate- define the nova-scheduler service + SchedulerServiceTemplate NovaSchedulerTemplate `json:"schedulerServiceTemplate"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={enabled: true} + // MetadataServiceTemplate - defines the metadata service that is global + // for the deployment serving all the cells. Note that if you want to + // deploy metadata per cell then the metadata service should be disabled + // here and enabled in the cellTemplates instead. + MetadataServiceTemplate NovaMetadataTemplate `json:"metadataServiceTemplate"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=memcached + // MemcachedInstance is the name of the Memcached CR that all nova service will use. + MemcachedInstance string `json:"memcachedInstance"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` + + // +kubebuilder:validation:Optional + // NotificationsBusInstance is the name of the RabbitMqCluster CR to select + // the Message Bus Service instance used by the Nova top level services and all cells to publish notifications. + // If undefined, the value will be inherited from OpenStackControlPlane. + // An empty value "" leaves the notification drivers unconfigured and emitting no notifications at all. + // Avoid colocating it with RabbitMqClusterName, APIMessageBusInstance or CellMessageBusInstance used for RPC. + // For particular Nova cells, notifications cannot be disabled, nor configured differently. + NotificationsBusInstance *string `json:"notificationsBusInstance,omitempty" deprecated:"true" deprecatedNew:"notificationsBus.cluster"` + + // +kubebuilder:validation:Optional + // NotificationsBus configuration (username, vhost, and cluster) for notifications + NotificationsBus *rabbitmqv1.RabbitMqConfig `json:"notificationsBus,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // Auth - Parameters related to authentication (shared by all Nova services) + Auth AuthSpec `json:"auth,omitempty"` +} + +// NovaSpec defines the desired state of Nova +type NovaSpec struct { + // NOTE(bogdando): Anything that is only submitted by openstack-operator should be in NovaSpec but not in NovaSpecCore. + + NovaSpecCore `json:",inline"` + + NovaImages `json:",inline"` +} + +// NovaStatus defines the observed state of Nova +type NovaStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // APIServiceReadyCount defines the number or replicas ready from nova-api + APIServiceReadyCount int32 `json:"apiServiceReadyCount,omitempty"` + + // SchedulerServiceReadyCount defines the number or replicas ready from nova-scheduler + SchedulerServiceReadyCount int32 `json:"schedulerServiceReadyCount,omitempty"` + + // MetadataReadyCount defines the number of replicas ready from + // nova-metadata service + MetadataServiceReadyCount int32 `json:"metadataServiceReadyCount,omitempty"` + + // RegisteredCells is a map keyed by cell names that are registered in the + // nova_api database with a value that is the hash of the given cell + // configuration. + RegisteredCells map[string]string `json:"registeredCells,omitempty"` + + // DiscoveredCells is a map keyed by cell names that have discovered all kubernetes managed + // computes in cell value is a hash of config from all kubernetes managed computes in cell + DiscoveredCells map[string]string `json:"discoveredCells,omitempty"` + + //ObservedGeneration - the most recent generation observed for this service. If the observed generation is less than the spec generation, then the controller has not processed the latest changes. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Nova is the Schema for the nova API +type Nova struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaSpec `json:"spec,omitempty"` + Status NovaStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaList contains a list of Nova +type NovaList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Nova `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Nova{}, &NovaList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// IsReady returns true if Nova reconciled successfully +func (instance Nova) IsReady() bool { + return instance.Status.Conditions.IsTrue(condition.ReadyCondition) +} + +// RbacConditionsSet - set the conditions for the rbac object +func (instance Nova) RbacConditionsSet(c *condition.Condition) { + instance.Status.Conditions.Set(c) +} + +// RbacNamespace - return the namespace +func (instance Nova) RbacNamespace() string { + return instance.Namespace +} + +// RbacResourceName - return the name to be used for rbac objects (serviceaccount, role, rolebinding) +func (instance Nova) RbacResourceName() string { + return "nova-" + instance.Name +} + +// GetSecret returns the value of the Nova.Spec.Secret +func (instance Nova) GetSecret() string { + return instance.Spec.Secret +} diff --git a/apis/nova/v1beta1/nova_webhook.go b/apis/nova/v1beta1/nova_webhook.go new file mode 100644 index 000000000..1e5762c79 --- /dev/null +++ b/apis/nova/v1beta1/nova_webhook.go @@ -0,0 +1,459 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind Nova --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + service "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/robfig/cron/v3" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/ptr" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// NovaDefaults - +type NovaDefaults struct { + APIContainerImageURL string + SchedulerContainerImageURL string + APITimeout int + NovaCellDefaults +} + +var novaDefaults NovaDefaults + +// log is for logging in this package. +var novalog = logf.Log.WithName("nova-resource") + +// SetupNovaDefaults - initialize Nova spec defaults for use with either internal or external webhooks +func SetupNovaDefaults(defaults NovaDefaults) { + novaDefaults = defaults + novalog.Info("Nova defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &Nova{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *Nova) Default() { + novalog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this NovaCore spec. +func (spec *NovaSpec) Default() { + spec.NovaImages.Default(novaDefaults) + spec.NovaSpecCore.Default() +} + +// Default - set defaults for this Nova spec. Expected to be called from +// the higher level meta operator. +func (spec *NovaSpecCore) Default() { + // NOTE(gibi): this cannot be expressed as kubebuilder defaults as the + // MetadataServiceTemplate is used both in the cellTemplate and in the + // NovaSpec but we need different defaults in the two places + if spec.MetadataServiceTemplate.Enabled == nil { + spec.MetadataServiceTemplate.Enabled = ptr.To(true) + } + + if spec.APITimeout == 0 { + spec.APITimeout = novaDefaults.APITimeout + } + + // Default MessagingBus.Cluster if not set + // Migration from deprecated fields is handled by openstack-operator + if spec.MessagingBus.Cluster == "" { + spec.MessagingBus.Cluster = "rabbitmq" + } + + // NotificationsBus.Cluster is not defaulted - it must be explicitly set if NotificationsBus is configured + // This ensures users make a conscious choice about which cluster to use for notifications + + for cellName, cellTemplate := range spec.CellTemplates { + + if cellTemplate.MetadataServiceTemplate.Enabled == nil { + cellTemplate.MetadataServiceTemplate.Enabled = ptr.To(false) + } + + if cellName == Cell0Name { + // in cell0 disable VNC by default + if cellTemplate.NoVNCProxyServiceTemplate.Enabled == nil { + cellTemplate.NoVNCProxyServiceTemplate.Enabled = ptr.To(false) + } + } else { + // in other cells enable VNC by default + if cellTemplate.NoVNCProxyServiceTemplate.Enabled == nil { + cellTemplate.NoVNCProxyServiceTemplate.Enabled = ptr.To(true) + } + } + + // Default MessagingBus.Cluster if not set + // Migration from deprecated fields is handled by openstack-operator + if cellTemplate.MessagingBus.Cluster == "" { + if cellName == Cell0Name { + cellTemplate.MessagingBus.Cluster = "rabbitmq" + } else { + cellTemplate.MessagingBus.Cluster = "rabbitmq-" + cellName + } + } + + // "cellTemplate" is a by-value copy, so we need to re-inject the updated version of it into the map + spec.CellTemplates[cellName] = cellTemplate + } +} + +var _ webhook.Validator = &Nova{} + +// ValidateCellTemplates validates cell templates configuration +func (spec *NovaSpecCore) ValidateCellTemplates(basePath *field.Path, namespace string) field.ErrorList { + var errors field.ErrorList + + if _, ok := spec.CellTemplates[Cell0Name]; !ok { + errors = append( + errors, + field.Required(basePath.Child("cellTemplates"), + "cell0 specification is missing, cell0 key is required in cellTemplates"), + ) + } + + cellMessageBusNames := make(map[string]string) + + for name, cell := range spec.CellTemplates { + cellPath := basePath.Child("cellTemplates").Key(name) + errors = append( + errors, + ValidateCellName(cellPath, name)..., + ) + // verify the topology namespace is valid + errors = append(errors, topologyv1.ValidateTopologyRef( + cell.TopologyRef, *basePath.Child("topologyRef"), namespace)...) + + if name != Cell0Name { + // Determine which rabbit cluster this cell is using + // Prefer the new MessagingBus.Cluster field, fall back to deprecated CellMessageBusInstance + var cellCluster string + if cell.MessagingBus.Cluster != "" { + cellCluster = cell.MessagingBus.Cluster + } else { + cellCluster = cell.CellMessageBusInstance + } + + // Check if this rabbit cluster is already used by another cell + if dupName, ok := cellMessageBusNames[cellCluster]; ok { + // Determine which field to report the error on + fieldPath := cellPath.Child("messagingBus").Child("cluster") + if cell.MessagingBus.Cluster == "" { + fieldPath = cellPath.Child("cellMessageBusInstance") + } + + errors = append(errors, field.Invalid( + fieldPath, + cellCluster, + fmt.Sprintf( + "RabbitMqCluster CR need to be uniq per cell. It's duplicated with cell: %s", + dupName), + ), + ) + } + + cellMessageBusNames[cellCluster] = name + } + if *cell.MetadataServiceTemplate.Enabled && *spec.MetadataServiceTemplate.Enabled { + errors = append( + errors, + field.Invalid( + cellPath.Child("metadataServiceTemplate").Child("enabled"), + *cell.MetadataServiceTemplate.Enabled, + "should be false as metadata is enabled on the top level too. "+ + "The metadata service can be either enabled on top "+ + "or in the cells but not in both places at the same time."), + ) + } + errors = append(errors, + cell.MetadataServiceTemplate.ValidateTopology( + cellPath.Child("metadataServiceTemplate"), + namespace)...) + + errors = append(errors, + cell.NoVNCProxyServiceTemplate.ValidateTopology( + cellPath.Child("noVNCProxyServiceTemplate"), + namespace)...) + + errors = append(errors, + cell.ConductorServiceTemplate.ValidateTopology( + cellPath.Child("conductorServiceTemplate"), + namespace)...) + + errors = append( + errors, + cell.MetadataServiceTemplate.ValidateDefaultConfigOverwrite( + cellPath.Child("metadataServiceTemplate"))...) + + errors = append( + errors, + cell.DBPurge.Validate(cellPath.Child("dbPurge"))...) + + if name == Cell0Name { + errors = append( + errors, + cell.MetadataServiceTemplate.ValidateCell0( + cellPath.Child("metadataServiceTemplate"))...) + errors = append( + errors, + cell.NoVNCProxyServiceTemplate.ValidateCell0( + cellPath.Child("noVNCProxyServiceTemplate"))...) + errors = append( + errors, + ValidateNovaComputeCell0( + cellPath.Child("novaComputeTemplates"), len(cell.NovaComputeTemplates))...) + } + + for computeName, computeTemplate := range cell.NovaComputeTemplates { + if computeTemplate.ComputeDriver == IronicDriver { + errors = append( + errors, computeTemplate.ValidateIronicDriverReplicas( + cellPath.Child("novaComputeTemplates").Key(computeName))..., + ) + } + errors = append( + errors, ValidateNovaComputeName( + cellPath.Child("novaComputeTemplates").Key(computeName), computeName)..., + ) + errors = append( + errors, computeTemplate.ValidateDefaultConfigOverwrite( + cellPath.Child("novaComputeTemplates").Key(computeName))..., + ) + errors = append( + errors, computeTemplate.ValidateTopology( + cellPath.Child("novaComputeTemplates").Key(computeName), namespace)..., + ) + } + } + + return errors +} + +// ValidateAPIServiceTemplate - +func (spec *NovaSpecCore) ValidateAPIServiceTemplate(basePath *field.Path, namespace string) field.ErrorList { + errors := field.ErrorList{} + + // validate the service override key is valid + errors = append(errors, + service.ValidateRoutedOverrides( + basePath.Child("apiServiceTemplate").Child("override").Child("service"), + spec.APIServiceTemplate.Override.Service)...) + + errors = append(errors, + ValidateAPIDefaultConfigOverwrite( + basePath.Child("apiServiceTemplate").Child("defaultConfigOverwrite"), + spec.APIServiceTemplate.DefaultConfigOverwrite)...) + + errors = append(errors, + spec.APIServiceTemplate.ValidateTopology( + basePath.Child("apiServiceTemplate"), + namespace)...) + + return errors +} + +// ValidateSchedulerServiceTemplate - +func (spec *NovaSpecCore) ValidateSchedulerServiceTemplate(basePath *field.Path, namespace string) field.ErrorList { + errors := field.ErrorList{} + // validate the referenced TopologyRef + errors = append(errors, + spec.SchedulerServiceTemplate.ValidateTopology( + basePath.Child("schedulerServiceTemplate"), + namespace)...) + return errors +} + + +// ValidateCreate validates the NovaSpec during the webhook invocation. +func (spec *NovaSpec) ValidateCreate(basePath *field.Path, namespace string) (admission.Warnings, field.ErrorList) { + return spec.NovaSpecCore.ValidateCreate(basePath, namespace) +} + +// ValidateCreate validates the NovaSpecCore during the webhook invocation. It is +// expected to be called by the validation webhook in the higher level meta +// operator +func (spec *NovaSpecCore) ValidateCreate(basePath *field.Path, namespace string) (admission.Warnings, field.ErrorList) { + var warnings admission.Warnings + + // Validate deprecated fields + deprecatedWarnings, deprecatedErrors := spec.validateDeprecatedFieldsCreate(basePath) + warnings = append(warnings, deprecatedWarnings...) + + errors := deprecatedErrors + errors = append(errors, spec.ValidateCellTemplates(basePath, namespace)...) + errors = append(errors, spec.ValidateAPIServiceTemplate(basePath, namespace)...) + errors = append(errors, spec.ValidateSchedulerServiceTemplate(basePath, namespace)...) + + // validate TopologyRef override for top-level MetadataServiceTemplate + errors = append(errors, + spec.MetadataServiceTemplate.ValidateTopology( + basePath.Child("metadataServiceTemplate"), + namespace)...) + + errors = append( + errors, + spec.MetadataServiceTemplate.ValidateDefaultConfigOverwrite( + basePath.Child("metadataServiceTemplate"))...) + + // validate top-level topology + errors = append(errors, + topologyv1.ValidateTopologyRef( + spec.TopologyRef, *basePath.Child("topologyRef"), namespace)...) + + return warnings, errors +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *Nova) ValidateCreate() (admission.Warnings, error) { + novalog.Info("validate create", "name", r.Name) + + warnings, errors := r.Spec.ValidateCreate(field.NewPath("spec"), r.Namespace) + if len(errors) != 0 { + novalog.Info("validation failed", "name", r.Name) + return warnings, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "Nova"}, + r.Name, errors) + } + return warnings, nil +} + +// ValidateUpdate validates the NovaSpec during the webhook invocation. +func (spec *NovaSpec) ValidateUpdate(old NovaSpec, basePath *field.Path, namespace string) (admission.Warnings, field.ErrorList) { + return spec.NovaSpecCore.ValidateUpdate(old.NovaSpecCore, basePath, namespace) +} + +// ValidateUpdate validates the NovaSpecCore during the webhook invocation. It is +// expected to be called by the validation webhook in the higher level meta +// operator +func (spec *NovaSpecCore) ValidateUpdate(old NovaSpecCore, basePath *field.Path, namespace string) (admission.Warnings, field.ErrorList) { + var errors field.ErrorList + var warnings admission.Warnings + + // Validate deprecated fields + deprecatedWarnings, deprecatedErrors := spec.validateDeprecatedFieldsUpdate(old, basePath) + warnings = append(warnings, deprecatedWarnings...) + errors = append(errors, deprecatedErrors...) + + errors = append(errors, spec.ValidateCellTemplates(basePath, namespace)...) + // Validate top-level TopologyRef + errors = append(errors, topologyv1.ValidateTopologyRef( + spec.TopologyRef, *basePath.Child("topologyRef"), namespace)...) + + errors = append(errors, spec.ValidateAPIServiceTemplate(basePath, namespace)...) + errors = append(errors, spec.ValidateSchedulerServiceTemplate(basePath, namespace)...) + + // validate TopologyRef override for top-level MetadataServiceTemplate + errors = append(errors, + spec.MetadataServiceTemplate.ValidateTopology( + basePath.Child("metadataServiceTemplate"), + namespace)...) + + errors = append( + errors, + spec.MetadataServiceTemplate.ValidateDefaultConfigOverwrite( + basePath.Child("metadataServiceTemplate"))...) + + return warnings, errors +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *Nova) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novalog.Info("validate update", "name", r.Name) + oldNova, ok := old.(*Nova) + if !ok || oldNova == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novalog.Info("validate update", "diff", cmp.Diff(oldNova, r)) + + warnings, errors := r.Spec.ValidateUpdate(oldNova.Spec, field.NewPath("spec"), r.Namespace) + if len(errors) != 0 { + novalog.Info("validation failed", "name", r.Name) + return warnings, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "Nova"}, + r.Name, errors) + } + return warnings, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *Nova) ValidateDelete() (admission.Warnings, error) { + novalog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// SetDefaultRouteAnnotations sets HAProxy timeout values of the route +// NOTE: it is used by ctlplane webhook on openstack-operator +func (spec *NovaSpecCore) SetDefaultRouteAnnotations(annotations map[string]string) { + const haProxyAnno = "haproxy.router.openshift.io/timeout" + // Use a custom annotation to flag when the operator has set the default HAProxy timeout + // With the annotation func determines when to overwrite existing HAProxy timeout with the APITimeout + const novaAnno = "api.nova.openstack.org/timeout" + + valNova, okNova := annotations[novaAnno] + valHAProxy, okHAProxy := annotations[haProxyAnno] + + // Human operator set the HAProxy timeout manually + if !okNova && okHAProxy { + return + } + + // Human operator modified the HAProxy timeout manually without removing the Nova flag + if okNova && okHAProxy && valNova != valHAProxy { + delete(annotations, novaAnno) + return + } + + timeout := fmt.Sprintf("%ds", spec.APITimeout) + annotations[novaAnno] = timeout + annotations[haProxyAnno] = timeout +} + +// Validate the field values +func (r *NovaCellDBPurge) Validate(basePath *field.Path) field.ErrorList { + var errors field.ErrorList + // k8s uses the same cron lib to validate the schedule of the CronJob + // https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/batch/validation/validation.go + if _, err := cron.ParseStandard(*r.Schedule); err != nil { + errors = append( + errors, + field.Invalid( + basePath.Child("schedule"), r.Schedule, err.Error()), + ) + } + return errors +} diff --git a/apis/nova/v1beta1/novaapi_types.go b/apis/nova/v1beta1/novaapi_types.go new file mode 100644 index 000000000..dfa416fac --- /dev/null +++ b/apis/nova/v1beta1/novaapi_types.go @@ -0,0 +1,260 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + service "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NovaAPITemplate defines the input parameters specified by the user to +// create a NovaAPI via higher level CRDs. +// NOTE(gibi): NovaAPITemplate has the same structure than NovaServiceBase BUT +// we want to default ContainerImage for the template, therefore the structs +// are duplicated. +type NovaAPITemplate struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of the service to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service. Setting here overrides + // any global NodeSelector settings within the Nova CR. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // DefaultConfigOverwrite - interface to overwrite default config files like e.g. api-paste.ini or policy.yaml. + DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override APIOverrideSpec `json:"override,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.API `json:"tls,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,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"` +} + +// NovaAPISpec defines the desired state of NovaAPI +type NovaAPISpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Optional + // +kubebuilder:default=60 + // +kubebuilder:validation:Minimum=10 + // APITimeout for Route and Apache + APITimeout int `json:"apiTimeout"` + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for the nova-api service. This secret is expected to be + // generated by the nova-operator based on the information passed to the + // Nova CR. + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova" + // ServiceUser - optional username used for this service to register in + // keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + // KeystoneAuthURL configures the keystone API endpoint to be used + // by the service for authentication and authorization + KeystoneAuthURL string `json:"keystoneAuthURL"` + + // +kubebuilder:validation:Required + // KeystonePublicAuthURL configures the public keystone API endpoint. This + // can be different from KeystoneAuthURL. The service uses this value + // to redirect unauthenticated users. + KeystonePublicAuthURL string `json:"keystonePublicAuthURL"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=regionOne + // Region - the region name to use for service endpoint discovery + Region string `json:"region"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova-api" + // APIDatabaseAccount - MariaDBAccount to use when accessing the API DB + APIDatabaseAccount string `json:"apiDatabaseAccount"` + + // +kubebuilder:validation:Required + // APIDatabaseHostname - hostname to use when accessing the API DB + APIDatabaseHostname string `json:"apiDatabaseHostname"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova-cell0" + // APIDatabaseAccount - MariaDBAccount to use when accessing the cell0 DB + Cell0DatabaseAccount string `json:"cell0DatabaseAccount"` + + // +kubebuilder:validation:Required + // APIDatabaseHostname - hostname to use when accessing the cell0 DB + Cell0DatabaseHostname string `json:"cell0DatabaseHostname"` + + // NovaServiceBase specifies the generic fields of the service + NovaServiceBase `json:",inline"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override APIOverrideSpec `json:"override,omitempty"` + + // +kubebuilder:validation:Required + // ServiceAccount - service account name used internally to provide Nova services the default SA name + ServiceAccount string `json:"serviceAccount"` + + // RegisteredCells is a map keyed by cell names that are registered in the + // nova_api database with a value that is the hash of the given cell + // configuration. + // This is used to detect when a new cell is added or an existing cell is + // reconfigured to trigger refresh of the in memory cell caches of the + // service. + RegisteredCells map[string]string `json:"registeredCells"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.API `json:"tls,omitempty"` + + // +kubebuilder:validation:Optional + // DefaultConfigOverwrite - interface to overwrite default config files like e.g. api-paste.ini or policy.yaml. + DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` + + // +kubebuilder:validation:Required + // MemcachedInstance is the name of the Memcached CR that all nova service will use. + MemcachedInstance string `json:"memcachedInstance"` +} + +// NovaAPIStatus defines the observed state of NovaAPI +type NovaAPIStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // ReadyCount defines the number of replicas ready from nova-api + ReadyCount int32 `json:"readyCount,omitempty"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the openstack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAppliedTopology - the last applied Topology + LastAppliedTopology *topologyv1.TopoRef `json:"lastAppliedTopology,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="NetworkAttachments",type="string",JSONPath=".spec.networkAttachments",description="NetworkAttachments" +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status" +//+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message" + +// NovaAPI is the Schema for the novaapis API +type NovaAPI struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaAPISpec `json:"spec,omitempty"` + Status NovaAPIStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaAPIList contains a list of NovaAPI +type NovaAPIList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NovaAPI `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NovaAPI{}, &NovaAPIList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaAPIStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// GetSecret returns the value of the NovaAPI.Spec.Secret +func (n NovaAPI) GetSecret() string { + return n.Spec.Secret +} + +// GetSpecTopologyRef - Returns the LastAppliedTopology Set in the Status +func (n *NovaAPI) GetSpecTopologyRef() *topologyv1.TopoRef { + return n.Spec.TopologyRef +} + +// GetLastAppliedTopology - Returns the LastAppliedTopology Set in the Status +func (n *NovaAPI) GetLastAppliedTopology() *topologyv1.TopoRef { + return n.Status.LastAppliedTopology +} + +// SetLastAppliedTopology - Sets the LastAppliedTopology value in the Status +func (n *NovaAPI) SetLastAppliedTopology(topologyRef *topologyv1.TopoRef) { + n.Status.LastAppliedTopology = topologyRef +} diff --git a/apis/nova/v1beta1/novaapi_webhook.go b/apis/nova/v1beta1/novaapi_webhook.go new file mode 100644 index 000000000..705c398ea --- /dev/null +++ b/apis/nova/v1beta1/novaapi_webhook.go @@ -0,0 +1,164 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind NovaAPI --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" +) + +// NovaAPIDefaults - +type NovaAPIDefaults struct { + ContainerImageURL string +} + +var novaAPIDefaults NovaAPIDefaults + +// log is for logging in this package. +var novaapilog = logf.Log.WithName("novaapi-resource") + +// SetupNovaAPIDefaults - initialize NovaAPI spec defaults for use with either internal or external webhooks +func SetupNovaAPIDefaults(defaults NovaAPIDefaults) { + novaAPIDefaults = defaults + novaapilog.Info("NovaAPI defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &NovaAPI{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *NovaAPI) Default() { + novaapilog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this NovaAPI spec +func (spec *NovaAPISpec) Default() { + if spec.ContainerImage == "" { + spec.ContainerImage = novaAPIDefaults.ContainerImageURL + } +} + +var _ webhook.Validator = &NovaAPI{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaAPI) ValidateCreate() (admission.Warnings, error) { + novaapilog.Info("validate create", "name", r.Name) + + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + // validate the service override key is valid + errors = append(errors, service.ValidateRoutedOverrides(basePath.Child("override").Child("service"), r.Spec.Override.Service)...) + + errors = append(errors, + ValidateAPIDefaultConfigOverwrite( + basePath.Child("defaultConfigOverwrite"), + r.Spec.DefaultConfigOverwrite)...) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novaapilog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaAPI"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaAPI) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novaapilog.Info("validate update", "name", r.Name) + oldNovaAPI, ok := old.(*NovaAPI) + if !ok || oldNovaAPI == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novaapilog.Info("validate update", "diff", cmp.Diff(oldNovaAPI, r)) + + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + // validate the service override key is valid + errors = append(errors, service.ValidateRoutedOverrides(basePath.Child("override").Child("service"), r.Spec.Override.Service)...) + + errors = append(errors, + ValidateAPIDefaultConfigOverwrite( + basePath.Child("defaultConfigOverwrite"), + r.Spec.DefaultConfigOverwrite)...) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novaapilog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaAPI"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *NovaAPI) ValidateDelete() (admission.Warnings, error) { + novaapilog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ValidateAPIDefaultConfigOverwrite validates the defaultConfigOverwrite for NovaAPI +func ValidateAPIDefaultConfigOverwrite( + basePath *field.Path, + defaultConfigOverwrite map[string]string, +) field.ErrorList { + return ValidateDefaultConfigOverwrite( + basePath, + defaultConfigOverwrite, + []string{"policy.yaml", "api-paste.ini"}) +} + +// ValidateTopology validates the referenced TopoRef.Namespace. +func (r *NovaAPITemplate) ValidateTopology( + basePath *field.Path, + namespace string, +) field.ErrorList { + return topologyv1.ValidateTopologyRef( + r.TopologyRef, + *basePath.Child("topologyRef"), + namespace, + ) +} diff --git a/apis/nova/v1beta1/novacell_types.go b/apis/nova/v1beta1/novacell_types.go new file mode 100644 index 000000000..7b35e9ddf --- /dev/null +++ b/apis/nova/v1beta1/novacell_types.go @@ -0,0 +1,323 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + rabbitmqv1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +const ( + // Cell0Name is the name of Cell0 cell that is mandatory in every deployment + Cell0Name = "cell0" +) + +// NovaCellTemplate defines the input parameters specified by the user to +// create a NovaCell via higher level CRDs. +type NovaCellTemplate struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=openstack + // CellDatabaseInstance is the name of the MariaDB CR to select the DB + // Service instance used as the DB of this cell. + CellDatabaseInstance string `json:"cellDatabaseInstance"` + + // +kubebuilder:validation:Required + // CellDatabaseAccount - MariaDBAccount to use when accessing the give cell DB + CellDatabaseAccount string `json:"cellDatabaseAccount"` + + // +kubebuilder:validation:Optional + // CellMessageBusInstance is the name of the RabbitMqCluster CR to select + // the Message Bus Service instance used by the nova services to + // communicate in this cell. For cell0 it is unused. + // Deprecated: Use MessagingBus.Cluster instead + CellMessageBusInstance string `json:"cellMessageBusInstance,omitempty"` + + // +kubebuilder:validation:Optional + // MessagingBus configuration (username, vhost, and cluster) + MessagingBus rabbitmqv1.RabbitMqConfig `json:"messagingBus,omitempty"` + + // +kubebuilder:validation:Required + // HasAPIAccess defines if this Cell is configured to have access to the + // API DB and message bus. + HasAPIAccess bool `json:"hasAPIAccess"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running cell. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` + + // +kubebuilder:validation:Optional + // ConductorServiceTemplate - defines the cell conductor deployment for the cell. + ConductorServiceTemplate NovaConductorTemplate `json:"conductorServiceTemplate"` + + // +kubebuilder:validation:Optional + // MetadataServiceTemplate - defines the metadata service dedicated for the + // cell. Note that for cell0 metadata service should not be deployed. Also + // if metadata service needs to be deployed per cell here then it should + // not be enabled to be deployed on the top level via the Nova CR at the + // same time. By default Nova CR deploys the metadata service at the top + // level and disables it on the cell level. + MetadataServiceTemplate NovaMetadataTemplate `json:"metadataServiceTemplate"` + + // +kubebuilder:validation:Optional + // NoVNCProxyServiceTemplate - defines the novncproxy service dedicated for + // the cell. Note that for cell0 novncproxy should not be deployed so + // the enabled field of this template is defaulted to false in cell0 but + // defaulted to true in other cells. + NoVNCProxyServiceTemplate NovaNoVNCProxyTemplate `json:"noVNCProxyServiceTemplate"` + + // +kubebuilder:validation:Optional + // NovaComputeTemplates - map of nova computes template with selected drivers in format + // compute_name: compute_template. Key from map is arbitrary name for the compute with + // a limit of 20 characters. + NovaComputeTemplates map[string]NovaComputeTemplate `json:"novaComputeTemplates,omitempty"` + + // +kubebuilder:validation:Optional + // MemcachedInstance is the name of the Memcached CR that the services in the cell will use. + // If defined then this takes precedence over Nova.Spec.MemcachedInstance for this cel + MemcachedInstance string `json:"memcachedInstance"` + + // +kubebuilder:validation:Optional + // DBPurge defines the parameters for the DB archiving and purging cron job + DBPurge NovaCellDBPurge `json:"dbPurge"` +} + +// NovaCellSpec defines the desired state of NovaCell +type NovaCellSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Required + // CellName is the name of the Nova Cell. The value "cell0" has a special + // meaning. The "cell0" Cell cannot have compute nodes associated and the + // conductor in this cell acts as the super conductor for all the cells in + // the deployment. + CellName string `json:"cellName"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=60 + // +kubebuilder:validation:Minimum=10 + // APITimeout for Route and Apache + APITimeout int `json:"apiTimeout"` + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for the nova cell. This secret is expected to be + // generated by the nova-operator based on the information passed to the + // Nova CR. + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this services. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // ServiceUser - optional username used for this service to register in + // keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + // KeystoneAuthURL - the URL that the service in the cell can use to talk + // to keystone + KeystoneAuthURL string `json:"keystoneAuthURL"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=regionOne + // Region - the region name to use for service endpoint discovery + Region string `json:"region"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // APIDatabaseAccount - MariaDBAccount to use when accessing the API DB + APIDatabaseAccount string `json:"apiDatabaseAccount"` + + // +kubebuilder:validation:Optional + // APIDatabaseHostname - hostname to use when accessing the API DB. If not + // provided then up-calls will be disabled. This filed is Required for + // cell0. + // TODO(gibi): Add a webhook to validate cell0 constraint + APIDatabaseHostname string `json:"apiDatabaseHostname"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // CellDatabaseAccount - MariaDBAccount to use when accessing the cell DB + CellDatabaseAccount string `json:"cellDatabaseAccount"` + + // +kubebuilder:validation:Required + // CellDatabaseHostname - hostname to use when accessing the cell DB + CellDatabaseHostname string `json:"cellDatabaseHostname"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + // PreserveJobs - do not delete jobs after they finished e.g. to check logs + PreserveJobs bool `json:"preserveJobs"` + + // +kubebuilder:validation:Required + // ConductorServiceTemplate - defines the cell conductor deployment for the cell + ConductorServiceTemplate NovaConductorTemplate `json:"conductorServiceTemplate"` + + // +kubebuilder:validation:Optional + // MetadataServiceTemplate - defines the metadata service dedicated for the cell. + MetadataServiceTemplate NovaMetadataTemplate `json:"metadataServiceTemplate"` + + // +kubebuilder:validation:Required + // NoVNCProxyServiceTemplate - defines the novncproxy service dedicated for + // the cell. + NoVNCProxyServiceTemplate NovaNoVNCProxyTemplate `json:"noVNCProxyServiceTemplate"` + + // +kubebuilder:validation:Optional + // NovaComputeTemplates - map of nova computes template with selected drivers in format + // compute_name: compute_template. Key from map is arbitrary name for the compute. + // because of that there is a 20 character limit on the compute name. + NovaComputeTemplates map[string]NovaComputeTemplate `json:"novaComputeTemplates,omitempty"` + + // +kubebuilder:validation:Required + // ServiceAccount - service account name used internally to provide Nova services the default SA name + ServiceAccount string `json:"serviceAccount"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.Ca `json:"tls,omitempty"` + + // +kubebuilder:validation:Required + // MemcachedInstance is the name of the Memcached CR that all nova service will use. + MemcachedInstance string `json:"memcachedInstance"` + + // +kubebuilder:validation:Optional + // DBPurge defines the parameters for the DB archiving and purging cron job + DBPurge NovaCellDBPurge `json:"dbPurge"` + + NovaCellImages `json:",inline"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` +} + +// NovaCellDBPurge defines the parameters for the DB archiving and purging +// cron job +type NovaCellDBPurge struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default="0 0 * * *" + // Schedule defines when to run the DB maintenance job in a cron format. + // By default it runs every midnight. + Schedule *string `json:"schedule"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=30 + // +kubebuilder:validation:Minimum=1 + // ArchiveAge defines the minimum age of the records in days that can be + // moved to the shadow tables. + ArchiveAge *int `json:"archiveAge"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=90 + // +kubebuilder:validation:Minimum=1 + // PurgeAge defines the minimum age of the records in days that can be + // deleted from the shadow tables + PurgeAge *int `json:"purgeAge"` +} + +// NovaCellStatus defines the observed state of NovaCell +type NovaCellStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // ConductorServiceReadyCount defines the number of replicas ready from + // nova-conductor service in the cell + ConductorServiceReadyCount int32 `json:"conductorServiceReadyCount,omitempty"` + + // MetadataServiceReadyCount defines the number of replicas ready from + // nova-metadata service in the cell + MetadataServiceReadyCount int32 `json:"metadataServiceReadyCount,omitempty"` + + // NoVNCPRoxyServiceReadyCount defines the number of replicas ready from + // nova-novncproxy service in the cell + NoVNCPRoxyServiceReadyCount int32 `json:"noVNCProxyServiceReadyCount,omitempty"` + + // NovaComputesStatus is a map with format cell_name: NovaComputeCellStatus + // where NovaComputeCellStatus tell if compute with selected name deployed successfully + // and indicates if the compute is successfully mapped to the cell in + // the nova_api database. + // When a compute is removed from the Spec the operator will delete the + // related NovaCompute CR and then remove the compute from this Status field. + NovaComputesStatus map[string]NovaComputeCellStatus `json:"novaComputesStatus,omitempty"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the openstack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// NovaCell is the Schema for the novacells API +type NovaCell struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaCellSpec `json:"spec,omitempty"` + Status NovaCellStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaCellList contains a list of NovaCell +type NovaCellList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NovaCell `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NovaCell{}, &NovaCellList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaCellStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// IsReady returns true if the Cell reconciled successfully +func (instance NovaCell) IsReady() bool { + return instance.Status.Conditions.IsTrue(condition.ReadyCondition) +} + +// GetSecret returns the value of the NovaCell.Spec.Secret +func (instance NovaCell) GetSecret() string { + return instance.Spec.Secret +} diff --git a/apis/nova/v1beta1/novacell_webhook.go b/apis/nova/v1beta1/novacell_webhook.go new file mode 100644 index 000000000..96153652d --- /dev/null +++ b/apis/nova/v1beta1/novacell_webhook.go @@ -0,0 +1,249 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind NovaCell --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + "regexp" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/ptr" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" +) + +// CRDNameRegex is the regex pattern for validating CRD names +const CRDNameRegex = "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" + +// NovaCellDefaults - +type NovaCellDefaults struct { + ConductorContainerImageURL string + MetadataContainerImageURL string + NoVNCContainerImageURL string + NovaComputeContainerImageURL string +} + +var novaCellDefaults NovaCellDefaults + +// log is for logging in this package. +var novacelllog = logf.Log.WithName("novacell-resource") + +// SetupNovaCellDefaults - initialize NovaCell spec defaults for use with either internal or external webhooks +func SetupNovaCellDefaults(defaults NovaCellDefaults) { + novaCellDefaults = defaults + novacelllog.Info("NovaCell defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &NovaCell{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *NovaCell) Default() { + novacelllog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this NovaCell spec +func (spec *NovaCellSpec) Default() { + spec.NovaCellImages.Default(novaCellDefaults) + + if spec.MetadataServiceTemplate.Enabled == nil { + spec.MetadataServiceTemplate.Enabled = ptr.To(false) + } + + if spec.CellName == Cell0Name { + // in cell0 disable VNC by default + if spec.NoVNCProxyServiceTemplate.Enabled == nil { + spec.NoVNCProxyServiceTemplate.Enabled = ptr.To(false) + } + } else { + // in other cells enable VNC by default + if spec.NoVNCProxyServiceTemplate.Enabled == nil { + spec.NoVNCProxyServiceTemplate.Enabled = ptr.To(true) + } + } +} + +var _ webhook.Validator = &NovaCell{} + +func (spec *NovaCellSpec) validate(basePath *field.Path, namespace string) field.ErrorList { + var errors field.ErrorList + + if spec.CellName == Cell0Name { + errors = append( + errors, spec.MetadataServiceTemplate.ValidateCell0( + basePath.Child("metadataServiceTemplate"))..., + ) + errors = append( + errors, spec.NoVNCProxyServiceTemplate.ValidateCell0( + basePath.Child("noVNCProxyServiceTemplate"))..., + ) + errors = append( + errors, + ValidateNovaComputeCell0( + basePath.Child("novaComputeTemplates"), len(spec.NovaComputeTemplates))...) + } + + // validate topology if passed to the CellSpec (regardless of CellName). + // because a NovaCell CR can be created independently, TopologyRef should + // be validated for the underlying components (metadata, conductor, + // novncproxy, compute templates). + errors = append(errors, topologyv1.ValidateTopologyRef( + spec.TopologyRef, *basePath.Child("topologyRef"), namespace)...) + + errors = append(errors, + spec.MetadataServiceTemplate.ValidateTopology( + basePath.Child("metadataServiceTemplate"), + namespace)...) + + errors = append(errors, + spec.NoVNCProxyServiceTemplate.ValidateTopology( + basePath.Child("noVNCProxyServiceTemplate"), + namespace)...) + + errors = append(errors, + spec.ConductorServiceTemplate.ValidateTopology( + basePath.Child("conductorServiceTemplate"), + namespace)...) + + for computeName, computeTemplate := range spec.NovaComputeTemplates { + if computeTemplate.ComputeDriver == IronicDriver { + errors = append( + errors, computeTemplate.ValidateIronicDriverReplicas( + basePath.Child("novaComputeTemplates").Key(computeName))..., + ) + } + errors = append( + errors, ValidateNovaComputeName( + basePath.Child("novaComputeTemplates").Key(computeName), computeName)..., + ) + errors = append( + errors, computeTemplate.ValidateDefaultConfigOverwrite( + basePath.Child("novaComputeTemplates").Key(computeName))..., + ) + + errors = append( + errors, computeTemplate.ValidateTopology( + basePath.Child("novaComputeTemplates").Key(computeName), namespace)..., + ) + } + + errors = append( + errors, ValidateCellName( + basePath.Child("cellName"), spec.CellName)..., + ) + errors = append( + errors, + spec.DBPurge.Validate(basePath.Child("dbPurge"))..., + ) + + return errors +} + +// ValidateCreate validates the NovaCellSpec during the webhook invocation +func (spec *NovaCellSpec) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList { + return spec.validate(basePath, namespace) +} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaCell) ValidateCreate() (admission.Warnings, error) { + novacelllog.Info("validate create", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + errors = r.Spec.ValidateCreate(basePath, r.Namespace) + + if len(errors) != 0 { + novacelllog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaCell"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate validates the NovaCellSpec during the webhook invocation +func (spec *NovaCellSpec) ValidateUpdate(old NovaCellSpec, basePath *field.Path, namespace string) field.ErrorList { + return spec.validate(basePath, namespace) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaCell) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novacelllog.Info("validate update", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + oldCell, ok := old.(*NovaCell) + if !ok || oldCell == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novacelllog.Info("validate update", "diff", cmp.Diff(oldCell, r)) + + errors = r.Spec.ValidateUpdate(oldCell.Spec, basePath, r.Namespace) + + if len(errors) != 0 { + novacelllog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaCell"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *NovaCell) ValidateDelete() (admission.Warnings, error) { + novacelllog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ValidateCellName validates the cell name. It is expected to be called +// from various webhooks. +func ValidateCellName(path *field.Path, cellName string) field.ErrorList { + var errors field.ErrorList + if len(cellName) > 35 { + errors = append( + errors, + field.Invalid( + path, cellName, "should be shorter than 36 characters"), + ) + } + match, _ := regexp.MatchString(CRDNameRegex, cellName) + if !match { + errors = append( + errors, + field.Invalid( + path, cellName, + fmt.Sprintf("should match with the regex '%s'", CRDNameRegex)), + ) + } + return errors +} diff --git a/apis/nova/v1beta1/novacompute_types.go b/apis/nova/v1beta1/novacompute_types.go new file mode 100644 index 000000000..38e725ae7 --- /dev/null +++ b/apis/nova/v1beta1/novacompute_types.go @@ -0,0 +1,261 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NovaComputeTemplate defines the input parameters specified by the user to +// create a NovaCompute via higher level CRDs. +type NovaComputeTemplate struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of the service to run. For ironic.IronicDriver the max replica is 1 + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service. Setting here overrides + // any global NodeSelector settings within the Nova CR. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // DefaultConfigOverwrite - interface to overwrite default config files like e.g. provider.yaml + DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Required + // +kubebuilder:validation:Enum=ironic.IronicDriver;fake.FakeDriver + // ComputeDriver - defines which driver to use for controlling virtualization + ComputeDriver string `json:"computeDriver"` +} + +// NovaComputeSpec defines the desired state of NovaCompute +type NovaComputeSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Required + // CellName is the name of the Nova Cell this NovaCompute belongs to. + CellName string `json:"cellName"` + + // +kubebuilder:validation:Required + // ComputeName - compute name. + ComputeName string `json:"computeName"` + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for the NovaCompute service. This secret is expected to be + // generated by the nova-operator based on the information passed to the + // Nova CR. + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova" + // ServiceUser - optional username used for this service to register in + // keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + KeystoneAuthURL string `json:"keystoneAuthURL"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=regionOne + // Region - the region name to use for service endpoint discovery + Region string `json:"region"` + + // NovaServiceBase specifies the generic fields of the service + NovaServiceBase `json:",inline"` + + // +kubebuilder:validation:Required + // ServiceAccount - service account name used internally to provide Nova services the default SA name + ServiceAccount string `json:"serviceAccount"` + + // +kubebuilder:validation:Required + // +kubebuilder:validation:Enum=ironic.IronicDriver;fake.FakeDriver + // ComputeDriver defines which driver to use for controlling virtualization + ComputeDriver string `json:"computeDriver"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.Ca `json:"tls,omitempty"` + + // +kubebuilder:validation:Optional + // DefaultConfigOverwrite - interface to overwrite default config files like e.g. provider.yaml + DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` +} + +// NovaComputeStatus defines the observed state of NovaCompute +type NovaComputeStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // ReadyCount defines the number of replicas ready from NovaCompute + ReadyCount int32 `json:"readyCount,omitempty"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the openstack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAppliedTopology - the last applied Topology + LastAppliedTopology *topologyv1.TopoRef `json:"lastAppliedTopology,omitempty"` +} + +// NovaComputeCellStatus defines state of NovaCompute in cell +type NovaComputeCellStatus struct { + // Deployed value: true means that the compute is deployed but can still be undiscovered + Deployed bool `json:"deployed"` + // Errors value True means that during deployment, errors appear, and the user needs to check the compute for problems + Errors bool `json:"errors"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="NetworkAttachments",type="string",JSONPath=".spec.networkAttachments",description="NetworkAttachments" +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status" +//+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message" + +// NovaCompute is the Schema for the NovaCompute +type NovaCompute struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaComputeSpec `json:"spec,omitempty"` + Status NovaComputeStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaComputeList contains a list of NovaCompute +type NovaComputeList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NovaCompute `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NovaCompute{}, &NovaComputeList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaComputeStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// GetSecret returns the value of the NovaCompute.Spec.Secret +func (n NovaCompute) GetSecret() string { + return n.Spec.Secret +} + +// IsReady returns true if the Cell reconciled successfully +func (n NovaCompute) IsReady() bool { + return n.Status.Conditions.IsTrue(condition.ReadyCondition) +} + +// NewNovaComputeSpec constructs a NewNovaComputeSpec +func NewNovaComputeSpec( + novaCell NovaCellSpec, + computeTemplate NovaComputeTemplate, + novaComputeName string, +) NovaComputeSpec { + novaComputeSpec := NovaComputeSpec{ + CellName: novaCell.CellName, + ComputeName: novaComputeName, + Secret: novaCell.Secret, + NovaServiceBase: NovaServiceBase{ + ContainerImage: novaCell.NovaComputeContainerImageURL, + Replicas: computeTemplate.Replicas, + NodeSelector: computeTemplate.NodeSelector, + TopologyRef: computeTemplate.TopologyRef, + CustomServiceConfig: computeTemplate.CustomServiceConfig, + Resources: computeTemplate.Resources, + NetworkAttachments: computeTemplate.NetworkAttachments, + }, + KeystoneAuthURL: novaCell.KeystoneAuthURL, + ServiceUser: novaCell.ServiceUser, + Region: novaCell.Region, + ServiceAccount: novaCell.ServiceAccount, + ComputeDriver: computeTemplate.ComputeDriver, + TLS: novaCell.TLS, + DefaultConfigOverwrite: computeTemplate.DefaultConfigOverwrite, + } + + if novaComputeSpec.NodeSelector == nil { + novaComputeSpec.NodeSelector = novaCell.NodeSelector + } + + if novaComputeSpec.TopologyRef == nil { + novaComputeSpec.TopologyRef = novaCell.TopologyRef + } + + return novaComputeSpec +} + +// GetSpecTopologyRef - Returns the LastAppliedTopology Set in the Status +func (n *NovaCompute) GetSpecTopologyRef() *topologyv1.TopoRef { + return n.Spec.TopologyRef +} + +// GetLastAppliedTopology - Returns the LastAppliedTopology Set in the Status +func (n *NovaCompute) GetLastAppliedTopology() *topologyv1.TopoRef { + return n.Status.LastAppliedTopology +} + +// SetLastAppliedTopology - Sets the LastAppliedTopology value in the Status +func (n *NovaCompute) SetLastAppliedTopology(topologyRef *topologyv1.TopoRef) { + n.Status.LastAppliedTopology = topologyRef +} diff --git a/apis/nova/v1beta1/novacompute_webhook.go b/apis/nova/v1beta1/novacompute_webhook.go new file mode 100644 index 000000000..c3d06916a --- /dev/null +++ b/apis/nova/v1beta1/novacompute_webhook.go @@ -0,0 +1,226 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind NovaCompute --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + "regexp" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" +) + +// NovaComputeDefaults - +type NovaComputeDefaults struct { + ContainerImageURL string +} + +var novaComputeDefaults NovaComputeDefaults + +// log is for logging in this package. +var novacomputelog = logf.Log.WithName("novacompute-resource") + +// SetupNovaComputeDefaults - initialize NovaCompute spec defaults for use with either internal or external webhooks +func SetupNovaComputeDefaults(defaults NovaComputeDefaults) { + novaComputeDefaults = defaults + novacomputelog.Info("NovaCompute defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &NovaCompute{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *NovaCompute) Default() { + novacomputelog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this novacompute spec +func (spec *NovaComputeSpec) Default() { + if spec.ContainerImage == "" { + spec.ContainerImage = novaComputeDefaults.ContainerImageURL + } +} + +var _ webhook.Validator = &NovaCompute{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaCompute) ValidateCreate() (admission.Warnings, error) { + novacomputelog.Info("validate create", "name", r.Name) + + errors := r.Spec.ValidateCreate(field.NewPath("spec"), r.Namespace) + + if len(errors) != 0 { + novacomputelog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaCompute"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaCompute) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novacomputelog.Info("validate update", "name", r.Name) + oldNovaCompute, ok := old.(*NovaCompute) + if !ok || oldNovaCompute == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novacomputelog.Info("validate update", "diff", cmp.Diff(oldNovaCompute, r)) + + errors := r.Spec.ValidateUpdate(oldNovaCompute.Spec, field.NewPath("spec"), r.Namespace) + + if len(errors) != 0 { + novacomputelog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaCompute"}, + r.Name, errors) + } + + return nil, nil +} + +// ValidateCreate validates the NovaComputeSpec during the webhook invocation +func (spec *NovaComputeSpec) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList { + return spec.validate(basePath, namespace) +} + +// ValidateUpdate validates the NovaComputeSpec during the webhook invocation +func (spec *NovaComputeSpec) ValidateUpdate(old NovaComputeSpec, basePath *field.Path, namespace string) field.ErrorList { + return spec.validate(basePath, namespace) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *NovaCompute) ValidateDelete() (admission.Warnings, error) { + novacomputelog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +func (spec *NovaComputeSpec) validate(basePath *field.Path, namespace string) field.ErrorList { + var errors field.ErrorList + + if spec.ComputeDriver == IronicDriver && *spec.NovaServiceBase.Replicas > 1 { + errors = append( + errors, + field.Invalid( + basePath.Child("replicas"), *spec.NovaServiceBase.Replicas, "should be max 1 for ironic.IronicDriver"), + ) + } + errors = append( + errors, + ValidateComputeDefaultConfigOverwrite( + basePath.Child("defaultConfigOverwrite"), spec.DefaultConfigOverwrite)...) + + errors = append(errors, topologyv1.ValidateTopologyRef( + spec.TopologyRef, *basePath.Child("topologyRef"), namespace)...) + + return errors +} + +// ValidateIronicDriverReplicas validates replicas depend on compute driver +func (r *NovaComputeTemplate) ValidateIronicDriverReplicas(basePath *field.Path) field.ErrorList { + var errors field.ErrorList + if *r.Replicas > 1 { + errors = append( + errors, + field.Invalid( + basePath.Child("replicas"), *r.Replicas, "should be max 1 for ironic.IronicDriver"), + ) + } + return errors +} + +// ValidateDefaultConfigOverwrite validates the defaultConfigOverwrite for NovaComputeTemplate +func (r *NovaComputeTemplate) ValidateDefaultConfigOverwrite(basePath *field.Path) field.ErrorList { + return ValidateComputeDefaultConfigOverwrite( + basePath.Child("defaultConfigOverwrite"), r.DefaultConfigOverwrite) +} + +// ValidateComputeDefaultConfigOverwrite validates the defaultConfigOverwrite for NovaCompute +func ValidateComputeDefaultConfigOverwrite( + basePath *field.Path, + defaultConfigOverwrite map[string]string, +) field.ErrorList { + return ValidateDefaultConfigOverwrite( + basePath, defaultConfigOverwrite, []string{"provider*.yaml"}) +} + +// ValidateNovaComputeName validates the compute name. It is expected to be called +// from various webhooks. +func ValidateNovaComputeName(path *field.Path, computeName string) field.ErrorList { + var errors field.ErrorList + if len(computeName) > 20 { + errors = append( + errors, + field.Invalid( + path, computeName, "should be shorter than 20 characters"), + ) + } + match, _ := regexp.MatchString(CRDNameRegex, computeName) + if !match { + errors = append( + errors, + field.Invalid( + path, computeName, + fmt.Sprintf("should match with the regex '%s'", CRDNameRegex)), + ) + } + return errors +} + +// ValidateNovaComputeCell0 validates cell0 NoVNCProxy template. This is expected to be +// called by higher level validation webhooks +func ValidateNovaComputeCell0(basePath *field.Path, mapLength int) field.ErrorList { + var errors field.ErrorList + if mapLength > 0 { + errors = append( + errors, + field.Invalid( + basePath, "novaComputeTemplates", "should have zero elements for cell0"), + ) + } + return errors +} + + +// ValidateTopology validates the referenced TopoRef.Namespace. +func (r *NovaComputeTemplate) ValidateTopology( + basePath *field.Path, + namespace string, +) field.ErrorList { + return topologyv1.ValidateTopologyRef( + r.TopologyRef, + *basePath.Child("topologyRef"), + namespace, + ) +} diff --git a/apis/nova/v1beta1/novaconductor_types.go b/apis/nova/v1beta1/novaconductor_types.go new file mode 100644 index 000000000..bcdda55fd --- /dev/null +++ b/apis/nova/v1beta1/novaconductor_types.go @@ -0,0 +1,285 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NovaConductorTemplate defines the input parameters specified by the user to +// create a NovaConductor via higher level CRDs. +type NovaConductorTemplate struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of the service to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service. Setting here overrides + // any global NodeSelector settings within the Nova CR. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` +} + +// NovaConductorSpec defines the desired state of NovaConductor +type NovaConductorSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Required + // CellName is the name of the Nova Cell this conductor belongs to. + CellName string `json:"cellName"` + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for the nova-conductor service. This secret is expected to + // be generated by the nova-operator based on the information passed to the + // Nova CR. + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // ServiceUser - optional username used for this service to register in + // keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + // KeystoneAuthURL - the URL that the nova-conductor service can use to + // talk to keystone + KeystoneAuthURL string `json:"keystoneAuthURL"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=regionOne + // Region - the region name to use for service endpoint discovery + Region string `json:"region"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // APIDatabaseAccount - MariaDBAccount to use when accessing the API DB + APIDatabaseAccount string `json:"apiDatabaseAccount"` + + // +kubebuilder:validation:Optional + // APIDatabaseHostname - hostname to use when accessing the API DB. If not + // provided then up-calls will be disabled. This filed is Required for + // cell0. + // TODO(gibi): Add a webhook to validate cell0 constraint + APIDatabaseHostname string `json:"apiDatabaseHostname"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // CellDatabaseAccount - MariaDBAccount to use when accessing the cell DB + CellDatabaseAccount string `json:"cellDatabaseAccount"` + + // +kubebuilder:validation:Optional + // NOTE(gibi): This should be Required, see notes in KeystoneAuthURL + // CellDatabaseHostname - hostname to use when accessing the cell DB + CellDatabaseHostname string `json:"cellDatabaseHostname"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + // PreserveJobs - do not delete jobs after they finished e.g. to check logs + PreserveJobs bool `json:"preserveJobs"` + + // NovaServiceBase specifies the generic fields of the service + NovaServiceBase `json:",inline"` + + // +kubebuilder:validation:Required + // ServiceAccount - service account name used internally to provide Nova services the default SA name + ServiceAccount string `json:"serviceAccount"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.Ca `json:"tls,omitempty"` + + // +kubebuilder:validation:Required + // MemcachedInstance is the name of the Memcached CR that all nova service will use. + MemcachedInstance string `json:"memcachedInstance"` + + // +kubebuilder:validation:Optional + // DBPurge defines the parameters for the DB archiving and purging cron job + DBPurge NovaCellDBPurge `json:"dbPurge"` +} + +// NovaConductorStatus defines the observed state of NovaConductor +type NovaConductorStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // ReadyCount defines the number of replicas ready from nova-conductor + ReadyCount int32 `json:"readyCount,omitempty"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the openstack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAppliedTopology - the last applied Topology + LastAppliedTopology *topologyv1.TopoRef `json:"lastAppliedTopology,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="NetworkAttachments",type="string",JSONPath=".spec.networkAttachments",description="NetworkAttachments" +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status" +//+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message" + +// NovaConductor is the Schema for the novaconductors API +type NovaConductor struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaConductorSpec `json:"spec,omitempty"` + Status NovaConductorStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaConductorList contains a list of NovaConductor +type NovaConductorList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NovaConductor `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NovaConductor{}, &NovaConductorList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaConductorStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// NewNovaConductorSpec constructs a NovaConductorSpec +func NewNovaConductorSpec( + novaCell NovaCellSpec, +) NovaConductorSpec { + conductorSpec := NovaConductorSpec{ + CellName: novaCell.CellName, + Secret: novaCell.Secret, + CellDatabaseHostname: novaCell.CellDatabaseHostname, + CellDatabaseAccount: novaCell.CellDatabaseAccount, + APIDatabaseHostname: novaCell.APIDatabaseHostname, + APIDatabaseAccount: novaCell.APIDatabaseAccount, + NovaServiceBase: NovaServiceBase{ + ContainerImage: novaCell.ConductorContainerImageURL, + Replicas: novaCell.ConductorServiceTemplate.Replicas, + NodeSelector: novaCell.ConductorServiceTemplate.NodeSelector, + TopologyRef: novaCell.ConductorServiceTemplate.TopologyRef, + CustomServiceConfig: novaCell.ConductorServiceTemplate.CustomServiceConfig, + Resources: novaCell.ConductorServiceTemplate.Resources, + NetworkAttachments: novaCell.ConductorServiceTemplate.NetworkAttachments, + }, + KeystoneAuthURL: novaCell.KeystoneAuthURL, + ServiceUser: novaCell.ServiceUser, + Region: novaCell.Region, + ServiceAccount: novaCell.ServiceAccount, + TLS: novaCell.TLS, + PreserveJobs: novaCell.PreserveJobs, + MemcachedInstance: novaCell.MemcachedInstance, + DBPurge: novaCell.DBPurge, + } + + if conductorSpec.NodeSelector == nil { + conductorSpec.NodeSelector = novaCell.NodeSelector + } + + if conductorSpec.TopologyRef == nil { + conductorSpec.TopologyRef = novaCell.TopologyRef + } + + return conductorSpec +} + +// GetSecret returns the value of the NovaConductor.Spec.Secret +func (n NovaConductor) GetSecret() string { + return n.Spec.Secret +} + +// GetKeystoneAuthURL returns the KeystoneAuthURL from the Spec +func (n NovaConductor) GetKeystoneAuthURL() string { + return n.Spec.KeystoneAuthURL +} + +// GetKeystoneUser returns the Service user from the Spec +func (n NovaConductor) GetKeystoneUser() string { + return n.Spec.ServiceUser +} + +// GetCABundleSecretName returns the TLS CA bundle name from the Spec +func (n NovaConductor) GetCABundleSecretName() string { + return n.Spec.TLS.CaBundleSecretName +} + +// GetRegion returns the Region from the Spec +func (n NovaConductor) GetRegion() string { + return n.Spec.Region +} + +// GetSpecTopologyRef - Returns the LastAppliedTopology Set in the Status +func (n *NovaConductor) GetSpecTopologyRef() *topologyv1.TopoRef { + return n.Spec.TopologyRef +} + +// GetLastAppliedTopology - Returns the LastAppliedTopology Set in the Status +func (n *NovaConductor) GetLastAppliedTopology() *topologyv1.TopoRef { + return n.Status.LastAppliedTopology +} + +// SetLastAppliedTopology - Sets the LastAppliedTopology value in the Status +func (n *NovaConductor) SetLastAppliedTopology(topologyRef *topologyv1.TopoRef) { + n.Status.LastAppliedTopology = topologyRef +} diff --git a/apis/nova/v1beta1/novaconductor_webhook.go b/apis/nova/v1beta1/novaconductor_webhook.go new file mode 100644 index 000000000..26c23e9a8 --- /dev/null +++ b/apis/nova/v1beta1/novaconductor_webhook.go @@ -0,0 +1,140 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind NovaConductor --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" +) + +// NovaConductorDefaults - +type NovaConductorDefaults struct { + ContainerImageURL string +} + +var novaConductorDefaults NovaConductorDefaults + +// log is for logging in this package. +var novaconductorlog = logf.Log.WithName("novaconductor-resource") + +// SetupNovaConductorDefaults - initialize NovaConductor spec defaults for use with either internal or external webhooks +func SetupNovaConductorDefaults(defaults NovaConductorDefaults) { + novaConductorDefaults = defaults + novaconductorlog.Info("NovaConductor defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &NovaConductor{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *NovaConductor) Default() { + novaconductorlog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this NovaConductor spec +func (spec *NovaConductorSpec) Default() { + if spec.ContainerImage == "" { + spec.ContainerImage = novaConductorDefaults.ContainerImageURL + } +} + +var _ webhook.Validator = &NovaConductor{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaConductor) ValidateCreate() (admission.Warnings, error) { + novaconductorlog.Info("validate create", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + errors = append(errors,r.Spec.DBPurge.Validate( + basePath.Child("dbPurge"))...) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novaconductorlog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaConductor"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaConductor) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novaconductorlog.Info("validate update", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + oldConductor, ok := old.(*NovaConductor) + if !ok || oldConductor == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novaconductorlog.Info("validate update", "diff", cmp.Diff(oldConductor, r)) + + errors = append(errors, r.Spec.DBPurge.Validate( + basePath.Child("dbPurge"))...) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novaconductorlog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaConductor"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *NovaConductor) ValidateDelete() (admission.Warnings, error) { + novaconductorlog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ValidateTopology validates the referenced TopoRef.Namespace. +func (r *NovaConductorTemplate) ValidateTopology( + basePath *field.Path, + namespace string, +) field.ErrorList { + return topologyv1.ValidateTopologyRef( + r.TopologyRef, + *basePath.Child("topologyRef"), + namespace, + ) +} diff --git a/apis/nova/v1beta1/novametadata_types.go b/apis/nova/v1beta1/novametadata_types.go new file mode 100644 index 000000000..a39595201 --- /dev/null +++ b/apis/nova/v1beta1/novametadata_types.go @@ -0,0 +1,321 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + service "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NovaMetadataTemplate defines the input parameters specified by the user to +// create a NovaMetadata via higher level CRDs. +type NovaMetadataTemplate struct { + // +kubebuilder:validation:Optional + // Enabled - Whether NovaMetadata services should be deployed and managed. + // If it is set to false then the related NovaMetadata CR will be deleted + // if exists and owned by a higher level nova CR (Nova or NovaCell). If it + // exist but not owned by a higher level nova CR then the NovaMetadata CR + // will not be touched. + // If it is set to true the a NovaMetadata CR will be created. + // If there is already a manually created NovaMetadata CR with the relevant + // name then this operator will not try to update that CR, instead + // the higher level nova CR will be in error state until the manually + // create NovaMetadata CR is deleted manually. + Enabled *bool `json:"enabled"` + // NOTE(gibi): the default value of enabled depends on the context so + // it is set by webhooks + + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of the service to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service. Setting here overrides + // any global NodeSelector settings within the Nova CR. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // DefaultConfigOverwrite - interface to overwrite default config files like e.g. api-paste.ini. + DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override MetadataOverrideSpec `json:"override,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.SimpleService `json:"tls,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` +} + +// MetadataOverrideSpec to override the generated manifest of several child resources. +type MetadataOverrideSpec struct { + // Override configuration for the Service created to serve traffic to the cluster for internal + // communication. + Service *service.OverrideSpec `json:"service,omitempty"` +} + +// NovaMetadataSpec defines the desired state of NovaMetadata +type NovaMetadataSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Optional + // CellName is the name of the Nova Cell this metadata service belongs to. + // If not provided then the metadata serving every cells in the deployment + CellName string `json:"cellName,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=60 + // +kubebuilder:validation:Minimum=10 + // APITimeout for Route and Apache + APITimeout int `json:"apiTimeout"` + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for the nova-conductor service. This secret is expected to + // be generated by the nova-operator based on the information passed to the + // Nova CR. + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // ServiceUser - optional username used for this service to register in + // keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + // KeystoneAuthURL - the URL that the nova-metadata service can use to talk + // to keystone + // TODO(ksambor) Add checking if dynamic vendor data is configured + KeystoneAuthURL string `json:"keystoneAuthURL"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=regionOne + // Region - the region name to use for service endpoint discovery + Region string `json:"region"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova-api" + // APIDatabaseAccount - MariaDBAccount to use when accessing the API DB + APIDatabaseAccount string `json:"apiDatabaseAccount"` + + // +kubebuilder:validation:Optional + // APIDatabaseHostname - hostname to use when accessing the API DB. + // This filed is Required if the CellName is not provided + // TODO(gibi): Add a webhook to validate the CellName constraint + APIDatabaseHostname string `json:"apiDatabaseHostname"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // CellDatabaseAccount - MariaDBAccount to use when accessing the cell DB + CellDatabaseAccount string `json:"cellDatabaseAccount"` + + // +kubebuilder:validation:Optional + // CellDatabaseHostname - hostname to use when accessing the cell DB + // This is unused if CellName is not provided. But if it is provided then + // CellDatabaseHostName is also Required. + // TODO(gibi): add webhook to validate this CellName constraint + CellDatabaseHostname string `json:"cellDatabaseHostname"` + + // NovaServiceBase specifies the generic fields of the service + NovaServiceBase `json:",inline"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override MetadataOverrideSpec `json:"override,omitempty"` + + // +kubebuilder:validation:Required + // ServiceAccount - service account name used internally to provide Nova services the default SA name + ServiceAccount string `json:"serviceAccount"` + + // +kubebuilder:validation:Optional + // RegisteredCells is a map keyed by cell names that are registered in the + // nova_api database with a value that is the hash of the given cell + // configuration. + // This is used to detect when a new cell is added or an existing cell is + // reconfigured to trigger refresh of the in memory cell caches of the + // service. + // This is empty for the case when nova-metadata runs within the cell. + RegisteredCells map[string]string `json:"registeredCells,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.SimpleService `json:"tls,omitempty"` + + // +kubebuilder:validation:Optional + // DefaultConfigOverwrite - interface to overwrite default config files like e.g. api-paste.ini. + DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` + + // +kubebuilder:validation:Required + // MemcachedInstance is the name of the Memcached CR that all nova service will use. + MemcachedInstance string `json:"memcachedInstance"` +} + +// NovaMetadataStatus defines the observed state of NovaMetadata +type NovaMetadataStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // ReadyCount defines the number of replicas ready from nova-metadata + ReadyCount int32 `json:"readyCount,omitempty"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the openstack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAppliedTopology - the last applied Topology + LastAppliedTopology *topologyv1.TopoRef `json:"lastAppliedTopology,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="NetworkAttachments",type="string",JSONPath=".spec.networkAttachments",description="NetworkAttachments" +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status" +//+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message" + +// NovaMetadata is the Schema for the novametadata API +type NovaMetadata struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaMetadataSpec `json:"spec,omitempty"` + Status NovaMetadataStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaMetadataList contains a list of NovaMetadata +type NovaMetadataList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NovaMetadata `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NovaMetadata{}, &NovaMetadataList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaMetadataStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// NewNovaMetadataSpec constructs a NovaMetadataSpec +func NewNovaMetadataSpec( + novaCell NovaCellSpec, +) NovaMetadataSpec { + + metadataSpec := NovaMetadataSpec{ + CellName: novaCell.CellName, + Secret: novaCell.Secret, + CellDatabaseHostname: novaCell.CellDatabaseHostname, + CellDatabaseAccount: novaCell.CellDatabaseAccount, + APIDatabaseHostname: novaCell.APIDatabaseHostname, + APIDatabaseAccount: novaCell.APIDatabaseAccount, + NovaServiceBase: NovaServiceBase{ + ContainerImage: novaCell.MetadataContainerImageURL, + Replicas: novaCell.MetadataServiceTemplate.Replicas, + NodeSelector: novaCell.MetadataServiceTemplate.NodeSelector, + TopologyRef: novaCell.MetadataServiceTemplate.TopologyRef, + CustomServiceConfig: novaCell.MetadataServiceTemplate.CustomServiceConfig, + Resources: novaCell.MetadataServiceTemplate.Resources, + NetworkAttachments: novaCell.MetadataServiceTemplate.NetworkAttachments, + }, + KeystoneAuthURL: novaCell.KeystoneAuthURL, + ServiceUser: novaCell.ServiceUser, + Region: novaCell.Region, + ServiceAccount: novaCell.ServiceAccount, + Override: novaCell.MetadataServiceTemplate.Override, + TLS: novaCell.MetadataServiceTemplate.TLS, + DefaultConfigOverwrite: novaCell.MetadataServiceTemplate.DefaultConfigOverwrite, + MemcachedInstance: novaCell.MemcachedInstance, + APITimeout: novaCell.APITimeout, + } + + if metadataSpec.NodeSelector == nil { + metadataSpec.NodeSelector = novaCell.NodeSelector + } + + if metadataSpec.TopologyRef == nil { + metadataSpec.TopologyRef = novaCell.TopologyRef + } + + return metadataSpec +} + +// GetSecret returns the value of the NovaMetadata.Spec.Secret +func (n NovaMetadata) GetSecret() string { + return n.Spec.Secret +} + +// GetSpecTopologyRef - Returns the LastAppliedTopology Set in the Status +func (n *NovaMetadata) GetSpecTopologyRef() *topologyv1.TopoRef { + return n.Spec.TopologyRef +} + +// GetLastAppliedTopology - Returns the LastAppliedTopology Set in the Status +func (n *NovaMetadata) GetLastAppliedTopology() *topologyv1.TopoRef { + return n.Status.LastAppliedTopology +} + +// SetLastAppliedTopology - Sets the LastAppliedTopology value in the Status +func (n *NovaMetadata) SetLastAppliedTopology(topologyRef *topologyv1.TopoRef) { + n.Status.LastAppliedTopology = topologyRef +} diff --git a/apis/nova/v1beta1/novametadata_webhook.go b/apis/nova/v1beta1/novametadata_webhook.go new file mode 100644 index 000000000..de0f1cfb3 --- /dev/null +++ b/apis/nova/v1beta1/novametadata_webhook.go @@ -0,0 +1,172 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind NovaMetadata --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" +) + +// NovaMetadataDefaults - +type NovaMetadataDefaults struct { + ContainerImageURL string +} + +var novaMetadataDefaults NovaMetadataDefaults + +// log is for logging in this package. +var novametadatalog = logf.Log.WithName("novametadata-resource") + +// SetupNovaMetadataDefaults - initialize NovaMetadata spec defaults for use with either internal or external webhooks +func SetupNovaMetadataDefaults(defaults NovaMetadataDefaults) { + novaMetadataDefaults = defaults + novametadatalog.Info("NovaMetadata defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &NovaMetadata{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *NovaMetadata) Default() { + novametadatalog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this NovaMetadata spec +func (spec *NovaMetadataSpec) Default() { + if spec.ContainerImage == "" { + spec.ContainerImage = novaMetadataDefaults.ContainerImageURL + } +} + +var _ webhook.Validator = &NovaMetadata{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaMetadata) ValidateCreate() (admission.Warnings, error) { + novametadatalog.Info("validate create", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + errors = append(errors, ValidateMetadataDefaultConfigOverwrite( + basePath.Child("defaultConfigOverwrite"), + r.Spec.DefaultConfigOverwrite)...) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novametadatalog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaMetadata"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaMetadata) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novametadatalog.Info("validate update", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + oldMetadata, ok := old.(*NovaMetadata) + if !ok || oldMetadata == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novametadatalog.Info("validate update", "diff", cmp.Diff(oldMetadata, r)) + + errors = append(errors, ValidateMetadataDefaultConfigOverwrite( + basePath.Child("defaultConfigOverwrite"), + r.Spec.DefaultConfigOverwrite)...) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novametadatalog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaMetadata"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *NovaMetadata) ValidateDelete() (admission.Warnings, error) { + novametadatalog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ValidateCell0 validates cell0 Metadata template. This is expected to be called +// by higher level validation webhooks +func (r *NovaMetadataTemplate) ValidateCell0(basePath *field.Path) field.ErrorList { + var errors field.ErrorList + if *r.Enabled { + errors = append( + errors, + field.Invalid( + basePath.Child("enabled"), *r.Enabled, "should be false for cell0"), + ) + } + return errors +} + +// ValidateDefaultConfigOverwrite validates the defaultConfigOverwrite for NovaMetadataTemplate +func (r *NovaMetadataTemplate) ValidateDefaultConfigOverwrite(basePath *field.Path) field.ErrorList { + return ValidateMetadataDefaultConfigOverwrite( + basePath.Child("defaultConfigOverwrite"), + r.DefaultConfigOverwrite) +} + +// ValidateMetadataDefaultConfigOverwrite validates the defaultConfigOverwrite for NovaMetadata +func ValidateMetadataDefaultConfigOverwrite( + basePath *field.Path, + defaultConfigOverwrite map[string]string, +) field.ErrorList { + return ValidateDefaultConfigOverwrite( + basePath, defaultConfigOverwrite, []string{"api-paste.ini"}) +} + +// ValidateTopology validates the referenced TopoRef.Namespace. +func (r *NovaMetadataTemplate) ValidateTopology( + basePath *field.Path, + namespace string, +) field.ErrorList { + return topologyv1.ValidateTopologyRef( + r.TopologyRef, + *basePath.Child("topologyRef"), + namespace, + ) +} diff --git a/apis/nova/v1beta1/novanovncproxy_types.go b/apis/nova/v1beta1/novanovncproxy_types.go new file mode 100644 index 000000000..33b7f2566 --- /dev/null +++ b/apis/nova/v1beta1/novanovncproxy_types.go @@ -0,0 +1,292 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + service "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NovaNoVNCProxyTemplate defines the input parameters specified by the user to +// create a NovaNoVNCProxy via higher level CRDs. +type NovaNoVNCProxyTemplate struct { + // +kubebuilder:validation:Optional + // Enabled - Whether NovaNoVNCProxy services should be deployed and managed. + // If it is set to false then the related NovaNoVNCProxy CR will be deleted + // if exists and owned by the NovaCell. If it exist but not owned by the + // NovaCell then the NovaNoVNCProxy will not be touched. + // If it is set to true the a NovaNoVNCProxy CR will be created. + // If there is already a manually created NovaNoVNCProxy CR with the + // relevant name then the cell will not try to update that CR, instead the + // NovaCell be in error state until the manually create NovaNoVNCProxy CR + // is deleted by the operator. + Enabled *bool `json:"enabled"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of the service to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override VNCProxyOverrideSpec `json:"override,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS TLSSection `json:"tls"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` +} + +// TLSSection defines the desired state of TLS configuration +type TLSSection struct { + // +kubebuilder:validation:optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Service - Cert secret used for the nova novnc service endpoint + Service tls.GenericService `json:"service,omitempty"` + + // +kubebuilder:validation:optional + //+operator-sdk:csv:customresourcedefinitions:type=spec + // Vencrypt - cert secret containing the x509 certificate to be presented to the VNC server. + // The CommonName field should match the primary hostname of the controller node. If using a HA deployment, + // the Organization field can also be configured to a value that is common across all console proxy instances in the deployment. + // https://docs.openstack.org/nova/latest/admin/remote-console-access.html#novnc-proxy-server-configuration + Vencrypt tls.GenericService `json:"vencrypt,omitempty"` + + // +kubebuilder:validation:optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // Secret containing CA bundle + tls.Ca `json:",inline"` +} + +// VNCProxyOverrideSpec to override the generated manifest of several child resources. +type VNCProxyOverrideSpec struct { + // Override configuration for the Service created to serve traffic to the cluster. + Service *service.RoutedOverrideSpec `json:"service,omitempty"` +} + +// NovaNoVNCProxySpec defines the desired state of NovaNoVNCProxy +type NovaNoVNCProxySpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Required + // CellName is the name of the Nova Cell this novncproxy belongs to. + CellName string `json:"cellName"` + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for the nova-novncproxy service. This secret is expected to + // be generated by the nova-operator based on the information passed to the + // Nova CR. + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // ServiceUser - optional username used for this service to register in + // keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + // KeystoneAuthURL - the URL that the nova-novncproxy service can use to + // talk to keystone + KeystoneAuthURL string `json:"keystoneAuthURL"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=regionOne + // Region - the region name to use for service endpoint discovery + Region string `json:"region"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // CellDatabaseAccount - MariaDBAccount to use when accessing the cell DB + CellDatabaseAccount string `json:"cellDatabaseAccount"` + + // +kubebuilder:validation:Required + // CellDatabaseHostname - hostname to use when accessing the cell DB + CellDatabaseHostname string `json:"cellDatabaseHostname"` + + // NovaServiceBase specifies the generic fields of the service + NovaServiceBase `json:",inline"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override VNCProxyOverrideSpec `json:"override,omitempty"` + + // +kubebuilder:validation:Required + // ServiceAccount - service account name used internally to provide Nova services the default SA name + ServiceAccount string `json:"serviceAccount"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS TLSSection `json:"tls"` + + // +kubebuilder:validation:Required + // MemcachedInstance is the name of the Memcached CR that all nova service will use. + MemcachedInstance string `json:"memcachedInstance"` +} + +// NovaNoVNCProxyStatus defines the observed state of NovaNoVNCProxy +type NovaNoVNCProxyStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // ReadyCount defines the number of replicas ready from nova-novncproxy + ReadyCount int32 `json:"readyCount,omitempty"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the openstack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAppliedTopology - the last applied Topology + LastAppliedTopology *topologyv1.TopoRef `json:"lastAppliedTopology,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="NetworkAttachments",type="string",JSONPath=".spec.networkAttachments",description="NetworkAttachments" +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status" +//+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message" + +// NovaNoVNCProxy is the Schema for the novanovncproxies API +type NovaNoVNCProxy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaNoVNCProxySpec `json:"spec,omitempty"` + Status NovaNoVNCProxyStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaNoVNCProxyList contains a list of NovaNoVNCProxy +type NovaNoVNCProxyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NovaNoVNCProxy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NovaNoVNCProxy{}, &NovaNoVNCProxyList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaNoVNCProxyStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// NewNovaNoVNCProxySpec constructs a NewNovaNoVNCProxySpec +func NewNovaNoVNCProxySpec( + novaCell NovaCellSpec, +) NovaNoVNCProxySpec { + noVNCProxSpec := NovaNoVNCProxySpec{ + CellName: novaCell.CellName, + Secret: novaCell.Secret, + CellDatabaseHostname: novaCell.CellDatabaseHostname, + CellDatabaseAccount: novaCell.CellDatabaseAccount, + NovaServiceBase: NovaServiceBase{ + ContainerImage: novaCell.NoVNCContainerImageURL, + Replicas: novaCell.NoVNCProxyServiceTemplate.Replicas, + NodeSelector: novaCell.NoVNCProxyServiceTemplate.NodeSelector, + CustomServiceConfig: novaCell.NoVNCProxyServiceTemplate.CustomServiceConfig, + Resources: novaCell.NoVNCProxyServiceTemplate.Resources, + NetworkAttachments: novaCell.NoVNCProxyServiceTemplate.NetworkAttachments, + TopologyRef: novaCell.NoVNCProxyServiceTemplate.TopologyRef, + }, + KeystoneAuthURL: novaCell.KeystoneAuthURL, + ServiceUser: novaCell.ServiceUser, + Region: novaCell.Region, + ServiceAccount: novaCell.ServiceAccount, + Override: novaCell.NoVNCProxyServiceTemplate.Override, + TLS: novaCell.NoVNCProxyServiceTemplate.TLS, + MemcachedInstance: novaCell.MemcachedInstance, + } + + if noVNCProxSpec.NodeSelector == nil { + noVNCProxSpec.NodeSelector = novaCell.NodeSelector + } + + if noVNCProxSpec.TopologyRef == nil { + noVNCProxSpec.TopologyRef = novaCell.TopologyRef + } + + return noVNCProxSpec +} + +// GetSecret returns the value of the NovaMetadata.Spec.Secret +func (n NovaNoVNCProxy) GetSecret() string { + return n.Spec.Secret +} + +// GetSpecTopologyRef - Returns the LastAppliedTopology Set in the Status +func (n *NovaNoVNCProxy) GetSpecTopologyRef() *topologyv1.TopoRef { + return n.Spec.TopologyRef +} + +// GetLastAppliedTopology - Returns the LastAppliedTopology Set in the Status +func (n *NovaNoVNCProxy) GetLastAppliedTopology() *topologyv1.TopoRef { + return n.Status.LastAppliedTopology +} + +// SetLastAppliedTopology - Sets the LastAppliedTopology value in the Status +func (n *NovaNoVNCProxy) SetLastAppliedTopology(topologyRef *topologyv1.TopoRef) { + n.Status.LastAppliedTopology = topologyRef +} diff --git a/apis/nova/v1beta1/novanovncproxy_webhook.go b/apis/nova/v1beta1/novanovncproxy_webhook.go new file mode 100644 index 000000000..6d9ea0090 --- /dev/null +++ b/apis/nova/v1beta1/novanovncproxy_webhook.go @@ -0,0 +1,150 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind NovaNoVNCProxy --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation/field" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" +) + +// NovaNoVNCProxyDefaults - +type NovaNoVNCProxyDefaults struct { + ContainerImageURL string +} + +var novaNoVNCProxyDefaults NovaNoVNCProxyDefaults + +// log is for logging in this package. +var novanovncproxylog = logf.Log.WithName("novanovncproxy-resource") + +// SetupNovaNoVNCProxyDefaults - initialize NovaNoVNCProxy spec defaults for use with either internal or external webhooks +func SetupNovaNoVNCProxyDefaults(defaults NovaNoVNCProxyDefaults) { + novaNoVNCProxyDefaults = defaults + novanovncproxylog.Info("NovaNoVNCProxy defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &NovaNoVNCProxy{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *NovaNoVNCProxy) Default() { + novanovncproxylog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this NovaNoVNCProxy spec +func (spec *NovaNoVNCProxySpec) Default() { + if spec.ContainerImage == "" { + spec.ContainerImage = novaNoVNCProxyDefaults.ContainerImageURL + } +} + +var _ webhook.Validator = &NovaNoVNCProxy{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaNoVNCProxy) ValidateCreate() (admission.Warnings, error) { + novanovncproxylog.Info("validate create", "name", r.Name) + + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novanovncproxylog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaNoVNCProxy"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaNoVNCProxy) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novanovncproxylog.Info("validate update", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + + oldProxy, ok := old.(*NovaNoVNCProxy) + if !ok || oldProxy == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novanovncproxylog.Info("validate update", "diff", cmp.Diff(oldProxy, r)) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novanovncproxylog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaNoVNCProxy"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *NovaNoVNCProxy) ValidateDelete() (admission.Warnings, error) { + novanovncproxylog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ValidateCell0 validates cell0 NoVNCProxy template. This is expected to be +// called by higher level validation webhooks +func (r *NovaNoVNCProxyTemplate) ValidateCell0(basePath *field.Path) field.ErrorList { + var errors field.ErrorList + if *r.Enabled { + errors = append( + errors, + field.Invalid( + basePath.Child("enabled"), *r.Enabled, "should be false for cell0"), + ) + } + return errors +} + +// ValidateTopology validates the referenced TopoRef.Namespace. +func (r *NovaNoVNCProxyTemplate) ValidateTopology( + basePath *field.Path, + namespace string, +) field.ErrorList { + return topologyv1.ValidateTopologyRef( + r.TopologyRef, + *basePath.Child("topologyRef"), + namespace, + ) +} diff --git a/apis/nova/v1beta1/novascheduler_types.go b/apis/nova/v1beta1/novascheduler_types.go new file mode 100644 index 000000000..75d79d5f3 --- /dev/null +++ b/apis/nova/v1beta1/novascheduler_types.go @@ -0,0 +1,234 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// NovaSchedulerTemplate defines the input parameters specified by the user to +// create a NovaScheduler via higher level CRDs. +type NovaSchedulerTemplate struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of the service to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service. Setting here overrides + // any global NodeSelector settings within the Nova CR. + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,omitempty"` +} + +// NovaSchedulerSpec defines the desired state of NovaScheduler +type NovaSchedulerSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:Required + // Secret is the name of the Secret instance containing password + // information for the nova-scheduler service. This secret is expected to + // be generated by the nova-operator based on the information passed to the + // Nova CR. + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova + // ServiceUser - optional username used for this service to register in + // keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + // KeystoneAuthURL - the URL that the nova-scheduler service can use to + // talk to keystone + KeystoneAuthURL string `json:"keystoneAuthURL"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=regionOne + // Region - the region name to use for service endpoint discovery + Region string `json:"region"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=nova-api + // APIDatabaseAccount - MariaDBAccount to use when accessing the API DB + APIDatabaseAccount string `json:"apiDatabaseAccount"` + + // +kubebuilder:validation:Required + // APIDatabaseHostname - hostname to use when accessing the API DB + APIDatabaseHostname string `json:"apiDatabaseHostname"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default="nova-cell0" + // Cell0DatabaseAccount - MariaDBAccount to use when accessing the cell0 DB + Cell0DatabaseAccount string `json:"cell0DatabaseAccount"` + + // +kubebuilder:validation:Required + // Cell0DatabaseHostname - hostname to use when accessing the cell0 DB + Cell0DatabaseHostname string `json:"cell0DatabaseHostname"` + + // NovaServiceBase specifies the generic fields of the service + NovaServiceBase `json:",inline"` + + // +kubebuilder:validation:Required + // ServiceAccount - service account name used internally to provide Nova services the default SA name + ServiceAccount string `json:"serviceAccount"` + + // RegisteredCells is a map keyed by cell names that are registered in the + // nova_api database with a value that is the hash of the given cell + // configuration. + // This is used to detect when a new cell is added or an existing cell is + // reconfigured to trigger refresh of the in memory cell caches of the + // service. + RegisteredCells map[string]string `json:"registeredCells"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.Ca `json:"tls,omitempty"` + + // +kubebuilder:validation:Required + // MemcachedInstance is the name of the Memcached CR that all nova service will use. + MemcachedInstance string `json:"memcachedInstance"` +} + +// NovaSchedulerStatus defines the observed state of NovaScheduler +type NovaSchedulerStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // ReadyCount defines the number of replicas ready from nova-scheduler + ReadyCount int32 `json:"readyCount,omitempty"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + // ObservedGeneration - the most recent generation observed for this + // service. If the observed generation is less than the spec generation, + // then the controller has not processed the latest changes injected by + // the openstack-operator in the top-level CR (e.g. the ContainerImage) + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAppliedTopology - the last applied Topology + LastAppliedTopology *topologyv1.TopoRef `json:"lastAppliedTopology,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:printcolumn:name="NetworkAttachments",type="string",JSONPath=".spec.networkAttachments",description="NetworkAttachments" +//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status" +//+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message" + +// NovaScheduler is the Schema for the novaschedulers API +type NovaScheduler struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NovaSchedulerSpec `json:"spec,omitempty"` + Status NovaSchedulerStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NovaSchedulerList contains a list of NovaScheduler +type NovaSchedulerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NovaScheduler `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NovaScheduler{}, &NovaSchedulerList{}) +} + +// GetConditions returns the list of conditions from the status +func (s NovaSchedulerStatus) GetConditions() condition.Conditions { + return s.Conditions +} + +// GetSecret returns the value of the NovaScheduler.Spec.Secret +func (n NovaScheduler) GetSecret() string { + return n.Spec.Secret +} + +// GetKeystoneAuthURL returns the KeystoneAuthURL from the Spec +func (n NovaScheduler) GetKeystoneAuthURL() string { + return n.Spec.KeystoneAuthURL +} + +// GetKeystoneUser returns the Service user from the Spec +func (n NovaScheduler) GetKeystoneUser() string { + return n.Spec.ServiceUser +} + +// GetCABundleSecretName returns the TLS CA bundle name from the Spec +func (n NovaScheduler) GetCABundleSecretName() string { + return n.Spec.TLS.CaBundleSecretName +} + +// GetRegion returns the Region from the Spec +func (n NovaScheduler) GetRegion() string { + return n.Spec.Region +} + +// GetSpecTopologyRef - Returns the LastAppliedTopology Set in the Status +func (n *NovaScheduler) GetSpecTopologyRef() *topologyv1.TopoRef { + return n.Spec.TopologyRef +} + +// GetLastAppliedTopology - Returns the LastAppliedTopology Set in the Status +func (n *NovaScheduler) GetLastAppliedTopology() *topologyv1.TopoRef { + return n.Status.LastAppliedTopology +} + +// SetLastAppliedTopology - Sets the LastAppliedTopology value in the Status +func (n *NovaScheduler) SetLastAppliedTopology(topologyRef *topologyv1.TopoRef) { + n.Status.LastAppliedTopology = topologyRef +} diff --git a/apis/nova/v1beta1/novascheduler_webhook.go b/apis/nova/v1beta1/novascheduler_webhook.go new file mode 100644 index 000000000..febb47dde --- /dev/null +++ b/apis/nova/v1beta1/novascheduler_webhook.go @@ -0,0 +1,134 @@ +/* +Copyright 2023. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// Generated by: +// +// operator-sdk create webhook --group nova --version v1beta1 --kind NovaScheduler --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + + "github.com/google/go-cmp/cmp" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "k8s.io/apimachinery/pkg/util/validation/field" + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// NovaSchedulerDefaults - +type NovaSchedulerDefaults struct { + ContainerImageURL string +} + +var novaSchedulerDefaults NovaSchedulerDefaults + +// log is for logging in this package. +var novaschedulerlog = logf.Log.WithName("novascheduler-resource") + +// SetupNovaSchedulerDefaults - initialize NovaScheduler spec defaults for use with either internal or external webhooks +func SetupNovaSchedulerDefaults(defaults NovaSchedulerDefaults) { + novaSchedulerDefaults = defaults + novaschedulerlog.Info("NovaScheduler defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &NovaScheduler{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *NovaScheduler) Default() { + novaschedulerlog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this NovaScheduler spec +func (spec *NovaSchedulerSpec) Default() { + if spec.ContainerImage == "" { + spec.ContainerImage = novaSchedulerDefaults.ContainerImageURL + } +} + +var _ webhook.Validator = &NovaScheduler{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaScheduler) ValidateCreate() (admission.Warnings, error) { + novaschedulerlog.Info("validate create", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novaschedulerlog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaScheduler"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *NovaScheduler) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + novaschedulerlog.Info("validate update", "name", r.Name) + errors := field.ErrorList{} + basePath := field.NewPath("spec") + + oldScheduler, ok := old.(*NovaScheduler) + if !ok || oldScheduler == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + novaschedulerlog.Info("validate update", "diff", cmp.Diff(oldScheduler, r)) + + errors = append(errors, topologyv1.ValidateTopologyRef( + r.Spec.TopologyRef, *basePath.Child("topologyRef"), r.Namespace)...) + + if len(errors) != 0 { + novaschedulerlog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "nova.openstack.org", Kind: "NovaScheduler"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *NovaScheduler) ValidateDelete() (admission.Warnings, error) { + novaschedulerlog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// ValidateTopology validates the referenced TopoRef.Namespace. +func (r *NovaSchedulerTemplate) ValidateTopology( + basePath *field.Path, + namespace string, +) field.ErrorList { + return topologyv1.ValidateTopologyRef( + r.TopologyRef, + *basePath.Child("topologyRef"), + namespace, + ) +} diff --git a/apis/nova/v1beta1/zz_generated.deepcopy.go b/apis/nova/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 000000000..0da528695 --- /dev/null +++ b/apis/nova/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,1817 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + rabbitmqv1beta1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1" + topologyv1beta1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIOverrideSpec) DeepCopyInto(out *APIOverrideSpec) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = make(map[service.Endpoint]service.RoutedOverrideSpec, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIOverrideSpec. +func (in *APIOverrideSpec) DeepCopy() *APIOverrideSpec { + if in == nil { + return nil + } + out := new(APIOverrideSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthSpec) DeepCopyInto(out *AuthSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthSpec. +func (in *AuthSpec) DeepCopy() *AuthSpec { + if in == nil { + return nil + } + out := new(AuthSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetadataOverrideSpec) DeepCopyInto(out *MetadataOverrideSpec) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(service.OverrideSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetadataOverrideSpec. +func (in *MetadataOverrideSpec) DeepCopy() *MetadataOverrideSpec { + if in == nil { + return nil + } + out := new(MetadataOverrideSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Nova) DeepCopyInto(out *Nova) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Nova. +func (in *Nova) DeepCopy() *Nova { + if in == nil { + return nil + } + out := new(Nova) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Nova) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaAPI) DeepCopyInto(out *NovaAPI) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaAPI. +func (in *NovaAPI) DeepCopy() *NovaAPI { + if in == nil { + return nil + } + out := new(NovaAPI) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaAPI) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaAPIDefaults) DeepCopyInto(out *NovaAPIDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaAPIDefaults. +func (in *NovaAPIDefaults) DeepCopy() *NovaAPIDefaults { + if in == nil { + return nil + } + out := new(NovaAPIDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaAPIList) DeepCopyInto(out *NovaAPIList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NovaAPI, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaAPIList. +func (in *NovaAPIList) DeepCopy() *NovaAPIList { + if in == nil { + return nil + } + out := new(NovaAPIList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaAPIList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaAPISpec) DeepCopyInto(out *NovaAPISpec) { + *out = *in + in.NovaServiceBase.DeepCopyInto(&out.NovaServiceBase) + in.Override.DeepCopyInto(&out.Override) + if in.RegisteredCells != nil { + in, out := &in.RegisteredCells, &out.RegisteredCells + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.TLS.DeepCopyInto(&out.TLS) + if in.DefaultConfigOverwrite != nil { + in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaAPISpec. +func (in *NovaAPISpec) DeepCopy() *NovaAPISpec { + if in == nil { + return nil + } + out := new(NovaAPISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaAPIStatus) DeepCopyInto(out *NovaAPIStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.LastAppliedTopology != nil { + in, out := &in.LastAppliedTopology, &out.LastAppliedTopology + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaAPIStatus. +func (in *NovaAPIStatus) DeepCopy() *NovaAPIStatus { + if in == nil { + return nil + } + out := new(NovaAPIStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaAPITemplate) DeepCopyInto(out *NovaAPITemplate) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + if in.DefaultConfigOverwrite != nil { + in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Override.DeepCopyInto(&out.Override) + in.TLS.DeepCopyInto(&out.TLS) + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaAPITemplate. +func (in *NovaAPITemplate) DeepCopy() *NovaAPITemplate { + if in == nil { + return nil + } + out := new(NovaAPITemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCell) DeepCopyInto(out *NovaCell) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCell. +func (in *NovaCell) DeepCopy() *NovaCell { + if in == nil { + return nil + } + out := new(NovaCell) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaCell) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCellDBPurge) DeepCopyInto(out *NovaCellDBPurge) { + *out = *in + if in.Schedule != nil { + in, out := &in.Schedule, &out.Schedule + *out = new(string) + **out = **in + } + if in.ArchiveAge != nil { + in, out := &in.ArchiveAge, &out.ArchiveAge + *out = new(int) + **out = **in + } + if in.PurgeAge != nil { + in, out := &in.PurgeAge, &out.PurgeAge + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCellDBPurge. +func (in *NovaCellDBPurge) DeepCopy() *NovaCellDBPurge { + if in == nil { + return nil + } + out := new(NovaCellDBPurge) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCellDefaults) DeepCopyInto(out *NovaCellDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCellDefaults. +func (in *NovaCellDefaults) DeepCopy() *NovaCellDefaults { + if in == nil { + return nil + } + out := new(NovaCellDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCellImages) DeepCopyInto(out *NovaCellImages) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCellImages. +func (in *NovaCellImages) DeepCopy() *NovaCellImages { + if in == nil { + return nil + } + out := new(NovaCellImages) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCellList) DeepCopyInto(out *NovaCellList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NovaCell, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCellList. +func (in *NovaCellList) DeepCopy() *NovaCellList { + if in == nil { + return nil + } + out := new(NovaCellList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaCellList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCellSpec) DeepCopyInto(out *NovaCellSpec) { + *out = *in + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + in.ConductorServiceTemplate.DeepCopyInto(&out.ConductorServiceTemplate) + in.MetadataServiceTemplate.DeepCopyInto(&out.MetadataServiceTemplate) + in.NoVNCProxyServiceTemplate.DeepCopyInto(&out.NoVNCProxyServiceTemplate) + if in.NovaComputeTemplates != nil { + in, out := &in.NovaComputeTemplates, &out.NovaComputeTemplates + *out = make(map[string]NovaComputeTemplate, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.TLS = in.TLS + in.DBPurge.DeepCopyInto(&out.DBPurge) + out.NovaCellImages = in.NovaCellImages + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCellSpec. +func (in *NovaCellSpec) DeepCopy() *NovaCellSpec { + if in == nil { + return nil + } + out := new(NovaCellSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCellStatus) DeepCopyInto(out *NovaCellStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NovaComputesStatus != nil { + in, out := &in.NovaComputesStatus, &out.NovaComputesStatus + *out = make(map[string]NovaComputeCellStatus, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCellStatus. +func (in *NovaCellStatus) DeepCopy() *NovaCellStatus { + if in == nil { + return nil + } + out := new(NovaCellStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCellTemplate) DeepCopyInto(out *NovaCellTemplate) { + *out = *in + out.MessagingBus = in.MessagingBus + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } + in.ConductorServiceTemplate.DeepCopyInto(&out.ConductorServiceTemplate) + in.MetadataServiceTemplate.DeepCopyInto(&out.MetadataServiceTemplate) + in.NoVNCProxyServiceTemplate.DeepCopyInto(&out.NoVNCProxyServiceTemplate) + if in.NovaComputeTemplates != nil { + in, out := &in.NovaComputeTemplates, &out.NovaComputeTemplates + *out = make(map[string]NovaComputeTemplate, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + in.DBPurge.DeepCopyInto(&out.DBPurge) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCellTemplate. +func (in *NovaCellTemplate) DeepCopy() *NovaCellTemplate { + if in == nil { + return nil + } + out := new(NovaCellTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaCompute) DeepCopyInto(out *NovaCompute) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaCompute. +func (in *NovaCompute) DeepCopy() *NovaCompute { + if in == nil { + return nil + } + out := new(NovaCompute) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaCompute) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaComputeCellStatus) DeepCopyInto(out *NovaComputeCellStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaComputeCellStatus. +func (in *NovaComputeCellStatus) DeepCopy() *NovaComputeCellStatus { + if in == nil { + return nil + } + out := new(NovaComputeCellStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaComputeDefaults) DeepCopyInto(out *NovaComputeDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaComputeDefaults. +func (in *NovaComputeDefaults) DeepCopy() *NovaComputeDefaults { + if in == nil { + return nil + } + out := new(NovaComputeDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaComputeList) DeepCopyInto(out *NovaComputeList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NovaCompute, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaComputeList. +func (in *NovaComputeList) DeepCopy() *NovaComputeList { + if in == nil { + return nil + } + out := new(NovaComputeList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaComputeList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaComputeSpec) DeepCopyInto(out *NovaComputeSpec) { + *out = *in + in.NovaServiceBase.DeepCopyInto(&out.NovaServiceBase) + out.TLS = in.TLS + if in.DefaultConfigOverwrite != nil { + in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaComputeSpec. +func (in *NovaComputeSpec) DeepCopy() *NovaComputeSpec { + if in == nil { + return nil + } + out := new(NovaComputeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaComputeStatus) DeepCopyInto(out *NovaComputeStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.LastAppliedTopology != nil { + in, out := &in.LastAppliedTopology, &out.LastAppliedTopology + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaComputeStatus. +func (in *NovaComputeStatus) DeepCopy() *NovaComputeStatus { + if in == nil { + return nil + } + out := new(NovaComputeStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaComputeTemplate) DeepCopyInto(out *NovaComputeTemplate) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } + if in.DefaultConfigOverwrite != nil { + in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaComputeTemplate. +func (in *NovaComputeTemplate) DeepCopy() *NovaComputeTemplate { + if in == nil { + return nil + } + out := new(NovaComputeTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaConductor) DeepCopyInto(out *NovaConductor) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaConductor. +func (in *NovaConductor) DeepCopy() *NovaConductor { + if in == nil { + return nil + } + out := new(NovaConductor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaConductor) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaConductorDefaults) DeepCopyInto(out *NovaConductorDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaConductorDefaults. +func (in *NovaConductorDefaults) DeepCopy() *NovaConductorDefaults { + if in == nil { + return nil + } + out := new(NovaConductorDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaConductorList) DeepCopyInto(out *NovaConductorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NovaConductor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaConductorList. +func (in *NovaConductorList) DeepCopy() *NovaConductorList { + if in == nil { + return nil + } + out := new(NovaConductorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaConductorList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaConductorSpec) DeepCopyInto(out *NovaConductorSpec) { + *out = *in + in.NovaServiceBase.DeepCopyInto(&out.NovaServiceBase) + out.TLS = in.TLS + in.DBPurge.DeepCopyInto(&out.DBPurge) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaConductorSpec. +func (in *NovaConductorSpec) DeepCopy() *NovaConductorSpec { + if in == nil { + return nil + } + out := new(NovaConductorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaConductorStatus) DeepCopyInto(out *NovaConductorStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.LastAppliedTopology != nil { + in, out := &in.LastAppliedTopology, &out.LastAppliedTopology + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaConductorStatus. +func (in *NovaConductorStatus) DeepCopy() *NovaConductorStatus { + if in == nil { + return nil + } + out := new(NovaConductorStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaConductorTemplate) DeepCopyInto(out *NovaConductorTemplate) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaConductorTemplate. +func (in *NovaConductorTemplate) DeepCopy() *NovaConductorTemplate { + if in == nil { + return nil + } + out := new(NovaConductorTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaDefaults) DeepCopyInto(out *NovaDefaults) { + *out = *in + out.NovaCellDefaults = in.NovaCellDefaults +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaDefaults. +func (in *NovaDefaults) DeepCopy() *NovaDefaults { + if in == nil { + return nil + } + out := new(NovaDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaImages) DeepCopyInto(out *NovaImages) { + *out = *in + out.NovaCellImages = in.NovaCellImages +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaImages. +func (in *NovaImages) DeepCopy() *NovaImages { + if in == nil { + return nil + } + out := new(NovaImages) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaList) DeepCopyInto(out *NovaList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Nova, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaList. +func (in *NovaList) DeepCopy() *NovaList { + if in == nil { + return nil + } + out := new(NovaList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaMetadata) DeepCopyInto(out *NovaMetadata) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaMetadata. +func (in *NovaMetadata) DeepCopy() *NovaMetadata { + if in == nil { + return nil + } + out := new(NovaMetadata) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaMetadata) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaMetadataDefaults) DeepCopyInto(out *NovaMetadataDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaMetadataDefaults. +func (in *NovaMetadataDefaults) DeepCopy() *NovaMetadataDefaults { + if in == nil { + return nil + } + out := new(NovaMetadataDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaMetadataList) DeepCopyInto(out *NovaMetadataList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NovaMetadata, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaMetadataList. +func (in *NovaMetadataList) DeepCopy() *NovaMetadataList { + if in == nil { + return nil + } + out := new(NovaMetadataList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaMetadataList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaMetadataSpec) DeepCopyInto(out *NovaMetadataSpec) { + *out = *in + in.NovaServiceBase.DeepCopyInto(&out.NovaServiceBase) + in.Override.DeepCopyInto(&out.Override) + if in.RegisteredCells != nil { + in, out := &in.RegisteredCells, &out.RegisteredCells + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.TLS.DeepCopyInto(&out.TLS) + if in.DefaultConfigOverwrite != nil { + in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaMetadataSpec. +func (in *NovaMetadataSpec) DeepCopy() *NovaMetadataSpec { + if in == nil { + return nil + } + out := new(NovaMetadataSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaMetadataStatus) DeepCopyInto(out *NovaMetadataStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.LastAppliedTopology != nil { + in, out := &in.LastAppliedTopology, &out.LastAppliedTopology + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaMetadataStatus. +func (in *NovaMetadataStatus) DeepCopy() *NovaMetadataStatus { + if in == nil { + return nil + } + out := new(NovaMetadataStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaMetadataTemplate) DeepCopyInto(out *NovaMetadataTemplate) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + if in.DefaultConfigOverwrite != nil { + in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Override.DeepCopyInto(&out.Override) + in.TLS.DeepCopyInto(&out.TLS) + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaMetadataTemplate. +func (in *NovaMetadataTemplate) DeepCopy() *NovaMetadataTemplate { + if in == nil { + return nil + } + out := new(NovaMetadataTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaNoVNCProxy) DeepCopyInto(out *NovaNoVNCProxy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaNoVNCProxy. +func (in *NovaNoVNCProxy) DeepCopy() *NovaNoVNCProxy { + if in == nil { + return nil + } + out := new(NovaNoVNCProxy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaNoVNCProxy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaNoVNCProxyDefaults) DeepCopyInto(out *NovaNoVNCProxyDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaNoVNCProxyDefaults. +func (in *NovaNoVNCProxyDefaults) DeepCopy() *NovaNoVNCProxyDefaults { + if in == nil { + return nil + } + out := new(NovaNoVNCProxyDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaNoVNCProxyList) DeepCopyInto(out *NovaNoVNCProxyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NovaNoVNCProxy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaNoVNCProxyList. +func (in *NovaNoVNCProxyList) DeepCopy() *NovaNoVNCProxyList { + if in == nil { + return nil + } + out := new(NovaNoVNCProxyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaNoVNCProxyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaNoVNCProxySpec) DeepCopyInto(out *NovaNoVNCProxySpec) { + *out = *in + in.NovaServiceBase.DeepCopyInto(&out.NovaServiceBase) + in.Override.DeepCopyInto(&out.Override) + in.TLS.DeepCopyInto(&out.TLS) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaNoVNCProxySpec. +func (in *NovaNoVNCProxySpec) DeepCopy() *NovaNoVNCProxySpec { + if in == nil { + return nil + } + out := new(NovaNoVNCProxySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaNoVNCProxyStatus) DeepCopyInto(out *NovaNoVNCProxyStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.LastAppliedTopology != nil { + in, out := &in.LastAppliedTopology, &out.LastAppliedTopology + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaNoVNCProxyStatus. +func (in *NovaNoVNCProxyStatus) DeepCopy() *NovaNoVNCProxyStatus { + if in == nil { + return nil + } + out := new(NovaNoVNCProxyStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaNoVNCProxyTemplate) DeepCopyInto(out *NovaNoVNCProxyTemplate) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Override.DeepCopyInto(&out.Override) + in.TLS.DeepCopyInto(&out.TLS) + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaNoVNCProxyTemplate. +func (in *NovaNoVNCProxyTemplate) DeepCopy() *NovaNoVNCProxyTemplate { + if in == nil { + return nil + } + out := new(NovaNoVNCProxyTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaScheduler) DeepCopyInto(out *NovaScheduler) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaScheduler. +func (in *NovaScheduler) DeepCopy() *NovaScheduler { + if in == nil { + return nil + } + out := new(NovaScheduler) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaScheduler) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaSchedulerDefaults) DeepCopyInto(out *NovaSchedulerDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSchedulerDefaults. +func (in *NovaSchedulerDefaults) DeepCopy() *NovaSchedulerDefaults { + if in == nil { + return nil + } + out := new(NovaSchedulerDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaSchedulerList) DeepCopyInto(out *NovaSchedulerList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NovaScheduler, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSchedulerList. +func (in *NovaSchedulerList) DeepCopy() *NovaSchedulerList { + if in == nil { + return nil + } + out := new(NovaSchedulerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NovaSchedulerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaSchedulerSpec) DeepCopyInto(out *NovaSchedulerSpec) { + *out = *in + in.NovaServiceBase.DeepCopyInto(&out.NovaServiceBase) + if in.RegisteredCells != nil { + in, out := &in.RegisteredCells, &out.RegisteredCells + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.TLS = in.TLS +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSchedulerSpec. +func (in *NovaSchedulerSpec) DeepCopy() *NovaSchedulerSpec { + if in == nil { + return nil + } + out := new(NovaSchedulerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaSchedulerStatus) DeepCopyInto(out *NovaSchedulerStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.LastAppliedTopology != nil { + in, out := &in.LastAppliedTopology, &out.LastAppliedTopology + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSchedulerStatus. +func (in *NovaSchedulerStatus) DeepCopy() *NovaSchedulerStatus { + if in == nil { + return nil + } + out := new(NovaSchedulerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaSchedulerTemplate) DeepCopyInto(out *NovaSchedulerTemplate) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSchedulerTemplate. +func (in *NovaSchedulerTemplate) DeepCopy() *NovaSchedulerTemplate { + if in == nil { + return nil + } + out := new(NovaSchedulerTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaServiceBase) DeepCopyInto(out *NovaServiceBase) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaServiceBase. +func (in *NovaServiceBase) DeepCopy() *NovaServiceBase { + if in == nil { + return nil + } + out := new(NovaServiceBase) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaSpec) DeepCopyInto(out *NovaSpec) { + *out = *in + in.NovaSpecCore.DeepCopyInto(&out.NovaSpecCore) + out.NovaImages = in.NovaImages +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSpec. +func (in *NovaSpec) DeepCopy() *NovaSpec { + if in == nil { + return nil + } + out := new(NovaSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaSpecCore) DeepCopyInto(out *NovaSpecCore) { + *out = *in + out.MessagingBus = in.MessagingBus + if in.CellTemplates != nil { + in, out := &in.CellTemplates, &out.CellTemplates + *out = make(map[string]NovaCellTemplate, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } + out.PasswordSelectors = in.PasswordSelectors + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + in.APIServiceTemplate.DeepCopyInto(&out.APIServiceTemplate) + in.SchedulerServiceTemplate.DeepCopyInto(&out.SchedulerServiceTemplate) + in.MetadataServiceTemplate.DeepCopyInto(&out.MetadataServiceTemplate) + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } + if in.NotificationsBusInstance != nil { + in, out := &in.NotificationsBusInstance, &out.NotificationsBusInstance + *out = new(string) + **out = **in + } + if in.NotificationsBus != nil { + in, out := &in.NotificationsBus, &out.NotificationsBus + *out = new(rabbitmqv1beta1.RabbitMqConfig) + **out = **in + } + out.Auth = in.Auth +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaSpecCore. +func (in *NovaSpecCore) DeepCopy() *NovaSpecCore { + if in == nil { + return nil + } + out := new(NovaSpecCore) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NovaStatus) DeepCopyInto(out *NovaStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RegisteredCells != nil { + in, out := &in.RegisteredCells, &out.RegisteredCells + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.DiscoveredCells != nil { + in, out := &in.DiscoveredCells, &out.DiscoveredCells + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NovaStatus. +func (in *NovaStatus) DeepCopy() *NovaStatus { + if in == nil { + return nil + } + out := new(NovaStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PasswordSelector) DeepCopyInto(out *PasswordSelector) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSelector. +func (in *PasswordSelector) DeepCopy() *PasswordSelector { + if in == nil { + return nil + } + out := new(PasswordSelector) + 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.Service.DeepCopyInto(&out.Service) + in.Vencrypt.DeepCopyInto(&out.Vencrypt) + 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 +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VNCProxyOverrideSpec) DeepCopyInto(out *VNCProxyOverrideSpec) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(service.RoutedOverrideSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VNCProxyOverrideSpec. +func (in *VNCProxyOverrideSpec) DeepCopy() *VNCProxyOverrideSpec { + if in == nil { + return nil + } + out := new(VNCProxyOverrideSpec) + in.DeepCopyInto(out) + return out +} diff --git a/apis/placement/v1beta1/groupversion_info.go b/apis/placement/v1beta1/groupversion_info.go new file mode 100644 index 000000000..4f0c92c13 --- /dev/null +++ b/apis/placement/v1beta1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1beta1 contains API Schema definitions for the placement v1beta1 API group +// +kubebuilder:object:generate=true +// +groupName=placement.openstack.org +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "placement.openstack.org", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/placement/v1beta1/placementapi_types.go b/apis/placement/v1beta1/placementapi_types.go new file mode 100644 index 000000000..3354e3b50 --- /dev/null +++ b/apis/placement/v1beta1/placementapi_types.go @@ -0,0 +1,259 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + "github.com/openstack-k8s-operators/lib-common/modules/common/util" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +const ( + // DbSyncHash hash + DbSyncHash = "dbsync" + + // DeploymentHash hash used to detect changes + DeploymentHash = "deployment" + + // Container image fall-back defaults + + // PlacementAPIContainerImage is the fall-back container image for PlacementAPI + PlacementAPIContainerImage = "quay.io/podified-antelope-centos9/openstack-placement-api:current-podified" +) + +// PlacementAPISpec defines the desired state of PlacementAPI +type PlacementAPISpec struct { + PlacementAPISpecCore `json:",inline"` + + // +kubebuilder:validation:Required + // PlacementAPI Container Image URL (will be set to environmental default if empty) + ContainerImage string `json:"containerImage"` +} + +// PlacementAPISpecCore - +type PlacementAPISpecCore struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default=60 + // +kubebuilder:validation:Minimum=10 + // APITimeout for HAProxy, Apache + APITimeout int `json:"apiTimeout"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=placement + // ServiceUser - optional username used for this service to register in keystone + ServiceUser string `json:"serviceUser"` + + // +kubebuilder:validation:Required + // MariaDB instance name + // Right now required by the maridb-operator to get the credentials from the instance to create the DB + // Might not be required in future + DatabaseInstance string `json:"databaseInstance"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=placement + // DatabaseAccount - name of MariaDBAccount which will be used to connect. + DatabaseAccount string `json:"databaseAccount"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=1 + // +kubebuilder:validation:Maximum=32 + // +kubebuilder:validation:Minimum=0 + // Replicas of placement API to run + Replicas *int32 `json:"replicas"` + + // +kubebuilder:validation:Required + // Secret containing OpenStack password information for placement PlacementPassword + Secret string `json:"secret"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default={service: PlacementPassword} + // PasswordSelectors - Selectors to identify the DB and ServiceUser password from the Secret + PasswordSelectors PasswordSelector `json:"passwordSelectors"` + + // +kubebuilder:validation:Optional + // NodeSelector to target subset of worker nodes running this service + NodeSelector *map[string]string `json:"nodeSelector,omitempty"` + + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + // PreserveJobs - do not delete jobs after they finished e.g. to check logs + PreserveJobs bool `json:"preserveJobs"` + + // +kubebuilder:validation:Optional + // CustomServiceConfig - customize the service config using this parameter to change service defaults, + // or overwrite rendered information using raw OpenStack config format. The content gets added to + // to /etc//.conf.d directory as custom.conf file. + CustomServiceConfig string `json:"customServiceConfig"` + + // +kubebuilder:validation:Optional + // DefaultConfigOverwrite - interface to overwrite default config files like policy.yaml. + DefaultConfigOverwrite map[string]string `json:"defaultConfigOverwrite,omitempty"` + + // +kubebuilder:validation:Optional + // Resources - Compute Resources required by this service (Limits/Requests). + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + + // +kubebuilder:validation:Optional + // NetworkAttachments is a list of NetworkAttachment resource names to expose the services to the given network + NetworkAttachments []string `json:"networkAttachments,omitempty"` + + // +kubebuilder:validation:Optional + // Override, provides the ability to override the generated manifest of several child resources. + Override APIOverrideSpec `json:"override,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // TLS - Parameters related to the TLS + TLS tls.API `json:"tls,omitempty"` + + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // Auth - Parameters related to authentication + Auth AuthSpec `json:"auth,omitempty"` + + // +kubebuilder:validation:Optional + // TopologyRef to apply the Topology defined by the associated CR referenced + // by name + TopologyRef *topologyv1.TopoRef `json:"topologyRef,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"` +} + +// AuthSpec defines authentication parameters +type AuthSpec struct { + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec + // ApplicationCredentialSecret - Secret containing Application Credential ID and Secret + ApplicationCredentialSecret string `json:"applicationCredentialSecret,omitempty"` +} + +// PasswordSelector to identify the DB and AdminUser password from the Secret +type PasswordSelector struct { + // +kubebuilder:validation:Optional + // +kubebuilder:default="PlacementPassword" + // Service - Selector to get the service user password from the Secret + Service string `json:"service"` +} + +// PlacementAPIStatus defines the observed state of PlacementAPI +type PlacementAPIStatus struct { + // ReadyCount of placement API instances + ReadyCount int32 `json:"readyCount,omitempty"` + + // Map of hashes to track e.g. job status + Hash map[string]string `json:"hash,omitempty"` + + // Conditions + Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"` + + // Placement Database Hostname + DatabaseHostname string `json:"databaseHostname,omitempty"` + + // NetworkAttachments status of the deployment pods + NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"` + + //ObservedGeneration - the most recent generation observed for this service. If the observed generation is less than the spec generation, then the controller has not processed the latest changes. + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // LastAppliedTopology - the last applied Topology + LastAppliedTopology *topologyv1.TopoRef `json:"lastAppliedTopology,omitempty"` +} + +// PlacementAPI is the Schema for the placementapis API +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="NetworkAttachments",type="string",JSONPath=".spec.networkAttachments",description="NetworkAttachments" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status" +// +kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message" +type PlacementAPI struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PlacementAPISpec `json:"spec,omitempty"` + Status PlacementAPIStatus `json:"status,omitempty"` +} + +// PlacementAPIList contains a list of PlacementAPI +// +kubebuilder:object:root=true +type PlacementAPIList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PlacementAPI `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PlacementAPI{}, &PlacementAPIList{}) +} + +// IsReady - returns true if PlacementAPI is reconciled successfully +func (instance PlacementAPI) IsReady() bool { + return instance.Status.Conditions.IsTrue(condition.ReadyCondition) +} + +// RbacConditionsSet - set the conditions for the rbac object +func (instance PlacementAPI) RbacConditionsSet(c *condition.Condition) { + instance.Status.Conditions.Set(c) +} + +// RbacNamespace - return the namespace +func (instance PlacementAPI) RbacNamespace() string { + return instance.Namespace +} + +// RbacResourceName - return the name to be used for rbac objects (serviceaccount, role, rolebinding) +func (instance PlacementAPI) RbacResourceName() string { + return "placement-" + instance.Name +} + +// SetupDefaults - initializes any CRD field defaults based on environment variables (the defaulting mechanism itself is implemented via webhooks) +func SetupDefaults() { + // Acquire environmental defaults and initialize Placement defaults with them + placementDefaults := PlacementAPIDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_PLACEMENT_API_IMAGE_URL_DEFAULT", PlacementAPIContainerImage), + APITimeout: 60, + } + + SetupPlacementAPIDefaults(placementDefaults) +} + +// GetSecret returns the value of the Nova.Spec.Secret +func (instance PlacementAPI) GetSecret() string { + return instance.Spec.Secret +} + +// ValidateTopology - +func (instance *PlacementAPISpecCore) ValidateTopology( + basePath *field.Path, + namespace string, +) field.ErrorList { + var allErrs field.ErrorList + allErrs = append(allErrs, topologyv1.ValidateTopologyRef( + instance.TopologyRef, + *basePath.Child("topologyRef"), namespace)...) + return allErrs +} diff --git a/apis/placement/v1beta1/placementapi_webhook.go b/apis/placement/v1beta1/placementapi_webhook.go new file mode 100644 index 000000000..c14982277 --- /dev/null +++ b/apis/placement/v1beta1/placementapi_webhook.go @@ -0,0 +1,199 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Generated by: +// +// operator-sdk create webhook --group placement --version v1beta1 --kind PlacementAPI --programmatic-validation --defaulting +// + +package v1beta1 + +import ( + "fmt" + + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/validation/field" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// PlacementAPIDefaults - +type PlacementAPIDefaults struct { + ContainerImageURL string + APITimeout int +} + +var placementAPIDefaults PlacementAPIDefaults + +// log is for logging in this package. +var placementapilog = logf.Log.WithName("placementapi-resource") + +// SetupPlacementAPIDefaults - initialize PlacementAPI spec defaults for use with either internal or external webhooks +func SetupPlacementAPIDefaults(defaults PlacementAPIDefaults) { + placementAPIDefaults = defaults + placementapilog.Info("PlacementAPI defaults initialized", "defaults", defaults) +} + +var _ webhook.Defaulter = &PlacementAPI{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *PlacementAPI) Default() { + placementapilog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this PlacementAPI spec +func (spec *PlacementAPISpec) Default() { + if spec.ContainerImage == "" { + spec.ContainerImage = placementAPIDefaults.ContainerImageURL + } + if spec.APITimeout == 0 { + spec.APITimeout = placementAPIDefaults.APITimeout + } +} + +// Default - set defaults for this PlacementAPI core spec (this version is used by the OpenStackControlplane webhook) +func (spec *PlacementAPISpecCore) Default() { + // nothing here yet +} + +var _ webhook.Validator = &PlacementAPI{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *PlacementAPI) ValidateCreate() (admission.Warnings, error) { + placementapilog.Info("validate create", "name", r.Name) + + errors := r.Spec.ValidateCreate(field.NewPath("spec"), r.Namespace) + if len(errors) != 0 { + placementapilog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "placement.openstack.org", Kind: "PlacementAPI"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *PlacementAPI) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + placementapilog.Info("validate update", "name", r.Name) + oldPlacement, ok := old.(*PlacementAPI) + if !ok || oldPlacement == nil { + return nil, apierrors.NewInternalError(fmt.Errorf("unable to convert existing object")) + } + + errors := r.Spec.ValidateUpdate(oldPlacement.Spec, field.NewPath("spec"), r.Namespace) + if len(errors) != 0 { + placementapilog.Info("validation failed", "name", r.Name) + return nil, apierrors.NewInvalid( + schema.GroupKind{Group: "placement.openstack.org", Kind: "PlacementAPI"}, + r.Name, errors) + } + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *PlacementAPI) ValidateDelete() (admission.Warnings, error) { + placementapilog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +func (r PlacementAPISpec) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList { + return r.PlacementAPISpecCore.ValidateCreate(basePath, namespace) +} + +func (r PlacementAPISpec) ValidateUpdate(old PlacementAPISpec, basePath *field.Path, namespace string) field.ErrorList { + return r.PlacementAPISpecCore.ValidateCreate(basePath, namespace) +} + +func (r PlacementAPISpecCore) ValidateCreate(basePath *field.Path, namespace string) field.ErrorList { + var allErrs field.ErrorList + + // validate the service override key is valid + allErrs = append(allErrs, service.ValidateRoutedOverrides(basePath.Child("override").Child("service"), r.Override.Service)...) + + allErrs = append(allErrs, ValidateDefaultConfigOverwrite(basePath, r.DefaultConfigOverwrite)...) + + // When a TopologyRef CR is referenced, fail if a different Namespace is + // referenced because is not supported + allErrs = append(allErrs, r.ValidateTopology(basePath, namespace)...) + + return allErrs +} + +func (r PlacementAPISpecCore) ValidateUpdate(old PlacementAPISpecCore, basePath *field.Path, namespace string) field.ErrorList { + var allErrs field.ErrorList + + // validate the service override key is valid + allErrs = append(allErrs, service.ValidateRoutedOverrides(basePath.Child("override").Child("service"), r.Override.Service)...) + + allErrs = append(allErrs, ValidateDefaultConfigOverwrite(basePath, r.DefaultConfigOverwrite)...) + + // When a TopologyRef CR is referenced, fail if a different Namespace is + // referenced because is not supported + allErrs = append(allErrs, r.ValidateTopology(basePath, namespace)...) + + return allErrs +} + +func ValidateDefaultConfigOverwrite( + basePath *field.Path, + validateConfigOverwrite map[string]string, +) field.ErrorList { + var errors field.ErrorList + for requested := range validateConfigOverwrite { + if requested != "policy.yaml" { + errors = append( + errors, + field.Invalid( + basePath.Child("defaultConfigOverwrite"), + requested, + "Only the following keys are valid: policy.yaml", + ), + ) + } + } + return errors +} + +// SetDefaultRouteAnnotations sets HAProxy timeout values of the route +func (spec *PlacementAPISpecCore) SetDefaultRouteAnnotations(annotations map[string]string) { + const haProxyAnno = "haproxy.router.openshift.io/timeout" + // Use a custom annotation to flag when the operator has set the default HAProxy timeout + // With the annotation func determines when to overwrite existing HAProxy timeout with the APITimeout + const placementAnno = "api.placement.openstack.org/timeout" + valPlacementAPI, okPlacementAPI := annotations[placementAnno] + valHAProxy, okHAProxy := annotations[haProxyAnno] + // Human operator set the HAProxy timeout manually + if !okPlacementAPI && okHAProxy { + return + } + // Human operator modified the HAProxy timeout manually without removing the Placemen flag + if okPlacementAPI && okHAProxy && valPlacementAPI != valHAProxy { + delete(annotations, placementAnno) + placementapilog.Info("Human operator modified the HAProxy timeout manually without removing the Placement flag. Deleting the Placement flag to ensure proper configuration.") + return + } + timeout := fmt.Sprintf("%ds", spec.APITimeout) + annotations[placementAnno] = timeout + annotations[haProxyAnno] = timeout +} diff --git a/apis/placement/v1beta1/zz_generated.deepcopy.go b/apis/placement/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 000000000..8c4b13be3 --- /dev/null +++ b/apis/placement/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,273 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + topologyv1beta1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" + "github.com/openstack-k8s-operators/lib-common/modules/common/condition" + "github.com/openstack-k8s-operators/lib-common/modules/common/service" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIOverrideSpec) DeepCopyInto(out *APIOverrideSpec) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = make(map[service.Endpoint]service.RoutedOverrideSpec, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIOverrideSpec. +func (in *APIOverrideSpec) DeepCopy() *APIOverrideSpec { + if in == nil { + return nil + } + out := new(APIOverrideSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthSpec) DeepCopyInto(out *AuthSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthSpec. +func (in *AuthSpec) DeepCopy() *AuthSpec { + if in == nil { + return nil + } + out := new(AuthSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PasswordSelector) DeepCopyInto(out *PasswordSelector) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSelector. +func (in *PasswordSelector) DeepCopy() *PasswordSelector { + if in == nil { + return nil + } + out := new(PasswordSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementAPI) DeepCopyInto(out *PlacementAPI) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementAPI. +func (in *PlacementAPI) DeepCopy() *PlacementAPI { + if in == nil { + return nil + } + out := new(PlacementAPI) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlacementAPI) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementAPIDefaults) DeepCopyInto(out *PlacementAPIDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementAPIDefaults. +func (in *PlacementAPIDefaults) DeepCopy() *PlacementAPIDefaults { + if in == nil { + return nil + } + out := new(PlacementAPIDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementAPIList) DeepCopyInto(out *PlacementAPIList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PlacementAPI, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementAPIList. +func (in *PlacementAPIList) DeepCopy() *PlacementAPIList { + if in == nil { + return nil + } + out := new(PlacementAPIList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PlacementAPIList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementAPISpec) DeepCopyInto(out *PlacementAPISpec) { + *out = *in + in.PlacementAPISpecCore.DeepCopyInto(&out.PlacementAPISpecCore) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementAPISpec. +func (in *PlacementAPISpec) DeepCopy() *PlacementAPISpec { + if in == nil { + return nil + } + out := new(PlacementAPISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementAPISpecCore) DeepCopyInto(out *PlacementAPISpecCore) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + out.PasswordSelectors = in.PasswordSelectors + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = new(map[string]string) + if **in != nil { + in, out := *in, *out + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + if in.DefaultConfigOverwrite != nil { + in, out := &in.DefaultConfigOverwrite, &out.DefaultConfigOverwrite + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Override.DeepCopyInto(&out.Override) + in.TLS.DeepCopyInto(&out.TLS) + out.Auth = in.Auth + if in.TopologyRef != nil { + in, out := &in.TopologyRef, &out.TopologyRef + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementAPISpecCore. +func (in *PlacementAPISpecCore) DeepCopy() *PlacementAPISpecCore { + if in == nil { + return nil + } + out := new(PlacementAPISpecCore) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PlacementAPIStatus) DeepCopyInto(out *PlacementAPIStatus) { + *out = *in + if in.Hash != nil { + in, out := &in.Hash, &out.Hash + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(condition.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.NetworkAttachments != nil { + in, out := &in.NetworkAttachments, &out.NetworkAttachments + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + inVal := (*in)[key] + in, out := &inVal, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.LastAppliedTopology != nil { + in, out := &in.LastAppliedTopology, &out.LastAppliedTopology + *out = new(topologyv1beta1.TopoRef) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementAPIStatus. +func (in *PlacementAPIStatus) DeepCopy() *PlacementAPIStatus { + if in == nil { + return nil + } + out := new(PlacementAPIStatus) + in.DeepCopyInto(out) + return out +} diff --git a/cmd/main.go b/cmd/main.go index 16fb0ce7d..a8e5171e9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -48,7 +48,7 @@ import ( keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common/operator" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" diff --git a/config/crd/bases/placement.openstack.org_placementapis.yaml b/config/crd/bases/placement.openstack.org_placementapis.yaml new file mode 100644 index 000000000..73ef0098b --- /dev/null +++ b/config/crd/bases/placement.openstack.org_placementapis.yaml @@ -0,0 +1,502 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: placementapis.placement.openstack.org +spec: + group: placement.openstack.org + names: + kind: PlacementAPI + listKind: PlacementAPIList + plural: placementapis + singular: placementapi + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: NetworkAttachments + jsonPath: .spec.networkAttachments + name: NetworkAttachments + type: string + - description: Status + jsonPath: .status.conditions[0].status + name: Status + type: string + - description: Message + jsonPath: .status.conditions[0].message + name: Message + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: PlacementAPI is the Schema for the placementapis API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: PlacementAPISpec defines the desired state of PlacementAPI + properties: + apiTimeout: + default: 60 + description: APITimeout for HAProxy, Apache + minimum: 10 + type: integer + auth: + description: Auth - Parameters related to authentication + properties: + applicationCredentialSecret: + description: ApplicationCredentialSecret - Secret containing Application + Credential ID and Secret + type: string + type: object + containerImage: + description: PlacementAPI Container Image URL (will be set to environmental + default if empty) + type: string + customServiceConfig: + description: |- + CustomServiceConfig - customize the service config using this parameter to change service defaults, + or overwrite rendered information using raw OpenStack config format. The content gets added to + to /etc//.conf.d directory as custom.conf file. + type: string + databaseAccount: + default: placement + description: DatabaseAccount - name of MariaDBAccount which will be + used to connect. + type: string + databaseInstance: + description: |- + MariaDB instance name + Right now required by the maridb-operator to get the credentials from the instance to create the DB + Might not be required in future + type: string + defaultConfigOverwrite: + additionalProperties: + type: string + description: DefaultConfigOverwrite - interface to overwrite default + config files like policy.yaml. + type: object + networkAttachments: + description: NetworkAttachments is a list of NetworkAttachment resource + names to expose the services to the given network + items: + type: string + type: array + nodeSelector: + additionalProperties: + type: string + 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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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: https://kubernetes.io/docs/concepts/overview/working-with-objects/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 + x-kubernetes-list-type: atomic + 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: + service: PlacementPassword + description: PasswordSelectors - Selectors to identify the DB and + ServiceUser password from the Secret + properties: + service: + default: PlacementPassword + description: Service - Selector to get the service user password + from the Secret + type: string + type: object + preserveJobs: + default: false + description: PreserveJobs - do not delete jobs after they finished + e.g. to check logs + type: boolean + replicas: + default: 1 + description: Replicas of placement API to run + format: int32 + maximum: 32 + minimum: 0 + type: integer + resources: + description: |- + Resources - Compute Resources required by this service (Limits/Requests). + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + secret: + description: Secret containing OpenStack password information for + placement PlacementPassword + type: string + serviceUser: + default: placement + description: ServiceUser - optional username used for this service + to register in keystone + type: string + tls: + description: TLS - Parameters related to the TLS + properties: + api: + description: API tls type which encapsulates for API services + properties: + internal: + description: Internal GenericService - holds the secret for + the internal endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + public: + description: Public GenericService - holds the secret for + the public endpoint + properties: + secretName: + description: SecretName - holding the cert, key for the + service + type: string + type: object + type: object + caBundleSecretName: + description: CaBundleSecretName - holding the CA certs in a pre-created + bundle file + type: string + type: object + topologyRef: + description: |- + TopologyRef to apply the Topology defined by the associated CR referenced + by name + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + required: + - containerImage + - databaseInstance + - secret + type: object + status: + description: PlacementAPIStatus defines the observed state of PlacementAPI + properties: + conditions: + description: Conditions + items: + description: Condition defines an observation of a API resource + operational state. + properties: + lastTransitionTime: + description: |- + Last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when + the API field changed is acceptable. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition + in CamelCase. + type: string + severity: + description: |- + Severity provides a classification of Reason code, so the current situation is immediately + understandable and could act accordingly. + It is meant for situations where Status=False and it should be indicated if it is just + informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue + and no actions to automatically resolve the issue can/should be done). + For conditions where Status=Unknown or Status=True the Severity should be SeverityNone. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition in CamelCase. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + databaseHostname: + description: Placement Database Hostname + type: string + hash: + additionalProperties: + type: string + description: Map of hashes to track e.g. job status + type: object + lastAppliedTopology: + description: LastAppliedTopology - the last applied Topology + properties: + name: + description: Name - The Topology CR name that the Service references + type: string + namespace: + description: |- + Namespace - The Namespace to fetch the Topology CR referenced + NOTE: Namespace currently points by default to the same namespace where + the Service is deployed. Customizing the namespace is not supported and + webhooks prevent editing this field to a value different from the + current project + type: string + type: object + networkAttachments: + additionalProperties: + items: + type: string + type: array + description: NetworkAttachments status of the deployment pods + type: object + observedGeneration: + description: ObservedGeneration - the most recent generation observed + for this service. If the observed generation is less than the spec + generation, then the controller has not processed the latest changes. + format: int64 + type: integer + readyCount: + description: ReadyCount of placement API instances + format: int32 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/go.mod b/go.mod index 49d1cc228..7930d49a5 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20260128142552-e2c25eccae5a github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260128142552-e2c25eccae5a github.com/openstack-k8s-operators/mariadb-operator/api v0.6.1-0.20260120155328-e04f52e73f01 - github.com/openstack-k8s-operators/nova-operator/api v0.0.0-20221209164002-f9e6b9363961 + github.com/openstack-k8s-operators/nova-operator/apis v0.0.0-20221209164002-f9e6b9363961 go.uber.org/zap v1.27.1 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 gopkg.in/ini.v1 v1.67.0 @@ -117,7 +117,7 @@ require ( sigs.k8s.io/yaml v1.6.0 // indirect ) -replace github.com/openstack-k8s-operators/nova-operator/api => ./api +replace github.com/openstack-k8s-operators/nova-operator/apis => ./apis //allow-merging // mschuppert: map to latest commit from release-4.18 tag // must consistent within modules and service operators diff --git a/internal/controller/common.go b/internal/controller/common.go index dfe5bfa12..6c896b75a 100644 --- a/internal/controller/common.go +++ b/internal/controller/common.go @@ -40,7 +40,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" gophercloud "github.com/gophercloud/gophercloud/v2" diff --git a/internal/controller/nova_controller.go b/internal/controller/nova_controller.go index b6c751792..61df46c3e 100644 --- a/internal/controller/nova_controller.go +++ b/internal/controller/nova_controller.go @@ -54,7 +54,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/tls" util "github.com/openstack-k8s-operators/lib-common/modules/common/util" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" "github.com/openstack-k8s-operators/nova-operator/internal/novaapi" diff --git a/internal/controller/novaapi_controller.go b/internal/controller/novaapi_controller.go index bcfd69b49..d04f13061 100644 --- a/internal/controller/novaapi_controller.go +++ b/internal/controller/novaapi_controller.go @@ -53,7 +53,7 @@ import ( topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" "github.com/openstack-k8s-operators/nova-operator/internal/novaapi" diff --git a/internal/controller/novacell_controller.go b/internal/controller/novacell_controller.go index 029ed0438..bd09d5272 100644 --- a/internal/controller/novacell_controller.go +++ b/internal/controller/novacell_controller.go @@ -46,7 +46,7 @@ import ( util "github.com/openstack-k8s-operators/lib-common/modules/common/util" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // NovaCellReconciler reconciles a NovaCell object diff --git a/internal/controller/novacompute_controller.go b/internal/controller/novacompute_controller.go index 23a174172..236b771f5 100644 --- a/internal/controller/novacompute_controller.go +++ b/internal/controller/novacompute_controller.go @@ -47,7 +47,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/tls" util "github.com/openstack-k8s-operators/lib-common/modules/common/util" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" "github.com/openstack-k8s-operators/nova-operator/internal/novacompute" diff --git a/internal/controller/novaconductor_controller.go b/internal/controller/novaconductor_controller.go index 7b0b28d64..3f9efaa43 100644 --- a/internal/controller/novaconductor_controller.go +++ b/internal/controller/novaconductor_controller.go @@ -53,7 +53,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/tls" util "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/novaconductor" ) diff --git a/internal/controller/novametadata_controller.go b/internal/controller/novametadata_controller.go index 119adc053..74372cac1 100644 --- a/internal/controller/novametadata_controller.go +++ b/internal/controller/novametadata_controller.go @@ -53,7 +53,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/tls" util "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" "github.com/openstack-k8s-operators/nova-operator/internal/novametadata" k8s_errors "k8s.io/apimachinery/pkg/api/errors" diff --git a/internal/controller/novanovncproxy_controller.go b/internal/controller/novanovncproxy_controller.go index a44707dd6..b087ea529 100644 --- a/internal/controller/novanovncproxy_controller.go +++ b/internal/controller/novanovncproxy_controller.go @@ -50,7 +50,7 @@ import ( "github.com/openstack-k8s-operators/lib-common/modules/common/tls" util "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" "github.com/openstack-k8s-operators/nova-operator/internal/novncproxy" k8s_errors "k8s.io/apimachinery/pkg/api/errors" diff --git a/internal/controller/novascheduler_controller.go b/internal/controller/novascheduler_controller.go index 7122a0efe..4ace5578b 100644 --- a/internal/controller/novascheduler_controller.go +++ b/internal/controller/novascheduler_controller.go @@ -51,7 +51,7 @@ import ( mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" "github.com/openstack-k8s-operators/nova-operator/internal/novascheduler" ) diff --git a/internal/nova/celldelete.go b/internal/nova/celldelete.go index 59239cf11..2f91824d8 100644 --- a/internal/nova/celldelete.go +++ b/internal/nova/celldelete.go @@ -8,7 +8,7 @@ import ( "k8s.io/utils/ptr" "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // CellDeleteJob creates a Kubernetes job to delete a Nova cell diff --git a/internal/nova/cellmapping.go b/internal/nova/cellmapping.go index 6ef3bda94..11bea589d 100644 --- a/internal/nova/cellmapping.go +++ b/internal/nova/cellmapping.go @@ -7,7 +7,7 @@ import ( "k8s.io/utils/ptr" "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // CellMappingJob creates a Kubernetes job to create Nova cell mappings diff --git a/internal/nova/host_discover.go b/internal/nova/host_discover.go index 2828eef07..4ac2f0346 100644 --- a/internal/nova/host_discover.go +++ b/internal/nova/host_discover.go @@ -17,7 +17,7 @@ package nova import ( env "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" diff --git a/internal/novaapi/deployment.go b/internal/novaapi/deployment.go index 2f32b6639..61d49c047 100644 --- a/internal/novaapi/deployment.go +++ b/internal/novaapi/deployment.go @@ -24,7 +24,7 @@ import ( env "github.com/openstack-k8s-operators/lib-common/modules/common/env" "github.com/openstack-k8s-operators/lib-common/modules/common/service" "github.com/openstack-k8s-operators/lib-common/modules/common/tls" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" appsv1 "k8s.io/api/apps/v1" diff --git a/internal/novacompute/deployment.go b/internal/novacompute/deployment.go index 4e058fa39..203cbc603 100644 --- a/internal/novacompute/deployment.go +++ b/internal/novacompute/deployment.go @@ -21,7 +21,7 @@ import ( common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" diff --git a/internal/novaconductor/dbpurge.go b/internal/novaconductor/dbpurge.go index 18ecf83cc..5a45d0e9f 100644 --- a/internal/novaconductor/dbpurge.go +++ b/internal/novaconductor/dbpurge.go @@ -12,7 +12,7 @@ import ( memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" ) diff --git a/internal/novaconductor/dbsync.go b/internal/novaconductor/dbsync.go index a9e1f2d78..369c0b5f5 100644 --- a/internal/novaconductor/dbsync.go +++ b/internal/novaconductor/dbsync.go @@ -17,7 +17,7 @@ limitations under the License. package novaconductor import ( - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" diff --git a/internal/novaconductor/deployment.go b/internal/novaconductor/deployment.go index 31bf7af99..0889e3290 100644 --- a/internal/novaconductor/deployment.go +++ b/internal/novaconductor/deployment.go @@ -22,7 +22,7 @@ import ( common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" appsv1 "k8s.io/api/apps/v1" diff --git a/internal/novametadata/deployment.go b/internal/novametadata/deployment.go index 6eccf3b95..f276ed1df 100644 --- a/internal/novametadata/deployment.go +++ b/internal/novametadata/deployment.go @@ -22,7 +22,7 @@ import ( common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" appsv1 "k8s.io/api/apps/v1" diff --git a/internal/novascheduler/deployment.go b/internal/novascheduler/deployment.go index e3fad7f92..5bc10bdec 100644 --- a/internal/novascheduler/deployment.go +++ b/internal/novascheduler/deployment.go @@ -21,7 +21,7 @@ import ( common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1" diff --git a/internal/novncproxy/deployment.go b/internal/novncproxy/deployment.go index 325dfe244..11b10ab48 100644 --- a/internal/novncproxy/deployment.go +++ b/internal/novncproxy/deployment.go @@ -22,7 +22,7 @@ import ( common "github.com/openstack-k8s-operators/lib-common/modules/common" affinity "github.com/openstack-k8s-operators/lib-common/modules/common/affinity" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" "github.com/openstack-k8s-operators/nova-operator/internal/nova" appsv1 "k8s.io/api/apps/v1" diff --git a/internal/webhook/v1beta1/nova_webhook.go b/internal/webhook/v1beta1/nova_webhook.go index 8e42f9c05..68f320d13 100644 --- a/internal/webhook/v1beta1/nova_webhook.go +++ b/internal/webhook/v1beta1/nova_webhook.go @@ -28,7 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/internal/webhook/v1beta1/novaapi_webhook.go b/internal/webhook/v1beta1/novaapi_webhook.go index 0d43f734f..3e587d194 100644 --- a/internal/webhook/v1beta1/novaapi_webhook.go +++ b/internal/webhook/v1beta1/novaapi_webhook.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/internal/webhook/v1beta1/novacell_webhook.go b/internal/webhook/v1beta1/novacell_webhook.go index ffbb8ab2e..aaf332bc8 100644 --- a/internal/webhook/v1beta1/novacell_webhook.go +++ b/internal/webhook/v1beta1/novacell_webhook.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/internal/webhook/v1beta1/novacompute_webhook.go b/internal/webhook/v1beta1/novacompute_webhook.go index 832e6a416..c15b2f333 100644 --- a/internal/webhook/v1beta1/novacompute_webhook.go +++ b/internal/webhook/v1beta1/novacompute_webhook.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/internal/webhook/v1beta1/novaconductor_webhook.go b/internal/webhook/v1beta1/novaconductor_webhook.go index dfbc559bc..1dfdf27cf 100644 --- a/internal/webhook/v1beta1/novaconductor_webhook.go +++ b/internal/webhook/v1beta1/novaconductor_webhook.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/internal/webhook/v1beta1/novametadata_webhook.go b/internal/webhook/v1beta1/novametadata_webhook.go index 5dd2e8207..772f00f06 100644 --- a/internal/webhook/v1beta1/novametadata_webhook.go +++ b/internal/webhook/v1beta1/novametadata_webhook.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/internal/webhook/v1beta1/novanovncproxy_webhook.go b/internal/webhook/v1beta1/novanovncproxy_webhook.go index ab6ef86d1..a772c2209 100644 --- a/internal/webhook/v1beta1/novanovncproxy_webhook.go +++ b/internal/webhook/v1beta1/novanovncproxy_webhook.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/internal/webhook/v1beta1/novascheduler_webhook.go b/internal/webhook/v1beta1/novascheduler_webhook.go index 25475e18e..3f3d5dea2 100644 --- a/internal/webhook/v1beta1/novascheduler_webhook.go +++ b/internal/webhook/v1beta1/novascheduler_webhook.go @@ -27,7 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" - novav1beta1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1beta1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) // nolint:unused diff --git a/test/functional/base_test.go b/test/functional/base_test.go index 9a305a0ef..ef2c97f77 100644 --- a/test/functional/base_test.go +++ b/test/functional/base_test.go @@ -36,7 +36,7 @@ import ( keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/test/functional/nova_compute_ironic_controller_test.go b/test/functional/nova_compute_ironic_controller_test.go index 21eedb00c..9b82baf2b 100644 --- a/test/functional/nova_compute_ironic_controller_test.go +++ b/test/functional/nova_compute_ironic_controller_test.go @@ -32,7 +32,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) var _ = Describe("NovaCompute controller", func() { diff --git a/test/functional/nova_controller_test.go b/test/functional/nova_controller_test.go index 37f83656b..6e6e8da36 100644 --- a/test/functional/nova_controller_test.go +++ b/test/functional/nova_controller_test.go @@ -35,7 +35,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" controllers "github.com/openstack-k8s-operators/nova-operator/internal/controller" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/test/functional/nova_metadata_controller_test.go b/test/functional/nova_metadata_controller_test.go index 103c710e8..34732537e 100644 --- a/test/functional/nova_metadata_controller_test.go +++ b/test/functional/nova_metadata_controller_test.go @@ -36,7 +36,7 @@ import ( topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" ) var _ = Describe("NovaMetadata controller", func() { diff --git a/test/functional/nova_multicell_test.go b/test/functional/nova_multicell_test.go index 53bc5ec5e..969f441b4 100644 --- a/test/functional/nova_multicell_test.go +++ b/test/functional/nova_multicell_test.go @@ -26,7 +26,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" controllers "github.com/openstack-k8s-operators/nova-operator/internal/controller" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/test/functional/nova_novncproxy_test.go b/test/functional/nova_novncproxy_test.go index b3dfb8a40..38e30ea33 100644 --- a/test/functional/nova_novncproxy_test.go +++ b/test/functional/nova_novncproxy_test.go @@ -29,7 +29,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" diff --git a/test/functional/nova_reconfiguration_test.go b/test/functional/nova_reconfiguration_test.go index 664312cc9..290235b94 100644 --- a/test/functional/nova_reconfiguration_test.go +++ b/test/functional/nova_reconfiguration_test.go @@ -31,7 +31,7 @@ import ( topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1" condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" diff --git a/test/functional/nova_scheduler_test.go b/test/functional/nova_scheduler_test.go index ab81750e6..108d7319e 100644 --- a/test/functional/nova_scheduler_test.go +++ b/test/functional/nova_scheduler_test.go @@ -30,7 +30,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" diff --git a/test/functional/novaapi_controller_test.go b/test/functional/novaapi_controller_test.go index 08dda27cd..89b64acb2 100644 --- a/test/functional/novaapi_controller_test.go +++ b/test/functional/novaapi_controller_test.go @@ -32,7 +32,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" diff --git a/test/functional/novacell_controller_test.go b/test/functional/novacell_controller_test.go index cd7141896..1d1610ee3 100644 --- a/test/functional/novacell_controller_test.go +++ b/test/functional/novacell_controller_test.go @@ -28,7 +28,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/service" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" controllers "github.com/openstack-k8s-operators/nova-operator/internal/controller" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" diff --git a/test/functional/novaconductor_controller_test.go b/test/functional/novaconductor_controller_test.go index 748cb9f74..a3b42d1ca 100644 --- a/test/functional/novaconductor_controller_test.go +++ b/test/functional/novaconductor_controller_test.go @@ -30,7 +30,7 @@ import ( condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition" "github.com/openstack-k8s-operators/lib-common/modules/common/util" mariadbv1 "github.com/openstack-k8s-operators/mariadb-operator/api/v1beta1" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" diff --git a/test/functional/suite_test.go b/test/functional/suite_test.go index c20ac2c41..cddd726fc 100644 --- a/test/functional/suite_test.go +++ b/test/functional/suite_test.go @@ -55,7 +55,7 @@ import ( 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" - novav1 "github.com/openstack-k8s-operators/nova-operator/api/v1beta1" + novav1 "github.com/openstack-k8s-operators/nova-operator/apis/nova/v1beta1" controllers "github.com/openstack-k8s-operators/nova-operator/internal/controller" webhookv1beta1 "github.com/openstack-k8s-operators/nova-operator/internal/webhook/v1beta1"