Skip to content

Commit

Permalink
test: test cases for pod container selection logic
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <[email protected]>
  • Loading branch information
manusa committed Nov 7, 2022
1 parent b34bd57 commit e32230e
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,11 @@ private URL getURL(String operation, String[] commands) throws MalformedURLExcep
*/
String validateOrDefaultContainerId(String name) {
Pod pod = this.require();
List<Container> containers = pod.getSpec().getContainers();
if (containers.isEmpty()) {
// spec and container null-checks are not necessary for real k8s clusters, added them to simplify some tests running in the mockserver
if (pod.getSpec() == null || pod.getSpec().getContainers() == null || pod.getSpec().getContainers().isEmpty()) {
throw new KubernetesClientException("Pod has no containers!");
}
final List<Container> containers = pod.getSpec().getContainers();
if (name == null) {
name = pod.getMetadata().getAnnotations().get(DEFAULT_CONTAINER_ANNOTATION_NAME);
if (name != null && !hasContainer(containers, name)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ void setUp() {
}

@Test
void testWithForgedTar(@TempDir Path targetDirParent) throws Exception {
void testWithForgedTar(@TempDir Path targetDirParent) {
// Given
final Path targetDir = targetDirParent.resolve("target");
final PodOperationsImpl poi = spy(new PodOperationsImpl(baseContext.withDir("/var/source-dir"), new OperationContext()));
doReturn(PodOperationsImpl_CVE2021_20218_Test.class.getResourceAsStream("/2021_20218/tar-with-parent-traversal.tar"))
.when(poi).readTar("/var/source-dir");
.when(poi).readTar("/var/source-dir");
// When
final KubernetesClientException exception = assertThrows(KubernetesClientException.class, () -> poi.copy(targetDir));
// Then
assertThat(exception).getCause()
.hasMessage("Tar entry '../youve-been-hacked' has an invalid name");
.hasMessage("Tar entry '../youve-been-hacked' has an invalid name");
assertThat(targetDirParent).isDirectoryNotContaining("glob:**/youve-been-hacked");
}

Expand All @@ -59,13 +59,13 @@ void testWithValidTar(@TempDir Path targetDirParent) throws Exception {
final Path targetDir = targetDirParent.resolve("target");
final PodOperationsImpl poi = spy(new PodOperationsImpl(baseContext.withDir("/var/source-dir"), new OperationContext()));
doReturn(PodOperationsImpl_CVE2021_20218_Test.class.getResourceAsStream("/2021_20218/valid.tar"))
.when(poi).readTar("/var/source-dir");
.when(poi).readTar("/var/source-dir");
// When
final boolean result = poi.copy(targetDir);
// Then
assertThat(result).isTrue();
assertThat(targetDir)
.isDirectoryContaining("glob:**/hello.txt")
.isDirectoryRecursivelyContaining("glob:**/very/nested/dir/answer.txt");
.isDirectoryContaining("glob:**/hello.txt")
.isDirectoryRecursivelyContaining("glob:**/very/nested/dir/answer.txt");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* 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 io.fabric8.kubernetes.api.model.PodBuilder;
import io.fabric8.kubernetes.api.model.Status;
import io.fabric8.kubernetes.api.model.StatusBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.dsl.ContainerResource;
import io.fabric8.kubernetes.client.dsl.ExecWatch;
import io.fabric8.kubernetes.client.dsl.PodResource;
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.fabric8.mockwebserver.internal.WebSocketMessage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.concurrent.TimeUnit;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

@SuppressWarnings("unused")
@EnableKubernetesMockClient(crud = true)
class PodExecTest {

private KubernetesMockServer server;
private KubernetesClient client;

@BeforeEach
void setUp() {
client.pods().inAnyNamespace().delete();
}

@Test
@DisplayName("With no containers, should throw exception")
void withNoContainers() {
client.pods().resource(new PodBuilder().withNewMetadata().withName("no-containers").endMetadata().build())
.createOrReplace();
final PodResource pr = client.pods().withName("no-containers");
assertThatThrownBy(() -> pr.exec("sh", "-c", "echo Greetings Professor Falken"))
.isInstanceOf(KubernetesClientException.class)
.hasMessage("Pod has no containers!");
}

@Test
@DisplayName("With single container, should exec in the single container")
void withSingleContainer() throws Exception {
client.pods().resource(new PodBuilder().withNewMetadata().withName("single-container").endMetadata()
.withNewSpec()
.addNewContainer()
.withName("the-single-container")
.endContainer()
.endSpec()
.build())
.createOrReplace();
server.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/single-container/exec?command=sleep%201&container=the-single-container")
.andUpgradeToWebSocket()
.open()
.immediately().andEmit(exitZeroEvent())
.done()
.always();
final ExecWatch result = client.pods().withName("single-container").exec("sleep 1");
assertThat(result.exitCode().get(1, TimeUnit.SECONDS)).isZero();
}

@Test
@DisplayName("With single container and inContainer with non-existent name, should throw exception")
void withSingleContainerAndInContainer() {
client.pods().resource(new PodBuilder().withNewMetadata().withName("single-container").endMetadata()
.withNewSpec()
.addNewContainer()
.withName("the-single-container")
.endContainer()
.endSpec()
.build())
.createOrReplace();
final ContainerResource cr = client.pods().withName("single-container").inContainer("non-existent");
assertThatThrownBy(() -> cr.exec("exit 0"))
.isInstanceOf(KubernetesClientException.class)
.hasMessage("container non-existent not found in pod single-container");
}

@Test
@DisplayName("With multiple containers, should exec in the first container")
void withMultipleContainers() throws Exception {
client.pods().resource(new PodBuilder().withNewMetadata().withName("multiple-containers").endMetadata()
.withNewSpec()
.addNewContainer()
.withName("the-first-container")
.endContainer()
.addNewContainer()
.withName("the-second-container")
.endContainer()
.endSpec()
.build())
.createOrReplace();
server.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/multiple-containers/exec?command=sleep%201&container=the-first-container")
.andUpgradeToWebSocket()
.open()
.immediately().andEmit(exitZeroEvent())
.done()
.always();
final ExecWatch result = client.pods().withName("multiple-containers").exec("sleep 1");
assertThat(result.exitCode().get(1, TimeUnit.SECONDS)).isZero();
}

@Test
@DisplayName("With multiple containers and inContainer with existent name, should exec in the selected container")
void withMultipleContainersAndInContainer() throws Exception {
client.pods().resource(new PodBuilder().withNewMetadata().withName("multiple-containers").endMetadata()
.withNewSpec()
.addNewContainer()
.withName("the-first-container")
.endContainer()
.addNewContainer()
.withName("the-second-container")
.endContainer()
.endSpec()
.build())
.createOrReplace();
server.expect()
.get()
.withPath("/api/v1/namespaces/test/pods/multiple-containers/exec?command=sleep%201&container=the-second-container")
.andUpgradeToWebSocket()
.open()
.immediately().andEmit(exitZeroEvent())
.done()
.always();
final ExecWatch result = client.pods().withName("multiple-containers").inContainer("the-second-container").exec("sleep 1");
assertThat(result.exitCode().get(1, TimeUnit.SECONDS)).isZero();
}

private static WebSocketMessage exitZeroEvent() {
final Status success = new StatusBuilder().withStatus("Success").build();
return new WebSocketMessage(0L, "\u0003" + Serialization.asJson(success), false, true);
}
}

0 comments on commit e32230e

Please sign in to comment.