diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 554318a97..f510e08f5 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -61,6 +61,11 @@ spec: configuration: type: object properties: + crd_categories: + type: array + nullable: true + items: + type: string docker_image: type: string default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p3" @@ -69,6 +74,7 @@ spec: default: true enable_crd_validation: type: boolean + description: deprecated default: true enable_lazy_spilo_upgrade: type: boolean @@ -90,11 +96,13 @@ spec: default: false max_instances: type: integer - minimum: -1 # -1 = disabled + description: "-1 = disabled" + minimum: -1 default: -1 min_instances: type: integer - minimum: -1 # -1 = disabled + description: "-1 = disabled" + minimum: -1 default: -1 resync_period: type: string @@ -184,12 +192,12 @@ spec: type: array items: type: string - enable_init_containers: - type: boolean - default: true enable_cross_namespace_secret: type: boolean default: false + enable_init_containers: + type: boolean + default: true enable_pod_antiaffinity: type: boolean default: false @@ -410,12 +418,12 @@ spec: type: string log_s3_bucket: type: string + wal_az_storage_account: + type: string wal_gs_bucket: type: string wal_s3_bucket: type: string - wal_az_storage_account: - type: string logical_backup: type: object properties: diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 3c03e4f3c..83043084c 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -147,7 +147,7 @@ spec: - "transaction" numberOfInstances: type: integer - minimum: 2 + minimum: 1 resources: type: object required: @@ -201,8 +201,9 @@ spec: type: boolean enableShmVolume: type: boolean - init_containers: # deprecated + init_containers: type: array + description: deprecated nullable: true items: type: object @@ -229,8 +230,8 @@ spec: items: type: object required: - - weight - preference + - weight properties: preference: type: object @@ -348,8 +349,9 @@ spec: type: object additionalProperties: type: string - pod_priority_class_name: # deprecated + pod_priority_class_name: type: string + description: deprecated podPriorityClassName: type: string postgresql: @@ -393,8 +395,9 @@ spec: type: boolean secretNamespace: type: string - replicaLoadBalancer: # deprecated + replicaLoadBalancer: type: boolean + description: deprecated resources: type: object required: @@ -512,14 +515,14 @@ spec: - PreferNoSchedule tolerationSeconds: type: integer - useLoadBalancer: # deprecated + useLoadBalancer: type: boolean + description: deprecated users: type: object additionalProperties: type: array nullable: true - description: "Role flags specified here must not contradict each other" items: type: string enum: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 0602a0e93..8183894d3 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -22,8 +22,9 @@ enableJsonLogging: false configGeneral: # the deployment should create/update the CRDs enable_crd_registration: true - # choose if deployment creates/updates CRDs with OpenAPIV3Validation - enable_crd_validation: true + # specify categories under which crds should be listed + crd_categories: + - "all" # update only the statefulsets without immediately doing the rolling update enable_lazy_spilo_upgrade: false # set the PGVERSION env var instead of providing the version via postgresql.bin_dir in SPILO_CONFIGURATION diff --git a/docs/administrator.md b/docs/administrator.md index d7ab8ef0c..3c5d8ae46 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -3,6 +3,25 @@ Learn how to configure and manage the Postgres Operator in your Kubernetes (K8s) environment. +## CRD registration and validation + +On startup, the operator will try to register the necessary +[CustomResourceDefinitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) +`Postgresql` and `OperatorConfiguration`. The latter will only get created if +the `POSTGRES_OPERATOR_CONFIGURATION_OBJECT` [environment variable](https://github.com/zalando/postgres-operator/blob/master/manifests/postgres-operator.yaml#L36) +is set in the deployment yaml and is not empty. If the CRDs already exists they +will only be patched. If you do not wish the operator to create or update the +CRDs set `enable_crd_registration` config option to `false`. + +CRDs are defined with a `openAPIV3Schema` structural schema against which new +manifests of [`postgresql`](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql.crd.yaml) or [`OperatorConfiguration`](https://github.com/zalando/postgres-operator/blob/master/manifests/operatorconfiguration.crd.yaml) +resources will be validated. On creation you can bypass the validation with +`kubectl create --validate=false`. + +By default, the operator will register the CRDs in the `all` category so +that resources are listed on `kubectl get all` commands. The `crd_categories` +config option allows for customization of categories. + ## Upgrading the operator The Postgres Operator is upgraded by changing the docker image within the @@ -63,30 +82,6 @@ upgrade procedure, refer to the [corresponding PR in Spilo](https://github.com/z When `major_version_upgrade_mode` is set to `manual` the operator will run the upgrade script for you after the manifest is updated and pods are rotated. -## CRD Validation - -[CustomResourceDefinitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) -will be registered with schema validation by default when the operator is -deployed. The `OperatorConfiguration` CRD will only get created if the -`POSTGRES_OPERATOR_CONFIGURATION_OBJECT` [environment variable](https://github.com/zalando/postgres-operator/blob/master/manifests/postgres-operator.yaml#L36) -in the deployment yaml is set and not empty. - -When submitting manifests of [`postgresql`](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql.crd.yaml) or -[`OperatorConfiguration`](https://github.com/zalando/postgres-operator/blob/master/manifests/operatorconfiguration.crd.yaml) custom -resources with kubectl, validation can be bypassed with `--validate=false`. The -operator can also be configured to not register CRDs with validation on `ADD` or -`UPDATE` events. Running instances are not affected when enabling the validation -afterwards unless the manifests is not changed then. Note, that the provided CRD -manifests contain the validation for users to understand what schema is -enforced. - -Once the validation is enabled it can only be disabled manually by editing or -patching the CRD manifest: - -```bash -kubectl patch crd postgresqls.acid.zalan.do -p '{"spec":{"validation": null}}' -``` - ## Non-default cluster domain If your cluster uses a DNS domain other than the default `cluster.local`, this diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 990909eb5..f3d9be88f 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -75,9 +75,12 @@ Those are top-level keys, containing both leaf keys and groups. The default is `true`. * **enable_crd_validation** - toggles if the operator will create or update CRDs with + *deprecated*: toggles if the operator will create or update CRDs with [OpenAPI v3 schema validation](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#validation) - The default is `true`. + The default is `true`. `false` will be ignored, since `apiextensions.io/v1` requires a structural schema definition. + +* **crd_categories** + The operator will register CRDs in the `all` category by default so that they will be returned by a `kubectl get all` call. You are free to change categories or leave them empty. * **enable_lazy_spilo_upgrade** Instruct operator to update only the statefulsets with new images (Spilo and InitContainers) without immediately doing the rolling update. The assumption is pods will be re-started later with new images, for example due to the node rotation. diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 7b3711b73..77c14e272 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -204,7 +204,8 @@ class EndToEndTestCase(unittest.TestCase): "enable_postgres_team_crd": "true", "enable_team_member_deprecation": "true", "role_deletion_suffix": "_delete_me", - "resync_period": "15s" + "resync_period": "15s", + "repair_period": "10s", }, } k8s.update_config(enable_postgres_team_crd) @@ -288,6 +289,7 @@ class EndToEndTestCase(unittest.TestCase): revert_resync = { "data": { "resync_period": "4m", + "repair_period": "1m", }, } k8s.update_config(revert_resync) @@ -1403,6 +1405,7 @@ class EndToEndTestCase(unittest.TestCase): "data": { "pod_label_wait_timeout": "2s", "resync_period": "30s", + "repair_period": "10s", } } @@ -1444,6 +1447,7 @@ class EndToEndTestCase(unittest.TestCase): "data": { "pod_label_wait_timeout": "10m", "resync_period": "4m", + "repair_period": "2m", } } k8s.update_config(patch_resync_config, "revert resync interval and pod_label_wait_timeout") diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 1a52a52da..b3aaa3c66 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -22,6 +22,7 @@ data: # connection_pooler_number_of_instances: 2 # connection_pooler_schema: "pooler" # connection_pooler_user: "pooler" + crd_categories: "all" # custom_service_annotations: "keyx:valuez,keya:valuea" # custom_pod_annotations: "keya:valuea,keyb:valueb" db_hosted_zone: db.example.com @@ -36,7 +37,6 @@ data: # downscaler_annotations: "deployment-time,downscaler/*" # enable_admin_role_for_users: "true" # enable_crd_registration: "true" - # enable_crd_validation: "true" # enable_cross_namespace_secret: "false" # enable_database_access: "true" enable_ebs_gp3_migration: "false" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 3313377a0..d086998cf 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -59,6 +59,11 @@ spec: configuration: type: object properties: + crd_categories: + type: array + nullable: true + items: + type: string docker_image: type: string default: "registry.opensource.zalan.do/acid/spilo-14:2.1-p3" @@ -67,6 +72,7 @@ spec: default: true enable_crd_validation: type: boolean + description: deprecated default: true enable_lazy_spilo_upgrade: type: boolean @@ -88,11 +94,13 @@ spec: default: false max_instances: type: integer - minimum: -1 # -1 = disabled + description: "-1 = disabled" + minimum: -1 default: -1 min_instances: type: integer - minimum: -1 # -1 = disabled + description: "-1 = disabled" + minimum: -1 default: -1 resync_period: type: string @@ -182,6 +190,9 @@ spec: type: array items: type: string + enable_cross_namespace_secret: + type: boolean + default: false enable_init_containers: type: boolean default: true diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index f8ccad7ce..87b5436d5 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -5,7 +5,8 @@ metadata: configuration: docker_image: registry.opensource.zalan.do/acid/spilo-14:2.1-p3 # enable_crd_registration: true - # enable_crd_validation: true + # crd_categories: + # - all # enable_lazy_spilo_upgrade: false enable_pgversion_env_var: true # enable_shm_volume: true diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index dafbf9038..88144dc05 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -145,7 +145,7 @@ spec: - "transaction" numberOfInstances: type: integer - minimum: 2 + minimum: 1 resources: type: object required: @@ -199,8 +199,9 @@ spec: type: boolean enableShmVolume: type: boolean - init_containers: # deprecated + init_containers: type: array + description: deprecated nullable: true items: type: object @@ -227,8 +228,8 @@ spec: items: type: object required: - - weight - preference + - weight properties: preference: type: object @@ -346,8 +347,9 @@ spec: type: object additionalProperties: type: string - pod_priority_class_name: # deprecated + pod_priority_class_name: type: string + description: deprecated podPriorityClassName: type: string postgresql: @@ -391,8 +393,9 @@ spec: type: boolean secretNamespace: type: string - replicaLoadBalancer: # deprecated + replicaLoadBalancer: type: boolean + description: deprecated resources: type: object required: @@ -510,14 +513,14 @@ spec: - PreferNoSchedule tolerationSeconds: type: integer - useLoadBalancer: # deprecated + useLoadBalancer: type: boolean + description: deprecated users: type: object additionalProperties: type: array nullable: true - description: "Role flags specified here must not contradict each other" items: type: string enum: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index da18cd20d..844ccef04 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1,6 +1,8 @@ package v1 import ( + "fmt" + acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do" "github.com/zalando/postgres-operator/pkg/util" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -11,11 +13,13 @@ import ( const ( PostgresCRDResourceKind = "postgresql" PostgresCRDResourcePlural = "postgresqls" + PostgresCRDResourceList = PostgresCRDResourceKind + "List" PostgresCRDResouceName = PostgresCRDResourcePlural + "." + acidzalando.GroupName PostgresCRDResourceShort = "pg" OperatorConfigCRDResouceKind = "OperatorConfiguration" OperatorConfigCRDResourcePlural = "operatorconfigurations" + OperatorConfigCRDResourceList = OperatorConfigCRDResouceKind + "List" OperatorConfigCRDResourceName = OperatorConfigCRDResourcePlural + "." + acidzalando.GroupName OperatorConfigCRDResourceShort = "opconfig" ) @@ -148,7 +152,8 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", }, "targetContainers": { - Type: "array", + Type: "array", + Nullable: true, Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ Type: "string", @@ -199,9 +204,8 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", }, "timestamp": { - Type: "string", - Description: "Date-time format that specifies a timezone as an offset relative to UTC e.g. 1996-12-19T16:39:57-08:00", - Pattern: "^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$", + Type: "string", + Pattern: "^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$", }, "uid": { Type: "string", @@ -242,14 +246,12 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Required: []string{"cpu", "memory"}, Properties: map[string]apiextv1.JSONSchemaProps{ "cpu": { - Type: "string", - Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Type: "string", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", }, "memory": { - Type: "string", - Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, }, }, @@ -258,14 +260,12 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Required: []string{"cpu", "memory"}, Properties: map[string]apiextv1.JSONSchemaProps{ "cpu": { - Type: "string", - Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Type: "string", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", }, "memory": { - Type: "string", - Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, }, }, @@ -283,8 +283,7 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "object", AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ Schema: &apiextv1.JSONSchemaProps{ - Type: "string", - Description: "User names specified here as database owners must be declared in the users key of the spec key", + Type: "string", }, }, }, @@ -311,7 +310,8 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, "init_containers": { Type: "array", - Description: "Deprecated", + Description: "deprecated", + Nullable: true, Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ Type: "object", @@ -320,7 +320,8 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, "initContainers": { - Type: "array", + Type: "array", + Nullable: true, Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ Type: "object", @@ -358,9 +359,23 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "array", Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Allows: true, + Type: "object", + Required: []string{"key", "operator"}, + Properties: map[string]apiextv1.JSONSchemaProps{ + "key": { + Type: "string", + }, + "operator": { + Type: "string", + }, + "values": { + Type: "array", + Items: &apiextv1.JSONSchemaPropsOrArray{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, }, }, }, @@ -369,9 +384,23 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "array", Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Allows: true, + Type: "object", + Required: []string{"key", "operator"}, + Properties: map[string]apiextv1.JSONSchemaProps{ + "key": { + Type: "string", + }, + "operator": { + Type: "string", + }, + "values": { + Type: "array", + Items: &apiextv1.JSONSchemaPropsOrArray{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, }, }, }, @@ -400,9 +429,23 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "array", Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Allows: true, + Type: "object", + Required: []string{"key", "operator"}, + Properties: map[string]apiextv1.JSONSchemaProps{ + "key": { + Type: "string", + }, + "operator": { + Type: "string", + }, + "values": { + Type: "array", + Items: &apiextv1.JSONSchemaPropsOrArray{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, }, }, }, @@ -411,9 +454,23 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "array", Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ - Type: "object", - AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ - Allows: true, + Type: "object", + Required: []string{"key", "operator"}, + Properties: map[string]apiextv1.JSONSchemaProps{ + "key": { + Type: "string", + }, + "operator": { + Type: "string", + }, + "values": { + Type: "array", + Items: &apiextv1.JSONSchemaPropsOrArray{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, }, }, }, @@ -492,7 +549,7 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, "pod_priority_class_name": { Type: "string", - Description: "Deprecated", + Description: "deprecated", }, "podPriorityClassName": { Type: "string", @@ -579,7 +636,7 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, "replicaLoadBalancer": { Type: "boolean", - Description: "Deprecated", + Description: "deprecated", }, "resources": { Type: "object", @@ -590,14 +647,12 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Required: []string{"cpu", "memory"}, Properties: map[string]apiextv1.JSONSchemaProps{ "cpu": { - Type: "string", - Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Type: "string", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", }, "memory": { - Type: "string", - Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, }, }, @@ -606,14 +661,12 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ Required: []string{"cpu", "memory"}, Properties: map[string]apiextv1.JSONSchemaProps{ "cpu": { - Type: "string", - Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Type: "string", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", }, "memory": { - Type: "string", - Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, }, }, @@ -631,7 +684,8 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, "sidecars": { - Type: "array", + Type: "array", + Nullable: true, Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ Type: "object", @@ -730,15 +784,14 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, "useLoadBalancer": { Type: "boolean", - Description: "Deprecated", + Description: "deprecated", }, "users": { Type: "object", AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ Schema: &apiextv1.JSONSchemaProps{ - Type: "array", - Description: "Role flags specified here must not contradict each other", - Nullable: true, + Type: "array", + Nullable: true, Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ Type: "string", @@ -907,9 +960,8 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, "size": { - Type: "string", - Description: "Value must not be zero", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Type: "string", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", }, "storageClass": { Type: "string", @@ -961,6 +1013,15 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "configuration": { Type: "object", Properties: map[string]apiextv1.JSONSchemaProps{ + "crd_categories": { + Type: "array", + Nullable: true, + Items: &apiextv1.JSONSchemaPropsOrArray{ + Schema: &apiextv1.JSONSchemaProps{ + Type: "string", + }, + }, + }, "docker_image": { Type: "string", }, @@ -968,7 +1029,8 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "boolean", }, "enable_crd_validation": { - Type: "boolean", + Type: "boolean", + Description: "deprecated", }, "enable_lazy_spilo_upgrade": { Type: "boolean", @@ -1013,7 +1075,8 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, "sidecars": { - Type: "array", + Type: "array", + Nullable: true, Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ Type: "object", @@ -1124,7 +1187,8 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "string", }, "infrastructure_roles_secrets": { - Type: "array", + Type: "array", + Nullable: true, Items: &apiextv1.JSONSchemaPropsOrArray{ Schema: &apiextv1.JSONSchemaProps{ Type: "object", @@ -1626,18 +1690,27 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, } -func buildCRD(name, kind, plural, short string, columns []apiextv1.CustomResourceColumnDefinition, validation apiextv1.CustomResourceValidation) *apiextv1.CustomResourceDefinition { +func buildCRD(name, kind, plural, list, short string, + categories []string, + columns []apiextv1.CustomResourceColumnDefinition, + validation apiextv1.CustomResourceValidation) *apiextv1.CustomResourceDefinition { return &apiextv1.CustomResourceDefinition{ + TypeMeta: metav1.TypeMeta{ + APIVersion: fmt.Sprintf("%s/%s", apiextv1.GroupName, apiextv1.SchemeGroupVersion.Version), + Kind: "CustomResourceDefinition", + }, ObjectMeta: metav1.ObjectMeta{ Name: name, }, Spec: apiextv1.CustomResourceDefinitionSpec{ Group: SchemeGroupVersion.Group, Names: apiextv1.CustomResourceDefinitionNames{ - Plural: plural, - ShortNames: []string{short}, Kind: kind, - Categories: []string{"all"}, + ListKind: list, + Plural: plural, + Singular: kind, + ShortNames: []string{short}, + Categories: categories, }, Scope: apiextv1.NamespaceScoped, Versions: []apiextv1.CustomResourceDefinitionVersion{ @@ -1657,33 +1730,25 @@ func buildCRD(name, kind, plural, short string, columns []apiextv1.CustomResourc } // PostgresCRD returns CustomResourceDefinition built from PostgresCRDResource -func PostgresCRD(enableValidation *bool) *apiextv1.CustomResourceDefinition { - postgresCRDvalidation := apiextv1.CustomResourceValidation{} - - if enableValidation != nil && *enableValidation { - postgresCRDvalidation = PostgresCRDResourceValidation - } - +func PostgresCRD(crdCategories []string) *apiextv1.CustomResourceDefinition { return buildCRD(PostgresCRDResouceName, PostgresCRDResourceKind, PostgresCRDResourcePlural, + PostgresCRDResourceList, PostgresCRDResourceShort, + crdCategories, PostgresCRDResourceColumns, - postgresCRDvalidation) + PostgresCRDResourceValidation) } // ConfigurationCRD returns CustomResourceDefinition built from OperatorConfigCRDResource -func ConfigurationCRD(enableValidation *bool) *apiextv1.CustomResourceDefinition { - opconfigCRDvalidation := apiextv1.CustomResourceValidation{} - - if enableValidation != nil && *enableValidation { - opconfigCRDvalidation = OperatorConfigCRDResourceValidation - } - +func ConfigurationCRD(crdCategories []string) *apiextv1.CustomResourceDefinition { return buildCRD(OperatorConfigCRDResourceName, OperatorConfigCRDResouceKind, OperatorConfigCRDResourcePlural, + OperatorConfigCRDResourceList, OperatorConfigCRDResourceShort, + crdCategories, OperatorConfigCRDResourceColumns, - opconfigCRDvalidation) + OperatorConfigCRDResourceValidation) } diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 124278e70..1298c6834 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -221,6 +221,7 @@ type OperatorLogicalBackupConfiguration struct { type OperatorConfigurationData struct { EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"` EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"` + CRDCategories []string `json:"crd_categories,omitempty"` EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"` EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"` EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"` diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index cfa315358..d960cd102 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -377,6 +377,11 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData *out = new(bool) **out = **in } + if in.CRDCategories != nil { + in, out := &in.CRDCategories, &out.CRDCategories + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.ShmVolume != nil { in, out := &in.ShmVolume, &out.ShmVolume *out = new(bool) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 54e50a45f..de0dec69f 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -310,7 +310,7 @@ func (c *Controller) initController() { if configObjectName := os.Getenv("POSTGRES_OPERATOR_CONFIGURATION_OBJECT"); configObjectName != "" { if c.opConfig.EnableCRDRegistration != nil && *c.opConfig.EnableCRDRegistration { - if err := c.createConfigurationCRD(c.opConfig.EnableCRDValidation); err != nil { + if err := c.createConfigurationCRD(); err != nil { c.logger.Fatalf("could not register Operator Configuration CustomResourceDefinition: %v", err) } } @@ -328,7 +328,7 @@ func (c *Controller) initController() { c.modifyConfigFromEnvironment() if c.opConfig.EnableCRDRegistration != nil && *c.opConfig.EnableCRDRegistration { - if err := c.createPostgresCRD(c.opConfig.EnableCRDValidation); err != nil { + if err := c.createPostgresCRD(); err != nil { c.logger.Fatalf("could not register Postgres CustomResourceDefinition: %v", err) } } diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 20958dd7b..fbf12bfb9 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -35,6 +35,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // general config result.EnableCRDRegistration = util.CoalesceBool(fromCRD.EnableCRDRegistration, util.True()) result.EnableCRDValidation = util.CoalesceBool(fromCRD.EnableCRDValidation, util.True()) + result.CRDCategories = util.CoalesceStrArr(fromCRD.CRDCategories, []string{"all"}) result.EnableLazySpiloUpgrade = fromCRD.EnableLazySpiloUpgrade result.EnablePgVersionEnvVar = fromCRD.EnablePgVersionEnvVar result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat diff --git a/pkg/controller/util.go b/pkg/controller/util.go index 563734ac9..bca8082f6 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -53,28 +53,35 @@ func (c *Controller) clusterWorkerID(clusterName spec.NamespacedName) uint32 { return c.clusterWorkers[clusterName] } -func (c *Controller) createOperatorCRD(crd *apiextv1.CustomResourceDefinition) error { - if _, err := c.KubeClient.CustomResourceDefinitions().Create(context.TODO(), crd, metav1.CreateOptions{}); err != nil { - if k8sutil.ResourceAlreadyExists(err) { - c.logger.Infof("customResourceDefinition %q is already registered and will only be updated", crd.Name) - - patch, err := json.Marshal(crd) - if err != nil { - return fmt.Errorf("could not marshal new customResourceDefintion: %v", err) - } - if _, err := c.KubeClient.CustomResourceDefinitions().Patch( - context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{}); err != nil { - return fmt.Errorf("could not update customResourceDefinition: %v", err) - } - } else { - c.logger.Errorf("could not create customResourceDefinition %q: %v", crd.Name, err) +func (c *Controller) createOperatorCRD(desiredCrd *apiextv1.CustomResourceDefinition) error { + crd, err := c.KubeClient.CustomResourceDefinitions().Get(context.TODO(), desiredCrd.Name, metav1.GetOptions{}) + if k8sutil.ResourceNotFound(err) { + if _, err := c.KubeClient.CustomResourceDefinitions().Create(context.TODO(), desiredCrd, metav1.CreateOptions{}); err != nil { + return fmt.Errorf("could not create customResourceDefinition %q: %v", desiredCrd.Name, err) + } + } + if err != nil { + c.logger.Errorf("could not get customResourceDefinition %q: %v", desiredCrd.Name, err) + } + if crd != nil { + c.logger.Infof("customResourceDefinition %q is already registered and will only be updated", crd.Name) + // copy annotations and labels from existing CRD since we do not define them + desiredCrd.Annotations = crd.Annotations + desiredCrd.Labels = crd.Labels + patch, err := json.Marshal(desiredCrd) + if err != nil { + return fmt.Errorf("could not marshal new customResourceDefintion %q: %v", desiredCrd.Name, err) + } + if _, err := c.KubeClient.CustomResourceDefinitions().Patch( + context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{}); err != nil { + return fmt.Errorf("could not update customResourceDefinition %q: %v", crd.Name, err) } } else { c.logger.Infof("customResourceDefinition %q has been registered", crd.Name) } return wait.Poll(c.config.CRDReadyWaitInterval, c.config.CRDReadyWaitTimeout, func() (bool, error) { - c, err := c.KubeClient.CustomResourceDefinitions().Get(context.TODO(), crd.Name, metav1.GetOptions{}) + c, err := c.KubeClient.CustomResourceDefinitions().Get(context.TODO(), desiredCrd.Name, metav1.GetOptions{}) if err != nil { return false, err } @@ -96,12 +103,12 @@ func (c *Controller) createOperatorCRD(crd *apiextv1.CustomResourceDefinition) e }) } -func (c *Controller) createPostgresCRD(enableValidation *bool) error { - return c.createOperatorCRD(acidv1.PostgresCRD(enableValidation)) +func (c *Controller) createPostgresCRD() error { + return c.createOperatorCRD(acidv1.PostgresCRD(c.opConfig.CRDCategories)) } -func (c *Controller) createConfigurationCRD(enableValidation *bool) error { - return c.createOperatorCRD(acidv1.ConfigurationCRD(enableValidation)) +func (c *Controller) createConfigurationCRD() error { + return c.createOperatorCRD(acidv1.ConfigurationCRD(c.opConfig.CRDCategories)) } func readDecodedRole(s string) (*spec.PgUser, error) { diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index fa5d3b770..0dc1004a7 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -20,6 +20,7 @@ type CRD struct { RepairPeriod time.Duration `name:"repair_period" default:"5m"` EnableCRDRegistration *bool `name:"enable_crd_registration" default:"true"` EnableCRDValidation *bool `name:"enable_crd_validation" default:"true"` + CRDCategories []string `name:"crd_categories" default:"all"` } // Resources describes kubernetes resource specific configuration parameters