/** |
|
* Copyright (C) 2015 Red Hat, Inc. |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
package io.fabric8.kubernetes.client.mock; |
|
|
|
import static org.junit.Assert.assertTrue; |
|
|
|
import io.fabric8.kubernetes.api.model.Pod; |
|
import io.fabric8.kubernetes.api.model.PodBuilder; |
|
import io.fabric8.kubernetes.api.model.Status; |
|
import io.fabric8.kubernetes.api.model.StatusBuilder; |
|
import io.fabric8.kubernetes.api.model.WatchEvent; |
|
import io.fabric8.kubernetes.api.model.WatchEventBuilder; |
|
import io.fabric8.kubernetes.client.KubernetesClient; |
|
import io.fabric8.kubernetes.client.KubernetesClientException; |
|
import io.fabric8.kubernetes.client.Watch; |
|
import io.fabric8.kubernetes.client.Watcher; |
|
import io.fabric8.kubernetes.client.server.mock.KubernetesServer; |
|
import junit.framework.AssertionFailedError; |
|
|
|
import java.net.HttpURLConnection; |
|
import java.util.concurrent.CountDownLatch; |
|
import java.util.concurrent.TimeUnit; |
|
|
|
import org.junit.Rule; |
|
import org.junit.Test; |
|
import org.slf4j.Logger; |
|
import org.slf4j.LoggerFactory; |
|
|
|
public class WatchTest { |
|
Logger logger = LoggerFactory.getLogger(WatchTest.class); |
|
|
|
@Rule |
|
public KubernetesServer server = new KubernetesServer(false); |
|
|
|
static final Pod pod1 = new PodBuilder().withNewMetadata().withNamespace("test").withName("pod1") |
|
.withResourceVersion("1").endMetadata().build(); |
|
|
|
static final Status outdatedStatus = new StatusBuilder().withCode(HttpURLConnection.HTTP_GONE) |
|
.withMessage( |
|
"401: The event in requested index is outdated and cleared (the requested history has been cleared [3/1]) [2]") |
|
.build(); |
|
static final WatchEvent outdatedEvent = new WatchEventBuilder().withStatusObject(outdatedStatus).build(); |
|
|
|
@Test |
|
public void testDeletedAndOutdated() throws InterruptedException { |
|
logger.info("testDeletedAndOutdated"); |
|
KubernetesClient client = server.getClient().inNamespace("test"); |
|
|
|
// DELETED event, then history outdated |
|
server.expect() |
|
.withPath("/api/v1/namespaces/test/pods?fieldSelector=metadata.name%3Dpod1&resourceVersion=1&watch=true") |
|
.andUpgradeToWebSocket().open().waitFor(2000).andEmit(new WatchEvent(pod1, "DELETED")).waitFor(2000) |
|
.andEmit(outdatedEvent).done().once(); |
|
|
|
final CountDownLatch deleteLatch = new CountDownLatch(1); |
|
final CountDownLatch closeLatch = new CountDownLatch(1); |
|
try (Watch watch = client.pods().withName("pod1").withResourceVersion("1").watch(new Watcher<Pod>() { |
|
@Override |
|
public void eventReceived(Action action, Pod resource) { |
|
switch (action) { |
|
case DELETED: |
|
deleteLatch.countDown(); |
|
break; |
|
default: |
|
throw new AssertionFailedError(); |
|
} |
|
} |
|
|
|
@Override |
|
public void onClose(KubernetesClientException cause) { |
|
closeLatch.countDown(); |
|
} |
|
})) /* autoclose */ { |
|
assertTrue(deleteLatch.await(10, TimeUnit.SECONDS)); |
|
assertTrue(closeLatch.await(10, TimeUnit.SECONDS)); |
|
} |
|
} |
|
|
|
@Test(expected = KubernetesClientException.class) |
|
public void testHttpErrorWithOutdated() { |
|
logger.info("testHttpErrorWithOutdated"); |
|
KubernetesClient client = server.getClient().inNamespace("test"); |
|
// http error: history outdated |
|
server.expect() |
|
.withPath("/api/v1/namespaces/test/pods?fieldSelector=metadata.name%3Dpod1&resourceVersion=1&watch=true") |
|
.andReturn(410, outdatedEvent).once(); |
|
final boolean[] onCloseCalled = {false}; |
|
try (Watch watch = client.pods().withName("pod1").withResourceVersion("1").watch(new Watcher<Pod>() { |
|
@Override |
|
public void eventReceived(Action action, Pod resource) { |
|
throw new AssertionFailedError(); |
|
} |
|
|
|
@Override |
|
public void onClose(KubernetesClientException cause) { |
|
onCloseCalled[0] =true; |
|
} |
|
})) { |
|
|
|
} |
|
assertTrue(onCloseCalled[0]); |
|
} |
|
|
|
@Test |
|
public void testHttpErrorReconnect() throws InterruptedException { |
|
logger.info("testHttpErrorReconnect"); |
|
String path = "/api/v1/namespaces/test/pods?fieldSelector=metadata.name%3Dpod1&resourceVersion=1&watch=true"; |
|
KubernetesClient client = server.getClient().inNamespace("test"); |
|
// accept watch and disconnect |
|
server.expect().withPath(path).andUpgradeToWebSocket().open().done().once(); |
|
// refuse reconnect attempts 6 times |
|
server.expect().withPath(path).andReturn(503, new StatusBuilder().withCode(503).build()).times(6); |
|
// accept next reconnect and send outdated event to stop the watch |
|
server.expect().withPath(path).andUpgradeToWebSocket().open(outdatedEvent).done().once(); |
|
|
|
final CountDownLatch closeLatch = new CountDownLatch(1); |
|
try (Watch watch = client.pods().withName("pod1").withResourceVersion("1").watch(new Watcher<Pod>() { |
|
@Override |
|
public void eventReceived(Action action, Pod resource) { |
|
throw new AssertionFailedError(); |
|
} |
|
|
|
@Override |
|
public void onClose(KubernetesClientException cause) { |
|
logger.debug("onClose", cause); |
|
closeLatch.countDown(); |
|
} |
|
})) /* autoclose */ { |
|
assertTrue(closeLatch.await(3, TimeUnit.MINUTES)); |
|
} |
|
} |
|
|
|
@Test |
|
public void testOnCloseEvent() throws InterruptedException { |
|
logger.info("testOnCloseEvent"); |
|
final CountDownLatch eventLatch = new CountDownLatch(2); |
|
final CountDownLatch closeLatch = new CountDownLatch(1); |
|
KubernetesClient client = server.getClient().inNamespace("test"); |
|
|
|
server.expect() |
|
.withPath("/api/v1/namespaces/test/pods?fieldSelector=metadata.name%3Dpod1&resourceVersion=1&watch=true") |
|
.andUpgradeToWebSocket().open().waitFor(2000).andEmit(new WatchEvent(pod1, "MODIFIED")).waitFor(2000) |
|
.andEmit(new WatchEvent(pod1, "MODIFIED")).done().once(); |
|
|
|
Watch watch = client.pods().withName("pod1").withResourceVersion("1").watch(new Watcher<Pod>() { |
|
@Override |
|
public void eventReceived(Action action, Pod resource) { |
|
eventLatch.countDown(); |
|
} |
|
|
|
@Override |
|
public void onClose(KubernetesClientException cause) { |
|
closeLatch.countDown(); |
|
} |
|
}); |
|
|
|
assertTrue(eventLatch.await(10, TimeUnit.SECONDS)); |
|
watch.close(); |
|
assertTrue(closeLatch.await(10, TimeUnit.SECONDS)); |
|
} |
|
|
|
} |