diff --git a/CHANGELOG.md b/CHANGELOG.md
index a14a2be7..70fad3d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,14 @@ All notable changes to this project will be documented in this file.
 
 ### Added
 
+- Generate OLM bundle ([#645])
+
+[#645]: https://github.com/stackabletech/commons-operator/pull/645
+
+## [23.1.0] - 2023-01-23
+
+### Added
+
 - Log aggregation added ([#588]).
 
 [#588]: https://github.com/stackabletech/zookeeper-operator/pull/588
diff --git a/deploy/experimental/olm/README.md b/deploy/experimental/olm/README.md
deleted file mode 100644
index edae7cde..00000000
--- a/deploy/experimental/olm/README.md
+++ /dev/null
@@ -1,120 +0,0 @@
-# OLM installation files
-
-The following steps describe how to install the Stackable operator for Apache Zookeeper using the [Operator Lifecycle Manager](https://olm.operatorframework.io/) (OLM).
-
-It specifically installs the version 0.10.0 of the operator. Installing additional versions in the future requires generating new bundle images and updating the catalog as described below.
-
-## Usage
-
-Prerequisite is of course a running OpenShift cluster.
-
-First, install the operator using OLM:
-
-    kubectl apply -f catalog-source.yaml \
-    -f operator-group.yaml \
-    -f subscription.yaml
-
-Then, install the operator dependencies with Helm:
-
-    helm install secret-operator stackable/secret-operator
-    helm install commons-operator stackable/commons-operator
-
-And finally, create an Apache Zookeeper cluster:
-
-    kubectl create -f examples/simple-zookeeper-cluster.yaml
-
-NOTE: The `kuttl` tests don't work because they themselves require SCCs which are not available.
-
-## OLM packaging requirements
-
-- An [OpenShift](https://developers.redhat.com/products/openshift-local/overview) cluster.
-- [opm](https://github.com/operator-framework/operator-registry/)
-- docker and kubectl
-- `kubeadmin` access
-
-It was tested with:
-
-    $ crc version
-    WARN A new version (2.5.1) has been published on https://developers.redhat.com/content-gateway/file/pub/openshift-v4/clients/crc/2.5.1/crc-linux-amd64.tar.xz
-    CRC version: 2.4.1+b877358
-    OpenShift version: 4.10.14
-    Podman version: 4.0.2
-
-    $ oc version
-    Client Version: 4.10.14
-    Server Version: 4.10.14
-    Kubernetes Version: v1.23.5+b463d71
-
-    $ opm version
-    Version: version.Version{OpmVersion:"v1.23.2", GitCommit:"82505333", BuildDate:"2022-07-04T13:45:39Z", GoOs:"linux", GoArch:"amd64"}
-
-## Open questions
-
-- OLM [doesn't support DaemonSet(s)](https://github.com/operator-framework/operator-lifecycle-manager/issues/1022) and we need them for the secret-operator. Currently we can deploy the secret-operator using Helm but this means we cannot configure the [required](https://olm.operatorframework.io/docs/tasks/creating-operator-manifests/#required-apis) apis of the Zookeeper bundle. What are the consequences for publishing and certification ?
-- Here we create a catalog for a single operator. We probably want a catalog for all Stackable operators in the future but this will get large very quickly. Figure out how to handle this. Especially figure out what happens with new versions of the same operator.
-- OLM cannot create SecurityContextConstraints objects. The Zookeeper cluster (not the operator) cannot run with the default `restricted` SCC. The current solution is to use the `hostmount-anyuid` SCC for the `zookeeper-clusterrole`. Will this pass the certification process ?
-- Everything (catalog, subscription, etc) is installed in the `stackable-operators` namespace. Is this a good idea ?
-- The Subscription object uses `installPlanApproval: Automatic` which means the operator is updated automatically for every new version. Is this a good idea?
-
-See the [OLM documentation](https://olm.operatorframework.io/docs/tasks/) for details.
-
-## Build and publish operator bundle image
-
-### Generate operator bundle
-
-    opm alpha bundle generate --directory manifests --package zookeeper-operator --output-dir bundle --channels stable --default stable
-
-### Build bundle image
-
-    docker build -t docker.stackable.tech/stackable/zookeeper-operator-bundle:latest -f bundle.Dockerfile .
-    docker push docker.stackable.tech/stackable/zookeeper-operator-bundle:latest
-
-### Validate bundle image
-
-    opm alpha bundle validate --tag docker.stackable.tech/stackable/zookeeper-operator-bundle:latest --image-builder docker
-
-## Create catalog
-
-    mkdir catalog
-
-    opm generate dockerfile catalog
-
-NOTE: with the command below we can add the Stackable logo as icon.
-
-    opm init zookeeper-operator \
-    --default-channel=preview \
-    --description=./README.md \
-    --output yaml > catalog/operator.yaml
-
-    cat << EOF >> catalog/operator.yaml
-    ---
-    schema: olm.channel
-    package: zookeeper-operator
-    name: preview
-    entries:
-    - name: zookeeper-operator.v0.10.0
-    EOF
-
-NOTE: the command below generates yaml with broken indentation. I had to manually fix it.
-
-    opm render docker.stackable.tech/stackable/zookeeper-operator-bundle:latest --output=yaml >> catalog/operator.yaml
-
-    opm validate catalog
-
-The catalog is correct if the command above returns successfully without any message. If the catalog doesn't validate, the operator will not install.
-
-    docker build .  -f catalog.Dockerfile -t docker.stackable.tech/stackable/zookeeper-operator-catalog:latest
-    docker push docker.stackable.tech/stackable/zookeeper-operator-catalog:latest
-
-## Install catalog
-
-    kubectl apply -f catalog-source.yaml
-
-## List available operators
-
-    kubectl get packagemanifest -n stackable-operators
-
-## Install operator
-
-    kubectl apply -f operator-group.yaml
-    kubectl apply -f subscription.yaml
diff --git a/deploy/experimental/olm/catalog-source.yaml b/deploy/experimental/olm/catalog-source.yaml
deleted file mode 100644
index 5d2fde48..00000000
--- a/deploy/experimental/olm/catalog-source.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-kind: Namespace
-apiVersion: v1
-metadata:
-  name: stackable-operators
-  labels:
-    name: stackable-operators
----
-apiVersion: operators.coreos.com/v1alpha1
-kind: CatalogSource
-metadata:
-  name: zookeeper-operator-catalog
-  namespace: stackable-operators
-spec:
-  sourceType: grpc
-  image: docker.stackable.tech/stackable/zookeeper-operator-catalog:latest
-  displayName: Stackable Catalog
-  publisher: Stackable GmbH
-  updateStrategy:
-    registryPoll:
-      interval: 10m
diff --git a/deploy/experimental/olm/catalog.Dockerfile b/deploy/experimental/olm/catalog.Dockerfile
deleted file mode 100644
index 550c7bc9..00000000
--- a/deploy/experimental/olm/catalog.Dockerfile
+++ /dev/null
@@ -1,14 +0,0 @@
-# The base image is expected to contain
-# /bin/opm (with a serve subcommand) and /bin/grpc_health_probe
-FROM quay.io/operator-framework/opm:latest
-
-# Configure the entrypoint and command
-ENTRYPOINT ["/bin/opm"]
-CMD ["serve", "/configs"]
-
-# Copy declarative config root into image at /configs
-COPY catalog /configs
-
-# Set DC-specific label for the location of the DC root directory
-# in the image
-LABEL operators.operatorframework.io.index.configs.v1=/configs
diff --git a/deploy/experimental/olm/manifests/zookeepercluster.crd.yaml b/deploy/experimental/olm/manifests/zookeepercluster.crd.yaml
deleted file mode 100644
index 6c1a0453..00000000
--- a/deploy/experimental/olm/manifests/zookeepercluster.crd.yaml
+++ /dev/null
@@ -1,350 +0,0 @@
----
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  name: zookeeperclusters.zookeeper.stackable.tech
-spec:
-  group: zookeeper.stackable.tech
-  names:
-    categories: []
-    kind: ZookeeperCluster
-    plural: zookeeperclusters
-    shortNames:
-      - zk
-    singular: zookeepercluster
-  scope: Namespaced
-  versions:
-    - additionalPrinterColumns: []
-      name: v1alpha1
-      schema:
-        openAPIV3Schema:
-          description: "Auto-generated derived type for ZookeeperClusterSpec via `CustomResource`"
-          properties:
-            spec:
-              description: A cluster of ZooKeeper nodes
-              properties:
-                config:
-                  nullable: true
-                  properties:
-                    clientAuthentication:
-                      description: "Only affects client connections. This setting controls: - If clients need to authenticate themselves against the server via TLS - Which ca.crt to use when validating the provided client certs Defaults to `None`"
-                      nullable: true
-                      properties:
-                        authenticationClass:
-                          type: string
-                      required:
-                        - authenticationClass
-                      type: object
-                    quorumTlsSecretClass:
-                      description: "Only affects quorum communication. Use mutual verification between Zookeeper Nodes (mandatory). This setting controls: - Which cert the servers should use to authenticate themselves against other servers - Which ca.crt to use when validating the other server"
-                      type: string
-                    tls:
-                      description: "Only affects client connections. This setting controls: - If TLS encryption is used at all - Which cert the servers should use to authenticate themselves against the client Defaults to `TlsSecretClass` { secret_class: \"tls\".to_string() }."
-                      nullable: true
-                      properties:
-                        secretClass:
-                          type: string
-                      required:
-                        - secretClass
-                      type: object
-                  type: object
-                servers:
-                  nullable: true
-                  properties:
-                    cliOverrides:
-                      additionalProperties:
-                        type: string
-                      default: {}
-                      type: object
-                    config:
-                      default: {}
-                      properties:
-                        initLimit:
-                          format: uint32
-                          minimum: 0.0
-                          nullable: true
-                          type: integer
-                        myidOffset:
-                          format: uint16
-                          minimum: 0.0
-                          nullable: true
-                          type: integer
-                        resources:
-                          nullable: true
-                          properties:
-                            cpu:
-                              default:
-                                min: ~
-                                max: ~
-                              properties:
-                                max:
-                                  description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                  nullable: true
-                                  type: string
-                                min:
-                                  description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                  nullable: true
-                                  type: string
-                              type: object
-                            memory:
-                              properties:
-                                limit:
-                                  description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                  nullable: true
-                                  type: string
-                                runtimeLimits:
-                                  type: object
-                              type: object
-                            storage:
-                              properties:
-                                data:
-                                  default:
-                                    capacity: ~
-                                  properties:
-                                    capacity:
-                                      description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                      nullable: true
-                                      type: string
-                                    selectors:
-                                      description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
-                                      nullable: true
-                                      properties:
-                                        matchExpressions:
-                                          description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
-                                          items:
-                                            description: "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values."
-                                            properties:
-                                              key:
-                                                description: key is the label key that the selector applies to.
-                                                type: string
-                                              operator:
-                                                description: "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist."
-                                                type: string
-                                              values:
-                                                description: "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch."
-                                                items:
-                                                  type: string
-                                                type: array
-                                            required:
-                                              - key
-                                              - operator
-                                            type: object
-                                          type: array
-                                        matchLabels:
-                                          additionalProperties:
-                                            type: string
-                                          description: "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed."
-                                          type: object
-                                      type: object
-                                    storageClass:
-                                      nullable: true
-                                      type: string
-                                  type: object
-                              type: object
-                          type: object
-                        syncLimit:
-                          format: uint32
-                          minimum: 0.0
-                          nullable: true
-                          type: integer
-                        tickTime:
-                          format: uint32
-                          minimum: 0.0
-                          nullable: true
-                          type: integer
-                      type: object
-                    configOverrides:
-                      additionalProperties:
-                        additionalProperties:
-                          type: string
-                        type: object
-                      default: {}
-                      type: object
-                    envOverrides:
-                      additionalProperties:
-                        type: string
-                      default: {}
-                      type: object
-                    roleGroups:
-                      additionalProperties:
-                        properties:
-                          cliOverrides:
-                            additionalProperties:
-                              type: string
-                            default: {}
-                            type: object
-                          config:
-                            default: {}
-                            properties:
-                              initLimit:
-                                format: uint32
-                                minimum: 0.0
-                                nullable: true
-                                type: integer
-                              myidOffset:
-                                format: uint16
-                                minimum: 0.0
-                                nullable: true
-                                type: integer
-                              resources:
-                                nullable: true
-                                properties:
-                                  cpu:
-                                    default:
-                                      min: ~
-                                      max: ~
-                                    properties:
-                                      max:
-                                        description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                        nullable: true
-                                        type: string
-                                      min:
-                                        description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                        nullable: true
-                                        type: string
-                                    type: object
-                                  memory:
-                                    properties:
-                                      limit:
-                                        description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                        nullable: true
-                                        type: string
-                                      runtimeLimits:
-                                        type: object
-                                    type: object
-                                  storage:
-                                    properties:
-                                      data:
-                                        default:
-                                          capacity: ~
-                                        properties:
-                                          capacity:
-                                            description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n<quantity>        ::= <signedNumber><suffix>\n  (Note that <suffix> may be empty, from the \"\" case in <decimalSI>.)\n<digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= \"+\" | \"-\" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei\n  (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n<decimalSI>       ::= m | \"\" | k | M | G | T | P | E\n  (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n<decimalExponent> ::= \"e\" <signedNumber> | \"E\" <signedNumber>\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n  a. No precision is lost\n  b. No fractional digits will be emitted\n  c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n  1.5 will be serialized as \"1500m\"\n  1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation."
-                                            nullable: true
-                                            type: string
-                                          selectors:
-                                            description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
-                                            nullable: true
-                                            properties:
-                                              matchExpressions:
-                                                description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
-                                                items:
-                                                  description: "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values."
-                                                  properties:
-                                                    key:
-                                                      description: key is the label key that the selector applies to.
-                                                      type: string
-                                                    operator:
-                                                      description: "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist."
-                                                      type: string
-                                                    values:
-                                                      description: "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch."
-                                                      items:
-                                                        type: string
-                                                      type: array
-                                                  required:
-                                                    - key
-                                                    - operator
-                                                  type: object
-                                                type: array
-                                              matchLabels:
-                                                additionalProperties:
-                                                  type: string
-                                                description: "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed."
-                                                type: object
-                                            type: object
-                                          storageClass:
-                                            nullable: true
-                                            type: string
-                                        type: object
-                                    type: object
-                                type: object
-                              syncLimit:
-                                format: uint32
-                                minimum: 0.0
-                                nullable: true
-                                type: integer
-                              tickTime:
-                                format: uint32
-                                minimum: 0.0
-                                nullable: true
-                                type: integer
-                            type: object
-                          configOverrides:
-                            additionalProperties:
-                              additionalProperties:
-                                type: string
-                              type: object
-                            default: {}
-                            type: object
-                          envOverrides:
-                            additionalProperties:
-                              type: string
-                            default: {}
-                            type: object
-                          replicas:
-                            format: uint16
-                            minimum: 0.0
-                            nullable: true
-                            type: integer
-                          selector:
-                            description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
-                            nullable: true
-                            properties:
-                              matchExpressions:
-                                description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
-                                items:
-                                  description: "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values."
-                                  properties:
-                                    key:
-                                      description: key is the label key that the selector applies to.
-                                      type: string
-                                    operator:
-                                      description: "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist."
-                                      type: string
-                                    values:
-                                      description: "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch."
-                                      items:
-                                        type: string
-                                      type: array
-                                  required:
-                                    - key
-                                    - operator
-                                  type: object
-                                type: array
-                              matchLabels:
-                                additionalProperties:
-                                  type: string
-                                description: "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed."
-                                type: object
-                            type: object
-                        type: object
-                      type: object
-                  required:
-                    - roleGroups
-                  type: object
-                stopped:
-                  description: "Emergency stop button, if `true` then all pods are stopped without affecting configuration (as setting `replicas` to `0` would)"
-                  nullable: true
-                  type: boolean
-                version:
-                  description: Desired ZooKeeper version
-                  nullable: true
-                  type: string
-              type: object
-            status:
-              nullable: true
-              properties:
-                discoveryHash:
-                  description: An opaque value that changes every time a discovery detail does
-                  nullable: true
-                  type: string
-              type: object
-          required:
-            - spec
-          title: ZookeeperCluster
-          type: object
-      served: true
-      storage: true
-      subresources:
-        status: {}
diff --git a/deploy/experimental/olm/manifests/zookeeperznode.crd.yaml b/deploy/experimental/olm/manifests/zookeeperznode.crd.yaml
deleted file mode 100644
index 3b67faea..00000000
--- a/deploy/experimental/olm/manifests/zookeeperznode.crd.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
----
-apiVersion: apiextensions.k8s.io/v1
-kind: CustomResourceDefinition
-metadata:
-  name: zookeeperznodes.zookeeper.stackable.tech
-spec:
-  group: zookeeper.stackable.tech
-  names:
-    categories: []
-    kind: ZookeeperZnode
-    plural: zookeeperznodes
-    shortNames:
-      - zno
-      - znode
-    singular: zookeeperznode
-  scope: Namespaced
-  versions:
-    - additionalPrinterColumns: []
-      name: v1alpha1
-      schema:
-        openAPIV3Schema:
-          description: "Auto-generated derived type for ZookeeperZnodeSpec via `CustomResource`"
-          properties:
-            spec:
-              description: "A claim for a single ZooKeeper ZNode tree (filesystem node)\n\nA `ConfigMap` will automatically be created with the same name, containing the connection string in the field `ZOOKEEPER`. Each `ZookeeperZnode` gets an isolated ZNode chroot, which the `ZOOKEEPER` automatically contains. All data inside of this chroot will be deleted when the corresponding `ZookeeperZnode` is.\n\n`ZookeeperZnode` is *not* designed to manage the contents of this ZNode. Instead, it should be used to create a chroot for an installation of an application to work inside. Initializing the contents is the responsibility of the application."
-              properties:
-                clusterRef:
-                  default:
-                    name: ~
-                    namespace: ~
-                  description: "A reference to a product cluster (for example, a `ZookeeperCluster`)\n\n`namespace`'s defaulting only applies when retrieved via [`ClusterRef::namespace_relative_from`]"
-                  properties:
-                    name:
-                      description: The name of the cluster
-                      nullable: true
-                      type: string
-                    namespace:
-                      description: "The namespace of the cluster\n\nThis field is optional, and will default to the namespace of the referring object."
-                      nullable: true
-                      type: string
-                  type: object
-              type: object
-          required:
-            - spec
-          title: ZookeeperZnode
-          type: object
-      served: true
-      storage: true
-      subresources: {}
diff --git a/deploy/experimental/olm/operator-group.yaml b/deploy/experimental/olm/operator-group.yaml
deleted file mode 100644
index e443ba90..00000000
--- a/deploy/experimental/olm/operator-group.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-apiVersion: operators.coreos.com/v1
-kind: OperatorGroup
-metadata:
-  name: zookeeper-operator-group
-  namespace: stackable-operators
diff --git a/deploy/experimental/olm/subscription.yaml b/deploy/experimental/olm/subscription.yaml
deleted file mode 100644
index 0337a7c1..00000000
--- a/deploy/experimental/olm/subscription.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
----
-apiVersion: operators.coreos.com/v1alpha1
-kind: Subscription
-metadata:
-  name: stackable-subscription
-  namespace: stackable-operators # namespace-you-want-your-operator-installed-in
-spec:
-  channel: preview
-  name: zookeeper-operator
-  source: zookeeper-operator-catalog
-  sourceNamespace: stackable-operators
-  installPlanApproval: Automatic # Manual
diff --git a/deploy/experimental/olm/.gitignore b/deploy/olm/23.1.0/.gitignore
similarity index 63%
rename from deploy/experimental/olm/.gitignore
rename to deploy/olm/23.1.0/.gitignore
index ad84c8dc..741604f1 100644
--- a/deploy/experimental/olm/.gitignore
+++ b/deploy/olm/23.1.0/.gitignore
@@ -1,4 +1,4 @@
 bundle
 bundle.Dockerfile
-
+catalog.Dockerfile
 catalog
diff --git a/deploy/experimental/olm/manifests/configmap.yaml b/deploy/olm/23.1.0/manifests/configmap.yaml
similarity index 99%
rename from deploy/experimental/olm/manifests/configmap.yaml
rename to deploy/olm/23.1.0/manifests/configmap.yaml
index 53c490cc..ea59e8e7 100644
--- a/deploy/experimental/olm/manifests/configmap.yaml
+++ b/deploy/olm/23.1.0/manifests/configmap.yaml
@@ -409,4 +409,4 @@ metadata:
   labels:
     app.kubernetes.io/name: zookeeper-operator
     app.kubernetes.io/instance: zookeeper-operator
-    app.kubernetes.io/version: "0.10.0"
+    app.kubernetes.io/version: "23.1.0"
diff --git a/deploy/experimental/olm/manifests/csv.yaml b/deploy/olm/23.1.0/manifests/csv.yaml
similarity index 95%
rename from deploy/experimental/olm/manifests/csv.yaml
rename to deploy/olm/23.1.0/manifests/csv.yaml
index ae0dec83..14996a53 100644
--- a/deploy/experimental/olm/manifests/csv.yaml
+++ b/deploy/olm/23.1.0/manifests/csv.yaml
@@ -2,7 +2,7 @@
 apiVersion: operators.coreos.com/v1alpha1
 kind: ClusterServiceVersion
 metadata:
-  name: zookeeper-operator.v0.10.0
+  name: zookeeper-operator.v23.1.0
 spec:
   annotations:
     support: stackable.tech
@@ -27,7 +27,7 @@ spec:
   provider:
     name: Stackable GmbH
     url: https://stackable.tech
-  version: 0.10.0
+  version: 23.1.0
   minKubeVersion: 1.20.0
 
   installModes:
@@ -37,7 +37,7 @@ spec:
       type: SingleNamespace
     - supported: false
       type: MultiNamespace
-    - supported: true
+    - supported: false
       type: AllNamespaces
 
   customresourcedefinitions:
@@ -184,6 +184,14 @@ spec:
                 - zookeeperclusters/status
               verbs:
                 - patch
+            - apiGroups:
+                - security.openshift.io
+              resources:
+                - securitycontextconstraints
+              resourceNames:
+                - hostmount-anyuid
+              verbs:
+                - use
 
       deployments:
         - name: zookeeper-operator
@@ -206,7 +214,7 @@ spec:
                 containers:
                   - name: zookeeper-operator
                     securityContext: {}
-                    image: "docker.stackable.tech/stackable/zookeeper-operator:0.10.0"
+                    image: "docker.stackable.tech/stackable/zookeeper-operator:23.1.0"
                     imagePullPolicy: IfNotPresent
                     resources: {}
                     volumeMounts:
diff --git a/deploy/experimental/olm/manifests/roles.yaml b/deploy/olm/23.1.0/manifests/roles.yaml
similarity index 93%
rename from deploy/experimental/olm/manifests/roles.yaml
rename to deploy/olm/23.1.0/manifests/roles.yaml
index ee1fc3be..554fc9e4 100644
--- a/deploy/experimental/olm/manifests/roles.yaml
+++ b/deploy/olm/23.1.0/manifests/roles.yaml
@@ -23,6 +23,6 @@ rules:
     resources:
       - securitycontextconstraints
     resourceNames:
-      - hostmount-anyuid
+      - stackable-platform-scc
     verbs:
       - use
diff --git a/deploy/olm/23.1.0/manifests/zkcluster.yaml b/deploy/olm/23.1.0/manifests/zkcluster.yaml
new file mode 100644
index 00000000..0c2cb076
--- /dev/null
+++ b/deploy/olm/23.1.0/manifests/zkcluster.yaml
@@ -0,0 +1,839 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: zookeeperclusters.zookeeper.stackable.tech
+  annotations:
+    helm.sh/resource-policy: keep
+spec:
+  group: zookeeper.stackable.tech
+  names:
+    categories: []
+    kind: ZookeeperCluster
+    plural: zookeeperclusters
+    shortNames:
+      - zk
+    singular: zookeepercluster
+  scope: Namespaced
+  versions:
+    - additionalPrinterColumns: []
+      name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          description: Auto-generated derived type for ZookeeperClusterSpec via `CustomResource`
+          properties:
+            spec:
+              description: A cluster of ZooKeeper nodes
+              properties:
+                clusterConfig:
+                  default:
+                    authentication: []
+                    tls:
+                      quorumSecretClass: tls
+                      serverSecretClass: tls
+                  description: Global ZooKeeper cluster configuration that applies to all roles and role groups.
+                  properties:
+                    authentication:
+                      default: []
+                      description: Authentication class settings for ZooKeeper like mTLS authentication.
+                      items:
+                        properties:
+                          authenticationClass:
+                            description: |-
+                              The AuthenticationClass <https://docs.stackable.tech/home/nightly/concepts/authenticationclass.html> to use.
+
+                              ## mTLS
+
+                              Only affects client connections. This setting controls: - If clients need to authenticate themselves against the server via TLS - Which ca.crt to use when validating the provided client certs This will override the server TLS settings (if set) in `spec.clusterConfig.tls.serverSecretClass`.
+                            type: string
+                        required:
+                          - authenticationClass
+                        type: object
+                      type: array
+                    logging:
+                      description: Logging options for ZooKeeper.
+                      nullable: true
+                      properties:
+                        vectorAggregatorConfigMapName:
+                          description: Name of the Vector discovery ConfigMap. It must contain the key `ADDRESS` with the address of the Vector aggregator.
+                          nullable: true
+                          type: string
+                      type: object
+                    tls:
+                      default:
+                        quorumSecretClass: tls
+                        serverSecretClass: tls
+                      description: TLS encryption settings for ZooKeeper (server, quorum).
+                      nullable: true
+                      properties:
+                        quorumSecretClass:
+                          default: tls
+                          description: 'The <https://docs.stackable.tech/secret-operator/stable/secretclass.html> to use for internal quorum communication. Use mutual verification between Zookeeper Nodes (mandatory). This setting controls: - Which cert the servers should use to authenticate themselves against other servers - Which ca.crt to use when validating the other server Defaults to `tls`'
+                          type: string
+                        serverSecretClass:
+                          default: tls
+                          description: 'The <https://docs.stackable.tech/secret-operator/stable/secretclass.html> to use for client connections. This setting controls: - If TLS encryption is used at all - Which cert the servers should use to authenticate themselves against the client Defaults to `tls`.'
+                          nullable: true
+                          type: string
+                      type: object
+                  type: object
+                image:
+                  anyOf:
+                    - required:
+                        - custom
+                        - productVersion
+                    - required:
+                        - productVersion
+                        - stackableVersion
+                  description: Desired ZooKeeper image to use.
+                  properties:
+                    custom:
+                      description: Overwrite the docker image. Specify the full docker image name, e.g. `docker.stackable.tech/stackable/superset:1.4.1-stackable2.1.0`
+                      type: string
+                    productVersion:
+                      description: Version of the product, e.g. `1.4.1`.
+                      type: string
+                    pullPolicy:
+                      default: IfNotPresent
+                      description: '[Pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) used when pulling the Images'
+                      enum:
+                        - IfNotPresent
+                        - Always
+                        - Never
+                      type: string
+                    pullSecrets:
+                      description: '[Image pull secrets](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) to pull images from a private registry'
+                      items:
+                        description: LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.
+                        properties:
+                          name:
+                            description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
+                            type: string
+                        type: object
+                      nullable: true
+                      type: array
+                    repo:
+                      description: Name of the docker repo, e.g. `docker.stackable.tech/stackable`
+                      nullable: true
+                      type: string
+                    stackableVersion:
+                      description: Stackable version of the product, e.g. 2.1.0
+                      type: string
+                  type: object
+                servers:
+                  description: ZooKeeper server configuration.
+                  nullable: true
+                  properties:
+                    cliOverrides:
+                      additionalProperties:
+                        type: string
+                      default: {}
+                      type: object
+                    config:
+                      default: {}
+                      properties:
+                        initLimit:
+                          format: uint32
+                          minimum: 0.0
+                          nullable: true
+                          type: integer
+                        logging:
+                          default:
+                            enableVectorAgent: null
+                            containers: {}
+                          properties:
+                            containers:
+                              additionalProperties:
+                                anyOf:
+                                  - required:
+                                      - custom
+                                  - {}
+                                description: Fragment derived from `ContainerLogConfigChoice`
+                                properties:
+                                  console:
+                                    nullable: true
+                                    properties:
+                                      level:
+                                        description: Log levels
+                                        enum:
+                                          - TRACE
+                                          - DEBUG
+                                          - INFO
+                                          - WARN
+                                          - ERROR
+                                          - FATAL
+                                          - NONE
+                                        nullable: true
+                                        type: string
+                                    type: object
+                                  custom:
+                                    description: Custom log configuration provided in a ConfigMap
+                                    properties:
+                                      configMap:
+                                        nullable: true
+                                        type: string
+                                    type: object
+                                  file:
+                                    nullable: true
+                                    properties:
+                                      level:
+                                        description: Log levels
+                                        enum:
+                                          - TRACE
+                                          - DEBUG
+                                          - INFO
+                                          - WARN
+                                          - ERROR
+                                          - FATAL
+                                          - NONE
+                                        nullable: true
+                                        type: string
+                                    type: object
+                                  loggers:
+                                    additionalProperties:
+                                      properties:
+                                        level:
+                                          description: Log levels
+                                          enum:
+                                            - TRACE
+                                            - DEBUG
+                                            - INFO
+                                            - WARN
+                                            - ERROR
+                                            - FATAL
+                                            - NONE
+                                          nullable: true
+                                          type: string
+                                      type: object
+                                    default: {}
+                                    type: object
+                                type: object
+                              type: object
+                            enableVectorAgent:
+                              nullable: true
+                              type: boolean
+                          type: object
+                        myidOffset:
+                          format: uint16
+                          minimum: 0.0
+                          nullable: true
+                          type: integer
+                        resources:
+                          default:
+                            memory:
+                              limit: null
+                              runtimeLimits: {}
+                            cpu:
+                              min: null
+                              max: null
+                            storage:
+                              data:
+                                capacity: null
+                          properties:
+                            cpu:
+                              default:
+                                min: null
+                                max: null
+                              properties:
+                                max:
+                                  description: |-
+                                    Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                    The serialization format is:
+
+                                    <quantity>        ::= <signedNumber><suffix>
+                                      (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                    <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                      (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                    <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                      (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                    <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                    No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                    When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                    Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                      a. No precision is lost
+                                      b. No fractional digits will be emitted
+                                      c. The exponent (or suffix) is as large as possible.
+                                    The sign will be omitted unless the number is negative.
+
+                                    Examples:
+                                      1.5 will be serialized as "1500m"
+                                      1.5Gi will be serialized as "1536Mi"
+
+                                    Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                    Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                    This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                  nullable: true
+                                  type: string
+                                min:
+                                  description: |-
+                                    Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                    The serialization format is:
+
+                                    <quantity>        ::= <signedNumber><suffix>
+                                      (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                    <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                      (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                    <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                      (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                    <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                    No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                    When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                    Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                      a. No precision is lost
+                                      b. No fractional digits will be emitted
+                                      c. The exponent (or suffix) is as large as possible.
+                                    The sign will be omitted unless the number is negative.
+
+                                    Examples:
+                                      1.5 will be serialized as "1500m"
+                                      1.5Gi will be serialized as "1536Mi"
+
+                                    Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                    Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                    This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                  nullable: true
+                                  type: string
+                              type: object
+                            memory:
+                              properties:
+                                limit:
+                                  description: |-
+                                    Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                    The serialization format is:
+
+                                    <quantity>        ::= <signedNumber><suffix>
+                                      (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                    <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                      (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                    <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                      (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                    <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                    No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                    When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                    Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                      a. No precision is lost
+                                      b. No fractional digits will be emitted
+                                      c. The exponent (or suffix) is as large as possible.
+                                    The sign will be omitted unless the number is negative.
+
+                                    Examples:
+                                      1.5 will be serialized as "1500m"
+                                      1.5Gi will be serialized as "1536Mi"
+
+                                    Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                    Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                    This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                  nullable: true
+                                  type: string
+                                runtimeLimits:
+                                  type: object
+                              type: object
+                            storage:
+                              properties:
+                                data:
+                                  default:
+                                    capacity: null
+                                  properties:
+                                    capacity:
+                                      description: |-
+                                        Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                        The serialization format is:
+
+                                        <quantity>        ::= <signedNumber><suffix>
+                                          (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                        <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                          (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                        <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                          (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                        <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                        No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                        When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                        Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                          a. No precision is lost
+                                          b. No fractional digits will be emitted
+                                          c. The exponent (or suffix) is as large as possible.
+                                        The sign will be omitted unless the number is negative.
+
+                                        Examples:
+                                          1.5 will be serialized as "1500m"
+                                          1.5Gi will be serialized as "1536Mi"
+
+                                        Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                        Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                        This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                      nullable: true
+                                      type: string
+                                    selectors:
+                                      description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                                      nullable: true
+                                      properties:
+                                        matchExpressions:
+                                          description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                                          items:
+                                            description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                                            properties:
+                                              key:
+                                                description: key is the label key that the selector applies to.
+                                                type: string
+                                              operator:
+                                                description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                                                type: string
+                                              values:
+                                                description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                                                items:
+                                                  type: string
+                                                type: array
+                                            required:
+                                              - key
+                                              - operator
+                                            type: object
+                                          type: array
+                                        matchLabels:
+                                          additionalProperties:
+                                            type: string
+                                          description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                                          type: object
+                                      type: object
+                                    storageClass:
+                                      nullable: true
+                                      type: string
+                                  type: object
+                              type: object
+                          type: object
+                        syncLimit:
+                          format: uint32
+                          minimum: 0.0
+                          nullable: true
+                          type: integer
+                        tickTime:
+                          format: uint32
+                          minimum: 0.0
+                          nullable: true
+                          type: integer
+                      type: object
+                    configOverrides:
+                      additionalProperties:
+                        additionalProperties:
+                          type: string
+                        type: object
+                      default: {}
+                      type: object
+                    envOverrides:
+                      additionalProperties:
+                        type: string
+                      default: {}
+                      type: object
+                    roleGroups:
+                      additionalProperties:
+                        properties:
+                          cliOverrides:
+                            additionalProperties:
+                              type: string
+                            default: {}
+                            type: object
+                          config:
+                            default: {}
+                            properties:
+                              initLimit:
+                                format: uint32
+                                minimum: 0.0
+                                nullable: true
+                                type: integer
+                              logging:
+                                default:
+                                  enableVectorAgent: null
+                                  containers: {}
+                                properties:
+                                  containers:
+                                    additionalProperties:
+                                      anyOf:
+                                        - required:
+                                            - custom
+                                        - {}
+                                      description: Fragment derived from `ContainerLogConfigChoice`
+                                      properties:
+                                        console:
+                                          nullable: true
+                                          properties:
+                                            level:
+                                              description: Log levels
+                                              enum:
+                                                - TRACE
+                                                - DEBUG
+                                                - INFO
+                                                - WARN
+                                                - ERROR
+                                                - FATAL
+                                                - NONE
+                                              nullable: true
+                                              type: string
+                                          type: object
+                                        custom:
+                                          description: Custom log configuration provided in a ConfigMap
+                                          properties:
+                                            configMap:
+                                              nullable: true
+                                              type: string
+                                          type: object
+                                        file:
+                                          nullable: true
+                                          properties:
+                                            level:
+                                              description: Log levels
+                                              enum:
+                                                - TRACE
+                                                - DEBUG
+                                                - INFO
+                                                - WARN
+                                                - ERROR
+                                                - FATAL
+                                                - NONE
+                                              nullable: true
+                                              type: string
+                                          type: object
+                                        loggers:
+                                          additionalProperties:
+                                            properties:
+                                              level:
+                                                description: Log levels
+                                                enum:
+                                                  - TRACE
+                                                  - DEBUG
+                                                  - INFO
+                                                  - WARN
+                                                  - ERROR
+                                                  - FATAL
+                                                  - NONE
+                                                nullable: true
+                                                type: string
+                                            type: object
+                                          default: {}
+                                          type: object
+                                      type: object
+                                    type: object
+                                  enableVectorAgent:
+                                    nullable: true
+                                    type: boolean
+                                type: object
+                              myidOffset:
+                                format: uint16
+                                minimum: 0.0
+                                nullable: true
+                                type: integer
+                              resources:
+                                default:
+                                  memory:
+                                    limit: null
+                                    runtimeLimits: {}
+                                  cpu:
+                                    min: null
+                                    max: null
+                                  storage:
+                                    data:
+                                      capacity: null
+                                properties:
+                                  cpu:
+                                    default:
+                                      min: null
+                                      max: null
+                                    properties:
+                                      max:
+                                        description: |-
+                                          Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                          The serialization format is:
+
+                                          <quantity>        ::= <signedNumber><suffix>
+                                            (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                          <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                            (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                          <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                            (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                          <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                          No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                          When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                          Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                            a. No precision is lost
+                                            b. No fractional digits will be emitted
+                                            c. The exponent (or suffix) is as large as possible.
+                                          The sign will be omitted unless the number is negative.
+
+                                          Examples:
+                                            1.5 will be serialized as "1500m"
+                                            1.5Gi will be serialized as "1536Mi"
+
+                                          Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                          Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                          This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                        nullable: true
+                                        type: string
+                                      min:
+                                        description: |-
+                                          Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                          The serialization format is:
+
+                                          <quantity>        ::= <signedNumber><suffix>
+                                            (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                          <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                            (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                          <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                            (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                          <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                          No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                          When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                          Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                            a. No precision is lost
+                                            b. No fractional digits will be emitted
+                                            c. The exponent (or suffix) is as large as possible.
+                                          The sign will be omitted unless the number is negative.
+
+                                          Examples:
+                                            1.5 will be serialized as "1500m"
+                                            1.5Gi will be serialized as "1536Mi"
+
+                                          Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                          Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                          This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                        nullable: true
+                                        type: string
+                                    type: object
+                                  memory:
+                                    properties:
+                                      limit:
+                                        description: |-
+                                          Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                          The serialization format is:
+
+                                          <quantity>        ::= <signedNumber><suffix>
+                                            (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                          <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                            (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                          <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                            (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                          <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                          No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                          When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                          Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                            a. No precision is lost
+                                            b. No fractional digits will be emitted
+                                            c. The exponent (or suffix) is as large as possible.
+                                          The sign will be omitted unless the number is negative.
+
+                                          Examples:
+                                            1.5 will be serialized as "1500m"
+                                            1.5Gi will be serialized as "1536Mi"
+
+                                          Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                          Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                          This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                        nullable: true
+                                        type: string
+                                      runtimeLimits:
+                                        type: object
+                                    type: object
+                                  storage:
+                                    properties:
+                                      data:
+                                        default:
+                                          capacity: null
+                                        properties:
+                                          capacity:
+                                            description: |-
+                                              Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.
+
+                                              The serialization format is:
+
+                                              <quantity>        ::= <signedNumber><suffix>
+                                                (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+                                              <digit>           ::= 0 | 1 | ... | 9 <digits>          ::= <digit> | <digit><digits> <number>          ::= <digits> | <digits>.<digits> | <digits>. | .<digits> <sign>            ::= "+" | "-" <signedNumber>    ::= <number> | <sign><number> <suffix>          ::= <binarySI> | <decimalExponent> | <decimalSI> <binarySI>        ::= Ki | Mi | Gi | Ti | Pi | Ei
+                                                (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+                                              <decimalSI>       ::= m | "" | k | M | G | T | P | E
+                                                (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+                                              <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+
+                                              No matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.
+
+                                              When a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.
+
+                                              Before serializing, Quantity will be put in "canonical form". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:
+                                                a. No precision is lost
+                                                b. No fractional digits will be emitted
+                                                c. The exponent (or suffix) is as large as possible.
+                                              The sign will be omitted unless the number is negative.
+
+                                              Examples:
+                                                1.5 will be serialized as "1500m"
+                                                1.5Gi will be serialized as "1536Mi"
+
+                                              Note that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.
+
+                                              Non-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)
+
+                                              This format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.
+                                            nullable: true
+                                            type: string
+                                          selectors:
+                                            description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                                            nullable: true
+                                            properties:
+                                              matchExpressions:
+                                                description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                                                items:
+                                                  description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                                                  properties:
+                                                    key:
+                                                      description: key is the label key that the selector applies to.
+                                                      type: string
+                                                    operator:
+                                                      description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                                                      type: string
+                                                    values:
+                                                      description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                                                      items:
+                                                        type: string
+                                                      type: array
+                                                  required:
+                                                    - key
+                                                    - operator
+                                                  type: object
+                                                type: array
+                                              matchLabels:
+                                                additionalProperties:
+                                                  type: string
+                                                description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                                                type: object
+                                            type: object
+                                          storageClass:
+                                            nullable: true
+                                            type: string
+                                        type: object
+                                    type: object
+                                type: object
+                              syncLimit:
+                                format: uint32
+                                minimum: 0.0
+                                nullable: true
+                                type: integer
+                              tickTime:
+                                format: uint32
+                                minimum: 0.0
+                                nullable: true
+                                type: integer
+                            type: object
+                          configOverrides:
+                            additionalProperties:
+                              additionalProperties:
+                                type: string
+                              type: object
+                            default: {}
+                            type: object
+                          envOverrides:
+                            additionalProperties:
+                              type: string
+                            default: {}
+                            type: object
+                          replicas:
+                            format: uint16
+                            minimum: 0.0
+                            nullable: true
+                            type: integer
+                          selector:
+                            description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                            nullable: true
+                            properties:
+                              matchExpressions:
+                                description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                                items:
+                                  description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                                  properties:
+                                    key:
+                                      description: key is the label key that the selector applies to.
+                                      type: string
+                                    operator:
+                                      description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                                      type: string
+                                    values:
+                                      description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                                      items:
+                                        type: string
+                                      type: array
+                                  required:
+                                    - key
+                                    - operator
+                                  type: object
+                                type: array
+                              matchLabels:
+                                additionalProperties:
+                                  type: string
+                                description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                                type: object
+                            type: object
+                        type: object
+                      type: object
+                  required:
+                    - roleGroups
+                  type: object
+                stopped:
+                  description: Emergency stop button, if `true` then all pods are stopped without affecting configuration (as setting `replicas` to `0` would).
+                  nullable: true
+                  type: boolean
+              required:
+                - image
+              type: object
+            status:
+              nullable: true
+              properties:
+                discoveryHash:
+                  description: An opaque value that changes every time a discovery detail does
+                  nullable: true
+                  type: string
+              type: object
+          required:
+            - spec
+          title: ZookeeperCluster
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
diff --git a/deploy/olm/23.1.0/manifests/zknode.yaml b/deploy/olm/23.1.0/manifests/zknode.yaml
new file mode 100644
index 00000000..c84add03
--- /dev/null
+++ b/deploy/olm/23.1.0/manifests/zknode.yaml
@@ -0,0 +1,62 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: zookeeperznodes.zookeeper.stackable.tech
+  annotations:
+    helm.sh/resource-policy: keep
+spec:
+  group: zookeeper.stackable.tech
+  names:
+    categories: []
+    kind: ZookeeperZnode
+    plural: zookeeperznodes
+    shortNames:
+      - zno
+      - znode
+    singular: zookeeperznode
+  scope: Namespaced
+  versions:
+    - additionalPrinterColumns: []
+      name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          description: Auto-generated derived type for ZookeeperZnodeSpec via `CustomResource`
+          properties:
+            spec:
+              description: |-
+                A claim for a single ZooKeeper ZNode tree (filesystem node)
+
+                A `ConfigMap` will automatically be created with the same name, containing the connection string in the field `ZOOKEEPER`. Each `ZookeeperZnode` gets an isolated ZNode chroot, which the `ZOOKEEPER` automatically contains. All data inside of this chroot will be deleted when the corresponding `ZookeeperZnode` is.
+
+                `ZookeeperZnode` is *not* designed to manage the contents of this ZNode. Instead, it should be used to create a chroot for an installation of an application to work inside. Initializing the contents is the responsibility of the application.
+              properties:
+                clusterRef:
+                  default:
+                    name: null
+                    namespace: null
+                  description: |-
+                    A reference to a product cluster (for example, a `ZookeeperCluster`)
+
+                    `namespace`'s defaulting only applies when retrieved via [`ClusterRef::namespace_relative_from`]
+                  properties:
+                    name:
+                      description: The name of the cluster
+                      nullable: true
+                      type: string
+                    namespace:
+                      description: |-
+                        The namespace of the cluster
+
+                        This field is optional, and will default to the namespace of the referring object.
+                      nullable: true
+                      type: string
+                  type: object
+              type: object
+          required:
+            - spec
+          title: ZookeeperZnode
+          type: object
+      served: true
+      storage: true
+      subresources: {}
diff --git a/deploy/olm/bundle.sh b/deploy/olm/bundle.sh
new file mode 100755
index 00000000..76d302c3
--- /dev/null
+++ b/deploy/olm/bundle.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# usage: bundle.sh <release>, called from base folder:
+# e.g. ./deploy/olm/bundle.sh 23.1.0
+
+set -euo pipefail
+set -x
+
+OPERATOR_NAME="zookeeper-operator"
+
+bundle-clean() {
+	rm -rf "deploy/olm/${VERSION}/bundle"
+	rm -rf "deploy/olm/${VERSION}/bundle.Dockerfile"
+}
+
+
+build-bundle() {
+	opm alpha bundle generate --directory manifests --package "${OPERATOR_NAME}-package" --output-dir bundle --channels stable --default stable
+  docker build -t "docker.stackable.tech/stackable/${OPERATOR_NAME}-bundle:${VERSION}" -f bundle.Dockerfile .
+  docker push "docker.stackable.tech/stackable/${OPERATOR_NAME}-bundle:${VERSION}"
+  opm alpha bundle validate --tag "docker.stackable.tech/stackable/${OPERATOR_NAME}-bundle:${VERSION}" --image-builder docker
+}
+
+main() {
+  VERSION="$1";
+
+  pushd "deploy/olm/${VERSION}"
+  bundle-clean
+  build-bundle
+  popd
+}
+
+main "$@"
\ No newline at end of file
