use of org.entando.kubernetes.test.common.ValueHolder in project entando-k8s-controller-coordinator by entando-k8s.
the class PodManagementTests method shouldKillAllPreviousPods.
@Test
@Description("Should always kill existing pods running against the same resource to avoid racing conditions")
void shouldKillAllPreviousPods() {
step("Given the Coordinator observes its own namespace", () -> {
System.setProperty(EntandoOperatorConfigProperty.ENTANDO_NAMESPACES_TO_OBSERVE.getJvmSystemProperty(), clientDouble.getNamespace());
coordinator.onStartup(new StartupEvent());
});
step("And automatic controller pod garbage collections has been switched off", () -> System.setProperty(EntandoOperatorConfigProperty.ENTANDO_K8S_OPERATOR_GC_CONTROLLER_PODS.getJvmSystemProperty(), "false"));
ValueHolder<SerializedEntandoResource> testResource = new ValueHolder<>();
step("And I have created a new TestResource resource", () -> {
final TestResource entandoCustomResource = new TestResource().withNames(clientDouble.getNamespace(), "test-keycloak").withSpec(new BasicDeploymentSpecBuilder().withDbms(DbmsVendor.EMBEDDED).build());
testResource.set(clientDouble.createOrPatchEntandoResource(CoordinatorTestUtils.toSerializedResource(entandoCustomResource)));
attachment("TestResource", objectMapper.writeValueAsString(testResource.get()));
});
ValueHolder<Pod> firstPod = new ValueHolder<>();
step("And I have waited to see at least one controller pod", () -> {
await().ignoreExceptions().atMost(5, TimeUnit.SECONDS).until(() -> clientDouble.loadPod(clientDouble.getNamespace(), labelsFromResource(testResource.get())) != null);
firstPod.set(clientDouble.loadPod(clientDouble.getNamespace(), labelsFromResource(testResource.get())));
attachment("FirstPod", objectMapper.writeValueAsString(firstPod.get()));
});
step("When I force a second attempt at processing the resource", () -> {
final SerializedEntandoResource reloaded = clientDouble.reload(testResource.get());
reloaded.getMetadata().setAnnotations(Map.of(AnnotationNames.PROCESSING_INSTRUCTION.getName(), "force"));
clientDouble.createOrPatchEntandoResource(reloaded);
});
step("Then a new pod was created", () -> {
await().atMost(4, TimeUnit.SECONDS).ignoreExceptions().until(() -> {
final List<Pod> pods = clientDouble.loadPods(clientDouble.getNamespace(), labelsFromResource(testResource.get()));
return !pods.get(pods.size() - 1).getMetadata().getUid().equals(firstPod.get().getMetadata().getUid());
});
final List<Pod> pods = clientDouble.loadPods(clientDouble.getNamespace(), labelsFromResource(testResource.get()));
attachment("Second Pod", objectMapper.writeValueAsString(pods.get(pods.size() - 1)));
});
step("And the old pod was removed", () -> {
final List<Pod> pods = clientDouble.loadPods(clientDouble.getNamespace(), labelsFromResource(testResource.get()));
assertThat(pods.size()).isOne();
});
}
use of org.entando.kubernetes.test.common.ValueHolder in project entando-k8s-controller-coordinator by entando-k8s.
the class ControllerCoordinatorProcessingCriteriaTest method testDuplicatesIgnored.
@Test
@Description("Resource modification events should be ignored when Kubernetes it twice with the same resource version")
void testDuplicatesIgnored() {
step("Given the Coordinator observes this namespace", () -> {
System.setProperty(EntandoOperatorConfigProperty.ENTANDO_NAMESPACES_TO_OBSERVE.getJvmSystemProperty(), OBSERVED_NAMESPACE);
coordinator.onStartup(new StartupEvent());
});
final ValueHolder<SerializedEntandoResource> testResource = new ValueHolder<>();
step("And I have created an TestResource resource", () -> {
testResource.set(createTestResource(1L, Collections.emptyMap()));
attachment("TestResource", objectMapper.writeValueAsString(testResource.get()));
});
final ValueHolder<Pod> oldPod = new ValueHolder<>();
step("And its controller pod has been created", () -> {
await().ignoreExceptions().atMost(3, TimeUnit.SECONDS).until(() -> clientDouble.loadPod(CONTROLLER_NAMESPACE, labelsFromResource(testResource.get())) != null);
oldPod.set(clientDouble.loadPod(CONTROLLER_NAMESPACE, labelsFromResource(testResource.get())));
attachment("Controller Pod", objectMapper.writeValueAsString(oldPod.get()));
});
step("When a duplicate event is fired", () -> {
// NB we have to invoke the observer directly as our test double doesn't generate duplicate events
coordinator.getObserver(CustomResourceDefinitionContext.fromCustomResourceType(TestResource.class)).eventReceived(Action.ADDED, testResource.get());
coordinator.shutdownObservers(10, TimeUnit.SECONDS);
attachment("Duplicate Resource", objectMapper.writeValueAsString(testResource.get()));
});
step("Then no new pod gets created", () -> {
// NB This assertion assumes the ClientDouble is storing the objects without cloning/serialization
assertThat(clientDouble.loadPod(CONTROLLER_NAMESPACE, labelsFromResource(testResource.get()))).isSameAs(oldPod.get());
});
step("And the duplicate event was logged", () -> {
final Optional<String> logEntry = LogInterceptor.getLogEntries().stream().filter(s -> s.startsWith("Duplicate event")).findFirst();
assertThat(logEntry).isPresent();
});
}
use of org.entando.kubernetes.test.common.ValueHolder in project entando-k8s-controller-coordinator by entando-k8s.
the class DefaultSimpleKubernetesClientTest method shouldUpdateStatusOfOpaqueCustomResource.
@Test
@Description("Should track phase updates on the status of opaque custom resources and in Kubernetes events")
void shouldUpdateStatusOfOpaqueCustomResource() throws IOException {
ValueHolder<TestResource> testResource = new ValueHolder<>();
step("Given I have created an instance of the CustomResourceDefinition TestResource", () -> {
testResource.set(createTestResource(new TestResource().withNames(MY_APP_NAMESPACE_1, MY_APP).withSpec(new BasicDeploymentSpecBuilder().withReplicas(1).build())));
attachResource("TestResource", testResource.get());
});
SerializedEntandoResource serializedEntandoResource = objectMapper.readValue(objectMapper.writeValueAsBytes(testResource.get()), SerializedEntandoResource.class);
step("And it is represented in an opaque format using the SerializedEntandoResource class", () -> {
serializedEntandoResource.setDefinition(CustomResourceDefinitionContext.fromCustomResourceType(TestResource.class));
attachResource("Opaque Resource", serializedEntandoResource);
});
step("When I update its phase to 'successful'", () -> getMyClient().updatePhase(serializedEntandoResource, EntandoDeploymentPhase.SUCCESSFUL));
step("Then the updated status reflects on the TestResource", () -> {
final TestResource actual = getFabric8Client().customResources(TestResource.class).inNamespace(MY_APP_NAMESPACE_1).withName(MY_APP).get();
assertThat(actual.getStatus().getPhase()).isEqualTo(EntandoDeploymentPhase.SUCCESSFUL);
attachResource("TestResource", actual);
});
step("And a PHASE_CHANGE event has been issued to Kubernetes", () -> {
final List<Event> events = getMyClient().listEventsFor(testResource.get());
attachResources("Events", events);
assertThat(events).allMatch(event -> event.getInvolvedObject().getName().equals(testResource.get().getMetadata().getName()));
assertThat(events).anyMatch(event -> event.getAction().equals("PHASE_CHANGE"));
});
}
use of org.entando.kubernetes.test.common.ValueHolder in project entando-k8s-controller-coordinator by entando-k8s.
the class DefaultSimpleKubernetesClientTest method shouldIssueDeathEvent.
@Test
@Description("Should issue Death events")
void shouldIssueDeathEvent() {
ValueHolder<Pod> startedPod = new ValueHolder<>();
step("Given I have started a dummy pod with the name 'my-pod'", () -> {
startedPod.set(getMyClient().startPod(new PodBuilder().withNewMetadata().withName(MY_POD).withNamespace(MY_APP_NAMESPACE_1).endMetadata().withNewSpec().addNewContainer().withImage("busybox").withName("busybox").endContainer().endSpec().build()));
System.setProperty(EntandoOperatorSpiConfigProperty.ENTANDO_CONTROLLER_POD_NAME.getJvmSystemProperty(), startedPod.get().getMetadata().getName());
attachment("Started Pod", objectMapper.writeValueAsString(startedPod.get()));
});
Event event = new EventBuilder().withNewMetadata().withName(EntandoOperatorSpiConfig.getControllerPodName() + "-restart-" + NameUtils.randomNumeric(4)).addToLabels("entando-operator-restarted", "true").endMetadata().withCount(1).withFirstTimestamp(FormatUtils.format(LocalDateTime.now())).withLastTimestamp(FormatUtils.format(LocalDateTime.now())).withMessage("blah").build();
step("When issue a death event against this pod", () -> myClient.issueOperatorDeathEvent(event));
step("It reflects on the cluster", () -> {
Event actual = kubernetesClient.v1().events().withName(event.getMetadata().getName()).fromServer().get();
assertThat(actual).isNotNull();
assertThat(actual.getInvolvedObject().getUid()).isEqualTo(startedPod.get().getMetadata().getUid());
});
}
use of org.entando.kubernetes.test.common.ValueHolder in project entando-k8s-controller-coordinator by entando-k8s.
the class LivenessTests method shouldCreateControllerPodPointingToResource.
@Test
@Description("Should create a controller pod that points to the newly created resource")
void shouldCreateControllerPodPointingToResource() {
System.setProperty(EntandoOperatorSpiConfigProperty.ENTANDO_CONTROLLER_POD_NAME.getJvmSystemProperty(), "my-pod");
step("Given the Coordinator observes its own namespace", () -> {
System.setProperty(EntandoOperatorConfigProperty.ENTANDO_NAMESPACES_TO_OBSERVE.getJvmSystemProperty(), clientDouble.getNamespace());
coordinator.onStartup(new StartupEvent());
});
ValueHolder<SerializedEntandoResource> testResource = new ValueHolder<>();
step("And I have created a new TestResource resource", () -> {
final TestResource r = new TestResource().withNames(clientDouble.getNamespace(), "test-keycloak").withSpec(new BasicDeploymentSpecBuilder().withDbms(DbmsVendor.EMBEDDED).build());
r.getMetadata().setGeneration(1L);
testResource.set(clientDouble.createOrPatchEntandoResource(CoordinatorTestUtils.toSerializedResource(r)));
attachment("TestResource", objectMapper.writeValueAsString(testResource.get()));
});
final File file = Paths.get("/tmp/EntandoControllerCoordinator.ready").toFile();
clientDouble.getCluster().getResourceProcessor().getAllWatchers().forEach(watcher -> {
Liveness.alive();
step(format("When the watcher %s is closed", watcher.getClass().getSimpleName()), () -> {
watcher.onClose(new WatcherException("Closed"));
});
step("Then the liveness probe will fail", () -> {
assertThat(file).doesNotExist();
});
});
}
Aggregations