diff --git a/CHANGELOG.md b/CHANGELOG.md index 69a4cf4bd83..b872c921eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ #### Bugs #### Improvements +* Fix #4477 exposing LeaderElector.release to force an elector to give up the lease #### Dependency Upgrade diff --git a/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesMockServerExtension.java b/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesMockServerExtension.java index b9d33a74e42..e3bd1e9f343 100644 --- a/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesMockServerExtension.java +++ b/junit/kubernetes-server-mock/src/main/java/io/fabric8/kubernetes/client/server/mock/KubernetesMockServerExtension.java @@ -19,6 +19,7 @@ import io.fabric8.kubernetes.client.Client; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.NamespacedKubernetesClient; +import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.mockwebserver.Context; import io.fabric8.mockwebserver.ServerRequest; import io.fabric8.mockwebserver.ServerResponse; @@ -98,7 +99,8 @@ protected void initializeKubernetesClientAndMockServer(Class testClass) { EnableKubernetesMockClient a = testClass.getAnnotation(EnableKubernetesMockClient.class); final Map> responses = new HashMap<>(); mock = a.crud() - ? new KubernetesMockServer(new Context(), new MockWebServer(), responses, new KubernetesMixedDispatcher(responses), + ? new KubernetesMockServer(new Context(Serialization.jsonMapper()), new MockWebServer(), responses, + new KubernetesMixedDispatcher(responses), a.https()) : new KubernetesMockServer(a.https()); mock.init(); diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElector.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElector.java index 03fce681954..6360b29601c 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElector.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElector.java @@ -120,22 +120,22 @@ private synchronized void stopLeading() { if (current == null || !isLeader(current)) { return; // not leading } - try { - // update current from latest - current = leaderElectionConfig.getLock().get(kubernetesClient); - if (current == null || !isLeader(current)) { - return; // lost leadership already - } - if (leaderElectionConfig.isReleaseOnCancel()) { - release(current); - } - } finally { - // called regardless of isReleaseOnCancel + if (leaderElectionConfig.isReleaseOnCancel()) { + release(); + } else { leaderElectionConfig.getLeaderCallbacks().onStopLeading(); } } - private void release(LeaderElectionRecord current) { + /** + * Release the leadership if currently held. If not cancelled, the elector will + * continue to try and re-acquire the lock. + */ + public synchronized void release() { + LeaderElectionRecord current = leaderElectionConfig.getLock().get(kubernetesClient); + if (current == null || !isLeader(current)) { + return; // lost leadership already + } try { ZonedDateTime now = now(); final LeaderElectionRecord newLeaderElectionRecord = new LeaderElectionRecord( @@ -144,9 +144,9 @@ private void release(LeaderElectionRecord current) { now, now, current.getLeaderTransitions()); - newLeaderElectionRecord.setVersion(current.getVersion()); leaderElectionConfig.getLock().update(kubernetesClient, newLeaderElectionRecord); + updateObserved(newLeaderElectionRecord); } catch (KubernetesClientException e) { final String lockDescription = leaderElectionConfig.getLock().describe(); LOGGER.error("Exception occurred while releasing lock '{}'", lockDescription, e); @@ -218,8 +218,7 @@ synchronized boolean tryAcquireOrRenew() { isLeader ? oldLeaderElectionRecord.getAcquireTime() : now, now, oldLeaderElectionRecord.getLeaderTransitions() + (isLeader ? 0 : 1)); - newLeaderElectionRecord.setVersion(oldLeaderElectionRecord.getVersion()); - leaderElectionConfig.getLock().update(kubernetesClient, newLeaderElectionRecord); + lock.update(kubernetesClient, newLeaderElectionRecord); updateObserved(newLeaderElectionRecord); return true; } @@ -232,6 +231,8 @@ private void updateObserved(LeaderElectionRecord leaderElectionRecord) { final String newLeader = leaderElectionRecord.getHolderIdentity(); if (!Objects.equals(newLeader, currentLeader)) { LOGGER.debug("Leader changed from {} to {}", currentLeader, newLeader); + // this will notify even if the newLeader is null or empty, which is the same behavior as the go client + // but does not seem entirely correct leaderElectionConfig.getLeaderCallbacks().onNewLeader(newLeader); if (Objects.equals(currentLeader, leaderElectionConfig.getLock().identity())) { leaderElectionConfig.getLeaderCallbacks().onStopLeading(); diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java index 57f9e2d6e77..531a091ccdb 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java @@ -18,6 +18,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.utils.Serialization; import org.slf4j.Logger; @@ -58,10 +59,10 @@ protected LeaderElectionRecord toRecord(ConfigMap resource) { } @Override - protected ConfigMap toResource(LeaderElectionRecord leaderElectionRecord, ObjectMeta meta) { - return new ConfigMapBuilder().withMetadata(meta).editMetadata() - .addToAnnotations(LEADER_ELECTION_RECORD_ANNOTATION_KEY, Serialization.asJson(leaderElectionRecord)) - .endMetadata() + protected ConfigMap toResource(LeaderElectionRecord leaderElectionRecord, ObjectMetaBuilder meta) { + return new ConfigMapBuilder() + .withMetadata( + meta.addToAnnotations(LEADER_ELECTION_RECORD_ANNOTATION_KEY, Serialization.asJson(leaderElectionRecord)).build()) .build(); } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaderElectionRecord.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaderElectionRecord.java index ac12714fc17..03228dc786e 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaderElectionRecord.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaderElectionRecord.java @@ -17,12 +17,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; import lombok.Builder; -import java.io.Serializable; import java.time.Duration; import java.time.ZonedDateTime; import java.util.Objects; @@ -35,7 +32,6 @@ * @see leaderelection/resourcelock/interface.go */ -@AllArgsConstructor @Builder(toBuilder = true) public class LeaderElectionRecord { @@ -46,8 +42,6 @@ public class LeaderElectionRecord { @JsonFormat(timezone = "UTC", pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'") private final ZonedDateTime renewTime; private final int leaderTransitions; - @JsonIgnore - private transient Serializable version; @JsonCreator public LeaderElectionRecord( @@ -82,14 +76,6 @@ public int getLeaderTransitions() { return leaderTransitions; } - public Serializable getVersion() { - return version; - } - - public void setVersion(Serializable version) { - this.version = version; - } - @Override public boolean equals(Object o) { if (this == o) @@ -101,13 +87,12 @@ public boolean equals(Object o) { Objects.equals(holderIdentity, that.holderIdentity) && Objects.equals(leaseDuration, that.leaseDuration) && Objects.equals(acquireTime, that.acquireTime) && - Objects.equals(renewTime, that.renewTime) && - Objects.equals(version, that.version); + Objects.equals(renewTime, that.renewTime); } @Override public int hashCode() { - return Objects.hash(holderIdentity, leaseDuration, acquireTime, renewTime, leaderTransitions, version); + return Objects.hash(holderIdentity, leaseDuration, acquireTime, renewTime, leaderTransitions); } } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLock.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLock.java index 8e4c7cae748..dada42cbac4 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLock.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLock.java @@ -16,6 +16,7 @@ package io.fabric8.kubernetes.client.extended.leaderelection.resourcelock; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.coordination.v1.Lease; import io.fabric8.kubernetes.api.model.coordination.v1.LeaseBuilder; @@ -39,8 +40,8 @@ protected Class getKind() { } @Override - protected Lease toResource(LeaderElectionRecord leaderElectionRecord, ObjectMeta meta) { - return new LeaseBuilder().withMetadata(meta) + protected Lease toResource(LeaderElectionRecord leaderElectionRecord, ObjectMetaBuilder meta) { + return new LeaseBuilder().withMetadata(meta.build()) .withNewSpec() .withHolderIdentity(leaderElectionRecord.getHolderIdentity()) .withLeaseDurationSeconds((int) leaderElectionRecord.getLeaseDuration().get(ChronoUnit.SECONDS)) diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ResourceLock.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ResourceLock.java index d321efc9085..eedc72e0acc 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ResourceLock.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ResourceLock.java @@ -23,14 +23,13 @@ import io.fabric8.kubernetes.client.dsl.base.PatchContext; import io.fabric8.kubernetes.client.dsl.base.PatchType; -import java.io.Serializable; import java.util.Objects; -import java.util.Optional; public abstract class ResourceLock implements Lock { private final ObjectMeta meta; private final String identity; + private T resource; public ResourceLock(String namespace, String name, String identity) { this(new ObjectMetaBuilder().withNamespace(namespace).withName(name).build(), identity); @@ -46,23 +45,24 @@ public ResourceLock(ObjectMeta meta, String identity) { protected abstract Class getKind(); @Override - public LeaderElectionRecord get(KubernetesClient client) { - return getResource(client).map(this::toRecordInternal).orElse(null); - } - - private Optional getResource(KubernetesClient client) { - return Optional.ofNullable(client.resources(getKind()).inNamespace(meta.getNamespace()).withName(meta.getName()).get()); + public synchronized LeaderElectionRecord get(KubernetesClient client) { + resource = client.resources(getKind()).inNamespace(meta.getNamespace()).withName(meta.getName()).get(); + if (resource != null) { + return toRecord(resource); + } + return null; } @Override - public void create(KubernetesClient client, LeaderElectionRecord leaderElectionRecord) { - client.resource(toResource(leaderElectionRecord, getObjectMeta(leaderElectionRecord.getVersion()))).create(); + public synchronized void create(KubernetesClient client, LeaderElectionRecord leaderElectionRecord) { + resource = client.resource(toResource(leaderElectionRecord, getObjectMeta(null))).create(); } @Override - public void update(KubernetesClient client, LeaderElectionRecord leaderElectionRecord) { - client.resource(toResource(leaderElectionRecord, getObjectMeta(leaderElectionRecord.getVersion()))) - .patch(PatchContext.of(PatchType.STRATEGIC_MERGE)); + public synchronized void update(KubernetesClient client, LeaderElectionRecord leaderElectionRecord) { + Objects.requireNonNull(resource, "get or create must be called first"); + client.resource(toResource(leaderElectionRecord, getObjectMeta(resource.getMetadata().getResourceVersion()))) + .patch(PatchContext.of(PatchType.JSON_MERGE)); } /** @@ -72,18 +72,12 @@ public void update(KubernetesClient client, LeaderElectionRecord leaderElectionR * @param meta not null * @return */ - protected abstract T toResource(LeaderElectionRecord leaderElectionRecord, ObjectMeta meta); - - protected LeaderElectionRecord toRecordInternal(T resource) { - LeaderElectionRecord result = toRecord(resource); - result.setVersion(resource.getMetadata().getResourceVersion()); - return result; - } + protected abstract T toResource(LeaderElectionRecord leaderElectionRecord, ObjectMetaBuilder meta); protected abstract LeaderElectionRecord toRecord(T resource); - protected ObjectMeta getObjectMeta(Serializable version) { - return new ObjectMetaBuilder(meta).withResourceVersion((String) version).build(); + protected ObjectMetaBuilder getObjectMeta(String version) { + return new ObjectMetaBuilder(meta).withResourceVersion((String) version); } /** @@ -102,4 +96,8 @@ public String describe() { return String.format("%sLock: %s - %s (%s)", getKind().getSimpleName(), meta.getNamespace(), meta.getName(), identity); } + void setResource(T resource) { + this.resource = resource; + } + } diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElectorTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElectorTest.java index 37cf6b069dc..0692bcd5619 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElectorTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/LeaderElectorTest.java @@ -24,6 +24,7 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.mockito.Answers; +import org.mockito.Mockito; import java.time.Duration; import java.time.Instant; @@ -56,8 +57,6 @@ class LeaderElectorTest { - final static AtomicReference activeLer = new AtomicReference<>(null); - @Test void runShouldAbortAfterRenewDeadlineExpired() throws Exception { // Given @@ -114,17 +113,14 @@ void runShouldEndlesslyRun() throws Exception { @Test void shouldReleaseWhenCanceled() throws Exception { // Given - final LeaderElectionConfig lec = mockLeaderElectionConfiguration(); + AtomicReference activeLer = new AtomicReference<>(); + final LeaderElectionConfig lec = mockLeaderElectionConfiguration(activeLer); final CountDownLatch signal = new CountDownLatch(1); final Lock mockedLock = lec.getLock(); when(lec.isReleaseOnCancel()).thenReturn(true); doAnswer(invocation -> { LeaderElectionRecord leaderRecord = invocation.getArgument(1, LeaderElectionRecord.class); - if (!activeLer.get().getVersion().equals(leaderRecord.getVersion())) { - throw new KubernetesClientException("Wrong"); - } - activeLer.set(leaderRecord.toBuilder() - .version(String.valueOf(Integer.parseInt(String.valueOf(leaderRecord.getVersion())) + 1)).build()); + activeLer.set(leaderRecord); signal.countDown(); return null; }).when(mockedLock).update(any(), any()); @@ -138,7 +134,6 @@ void shouldReleaseWhenCanceled() throws Exception { // Then Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> Utils.isNullOrEmpty(activeLer.get().getHolderIdentity())); assertEquals(0, activeLer.get().getLeaderTransitions()); - assertEquals("3", activeLer.get().getVersion()); // create a new elector, they are no good after a single use leaderElector = new LeaderElector(mock(NamespacedKubernetesClient.class), lec, CommonThreadPool.get()); @@ -149,6 +144,44 @@ void shouldReleaseWhenCanceled() throws Exception { assertEquals(1, activeLer.get().getLeaderTransitions()); } + @Test + void shouldRelease() throws Exception { + // Given + AtomicReference activeLer = new AtomicReference<>(); + final LeaderElectionConfig lec = mockLeaderElectionConfiguration(activeLer); + final CountDownLatch signal = new CountDownLatch(1); + final Lock mockedLock = lec.getLock(); + doAnswer(invocation -> { + LeaderElectionRecord leaderRecord = invocation.getArgument(1, LeaderElectionRecord.class); + activeLer.set(leaderRecord); + signal.countDown(); + return null; + }).when(mockedLock).update(any(), any()); + + // When + LeaderElector leaderElector = new LeaderElector(mock(NamespacedKubernetesClient.class), lec, CommonThreadPool.get()); + CompletableFuture started = leaderElector.start(); + assertTrue(signal.await(10, TimeUnit.SECONDS)); + + Mockito.verify(lec.getLeaderCallbacks(), times(1)).onStartLeading(); + + leaderElector.release(); + + // ensure that release cause us to stop leading + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { + Mockito.verify(lec.getLeaderCallbacks()).onStopLeading(); + return true; + }); + + // we haven't stopped, so we'll re-acquire + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { + Mockito.verify(lec.getLeaderCallbacks(), times(2)).onStartLeading(); + return true; + }); + + started.cancel(true); + } + @Test void isLeaderAndIsLeaderShouldReturnTrue() { // Given @@ -231,9 +264,7 @@ void loopCompletesOk() throws Exception { void loopCancel() throws Exception { // Given AtomicInteger count = new AtomicInteger(); - CompletableFuture cf = loop(completion -> { - count.getAndIncrement(); - }, () -> 10L, CommonThreadPool.get()); + CompletableFuture cf = loop(completion -> count.getAndIncrement(), () -> 10L, CommonThreadPool.get()); // When Awaitility.await().atMost(1, TimeUnit.SECONDS).until(() -> count.get() >= 1); @@ -281,7 +312,12 @@ void jitterWithNegativeShouldReturnDuration() { assertTrue(result.toMillis() > 1000L); } - private static LeaderElectionConfig mockLeaderElectionConfiguration() throws Exception { + private LeaderElectionConfig mockLeaderElectionConfiguration() throws Exception { + return mockLeaderElectionConfiguration(new AtomicReference<>()); + } + + private LeaderElectionConfig mockLeaderElectionConfiguration(AtomicReference activeLer) + throws Exception { final LeaderElectionConfig lec = mock(LeaderElectionConfig.class, Answers.RETURNS_DEEP_STUBS); when(lec.getLeaseDuration()).thenReturn(Duration.ofSeconds(2L)); when(lec.getRenewDeadline()).thenReturn(Duration.ofSeconds(1L)); @@ -291,7 +327,7 @@ private static LeaderElectionConfig mockLeaderElectionConfiguration() throws Exc when(mockedLock.get(any())).thenReturn(null).thenAnswer(invocation -> activeLer.get()); doAnswer(invocation -> { LeaderElectionRecord leaderRecord = invocation.getArgument(1, LeaderElectionRecord.class); - activeLer.set(leaderRecord.toBuilder().version("1").build()); + activeLer.set(leaderRecord); return null; }).when(mockedLock).create(any(), any()); return lec; diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java index 6e7d63a279d..d9c479964d4 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java @@ -80,10 +80,9 @@ void getWithExistingConfigMapShouldReturnLeaderElectionRecord() { .withResourceVersion("313373").build()); final ConfigMapLock lock = new ConfigMapLock("namespace", "name", "1337"); // When - final LeaderElectionRecord result = lock.toRecordInternal(cm); + final LeaderElectionRecord result = lock.toRecord(cm); // Then assertNotNull(result); - assertEquals("313373", result.getVersion()); assertEquals("1337", result.getHolderIdentity()); assertEquals(15, result.getLeaseDuration().getSeconds()); assertEquals(ZonedDateTime.of(2015, 10, 21, 4, 29, 0, 0, ZoneId.of("UTC")), result.getAcquireTime()); @@ -106,10 +105,11 @@ void updateWithValidLeaderElectionRecordShouldSendPatchRequest() throws Exceptio // Given final LeaderElectionRecord record = new LeaderElectionRecord( "1337", Duration.ofSeconds(1), ZonedDateTime.now(), ZonedDateTime.now(), 0); - record.setVersion("313373"); final ConfigMapLock lock = new ConfigMapLock("namespace", "name", "1337"); + ConfigMap configMap = lock.toResource(record, lock.getObjectMeta("313373")); + lock.setResource(configMap); // When - lock.update(kc, record); + lock.update(kc, record.toBuilder().leaseDuration(Duration.ofSeconds(2)).build()); // Then verify(kc.resource(any(ConfigMap.class))).patch(any(PatchContext.class)); } diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLockTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLockTest.java index c2d9ef8a167..4ec0c9b2cfa 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLockTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/LeaseLockTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; +import org.mockito.Mockito; import java.time.Duration; import java.time.ZoneId; @@ -82,10 +83,9 @@ void getWithExistingLeaseShouldReturnLeaderElectionRecord() { lease.setMetadata(new ObjectMetaBuilder().withResourceVersion("313373").build()); final LeaseLock lock = new LeaseLock("namespace", "name", "1337"); // When - final LeaderElectionRecord result = lock.toRecordInternal(lease); + final LeaderElectionRecord result = lock.toRecord(lease); // Then assertNotNull(result); - assertEquals("313373", result.getVersion()); assertEquals("1337", result.getHolderIdentity()); assertEquals(15, result.getLeaseDuration().getSeconds()); assertEquals(ZonedDateTime.of(2015, 10, 21, 4, 29, 0, 0, ZoneId.of("UTC")), result.getAcquireTime()); @@ -108,13 +108,13 @@ void updateWithValidLeaderElectionRecordShouldSendPatchRequest() throws Exceptio // Given final LeaderElectionRecord record = new LeaderElectionRecord( "1337", Duration.ofSeconds(1), ZonedDateTime.now(), ZonedDateTime.now(), 0); - record.setVersion("313373"); final LeaseLock lock = new LeaseLock("namespace", "name", "1337"); Lease lease = lock.toResource(record, lock.getObjectMeta("313373")); + lock.setResource(lease); // When - lock.update(kc, record); + lock.update(kc, record.toBuilder().leaseDuration(Duration.ofSeconds(2)).build()); // Then - verify(kc.resource(lease)).patch(any(PatchContext.class)); + verify(kc.resource(Mockito.any(Lease.class))).patch(any(PatchContext.class)); } @Test diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java index 4a9ab4a3902..19c0af84e49 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java @@ -42,7 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -@EnableKubernetesMockClient +@EnableKubernetesMockClient(crud = true) public class LeaderElectionTest { KubernetesMockServer server; @@ -84,6 +84,11 @@ public void singleLeaderConfigMapLockUpdateTest() throws Exception { // When - Then testAndAssertSingleLeader("lead-config-map", new ConfigMapLock("namespace", "name", "lead-config-map")); + + // verify crud mock behavior + server.clearExpectations(); + testAndAssertSingleLeader("lead-config-map", + new ConfigMapLock("namespace", "name", "lead-config-map")); } @Test