From 54a76612edfe4ecaf461bc675cbd57c40f1cf038 Mon Sep 17 00:00:00 2001 From: Kate Stanley <11195226+katheris@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:51:03 +0100 Subject: [PATCH] Add env var to disable creation of PodDisruptionBudget (#10614) Signed-off-by: Katherine Stanley <11195226+katheris@users.noreply.github.com> --- CHANGELOG.md | 5 +++-- .../cluster/ClusterOperatorConfig.java | 13 +++++++++++ .../assembly/AbstractConnectOperator.java | 22 ++++++++++++++++++- .../assembly/KafkaBridgeAssemblyOperator.java | 4 +++- .../KafkaConnectAssemblyOperator.java | 2 +- .../KafkaMirrorMaker2AssemblyOperator.java | 2 +- .../operator/assembly/KafkaReconciler.java | 10 +++++++-- .../assembly/ZooKeeperReconciler.java | 12 +++++++--- .../cluster/ClusterOperatorConfigTest.java | 5 +++++ .../configuring/assembly-config.adoc | 3 +++ ...able-pod-disruption-budget-generation.adoc | 11 ++++++++++ .../con-configuring-cluster-operator.adoc | 6 +++++ ...0-Deployment-strimzi-cluster-operator.yaml | 4 ++++ .../helm3/strimzi-kafka-operator/values.yaml | 4 +++- 14 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 documentation/modules/configuring/proc-disable-pod-disruption-budget-generation.adoc diff --git a/CHANGELOG.md b/CHANGELOG.md index 25194fad7d1..c9ab1aea834 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,9 @@ * The `ContinueReconciliationOnManualRollingUpdateFailure` feature gate moves to beta stage and is enabled by default. If needed, `ContinueReconciliationOnManualRollingUpdateFailure` can be disabled in the feature gates configuration in the Cluster Operator. * Add support for managing connector offsets via KafkaConnector and KafkaMirrorMaker2 custom resources. -* Add support for templating `host` and `advertisedHost` fields in listener configuration -* Allow configuration of environment variables based on Config Map or Secret for every container in the container template sections +* Add support for templating `host` and `advertisedHost` fields in listener configuration. +* Allow configuration of environment variables based on Config Map or Secret for every container in the container template sections. +* Add support for disabling the generation of PodDisruptionBudget resources by the Cluster Operator. ### Changes, deprecations and removals diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/ClusterOperatorConfig.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/ClusterOperatorConfig.java index f1a3b9b7a80..df00e87294c 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/ClusterOperatorConfig.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/ClusterOperatorConfig.java @@ -249,6 +249,11 @@ public class ClusterOperatorConfig { */ /* test */ static final ConfigParameter POD_SECURITY_PROVIDER_RESTRICTED_CLASS = new ConfigParameter<>("POD_SECURITY_PROVIDER_RESTRICTED_CLASS", STRING, "io.strimzi.plugin.security.profiles.impl.RestrictedPodSecurityProvider", CONFIG_VALUES); + /** + * Set true to generate Pod Disruption Budgets + */ + public static final ConfigParameter POD_DISRUPTION_BUDGET_GENERATION = new ConfigParameter<>("STRIMZI_POD_DISRUPTION_BUDGET_GENERATION", BOOLEAN, "true", CONFIG_VALUES); + /** * The configured Kafka versions */ @@ -598,6 +603,13 @@ public LeaderElectionManagerConfig getLeaderElectionConfig() { } } + /** + * @return Indicates whether Pod Disruption Budgets should be generated + */ + public boolean isPodDisruptionBudgetGeneration() { + return get(POD_DISRUPTION_BUDGET_GENERATION); + } + @Override public String toString() { return "ClusterOperatorConfig{" + @@ -620,6 +632,7 @@ public String toString() { "\n\toperatorName='" + getOperatorName() + '\'' + "\n\tpodSecurityProviderClass='" + getPodSecurityProviderClass() + '\'' + "\n\tleaderElectionConfig='" + getLeaderElectionConfig() + '\'' + + "\n\tpodDisruptionBudgetGeneration=" + isPodDisruptionBudgetGeneration() + "}"; } } diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java index 2c5ab37cd0f..ccfb95fc82d 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/AbstractConnectOperator.java @@ -101,6 +101,7 @@ public abstract class AbstractConnectOperator connectClientProvider; protected final ImagePullPolicy imagePullPolicy; @@ -164,6 +165,7 @@ public AbstractConnectOperator(Vertx vertx, PlatformFeaturesAvailability pfa, St this.sharedEnvironmentProvider = supplier.sharedEnvironmentProvider; this.port = port; this.continueOnManualRUFailure = config.featureGates().continueOnManualRUFailureEnabled(); + this.isPodDisruptionBudgetGeneration = config.isPodDisruptionBudgetGeneration(); } @Override @@ -299,7 +301,7 @@ protected Future reconcilePodSet(Reconciliation reconciliation, KafkaConnectCluster connect, Map podAnnotations, Map podSetAnnotations, - String customContainerImage) { + String customContainerImage) { return podSetOperations.reconcile(reconciliation, reconciliation.namespace(), connect.getComponentName(), connect.generatePodSet(connect.getReplicas(), podSetAnnotations, podAnnotations, pfa.isOpenshift(), imagePullPolicy, imagePullSecrets, customContainerImage)) .compose(reconciliationResult -> { KafkaConnectRoller roller = new KafkaConnectRoller(reconciliation, connect, operationTimeoutMs, podOperations); @@ -308,6 +310,24 @@ protected Future reconcilePodSet(Reconciliation reconciliation, .compose(i -> podSetOperations.readiness(reconciliation, reconciliation.namespace(), connect.getComponentName(), 1_000, operationTimeoutMs)); } + /** + * Reconciles the PodDisruptionBudget for the Connect cluster. + * + * @param reconciliation The reconciliation + * @param namespace Namespace of the Connect cluster + * @param connect KafkaConnectCluster object + * + * @return Future for tracking the asynchronous result of reconciling the PodDisruptionBudget + */ + protected Future connectPodDisruptionBudget(Reconciliation reconciliation, String namespace, KafkaConnectCluster connect) { + if (isPodDisruptionBudgetGeneration) { + return podDisruptionBudgetOperator.reconcile(reconciliation, namespace, connect.getComponentName(), connect.generatePodDisruptionBudget()) + .mapEmpty(); + } else { + return Future.succeededFuture(); + } + } + /** * Dynamically updates loggers in the Kafka Connect cluster. * diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaBridgeAssemblyOperator.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaBridgeAssemblyOperator.java index a5b6ddc5b3a..789826373d9 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaBridgeAssemblyOperator.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaBridgeAssemblyOperator.java @@ -48,6 +48,7 @@ public class KafkaBridgeAssemblyOperator extends AbstractAssemblyOperator createOrUpdate(Reconciliation reconciliation .compose(scale -> serviceOperations.reconcile(reconciliation, namespace, KafkaBridgeResources.serviceName(bridge.getCluster()), bridge.generateService())) .compose(i -> MetricsAndLoggingUtils.metricsAndLogging(reconciliation, configMapOperations, bridge.logging(), null)) .compose(metricsAndLogging -> configMapOperations.reconcile(reconciliation, namespace, KafkaBridgeResources.metricsAndLogConfigMapName(reconciliation.name()), bridge.generateMetricsAndLogConfigMap(metricsAndLogging))) - .compose(i -> podDisruptionBudgetOperator.reconcile(reconciliation, namespace, bridge.getComponentName(), bridge.generatePodDisruptionBudget())) + .compose(i -> isPodDisruptionBudgetGeneration ? podDisruptionBudgetOperator.reconcile(reconciliation, namespace, bridge.getComponentName(), bridge.generatePodDisruptionBudget()) : Future.succeededFuture()) .compose(i -> VertxUtil.authTlsHash(secretOperations, namespace, auth, trustedCertificates)) .compose(hash -> deploymentOperations.reconcile(reconciliation, namespace, bridge.getComponentName(), bridge.generateDeployment(Collections.singletonMap(Annotations.ANNO_STRIMZI_AUTH_HASH, Integer.toString(hash)), pfa.isOpenshift(), imagePullPolicy, imagePullSecrets))) .compose(i -> deploymentOperations.scaleUp(reconciliation, namespace, bridge.getComponentName(), bridge.getReplicas(), operationTimeoutMs)) diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java index 6e512f07030..cdabc68a2d8 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaConnectAssemblyOperator.java @@ -203,7 +203,7 @@ protected Future createOrUpdate(Reconciliation reconciliatio return configMapOperations.reconcile(reconciliation, namespace, logAndMetricsConfigMap.getMetadata().getName(), logAndMetricsConfigMap); }) .compose(i -> ReconcilerUtils.reconcileJmxSecret(reconciliation, secretOperations, connect)) - .compose(i -> podDisruptionBudgetOperator.reconcile(reconciliation, namespace, connect.getComponentName(), connect.generatePodDisruptionBudget())) + .compose(i -> connectPodDisruptionBudget(reconciliation, namespace, connect)) .compose(i -> generateAuthHash(namespace, kafkaConnect.getSpec())) .compose(hash -> { podAnnotations.put(Annotations.ANNO_STRIMZI_AUTH_HASH, Integer.toString(hash)); diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java index 16c3d5a4ac8..26dd2321e14 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaMirrorMaker2AssemblyOperator.java @@ -141,7 +141,7 @@ protected Future createOrUpdate(Reconciliation reconcil return configMapOperations.reconcile(reconciliation, namespace, logAndMetricsConfigMap.getMetadata().getName(), logAndMetricsConfigMap); }) .compose(i -> ReconcilerUtils.reconcileJmxSecret(reconciliation, secretOperations, mirrorMaker2Cluster)) - .compose(i -> podDisruptionBudgetOperator.reconcile(reconciliation, namespace, mirrorMaker2Cluster.getComponentName(), mirrorMaker2Cluster.generatePodDisruptionBudget())) + .compose(i -> connectPodDisruptionBudget(reconciliation, namespace, mirrorMaker2Cluster)) .compose(i -> generateAuthHash(namespace, kafkaMirrorMaker2.getSpec())) .compose(hash -> { podAnnotations.put(Annotations.ANNO_STRIMZI_AUTH_HASH, Integer.toString(hash)); diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaReconciler.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaReconciler.java index 69e7c188a3d..d9db321bd84 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaReconciler.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/KafkaReconciler.java @@ -114,6 +114,7 @@ public class KafkaReconciler { // Various settings private final long operationTimeoutMs; private final boolean isNetworkPolicyGeneration; + private final boolean isPodDisruptionBudgetGeneration; private final boolean isKafkaNodePoolsEnabled; private final List maintenanceWindows; private final String operatorNamespace; @@ -213,6 +214,7 @@ public KafkaReconciler( this.imagePullPolicy = config.getImagePullPolicy(); this.imagePullSecrets = config.getImagePullSecrets(); this.previousNodeIds = kafkaCr.getStatus() != null ? kafkaCr.getStatus().getRegisteredNodeIds() : null; + this.isPodDisruptionBudgetGeneration = config.isPodDisruptionBudgetGeneration(); this.stsOperator = supplier.stsOperations; this.strimziPodSetOperator = supplier.strimziPodSetOperator; @@ -765,9 +767,13 @@ protected Future jmxSecret() { * @return Completes when the PDB was successfully created or updated */ protected Future podDisruptionBudget() { - return podDisruptionBudgetOperator + if (isPodDisruptionBudgetGeneration) { + return podDisruptionBudgetOperator .reconcile(reconciliation, reconciliation.namespace(), KafkaResources.kafkaComponentName(reconciliation.name()), kafka.generatePodDisruptionBudget()) - .map((Void) null); + .mapEmpty(); + } else { + return Future.succeededFuture(); + } } /** diff --git a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/ZooKeeperReconciler.java b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/ZooKeeperReconciler.java index 8330f130e5a..ef0ca8a8249 100644 --- a/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/ZooKeeperReconciler.java +++ b/cluster-operator/src/main/java/io/strimzi/operator/cluster/operator/assembly/ZooKeeperReconciler.java @@ -83,6 +83,7 @@ public class ZooKeeperReconciler { private final String operatorNamespace; private final Labels operatorNamespaceLabels; private final boolean isNetworkPolicyGeneration; + private final boolean isPodDisruptionBudgetGeneration; private final PlatformFeaturesAvailability pfa; private final int adminSessionTimeoutMs; private final ImagePullPolicy imagePullPolicy; @@ -161,6 +162,7 @@ public ZooKeeperReconciler( this.imagePullPolicy = config.getImagePullPolicy(); this.imagePullSecrets = config.getImagePullSecrets(); this.isKRaftMigrationRollback = isKRaftMigrationRollback; + this.isPodDisruptionBudgetGeneration = config.isPodDisruptionBudgetGeneration(); this.stsOperator = supplier.stsOperations; this.strimziPodSetOperator = supplier.strimziPodSetOperator; @@ -492,9 +494,13 @@ protected Future loggingAndMetricsConfigMap() { * @return Completes when the PDB was successfully created or updated */ protected Future podDisruptionBudget() { - return podDisruptionBudgetOperator - .reconcile(reconciliation, reconciliation.namespace(), KafkaResources.zookeeperComponentName(reconciliation.name()), zk.generatePodDisruptionBudget()) - .map((Void) null); + if (isPodDisruptionBudgetGeneration) { + return podDisruptionBudgetOperator + .reconcile(reconciliation, reconciliation.namespace(), KafkaResources.zookeeperComponentName(reconciliation.name()), zk.generatePodDisruptionBudget()) + .mapEmpty(); + } else { + return Future.succeededFuture(); + } } /** diff --git a/cluster-operator/src/test/java/io/strimzi/operator/cluster/ClusterOperatorConfigTest.java b/cluster-operator/src/test/java/io/strimzi/operator/cluster/ClusterOperatorConfigTest.java index b758139513b..b50910235fe 100644 --- a/cluster-operator/src/test/java/io/strimzi/operator/cluster/ClusterOperatorConfigTest.java +++ b/cluster-operator/src/test/java/io/strimzi/operator/cluster/ClusterOperatorConfigTest.java @@ -47,6 +47,7 @@ public class ClusterOperatorConfigTest { ENV_VARS.put(ClusterOperatorConfig.FEATURE_GATES.key(), "-ContinueReconciliationOnManualRollingUpdateFailure"); ENV_VARS.put(ClusterOperatorConfig.DNS_CACHE_TTL.key(), "10"); ENV_VARS.put(ClusterOperatorConfig.POD_SECURITY_PROVIDER_CLASS.key(), "my.package.CustomPodSecurityProvider"); + ENV_VARS.put(ClusterOperatorConfig.POD_DISRUPTION_BUDGET_GENERATION.key(), "false"); } @Test @@ -57,6 +58,7 @@ public void testDefaultConfig() { envVars.remove(ClusterOperatorConfig.CONNECT_BUILD_TIMEOUT_MS.key()); envVars.remove(ClusterOperatorConfig.FEATURE_GATES.key()); envVars.remove(ClusterOperatorConfig.POD_SECURITY_PROVIDER_CLASS.key()); + envVars.remove(ClusterOperatorConfig.POD_DISRUPTION_BUDGET_GENERATION.key()); ClusterOperatorConfig config = ClusterOperatorConfig.buildFromMap(envVars, KafkaVersionTestUtils.getKafkaVersionLookup()); @@ -71,6 +73,7 @@ public void testDefaultConfig() { assertThat(config.isPodSetReconciliationOnly(), is(false)); assertThat(config.getPodSecurityProviderClass(), is(ClusterOperatorConfig.POD_SECURITY_PROVIDER_CLASS.defaultValue())); assertThat(config.getLeaderElectionConfig(), is(nullValue())); + assertThat(config.isPodDisruptionBudgetGeneration(), is(true)); } @Test @@ -103,6 +106,7 @@ public void testEnvVars() { assertThat(config.featureGates().continueOnManualRUFailureEnabled(), is(false)); assertThat(config.getDnsCacheTtlSec(), is(10)); assertThat(config.getPodSecurityProviderClass(), is("my.package.CustomPodSecurityProvider")); + assertThat(config.isPodDisruptionBudgetGeneration(), is(false)); } @Test @@ -120,6 +124,7 @@ public void testEnvVarsDefault() { assertThat(config.featureGates().continueOnManualRUFailureEnabled(), is(true)); assertThat(config.getDnsCacheTtlSec(), is(Integer.parseInt(ClusterOperatorConfig.DNS_CACHE_TTL.defaultValue()))); assertThat(config.getPodSecurityProviderClass(), is(ClusterOperatorConfig.POD_SECURITY_PROVIDER_CLASS.defaultValue())); + assertThat(config.isPodDisruptionBudgetGeneration(), is(true)); } private Map envWithImages() { diff --git a/documentation/assemblies/configuring/assembly-config.adoc b/documentation/assemblies/configuring/assembly-config.adoc index 1868205b8d0..731ad489b0d 100644 --- a/documentation/assemblies/configuring/assembly-config.adoc +++ b/documentation/assemblies/configuring/assembly-config.adoc @@ -177,6 +177,9 @@ include::../../modules/configuring/ref-kubernetes-labels.adoc[leveloffset=+1] //scheduling separate Kafka pods include::assembly-scheduling.adoc[leveloffset=+1] +//disabling pod disruption budgets +include::../../modules/configuring/proc-disable-pod-disruption-budget-generation.adoc[leveloffset=+1] + //configuring log levels include::assembly-logging-configuration.adoc[leveloffset=+1] diff --git a/documentation/modules/configuring/proc-disable-pod-disruption-budget-generation.adoc b/documentation/modules/configuring/proc-disable-pod-disruption-budget-generation.adoc new file mode 100644 index 00000000000..c83a1043856 --- /dev/null +++ b/documentation/modules/configuring/proc-disable-pod-disruption-budget-generation.adoc @@ -0,0 +1,11 @@ +// Module included in the following assemblies: +// +// assembly-config.adoc + +[id='disable-pod-disruption-budget-generation_{context}'] += Disabling pod disruption budget generation + +Strimzi generates pod disruption budget resources for Kafka, Kafka Connect worker, MirrorMaker2 worker, and Kafka Bridge worker nodes. + +If you want to use custom pod disruption budget resources, you can set the `STRIMZI_POD_DISRUPTION_BUDGET_GENERATION` environment variable to `false` in the Cluster Operator configuration. +For more information, see xref:ref-operator-cluster-{context}[]. diff --git a/documentation/modules/operators/con-configuring-cluster-operator.adoc b/documentation/modules/operators/con-configuring-cluster-operator.adoc index e181f355a39..ea41a080758 100644 --- a/documentation/modules/operators/con-configuring-cluster-operator.adoc +++ b/documentation/modules/operators/con-configuring-cluster-operator.adoc @@ -80,6 +80,12 @@ env: value: label1=value1,label2=value2 ---- +`STRIMZI_POD_DISRUPTION_BUDGET_GENERATION`:: Optional, default `true`. +Pod disruption budget for resources. +A pod disruption budget with the `maxUnavailable` value set to zero prevents Kubernetes from evicting pods automatically. ++ +Set this environment variable to `false` to disable pod disruption budget generation. You might do this, for example, if you want to manage the pod disruption budgets yourself, or if you have a development environment where availability is not important. + `STRIMZI_LABELS_EXCLUSION_PATTERN`:: Optional, default regex pattern is `(^app.kubernetes.io/(?!part-of).*|^kustomize.toolkit.fluxcd.io.*)`. The regex exclusion pattern used to filter labels propagation from the main custom resource to its subresources. The labels exclusion filter is not applied to labels in template sections such as `spec.kafka.template.pod.metadata.labels`. diff --git a/packaging/helm-charts/helm3/strimzi-kafka-operator/templates/060-Deployment-strimzi-cluster-operator.yaml b/packaging/helm-charts/helm3/strimzi-kafka-operator/templates/060-Deployment-strimzi-cluster-operator.yaml index 67564c1b783..16e4f0f225b 100644 --- a/packaging/helm-charts/helm3/strimzi-kafka-operator/templates/060-Deployment-strimzi-cluster-operator.yaml +++ b/packaging/helm-charts/helm3/strimzi-kafka-operator/templates/060-Deployment-strimzi-cluster-operator.yaml @@ -140,6 +140,10 @@ spec: - name: STRIMZI_CONNECT_BUILD_TIMEOUT_MS value: {{ .Values.connectBuildTimeoutMs | quote }} {{- end }} + {{- if ne .Values.generatePodDisruptionBudget true}} + - name: STRIMZI_POD_DISRUPTION_BUDGET_GENERATION + value: {{ .Values.generatePodDisruptionBudget | quote }} + {{- end }} {{- if .Values.extraEnvs }} {{ toYaml .Values.extraEnvs | indent 12 }} {{- end }} diff --git a/packaging/helm-charts/helm3/strimzi-kafka-operator/values.yaml b/packaging/helm-charts/helm3/strimzi-kafka-operator/values.yaml index 063f993a977..257ccbe17ba 100644 --- a/packaging/helm-charts/helm3/strimzi-kafka-operator/values.yaml +++ b/packaging/helm-charts/helm3/strimzi-kafka-operator/values.yaml @@ -180,4 +180,6 @@ labelsExclusionPattern: "" # Controls whether Strimzi generates network policy resources (By default true) generateNetworkPolicy: true # Override the value for Connect build timeout -connectBuildTimeoutMs: 300000 \ No newline at end of file +connectBuildTimeoutMs: 300000 +# Controls whether Strimzi generates pod disruption budget resources (By default true) +generatePodDisruptionBudget: true \ No newline at end of file