Skip to content

Commit

Permalink
test: functional tests for PodLogService
Browse files Browse the repository at this point in the history
These tests will serve as the base to allow for a refactoring of the
Pod Log service.

Signed-off-by: Marc Nuri <marc@marcnuri.com>
  • Loading branch information
manusa committed Jan 11, 2024
1 parent 9797b50 commit b47fa4a
Showing 1 changed file with 231 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
* Copyright (c) 2019 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at:
*
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/
package org.eclipse.jkube.kit.config.service;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.LabelSelector;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.DeploymentSpecBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.NonDeletingOperation;
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
import org.eclipse.jkube.kit.common.KitLogger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.Collection;
import java.util.Collections;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;

@EnableKubernetesMockClient(crud = true)
class PodLogServiceTest {

KubernetesMockServer kubernetesMockServer;
KubernetesClient kubernetesClient;
private KitLogger log;
private KitLogger newPodLog;

private PodLogService.PodLogServiceContext podLogServiceContext;
private Collection<HasMetadata> entities;
private Pod runningPod;

@BeforeEach
void setUp() {
log = spy(new KitLogger.SilentLogger());
newPodLog = spy(new KitLogger.SilentLogger());
podLogServiceContext = PodLogService.PodLogServiceContext.builder()
.log(log)
.oldPodLog(new KitLogger.SilentLogger())
.newPodLog(newPodLog)
.build();
// Create a Deployment to use as the source of the Selector
final Deployment deployment = kubernetesClient.resource(new DeploymentBuilder()
.withMetadata(new ObjectMetaBuilder()
.withName("the-deployment")
.build())
.withSpec(new DeploymentSpecBuilder()
.editOrNewSelector().addToMatchLabels("app", "the-app").endSelector()
.editOrNewTemplate()
.withNewMetadata().withName("the-app-pod").endMetadata()
.endTemplate()
.build())
.build())
.createOr(NonDeletingOperation::update);
entities = Collections.singletonList(deployment);
runningPod = new PodBuilder()
.withNewMetadata().withName("the-pod").addToLabels("app", "the-app").endMetadata()
.withNewSpec().endSpec()
.withNewStatus().withPhase("Running").endStatus()
.build();
}

@Test
@DisplayName("When no name specified, should log selector")
void shouldLogSelector() {
new PodLogService(podLogServiceContext)
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, true, null, false);
verify(log, timeout(1000))
.info(eq("Watching pods with selector %s waiting for a running pod..."), any(LabelSelector.class));
}

@Test
@DisplayName("When name specified, should log name")
void shouldLogName() {
new PodLogService(podLogServiceContext.toBuilder().podName("the-pod").build())
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, true, null, false);
verify(log, timeout(1000))
.info(eq("Watching pod with selector %s, and name %s waiting for a running pod..."), any(LabelSelector.class), eq("the-pod"));
}

@Test
@DisplayName("With no Pod found, should warn user")
void noPodShouldWarnUser() {
new PodLogService(podLogServiceContext)
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, true, null, false);
verify(log, timeout(1000))
.warn("No pod is running yet. Are you sure you deployed your app using Eclipse JKube apply/deploy mechanism?");
verify(log, timeout(1000))
.warn("Or did you undeploy it? If so try running the Eclipse JKube apply/deploy tasks again.");
}

@DisplayName("With Pod running and mismatched container, should error")
@ParameterizedTest(name = "follow: {0}")
@ValueSource(booleans = {true, false})
void podRunningNoContainer(boolean follow) {
// Given
kubernetesClient.resource(new PodBuilder(runningPod)
.editOrNewSpec()
.addNewContainer().withName("container-one").endContainer()
.addNewContainer().withName("container-two").endContainer()
.endSpec()
.build())
.createOr(NonDeletingOperation::update);
// When
new PodLogService(podLogServiceContext.toBuilder().logContainerName("non-existent").build())
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, follow, null, false);
// Then
verify(log, timeout(1000))
.error("log container name %s does not exist in pod!! Did you set the correct value for property 'jkube.log.container'", "non-existent");
}

@Test
@DisplayName("With Pod running, should log control messages")
void podRunningShouldLogControlMessages() {
// Given
kubernetesClient.resource(runningPod).createOr(NonDeletingOperation::update);
// When
new PodLogService(podLogServiceContext)
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, true, null, false);
// Then
verify(newPodLog, timeout(1000)).info("Tailing log of pod: the-pod");
verify(newPodLog, timeout(1000)).info("Press Ctrl-C to stop tailing the log");
}

@Test
@DisplayName("With Pod running, should log container logs")
void podRunningShouldLogContainerLogs() {
// Given
kubernetesClient.resource(runningPod).createOr(NonDeletingOperation::update);
kubernetesMockServer.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/the-pod/log?pretty=false&follow=true")
.andReturn(200, "The\nApplication\nLogs")
.always();
// When
new PodLogService(podLogServiceContext)
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, true, null, false);
// Then
verify(log, timeout(1000)).info("[[s]]%s", "The");
verify(log, timeout(1000)).info("[[s]]%s", "Application");
verify(log, timeout(1000)).info("[[s]]%s", "Logs");
}

@Test
@DisplayName("With Pod running and no follow, should log container logs synchronously")
void podRunningWithNoFollowShouldLogContainerLogs() {
// Given
kubernetesClient.resource(runningPod).createOr(NonDeletingOperation::update);
kubernetesMockServer.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/the-pod/log?pretty=false")
.andReturn(200, "The\nApplication\nLogs")
.always();
// When
new PodLogService(podLogServiceContext)
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, false, null, false);
// Then
verify(log).info("[[s]]%s", "The");
verify(log).info("[[s]]%s", "Application");
verify(log).info("[[s]]%s", "Logs");
}

@Test
@DisplayName("With Pod running and no follow in current thread, should log container logs synchronously")
void podRunningWithNoFollowInCurrentThreadShouldLogContainerLogs() {
// Given
kubernetesClient.resource(runningPod).createOr(NonDeletingOperation::update);
kubernetesMockServer.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/the-pod/log?pretty=false")
.andReturn(200, "The\nApplication\nLogs")
.always();
// When
new PodLogService(podLogServiceContext)
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, false, null, true);
// Then
verify(log).info("[[s]]%s", "The");
verify(log).info("[[s]]%s", "Application");
verify(log).info("[[s]]%s", "Logs");
}

@Test
@DisplayName("With Pod running and deleted, should close previous watcher")
void podRunningAndDeletedShouldLogContainerLogs() {
// Given
kubernetesClient.resource(runningPod).createOr(NonDeletingOperation::update);
kubernetesMockServer.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/the-pod/log?pretty=false&follow=true")
.andReturn(200, "The\nApplication\nLogs")
.always();
kubernetesMockServer.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/new-pod/log?pretty=false&follow=true")
.andReturn(200, "The\nApplication\nLogs")
.always();
new PodLogService(podLogServiceContext)
.tailAppPodsLogs(kubernetesClient, null, entities, false, null, true, null, false);
verify(newPodLog, timeout(1000)).info("Tailing log of pod: the-pod");
kubernetesClient.resource(runningPod).delete();
// When
kubernetesClient.resource(new PodBuilder(runningPod).editMetadata().withName("new-pod").endMetadata().build())
.createOr(NonDeletingOperation::update);
// Then
verify(log, timeout(1000)).info("Closing log watcher for %s as now watching %s", null, "new-pod");
}

}

0 comments on commit b47fa4a

Please sign in to comment.