package com.yammer.breakerbox.turbine.tests;
import com.google.common.collect.Lists;
import com.netflix.turbine.discovery.Instance;
import com.yammer.breakerbox.turbine.KubernetesInstanceDiscovery;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.dsl.ClientMixedOperation;
import io.fabric8.kubernetes.client.dsl.ClientPodResource;
import org.assertj.core.util.Maps;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.stub;
@RunWith(MockitoJUnitRunner.class)
public class KubernetesInstanceDiscoveryTest {
@Mock DefaultKubernetesClient client;
@Mock ClientMixedOperation<Pod, PodList, DoneablePod, ClientPodResource<Pod, DoneablePod>> podsOperation;
@Mock ClientMixedOperation<Pod, PodList, DoneablePod, ClientPodResource<Pod, DoneablePod>> podsAnyNsOperation;
@Mock PodList podList;
private KubernetesInstanceDiscovery discovery;
private List<Pod> pods;
@Before
public void setupDiscovery() {
stub(client.pods()).toReturn(podsOperation);
stub(podsOperation.inAnyNamespace()).toReturn(podsAnyNsOperation);
stub(podsAnyNsOperation.list()).toReturn(podList);
Pod serviceA1 = new Pod();
serviceA1.setMetadata(new ObjectMeta());
serviceA1.setStatus(new PodStatus());
serviceA1.getStatus().setPodIP("10.116.0.6");
serviceA1.getStatus().setPhase("Running");
serviceA1.getMetadata().setAnnotations(
Maps.newHashMap(KubernetesInstanceDiscovery.PORT_ANNOTATION_KEY, "8080"));
serviceA1.getMetadata().setName("service-a-67das7");
serviceA1.getMetadata().setGenerateName("service-a-");
serviceA1.getMetadata().setNamespace("staging");
Pod serviceA2 = new Pod();
serviceA2.setMetadata(new ObjectMeta());
serviceA2.setStatus(new PodStatus());
serviceA2.getStatus().setPodIP("10.116.0.7");
serviceA2.getStatus().setPhase("Running");
serviceA2.getMetadata().setAnnotations(
Maps.newHashMap(KubernetesInstanceDiscovery.PORT_ANNOTATION_KEY, "8080"));
serviceA2.getMetadata().setName("service-a-0889d23");
serviceA2.getMetadata().setGenerateName("service-a-");
serviceA2.getMetadata().setNamespace("staging");
Pod serviceB = new Pod();
serviceB.setMetadata(new ObjectMeta());
serviceB.setStatus(new PodStatus());
serviceB.getStatus().setPodIP("10.116.0.8");
serviceB.getStatus().setPhase("Running");
serviceB.getMetadata().setAnnotations(
Maps.newHashMap(KubernetesInstanceDiscovery.PORT_ANNOTATION_KEY, "80"));
serviceB.getMetadata().setName("service-b-097fsd");
serviceB.getMetadata().setGenerateName("service-b-");
serviceB.getMetadata().setNamespace("production");
// Should not be detected
Pod nonBreakerboxService = new Pod();
nonBreakerboxService.setMetadata(new ObjectMeta());
nonBreakerboxService.setStatus(new PodStatus());
nonBreakerboxService.getStatus().setPhase("Running");
nonBreakerboxService.getStatus().setPodIP("10.116.0.9");
nonBreakerboxService.getMetadata().setAnnotations(new HashMap<>());
nonBreakerboxService.getMetadata().setName("service-c-097900");
nonBreakerboxService.getMetadata().setGenerateName("service-c-");
nonBreakerboxService.getMetadata().setNamespace("production");
pods = Lists.newArrayList(serviceA1, serviceA2, serviceB, nonBreakerboxService);
stub(podList.getItems()).toReturn(pods);
this.discovery = new KubernetesInstanceDiscovery(client);
}
@Test
public void usesOnlyPodsWithPortAnnotation() throws Exception {
for (Instance instance: discovery.getInstanceList())
assertThat(instance.getCluster()).doesNotStartWith("production-service-c");
}
@Test
public void usesIpAddressForHostName() throws Exception {
for (Instance instance: discovery.getInstanceList())
assertThat(instance.getHostname())
.matches(Pattern.compile("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}:[0-9]{2,5}$"));
}
@Test
public void createsCorrectAmountOfInstances() throws Exception {
assertThat(discovery.getInstanceList().size()).isEqualTo(3);
}
@Test
public void usesPodGenerateNamesAndNamespaceToBuildClusters() throws Exception {
Collection<Instance> instances = discovery.getInstanceList();
for (Instance instance: instances) {
boolean validCluster =
instance.getCluster().equals("staging-service-a") ||
instance.getCluster().equals("production-service-b");
assertThat(validCluster).isTrue();
}
}
@Test
public void onlyAcceptsCorrectValuesInPortAnnotation() throws Exception {
Pod invalidAnnotation = new Pod();
invalidAnnotation.setMetadata(new ObjectMeta());
invalidAnnotation.setStatus(new PodStatus());
invalidAnnotation.getStatus().setPodIP("10.116.0.8");
invalidAnnotation.getStatus().setPhase("Running");
invalidAnnotation.getMetadata().setAnnotations(
Maps.newHashMap(KubernetesInstanceDiscovery.PORT_ANNOTATION_KEY, "invalid-port"));
invalidAnnotation.getMetadata().setName("service-invalid-097fsd");
invalidAnnotation.getMetadata().setGenerateName("service-invalid-");
invalidAnnotation.getMetadata().setNamespace("production");
pods.clear();
pods.add(invalidAnnotation);
assertThat(discovery.getInstanceList().size()).isEqualTo(0);
}
@Test
public void usesPhasePropertyAsInstanceStatus() throws Exception {
Pod invalidAnnotation = new Pod();
invalidAnnotation.setMetadata(new ObjectMeta());
invalidAnnotation.setStatus(new PodStatus());
invalidAnnotation.getStatus().setPodIP("10.116.0.8");
invalidAnnotation.getStatus().setPhase("Preparing");
invalidAnnotation.getMetadata().setAnnotations(
Maps.newHashMap(KubernetesInstanceDiscovery.PORT_ANNOTATION_KEY, "8080"));
invalidAnnotation.getMetadata().setName("service-preparing-097fsd");
invalidAnnotation.getMetadata().setGenerateName("service-preparing-");
invalidAnnotation.getMetadata().setNamespace("production");
pods.add(invalidAnnotation);
for (Instance instance: discovery.getInstanceList()) {
if (instance.getCluster().equals("production-service-preparing"))
assertThat(instance.isUp()).isFalse();
else
assertThat(instance.isUp()).isTrue();
}
}
@Test
public void removesPodTemplateHashFromClusterName() throws Exception {
Pod deploymentPod = new Pod();
deploymentPod.setMetadata(new ObjectMeta());
deploymentPod.setStatus(new PodStatus());
deploymentPod.getStatus().setPodIP("10.116.0.8");
deploymentPod.getStatus().setPhase("Running");
deploymentPod.getMetadata().setAnnotations(
Maps.newHashMap(KubernetesInstanceDiscovery.PORT_ANNOTATION_KEY, "8080"));
deploymentPod.getMetadata().setLabels(
Maps.newHashMap(KubernetesInstanceDiscovery.POD_HASH_LABEL_KEY, "5432543253"));
deploymentPod.getMetadata().setName("service-depl-5432543253-097fsd");
deploymentPod.getMetadata().setGenerateName("service-depl-5432543253-");
deploymentPod.getMetadata().setNamespace("production");
pods.add(deploymentPod);
Optional<Instance> instanceOptional = discovery.getInstanceList().stream()
.filter(instance -> instance.getCluster().equals("production-service-depl"))
.findAny();
assertThat(instanceOptional.isPresent()).isTrue();
}
}