From 0c9372ebde900a084bb4ddbe091544b049b01ba4 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Wed, 23 Sep 2020 22:31:36 +0530 Subject: [PATCH] Fix #364: jkube.watch.postExec property/parameter/configuration is ignored Added support for copying changed files and executing a command provided in postExec option after copying files to the application pod. --- CHANGELOG.md | 1 + .../build/api/assembly/AssemblyManager.java | 3 +- .../kit/build/service/docker/ServiceHub.java | 2 +- .../build/service/docker/WatchService.java | 57 ++++++-- .../eclipse/jkube/kit/common/TimeUtil.java | 20 +++ .../kit/common/util/KubernetesHelper.java | 11 ++ .../jkube/kit/common/util/TimeUtilTest.java | 42 ++++++ .../watcher/standard/DockerImageWatcher.java | 71 +++++++++- .../standard/DockerImageWatcherTest.java | 130 ++++++++++++++++++ 9 files changed, 320 insertions(+), 17 deletions(-) create mode 100644 jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/TimeUtilTest.java create mode 100644 jkube-kit/watcher/standard/src/test/java/org/eclipse/jkube/watcher/standard/DockerImageWatcherTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f18d88c384..c067470c92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Usage: * Fix #381: Remove root as default user in AssemblyConfigurationUtils#getAssemblyConfigurationOrCreateDefault * Fix #358: Prometheus is enabled by default, opt-out via AB_PROMETHEUS_OFF required to disable (like in FMP) * Fix #384: Enricher defined Container environment variables get merged with vars defined in Image Build Configuration +* Fix #364: jkube.watch.postExec property/parameter/configuration is ignored ### 1.0.0 (2020-09-09) * Fix #351: Fix AutoTLSEnricher - add annotation + volume config to resource diff --git a/jkube-kit/build/api/src/main/java/org/eclipse/jkube/kit/build/api/assembly/AssemblyManager.java b/jkube-kit/build/api/src/main/java/org/eclipse/jkube/kit/build/api/assembly/AssemblyManager.java index a1a3c190c1..be9e8761b0 100644 --- a/jkube-kit/build/api/src/main/java/org/eclipse/jkube/kit/build/api/assembly/AssemblyManager.java +++ b/jkube-kit/build/api/src/main/java/org/eclipse/jkube/kit/build/api/assembly/AssemblyManager.java @@ -201,7 +201,8 @@ public File createChangedFilesArchive( File archiveDir = createArchiveDir(dirs); for (AssemblyFileEntry entry : entries) { File dest = prepareChangedFilesArchivePath(archiveDir, entry.getDest(), assemblyDirectory); - Files.copy(Paths.get(entry.getSource().getAbsolutePath()), Paths.get(dest.getAbsolutePath())); + Files.createDirectories(dest.getParentFile().toPath()); + Files.copy(Paths.get(entry.getSource().getAbsolutePath()), Paths.get(dest.getAbsolutePath()), StandardCopyOption.REPLACE_EXISTING); } return JKubeTarArchiver.createTarBallOfDirectory(archive, archiveDir, ArchiveCompression.none); } catch (IOException exp) { diff --git a/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/ServiceHub.java b/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/ServiceHub.java index 55221af356..c3c57bd38a 100644 --- a/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/ServiceHub.java +++ b/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/ServiceHub.java @@ -52,7 +52,7 @@ public class ServiceHub { runService = new RunService(dockerAccess, queryService, containerTracker, logSpecFactory, logger); buildService = new BuildService(dockerAccess, queryService, registryService, archiveService, logger); volumeService = new VolumeService(dockerAccess); - watchService = new WatchService(archiveService, buildService, dockerAccess, queryService, runService, logger); + watchService = new WatchService(archiveService, buildService, queryService, runService, logger); waitService = new WaitService(dockerAccess, queryService, logger); } else { queryService = null; diff --git a/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/WatchService.java b/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/WatchService.java index 2cfdb8d9d7..3dd19b4948 100644 --- a/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/WatchService.java +++ b/jkube-kit/build/service/docker/src/main/java/org/eclipse/jkube/kit/build/service/docker/WatchService.java @@ -24,6 +24,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.function.Predicate; import lombok.AllArgsConstructor; import lombok.Builder; @@ -36,9 +38,6 @@ import org.eclipse.jkube.kit.config.image.WaitConfiguration; import org.eclipse.jkube.kit.config.image.build.JKubeConfiguration; import org.eclipse.jkube.kit.build.api.assembly.AssemblyFiles; -import org.eclipse.jkube.kit.build.service.docker.access.DockerAccess; -import org.eclipse.jkube.kit.build.service.docker.access.DockerAccessException; -import org.eclipse.jkube.kit.build.service.docker.access.ExecException; import org.eclipse.jkube.kit.build.service.docker.access.PortMapping; import org.eclipse.jkube.kit.build.service.docker.access.log.LogDispatcher; import org.eclipse.jkube.kit.config.image.WatchImageConfiguration; @@ -55,16 +54,14 @@ public class WatchService { private final ArchiveService archiveService; private final BuildService buildService; - private final DockerAccess dockerAccess; private final QueryService queryService; private final RunService runService; private final KitLogger log; - public WatchService(ArchiveService archiveService, BuildService buildService, DockerAccess dockerAccess, QueryService queryService, RunService + public WatchService(ArchiveService archiveService, BuildService buildService, QueryService queryService, RunService runService, KitLogger log) { this.archiveService = archiveService; this.buildService = buildService; - this.dockerAccess = dockerAccess; this.queryService = queryService; this.runService = runService; this.log = log; @@ -94,8 +91,7 @@ public synchronized void watch(WatchContext context, JKubeConfiguration buildCon if (imageConfig.getBuildConfiguration() != null && imageConfig.getBuildConfiguration().getAssembly() != null) { if (watcher.isCopy()) { - String containerBaseDir = imageConfig.getBuildConfiguration().getAssembly().getTargetDir(); - schedule(executor, createCopyWatchTask(watcher, context.getBuildContext(), containerBaseDir), interval); + schedule(executor, createCopyWatchTask(watcher, context.getBuildContext()), interval); tasks.add("copying artifacts"); } @@ -134,7 +130,7 @@ private void schedule(ScheduledExecutorService executor, Runnable runnable, long } private Runnable createCopyWatchTask(final ImageWatcher watcher, - final JKubeConfiguration jKubeConfiguration, final String containerBaseDir) throws IOException { + final JKubeConfiguration jKubeConfiguration) throws IOException { final ImageConfiguration imageConfig = watcher.getImageConfiguration(); final AssemblyFiles files = archiveService.getAssemblyFiles(imageConfig, jKubeConfiguration); @@ -148,9 +144,9 @@ public void run() { File changedFilesArchive = archiveService.createChangedFilesArchive(entries, files.getAssemblyDirectory(), imageConfig.getName(), jKubeConfiguration); - dockerAccess.copyArchive(watcher.getContainerId(), changedFilesArchive, containerBaseDir); + copyFilesToContainer(changedFilesArchive, watcher); callPostExec(watcher); - } catch (IOException | ExecException e) { + } catch (Exception e) { log.error("%s: Error when copying files to container %s: %s", imageConfig.getDescription(), watcher.getContainerId(), e.getMessage()); } @@ -159,13 +155,44 @@ public void run() { }; } - private void callPostExec(ImageWatcher watcher) throws DockerAccessException, ExecException { + private void copyFilesToContainer(File changedFilesArchive, ImageWatcher watcher) { + Predicate copyTask = watcher.getWatchContext().getContainerCopyTask(); + if (copyTask != null) { + boolean copyStatus = copyTask.test(changedFilesArchive); + if (!copyStatus) { + log.warn("Unable to copy files into container"); + return; + } + log.info("Files successfully coped to the container.."); + } else { + log.warn("No copy task found for copy mode. Ignoring.."); + } + } + + + private void callPostExec(ImageWatcher watcher) throws Exception { if (watcher.getPostExec() != null) { - String containerId = watcher.getContainerId(); - runService.execInContainer(containerId, watcher.getPostExec(), watcher.getImageConfiguration()); + Function execTask = watcher.getWatchContext().getContainerCommandExecutor(); + if (execTask == null) { + execTask = getDefaultContainerExecTask(); + } + String execOutput = execTask.apply(watcher); + log.info("postExec output: " + execOutput); } } + private Function getDefaultContainerExecTask() { + return watcher -> { + String containerId = watcher.getContainerId(); + try { + return runService.execInContainer(containerId, watcher.getPostExec(), watcher.getImageConfiguration()); + } catch (Exception e) { + log.info("Not able to execute command specified in postExec ", e.getMessage()); + } + return null; + }; + } + private Runnable createBuildWatchTask(final ImageWatcher watcher, final JKubeConfiguration mojoParameters, final boolean doRestart, final JKubeConfiguration buildContext) throws IOException { @@ -408,6 +435,8 @@ public static class WatchContext implements Serializable { private boolean autoCreateCustomNetworks; private Task imageCustomizer; private Task containerRestarter; + private Function containerCommandExecutor; + private Predicate containerCopyTask; private transient ServiceHub hub; private transient ServiceHubFactory serviceHubFactory; diff --git a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/TimeUtil.java b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/TimeUtil.java index a839f4c4f0..56f25a1f2a 100644 --- a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/TimeUtil.java +++ b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/TimeUtil.java @@ -14,6 +14,7 @@ package org.eclipse.jkube.kit.common; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import static java.util.concurrent.TimeUnit.HOURS; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -66,4 +67,23 @@ public static String formatDurationTill(long start) { return res.toString(); } + + /** + * Waits until a condition is satisfied upto a certain amount of time. + * + * @param predicate condition which is tested after each second + * @param item item which needs to be acted upon + * @param timeInMillis max timeout in seconds + * @param pollIntervalInMillis Poll interval for checking if condition is satisfied + * @param type for item + * @throws InterruptedException in case interrupted while waiting + */ + public static void waitUntilCondition(Predicate predicate, T item, int timeInMillis, int pollIntervalInMillis) throws InterruptedException { + for (int timeWaited = 0; timeWaited < timeInMillis; timeWaited += pollIntervalInMillis) { + if (predicate.test(item)) { + break; + } + Thread.sleep(pollIntervalInMillis); + } + } } diff --git a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java index 06b7cdcb24..74585b17e3 100644 --- a/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java +++ b/jkube-kit/common/src/main/java/org/eclipse/jkube/kit/common/util/KubernetesHelper.java @@ -75,6 +75,7 @@ import io.fabric8.kubernetes.api.model.batch.Job; import io.fabric8.kubernetes.api.model.batch.JobSpec; import io.fabric8.kubernetes.client.ConfigBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.Watch; import io.fabric8.kubernetes.client.Watcher; @@ -888,5 +889,15 @@ public static Map getCustomResourcesFileToNameMap( } return fileToCrdGroupMap; } + + public static String getNewestApplicationPodName(KubernetesClient client, String namespace, Set resources) { + LabelSelector selector = KubernetesHelper.getPodLabelSelector(resources); + PodList pods = client.pods().inNamespace(namespace).withLabelSelector(selector).list(); + Pod newestPod = KubernetesHelper.getNewestPod(pods.getItems()); + if (newestPod != null) { + return newestPod.getMetadata().getName(); + } + return null; + } } diff --git a/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/TimeUtilTest.java b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/TimeUtilTest.java new file mode 100644 index 0000000000..f257d3754b --- /dev/null +++ b/jkube-kit/common/src/test/java/org/eclipse/jkube/kit/common/util/TimeUtilTest.java @@ -0,0 +1,42 @@ +/** + * 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.common.util; + +import org.eclipse.jkube.kit.common.TimeUtil; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.assertTrue; + +public class TimeUtilTest { + @Test + public void testWaitUntilCondition() throws InterruptedException { + long timeBeforeWait = System.currentTimeMillis(); + AtomicBoolean value = new AtomicBoolean(false); + new Thread(() -> { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException interruptedException) { + interruptedException.printStackTrace(); + } + value.set(true); + }).start(); + + TimeUtil.waitUntilCondition(AtomicBoolean::get, value, 200, 50); + long timeAfterWait = System.currentTimeMillis(); + assertTrue(timeAfterWait - timeBeforeWait < 200); + } +} diff --git a/jkube-kit/watcher/standard/src/main/java/org/eclipse/jkube/watcher/standard/DockerImageWatcher.java b/jkube-kit/watcher/standard/src/main/java/org/eclipse/jkube/watcher/standard/DockerImageWatcher.java index c6318cfe57..8120454338 100644 --- a/jkube-kit/watcher/standard/src/main/java/org/eclipse/jkube/watcher/standard/DockerImageWatcher.java +++ b/jkube-kit/watcher/standard/src/main/java/org/eclipse/jkube/watcher/standard/DockerImageWatcher.java @@ -13,6 +13,9 @@ */ package org.eclipse.jkube.watcher.standard; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.util.Date; import java.util.List; import java.util.Set; @@ -29,9 +32,12 @@ import io.fabric8.kubernetes.api.model.apps.ReplicaSetSpec; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.dsl.ExecWatch; import io.fabric8.openshift.api.model.DeploymentConfig; import io.fabric8.openshift.api.model.DeploymentConfigSpec; import io.fabric8.openshift.client.OpenShiftClient; +import org.eclipse.jkube.kit.common.KitLogger; +import org.eclipse.jkube.kit.common.TimeUtil; import org.eclipse.jkube.kit.config.image.ImageConfiguration; import org.eclipse.jkube.kit.build.service.docker.ServiceHub; import org.eclipse.jkube.kit.build.service.docker.WatchService; @@ -45,6 +51,9 @@ public class DockerImageWatcher extends BaseWatcher { + private static final int WAIT_TIMEOUT_IN_SECONDS = 5000; + private static final int POLL_INTERVAL_IN_MILLISECONDS = 200; + public DockerImageWatcher(WatcherContext watcherContext) { super(watcherContext, "docker-image"); } @@ -61,7 +70,10 @@ public void watch(List configs, final Set resou // add a image customizer watchContext = watchContext.toBuilder() - .imageCustomizer(this::buildImage).containerRestarter(imageWatcher -> restartContainer(imageWatcher, resources)) + .imageCustomizer(this::buildImage) + .containerRestarter(imageWatcher -> restartContainer(imageWatcher, resources)) + .containerCommandExecutor(imageWatcher -> executeCommandInPod(imageWatcher, resources)) + .containerCopyTask(f -> copyFileToPod(f, resources)) .build(); ServiceHub hub = getContext().getJKubeServiceHub().getDockerServiceHub(); @@ -161,6 +173,63 @@ private void updateImageName(KubernetesClient kubernetes, String namespace, HasM } } + private String executeCommandInPod(WatchService.ImageWatcher imageWatcher, Set resources) { + ClusterAccess clusterAccess = getContext().getJKubeServiceHub().getClusterAccess(); + return executeCommandInPod(imageWatcher, resources, clusterAccess, this.log, WAIT_TIMEOUT_IN_SECONDS, POLL_INTERVAL_IN_MILLISECONDS); + } + + private boolean copyFileToPod(File fileToUpload, Set resources) { + ClusterAccess clusterAccess = getContext().getJKubeServiceHub().getClusterAccess(); + return copyFileToPod(fileToUpload, resources, clusterAccess, this.log, WAIT_TIMEOUT_IN_SECONDS, POLL_INTERVAL_IN_MILLISECONDS); + } + + + static String executeCommandInPod(WatchService.ImageWatcher imageWatcher, Set resources, ClusterAccess clusterAccess, KitLogger logger, int execWaitTimeoutInMillis, int pollIntervalInMillis) { + try (KubernetesClient client = clusterAccess.createDefaultClient()) { + String namespace = clusterAccess.getNamespace(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ExecWatch execWatch = client.pods().inNamespace(namespace).withName(KubernetesHelper.getNewestApplicationPodName(client, namespace, resources)).writingOutput(byteArrayOutputStream) + .exec(imageWatcher.getPostExec().split("[\\s']")); + + // Wait for at most 5 seconds for Exec to complete + TimeUtil.waitUntilCondition(o -> o.size() > 0, byteArrayOutputStream, execWaitTimeoutInMillis, pollIntervalInMillis); + String commandOutput = byteArrayOutputStream.toString(); + execWatch.close(); + return commandOutput; + } catch (KubernetesClientException e) { + KubernetesHelper.handleKubernetesClientException(e, logger); + } catch (IllegalStateException e) { + throw e; + } catch (Exception e) { + throw new IllegalStateException(e.getMessage(), e); + } + return null; + } + + static boolean copyFileToPod(File fileToUpload, Set resources, ClusterAccess clusterAccess, KitLogger logger, int execWaitTimeoutInSeconds, int pollIntervalInMillis) { + try (KubernetesClient client = clusterAccess.createDefaultClient()) { + String namespace = clusterAccess.getNamespace(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ExecWatch execWatch = client.pods().inNamespace(namespace) + .withName(KubernetesHelper.getNewestApplicationPodName(client, namespace, resources)) + .readingInput(new FileInputStream(fileToUpload)) + .writingOutput(out) + .exec("tar", "-xf", "-", "-C", "/"); + + // Wait for at most 5 seconds for Exec to complete + TimeUtil.waitUntilCondition(o -> o.size() > 0, out, execWaitTimeoutInSeconds, pollIntervalInMillis); + execWatch.close(); + return true; + } catch (KubernetesClientException e) { + KubernetesHelper.handleKubernetesClientException(e, logger); + } catch (IllegalStateException e) { + throw e; + } catch (Exception e) { + throw new IllegalStateException(e.getMessage(), e); + } + return false; + } + private boolean updateImageName(HasMetadata entity, PodTemplateSpec template, String imagePrefix, String imageName) { boolean answer = false; PodSpec spec = template.getSpec(); diff --git a/jkube-kit/watcher/standard/src/test/java/org/eclipse/jkube/watcher/standard/DockerImageWatcherTest.java b/jkube-kit/watcher/standard/src/test/java/org/eclipse/jkube/watcher/standard/DockerImageWatcherTest.java new file mode 100644 index 0000000000..2ee8c645e5 --- /dev/null +++ b/jkube-kit/watcher/standard/src/test/java/org/eclipse/jkube/watcher/standard/DockerImageWatcherTest.java @@ -0,0 +1,130 @@ +/** + * 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.watcher.standard; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.LabelSelector; +import io.fabric8.kubernetes.api.model.LabelSelectorBuilder; +import io.fabric8.kubernetes.api.model.PodBuilder; +import io.fabric8.kubernetes.api.model.PodListBuilder; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import mockit.Expectations; +import mockit.Mocked; +import mockit.Verifications; +import org.eclipse.jkube.kit.build.service.docker.WatchService; +import org.eclipse.jkube.kit.common.KitLogger; +import org.eclipse.jkube.kit.config.access.ClusterAccess; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class DockerImageWatcherTest { + @Mocked + WatchService.ImageWatcher imageWatcher; + + @Mocked + KitLogger logger; + + @Mocked + ClusterAccess clusterAccess; + + @Mocked + KubernetesClient kubernetesClient; + + @Before + public void init() { + new Expectations() {{ + + clusterAccess.getNamespace(); + result = "default"; + + clusterAccess.createDefaultClient(); + result = kubernetesClient; + + kubernetesClient.pods().inNamespace(anyString).withLabelSelector((LabelSelector)any).list(); + result = new PodListBuilder() + .addToItems(new PodBuilder() + .withNewMetadata() + .withName("testpod") + .endMetadata().build()) + .build(); + }}; + } + + @Test + public void testExecuteCommandInPod() { + // Given + new Expectations() {{ + imageWatcher.getPostExec(); + result = "ls -lt /deployments"; + }}; + Set resources = getMockedResourceList(); + + // When + DockerImageWatcher.executeCommandInPod(imageWatcher, resources, clusterAccess, logger, 0, 1); + + // Then + new Verifications() {{ + kubernetesClient.pods().inNamespace("default").withLabelSelector(new LabelSelectorBuilder().withMatchLabels(Collections.singletonMap("foo", "bar")).build()).list(); + times = 1; + + kubernetesClient.pods().inNamespace("default").withName("testpod").exec(new String[]{"ls", "-lt", "/deployments"}); + times = 1; + }}; + } + + @Test + public void testCopyFileToPod() throws IOException { + // Given + Set resources = getMockedResourceList(); + File fileToCopy = Files.createTempFile("text", "txt").toFile(); + + // When + DockerImageWatcher.copyFileToPod(fileToCopy, resources, clusterAccess, logger, 0, 1); + + // Then + new Verifications() {{ + LabelSelector labelSelector = new LabelSelectorBuilder() + .withMatchLabels(Collections.singletonMap("foo", "bar")) + .build(); + kubernetesClient.pods().inNamespace("default").withLabelSelector(labelSelector).list(); + times = 1; + + kubernetesClient.pods().inNamespace("default").withName("testpod").readingInput((FileInputStream)any).writingOutput((OutputStream)any).exec("tar", "-xf", "-", "-C", "/"); + times = 1; + }}; + } + + private Set getMockedResourceList() { + Set resources = new HashSet<>(); + resources.add(new DeploymentBuilder() + .withNewMetadata().withName("foo").endMetadata() + .withNewSpec() + .withNewSelector() + .addToMatchLabels("foo", "bar") + .endSelector() + .endSpec() + .build()); + return resources; + } +}