From b621d9ddf35ed002077a10e008a5d2527f166c12 Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Sun, 11 Sep 2022 19:35:34 +0200 Subject: [PATCH 01/10] Try to get lease namespace if unspecified --- .../operator/LeaderElectionManager.java | 72 +++++++++++++++---- .../config/LeaderElectionConfiguration.java | 13 +++- .../operator/LeaderElectionManagerTest.java | 63 ++++++++++++++++ 3 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java index 94d201dce9..9cfe4f8199 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java @@ -1,5 +1,9 @@ package io.javaoperatorsdk.operator; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -12,10 +16,13 @@ import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElector; import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElectorBuilder; import io.fabric8.kubernetes.client.extended.leaderelection.resourcelock.LeaseLock; -import io.fabric8.kubernetes.client.extended.leaderelection.resourcelock.Lock; +import io.fabric8.kubernetes.client.utils.Utils; import io.javaoperatorsdk.operator.api.config.ConfigurationServiceProvider; import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; +import static io.fabric8.kubernetes.client.Config.KUBERNETES_NAMESPACE_FILE; +import static io.fabric8.kubernetes.client.Config.KUBERNETES_NAMESPACE_PATH; + public class LeaderElectionManager { private static final Logger log = LoggerFactory.getLogger(LeaderElectionManager.class); @@ -31,14 +38,29 @@ public LeaderElectionManager(ControllerManager controllerManager) { public void init(LeaderElectionConfiguration config, KubernetesClient client) { this.identity = identity(config); - Lock lock = new LeaseLock(config.getLeaseNamespace(), config.getLeaseName(), identity); + final var leaseNamespace = + config.getLeaseNamespace().or(LeaderElectionManager::tryGetInClusterNamespace); + if (leaseNamespace.isEmpty()) { + final var message = + "Lease namespace is not set and cannot be inferred. Leader election cannot continue."; + log.error(message); + throw new IllegalArgumentException(message); + } + final var lock = new LeaseLock(leaseNamespace.orElseThrow(), config.getLeaseName(), identity); // releaseOnCancel is not used in the underlying implementation - leaderElector = new LeaderElectorBuilder(client, - ConfigurationServiceProvider.instance().getExecutorService()) - .withConfig( - new LeaderElectionConfig(lock, config.getLeaseDuration(), config.getRenewDeadline(), - config.getRetryPeriod(), leaderCallbacks(), true, config.getLeaseName())) - .build(); + leaderElector = + new LeaderElectorBuilder( + client, ConfigurationServiceProvider.instance().getExecutorService()) + .withConfig( + new LeaderElectionConfig( + lock, + config.getLeaseDuration(), + config.getRenewDeadline(), + config.getRetryPeriod(), + leaderCallbacks(), + true, + config.getLeaseName())) + .build(); } public boolean isLeaderElectionEnabled() { @@ -46,9 +68,12 @@ public boolean isLeaderElectionEnabled() { } private LeaderCallbacks leaderCallbacks() { - return new LeaderCallbacks(this::startLeading, this::stopLeading, leader -> { - log.info("New leader with identity: {}", leader); - }); + return new LeaderCallbacks( + this::startLeading, + this::stopLeading, + leader -> { + log.info("New leader with identity: {}", leader); + }); } private void startLeading() { @@ -64,13 +89,36 @@ private void stopLeading() { } private String identity(LeaderElectionConfiguration config) { - String id = config.getIdentity().orElse(System.getenv("HOSTNAME")); + var id = config.getIdentity().orElse(System.getenv("HOSTNAME")); if (id == null || id.isBlank()) { id = UUID.randomUUID().toString(); } return id; } + private static Optional tryGetInClusterNamespace() { + log.info("Trying to get namespace from Kubernetes service account namespace path..."); + + final var serviceAccountNamespace = + Utils.getSystemPropertyOrEnvVar(KUBERNETES_NAMESPACE_FILE, KUBERNETES_NAMESPACE_PATH); + final var serviceAccountNamespacePath = Path.of(serviceAccountNamespace); + + final var serviceAccountNamespaceExists = Files.isRegularFile(serviceAccountNamespacePath); + if (serviceAccountNamespaceExists) { + log.info("Found service account namespace at: [{}].", serviceAccountNamespace); + try { + return Optional.of(Files.readString(serviceAccountNamespacePath)); + } catch (IOException e) { + log.error( + "Error reading service account namespace from: [" + serviceAccountNamespace + "].", e); + return Optional.empty(); + } + } else { + log.warn("Did not find service account namespace at: [{}].", serviceAccountNamespace); + return Optional.empty(); + } + } + public void start() { if (isLeaderElectionEnabled()) { leaderElectionFuture = leaderElector.start(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java index 5146fa6a1e..5a2c322657 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java @@ -35,6 +35,15 @@ public LeaderElectionConfiguration(String leaseName, String leaseNamespace) { RETRY_PERIOD_DEFAULT_VALUE, null); } + public LeaderElectionConfiguration(String leaseName) { + this( + leaseName, + null, + LEASE_DURATION_DEFAULT_VALUE, + RENEW_DEADLINE_DEFAULT_VALUE, + RETRY_PERIOD_DEFAULT_VALUE, null); + } + public LeaderElectionConfiguration( String leaseName, String leaseNamespace, @@ -59,8 +68,8 @@ public LeaderElectionConfiguration( this.identity = identity; } - public String getLeaseNamespace() { - return leaseNamespace; + public Optional getLeaseNamespace() { + return Optional.ofNullable(leaseNamespace); } public String getLeaseName() { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java new file mode 100644 index 0000000000..e67e6251d2 --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java @@ -0,0 +1,63 @@ +package io.javaoperatorsdk.operator; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; + +import static io.fabric8.kubernetes.client.Config.KUBERNETES_NAMESPACE_FILE; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +class LeaderElectionManagerTest { + + private ControllerManager controllerManager; + private KubernetesClient kubernetesClient; + private LeaderElectionManager leaderElectionManager; + + @BeforeEach + void setUp() { + controllerManager = mock(ControllerManager.class); + kubernetesClient = mock(KubernetesClient.class); + leaderElectionManager = new LeaderElectionManager(controllerManager); + } + + @AfterEach + void tearDown() { + System.getProperties().remove(KUBERNETES_NAMESPACE_FILE); + } + + @Test + void testInit() { + leaderElectionManager.init(new LeaderElectionConfiguration("test", "testns"), kubernetesClient); + assertTrue(leaderElectionManager.isLeaderElectionEnabled()); + } + + @Test + void testInitInferLeaseNamespace(@TempDir Path tempDir) throws IOException { + var namespace = "foo"; + var namespacePath = tempDir.resolve("namespace"); + Files.writeString(namespacePath, namespace); + + System.setProperty(KUBERNETES_NAMESPACE_FILE, namespacePath.toString()); + + leaderElectionManager.init(new LeaderElectionConfiguration("test"), kubernetesClient); + assertTrue(leaderElectionManager.isLeaderElectionEnabled()); + } + + @Test + void testFailedToInitInferLeaseNamespace() { + assertThrows( + IllegalArgumentException.class, + () -> leaderElectionManager.init(new LeaderElectionConfiguration("test"), + kubernetesClient)); + } +} From 1317c4f9a36e0d07d4de0fc65c19152f65cf0670 Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Sun, 11 Sep 2022 19:57:06 +0200 Subject: [PATCH 02/10] More precise method name --- .../io/javaoperatorsdk/operator/LeaderElectionManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java index 9cfe4f8199..218f3fde3a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java @@ -39,7 +39,7 @@ public LeaderElectionManager(ControllerManager controllerManager) { public void init(LeaderElectionConfiguration config, KubernetesClient client) { this.identity = identity(config); final var leaseNamespace = - config.getLeaseNamespace().or(LeaderElectionManager::tryGetInClusterNamespace); + config.getLeaseNamespace().or(LeaderElectionManager::tryNamespaceFromPath); if (leaseNamespace.isEmpty()) { final var message = "Lease namespace is not set and cannot be inferred. Leader election cannot continue."; @@ -96,7 +96,7 @@ private String identity(LeaderElectionConfiguration config) { return id; } - private static Optional tryGetInClusterNamespace() { + private static Optional tryNamespaceFromPath() { log.info("Trying to get namespace from Kubernetes service account namespace path..."); final var serviceAccountNamespace = From cdd6feaf3b282b52072b909f11664951e9641b81 Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Mon, 12 Sep 2022 14:05:22 +0200 Subject: [PATCH 03/10] Strip to remove whitespaces --- .../javaoperatorsdk/operator/LeaderElectionManager.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java index 218f3fde3a..ebb3f95594 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java @@ -107,16 +107,17 @@ private static Optional tryNamespaceFromPath() { if (serviceAccountNamespaceExists) { log.info("Found service account namespace at: [{}].", serviceAccountNamespace); try { - return Optional.of(Files.readString(serviceAccountNamespacePath)); + return Optional + .of(Files.readString(serviceAccountNamespacePath).strip()); } catch (IOException e) { log.error( "Error reading service account namespace from: [" + serviceAccountNamespace + "].", e); return Optional.empty(); } - } else { - log.warn("Did not find service account namespace at: [{}].", serviceAccountNamespace); - return Optional.empty(); } + + log.warn("Did not find service account namespace at: [{}].", serviceAccountNamespace); + return Optional.empty(); } public void start() { From 0c1294753dba5cdca429e8f1487f792b4cf62bb3 Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Mon, 12 Sep 2022 14:20:59 +0200 Subject: [PATCH 04/10] Get namespace from cached cilent config --- .../operator/LeaderElectionManager.java | 7 ++++--- .../operator/LeaderElectionManagerTest.java | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java index ebb3f95594..7965ff8e6e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java @@ -39,14 +39,15 @@ public LeaderElectionManager(ControllerManager controllerManager) { public void init(LeaderElectionConfiguration config, KubernetesClient client) { this.identity = identity(config); final var leaseNamespace = - config.getLeaseNamespace().or(LeaderElectionManager::tryNamespaceFromPath); - if (leaseNamespace.isEmpty()) { + config.getLeaseNamespace().orElseGet( + () -> ConfigurationServiceProvider.instance().getClientConfiguration().getNamespace()); + if (leaseNamespace == null) { final var message = "Lease namespace is not set and cannot be inferred. Leader election cannot continue."; log.error(message); throw new IllegalArgumentException(message); } - final var lock = new LeaseLock(leaseNamespace.orElseThrow(), config.getLeaseName(), identity); + final var lock = new LeaseLock(leaseNamespace, config.getLeaseName(), identity); // releaseOnCancel is not used in the underlying implementation leaderElector = new LeaderElectorBuilder( diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java index e67e6251d2..6f44ba3a06 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java @@ -12,6 +12,7 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; +import static io.fabric8.kubernetes.client.Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY; import static io.fabric8.kubernetes.client.Config.KUBERNETES_NAMESPACE_FILE; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -33,6 +34,7 @@ void setUp() { @AfterEach void tearDown() { System.getProperties().remove(KUBERNETES_NAMESPACE_FILE); + System.getProperties().remove(KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY); } @Test @@ -47,6 +49,7 @@ void testInitInferLeaseNamespace(@TempDir Path tempDir) throws IOException { var namespacePath = tempDir.resolve("namespace"); Files.writeString(namespacePath, namespace); + System.setProperty(KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); System.setProperty(KUBERNETES_NAMESPACE_FILE, namespacePath.toString()); leaderElectionManager.init(new LeaderElectionConfiguration("test"), kubernetesClient); @@ -55,6 +58,15 @@ void testInitInferLeaseNamespace(@TempDir Path tempDir) throws IOException { @Test void testFailedToInitInferLeaseNamespace() { + System.setProperty(KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + assertThrows( + IllegalArgumentException.class, + () -> leaderElectionManager.init(new LeaderElectionConfiguration("test"), + kubernetesClient)); + } + + @Test + void testFailedToInitInferLeaseNamespaceProbablyUsingKubeConfig() { assertThrows( IllegalArgumentException.class, () -> leaderElectionManager.init(new LeaderElectionConfiguration("test"), From 014e05aa40b93a27f888e6fac9a0dce500e35ce5 Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Mon, 12 Sep 2022 14:23:48 +0200 Subject: [PATCH 05/10] Remove unused method --- .../operator/LeaderElectionManager.java | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java index 7965ff8e6e..931a7ddf27 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java @@ -1,9 +1,5 @@ package io.javaoperatorsdk.operator; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -16,13 +12,9 @@ import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElector; import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElectorBuilder; import io.fabric8.kubernetes.client.extended.leaderelection.resourcelock.LeaseLock; -import io.fabric8.kubernetes.client.utils.Utils; import io.javaoperatorsdk.operator.api.config.ConfigurationServiceProvider; import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; -import static io.fabric8.kubernetes.client.Config.KUBERNETES_NAMESPACE_FILE; -import static io.fabric8.kubernetes.client.Config.KUBERNETES_NAMESPACE_PATH; - public class LeaderElectionManager { private static final Logger log = LoggerFactory.getLogger(LeaderElectionManager.class); @@ -97,30 +89,6 @@ private String identity(LeaderElectionConfiguration config) { return id; } - private static Optional tryNamespaceFromPath() { - log.info("Trying to get namespace from Kubernetes service account namespace path..."); - - final var serviceAccountNamespace = - Utils.getSystemPropertyOrEnvVar(KUBERNETES_NAMESPACE_FILE, KUBERNETES_NAMESPACE_PATH); - final var serviceAccountNamespacePath = Path.of(serviceAccountNamespace); - - final var serviceAccountNamespaceExists = Files.isRegularFile(serviceAccountNamespacePath); - if (serviceAccountNamespaceExists) { - log.info("Found service account namespace at: [{}].", serviceAccountNamespace); - try { - return Optional - .of(Files.readString(serviceAccountNamespacePath).strip()); - } catch (IOException e) { - log.error( - "Error reading service account namespace from: [" + serviceAccountNamespace + "].", e); - return Optional.empty(); - } - } - - log.warn("Did not find service account namespace at: [{}].", serviceAccountNamespace); - return Optional.empty(); - } - public void start() { if (isLeaderElectionEnabled()) { leaderElectionFuture = leaderElector.start(); From 56a69befe2d10f6218f79ddf8d4d30494b03df7c Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Mon, 12 Sep 2022 15:52:17 +0200 Subject: [PATCH 06/10] Reset --- .../io/javaoperatorsdk/operator/LeaderElectionManagerTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java index 6f44ba3a06..65d78e02b3 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.io.TempDir; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.ConfigurationServiceProvider; import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; import static io.fabric8.kubernetes.client.Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY; @@ -33,6 +34,7 @@ void setUp() { @AfterEach void tearDown() { + ConfigurationServiceProvider.reset(); System.getProperties().remove(KUBERNETES_NAMESPACE_FILE); System.getProperties().remove(KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY); } From 87315bb0673ee21e109bc1d878931855b3c1381e Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Tue, 13 Sep 2022 13:27:14 +0200 Subject: [PATCH 07/10] Add e2e test to cover optional values --- ...amespace-inferred-operator-instance-2.yaml | 10 ++++ .../k8s/namespace-inferred-operator.yaml | 56 +++++++++++++++++++ sample-operators/leader-election/pom.xml | 6 +- .../sample/LeaderElectionTestOperator.java | 11 ++-- .../operator/sample/LeaderElectionE2E.java | 16 +++--- 5 files changed, 87 insertions(+), 12 deletions(-) create mode 100644 sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml create mode 100644 sample-operators/leader-election/k8s/namespace-inferred-operator.yaml diff --git a/sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml b/sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml new file mode 100644 index 0000000000..48453ebbbf --- /dev/null +++ b/sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Pod +metadata: + name: leader-election-operator-2 +spec: + serviceAccountName: leader-election-operator + containers: + - name: operator + image: leader-election-operator + imagePullPolicy: Never diff --git a/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml new file mode 100644 index 0000000000..63da9d996b --- /dev/null +++ b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml @@ -0,0 +1,56 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: leader-election-operator + +--- +apiVersion: v1 +kind: Pod +metadata: + name: leader-election-operator-1 +spec: + serviceAccountName: leader-election-operator + containers: + - name: operator + image: leader-election-operator + imagePullPolicy: Never + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: operator-admin +subjects: + - kind: ServiceAccount + name: leader-election-operator +roleRef: + kind: ClusterRole + name: leader-election-operator + apiGroup: "" + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: leader-election-operator +rules: + - apiGroups: + - "apiextensions.k8s.io" + resources: + - customresourcedefinitions + verbs: + - '*' + - apiGroups: + - "sample.javaoperatorsdk" + resources: + - leaderelectiontestcustomresources + - leaderelectiontestcustomresources/status + verbs: + - '*' + - apiGroups: + - "coordination.k8s.io" + resources: + - "leases" + verbs: + - '*' + diff --git a/sample-operators/leader-election/pom.xml b/sample-operators/leader-election/pom.xml index 1e8fadf645..a9cd376366 100644 --- a/sample-operators/leader-election/pom.xml +++ b/sample-operators/leader-election/pom.xml @@ -51,6 +51,10 @@ ${project.version} test + + org.junit.jupiter + junit-jupiter-params + @@ -75,4 +79,4 @@ - \ No newline at end of file + diff --git a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestOperator.java b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestOperator.java index 818fbe7ee1..6bb171cfd5 100644 --- a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestOperator.java +++ b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestOperator.java @@ -17,14 +17,17 @@ public static void main(String[] args) { log.info("Starting operator with identity: {}", identity); + LeaderElectionConfiguration leaderElectionConfiguration = + namespace == null + ? new LeaderElectionConfiguration("leader-election-test") + : new LeaderElectionConfiguration("leader-election-test", namespace, identity); + var client = new KubernetesClientBuilder().build(); - Operator operator = new Operator(client, - c -> c.withLeaderElectionConfiguration( - new LeaderElectionConfiguration("leader-election-test", namespace, identity))); + Operator operator = + new Operator(client, c -> c.withLeaderElectionConfiguration(leaderElectionConfiguration)); operator.register(new LeaderElectionTestReconciler(identity)); operator.installShutdownHook(); operator.start(); } - } diff --git a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java index 4c78ce5d01..9b4eb736db 100644 --- a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java +++ b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java @@ -13,8 +13,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,14 +47,15 @@ class LeaderElectionE2E { private String namespace; private KubernetesClient client; - @Test + @ParameterizedTest + @ValueSource(strings = {"namespace-inferred-", ""}) // not for local mode by design @EnabledIfSystemProperty(named = "test.deployment", matches = "remote") - void otherInstancesTakesOverWhenSteppingDown() { + void otherInstancesTakesOverWhenSteppingDown(String yamlFilePrefix) { log.info("Applying custom resource"); applyCustomResource(); log.info("Deploying operator instances"); - deployOperatorsInOrder(); + deployOperatorsInOrder(yamlFilePrefix); log.info("Awaiting custom resource reconciliations"); await().pollDelay(Duration.ofSeconds(MINIMAL_SECONDS_FOR_RENEWAL)) @@ -130,16 +132,16 @@ void tearDown() { .untilAsserted(() -> assertThat(client.namespaces().withName(namespace).get()).isNull()); } - private void deployOperatorsInOrder() { + private void deployOperatorsInOrder(String yamlFilePrefix) { log.info("Installing 1st instance"); - applyResources("k8s/operator.yaml"); + applyResources("k8s/" + yamlFilePrefix + "operator.yaml"); await().atMost(Duration.ofSeconds(POD_STARTUP_TIMEOUT)).untilAsserted(() -> { var pod = client.pods().inNamespace(namespace).withName(OPERATOR_1_POD_NAME).get(); assertThat(pod.getStatus().getContainerStatuses().get(0).getReady()).isTrue(); }); log.info("Installing 2nd instance"); - applyResources("k8s/operator-instance-2.yaml"); + applyResources("k8s/" + yamlFilePrefix + "operator-instance-2.yaml"); await().atMost(Duration.ofSeconds(POD_STARTUP_TIMEOUT)).untilAsserted(() -> { var pod = client.pods().inNamespace(namespace).withName(OPERATOR_2_POD_NAME).get(); assertThat(pod.getStatus().getContainerStatuses().get(0).getReady()).isTrue(); From cdbe8d8b854b6aa29f579d58ed4c1e71e0ca83cb Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Tue, 13 Sep 2022 13:49:10 +0200 Subject: [PATCH 08/10] POD_NAME is needed for test case --- .../k8s/namespace-inferred-operator-instance-2.yaml | 5 +++++ .../leader-election/k8s/namespace-inferred-operator.yaml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml b/sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml index 48453ebbbf..b8f09681e7 100644 --- a/sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml +++ b/sample-operators/leader-election/k8s/namespace-inferred-operator-instance-2.yaml @@ -8,3 +8,8 @@ spec: - name: operator image: leader-election-operator imagePullPolicy: Never + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name diff --git a/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml index 63da9d996b..b23aa2c918 100644 --- a/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml +++ b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml @@ -14,6 +14,11 @@ spec: - name: operator image: leader-election-operator imagePullPolicy: Never + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name --- apiVersion: rbac.authorization.k8s.io/v1 From 1204727776e686f036934b4ca971b7f083339023 Mon Sep 17 00:00:00 2001 From: Honnix Date: Tue, 13 Sep 2022 22:59:59 +0200 Subject: [PATCH 09/10] Fix scope --- sample-operators/leader-election/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/sample-operators/leader-election/pom.xml b/sample-operators/leader-election/pom.xml index a9cd376366..99b0bef044 100644 --- a/sample-operators/leader-election/pom.xml +++ b/sample-operators/leader-election/pom.xml @@ -54,6 +54,7 @@ org.junit.jupiter junit-jupiter-params + test From a2abd99133d6932b245daae97fa7d91c2e85342b Mon Sep 17 00:00:00 2001 From: Honnix Date: Wed, 14 Sep 2022 06:50:17 +0200 Subject: [PATCH 10/10] Update namespace-inferred-operator.yaml --- .../leader-election/k8s/namespace-inferred-operator.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml index b23aa2c918..68e00d81ad 100644 --- a/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml +++ b/sample-operators/leader-election/k8s/namespace-inferred-operator.yaml @@ -58,4 +58,3 @@ rules: - "leases" verbs: - '*' -