From 2b3b1fc4588c057b40e6d56e612d665df66490db Mon Sep 17 00:00:00 2001 From: David Kornel Date: Wed, 21 Feb 2024 16:30:55 +0100 Subject: [PATCH] Add kubecmd client and executor Signed-off-by: David Kornel --- .../skodjob/testframe/clients/KubeClient.java | 4 + .../clients/KubeClusterException.java | 43 ++ .../clients/cmdClient/BaseCmdKubeClient.java | 400 +++++++++++++++++ .../clients/cmdClient/KubeCmdClient.java | 206 +++++++++ .../testframe/clients/cmdClient/Kubectl.java | 57 +++ .../testframe/clients/cmdClient/Oc.java | 80 ++++ .../io/skodjob/testframe/executor/Exec.java | 424 ++++++++++++++++++ .../testframe/executor/ExecBuilder.java | 60 +++ .../testframe/executor/ExecResult.java | 48 ++ .../interfaces/NamespacedResourceType.java | 4 + .../testframe/interfaces/ResourceType.java | 4 + .../java/io/skodjob/testframe/wait/Wait.java | 4 + .../skodjob/testframe/wait/WaitException.java | 4 + .../resources/ClusterRoleBindingResource.java | 4 + .../resources/ClusterRoleResource.java | 4 + .../resources/ConfigMapResource.java | 4 + .../CustomResourceDefinitionResource.java | 4 + .../resources/DeploymentResource.java | 4 + .../testframe/resources/JobResource.java | 4 + .../testframe/resources/LeaseResource.java | 4 + .../resources/NamespaceResource.java | 4 + .../resources/NetworkPolicyResource.java | 4 + .../resources/RoleBindingResource.java | 4 + .../testframe/resources/RoleResource.java | 4 + .../testframe/resources/SecretResource.java | 4 + .../resources/ServiceAccountResource.java | 4 + .../testframe/resources/ServiceResource.java | 4 + ...alidatingWebhookConfigurationResource.java | 4 + .../resources/OperatorGroupResource.java | 4 + .../resources/SubscriptionResource.java | 4 + 30 files changed, 1406 insertions(+) create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClusterException.java create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/BaseCmdKubeClient.java create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/KubeCmdClient.java create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Kubectl.java create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Oc.java create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/executor/Exec.java create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecBuilder.java create mode 100644 test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecResult.java diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClient.java b/test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClient.java index 6ee3dfa..8edb3f8 100644 --- a/test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClient.java +++ b/test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClient.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.clients; import io.fabric8.kubernetes.client.Config; diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClusterException.java b/test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClusterException.java new file mode 100644 index 0000000..0a55100 --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/clients/KubeClusterException.java @@ -0,0 +1,43 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.clients; + + +import io.skodjob.testframe.executor.ExecResult; + +public class KubeClusterException extends RuntimeException { + public final ExecResult result; + + public KubeClusterException(ExecResult result, String s) { + super(s); + this.result = result; + } + + public KubeClusterException(Throwable cause) { + super(cause); + this.result = null; + } + + public static class NotFound extends KubeClusterException { + + public NotFound(ExecResult result, String s) { + super(result, s); + } + } + + public static class AlreadyExists extends KubeClusterException { + + public AlreadyExists(ExecResult result, String s) { + super(result, s); + } + } + + public static class InvalidResource extends KubeClusterException { + + public InvalidResource(ExecResult result, String s) { + super(result, s); + } + } +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/BaseCmdKubeClient.java b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/BaseCmdKubeClient.java new file mode 100644 index 0000000..5f63935 --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/BaseCmdKubeClient.java @@ -0,0 +1,400 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.clients.cmdClient; + +import io.skodjob.testframe.clients.KubeClusterException; +import io.skodjob.testframe.executor.Exec; +import io.skodjob.testframe.executor.ExecResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.nio.file.NoSuchFileException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static java.lang.String.join; +import static java.util.Arrays.asList; + +public abstract class BaseCmdKubeClient> implements KubeCmdClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(BaseCmdKubeClient.class); + + private static final String CREATE = "create"; + private static final String APPLY = "apply"; + private static final String DELETE = "delete"; + private static final String REPLACE = "replace"; + private static final String PROCESS = "process"; + + public static final String STATEFUL_SET = "statefulset"; + public static final String CM = "cm"; + + protected String config; + + String namespace = defaultNamespace(); + + protected BaseCmdKubeClient(String config) { + this.config = config; + } + + @Override + public abstract String cmd(); + + @Override + @SuppressWarnings("unchecked") + public K deleteByName(String resourceType, String resourceName) { + Exec.exec(namespacedCommand(DELETE, resourceType, resourceName)); + return (K) this; + } + + protected static class Context implements AutoCloseable { + @Override + public void close() { + } + } + + private static final Context NOOP = new Context(); + + protected Context defaultContext() { + return NOOP; + } + + // Admin context is not implemented now, because it's not needed + // In case it will be needed in the future, we should change the kubeconfig and apply it for both oc and kubectl + protected Context adminContext() { + return defaultContext(); + } + + protected List namespacedCommand(String... rest) { + return command(asList(rest), true); + } + + @Override + public String get(String resource, String resourceName) { + return Exec.exec(namespacedCommand("get", resource, resourceName, "-o", "yaml")).out(); + } + + @Override + public String getEvents() { + return Exec.exec(namespacedCommand("get", "events")).out(); + } + + @Override + @SuppressWarnings("unchecked") + public K create(File... files) { + try (Context context = defaultContext()) { + Map execResults = execRecursive(CREATE, files, Comparator.comparing(File::getName).reversed()); + for (Map.Entry entry : execResults.entrySet()) { + if (!entry.getValue().exitStatus()) { + LOGGER.warn("Failed to create {}!", entry.getKey().getAbsolutePath()); + LOGGER.debug(entry.getValue().err()); + } + } + return (K) this; + } + } + + @Override + @SuppressWarnings("unchecked") + public K apply(File... files) { + try (Context context = defaultContext()) { + Map execResults = execRecursive(APPLY, files, Comparator.comparing(File::getName).reversed()); + for (Map.Entry entry : execResults.entrySet()) { + if (!entry.getValue().exitStatus()) { + LOGGER.warn("Failed to apply {}!", entry.getKey().getAbsolutePath()); + LOGGER.debug(entry.getValue().err()); + } + } + return (K) this; + } + } + + @Override + @SuppressWarnings("unchecked") + public K delete(File... files) { + try (Context context = defaultContext()) { + Map execResults = execRecursive(DELETE, files, Comparator.comparing(File::getName).reversed()); + for (Map.Entry entry : execResults.entrySet()) { + if (!entry.getValue().exitStatus()) { + LOGGER.warn("Failed to delete {}!", entry.getKey().getAbsolutePath()); + LOGGER.debug(entry.getValue().err()); + } + } + return (K) this; + } + } + + private Map execRecursive(String subcommand, File[] files, Comparator cmp) { + Map execResults = new HashMap<>(25); + for (File f : files) { + if (f.isFile()) { + if (f.getName().endsWith(".yaml")) { + execResults.put(f, Exec.exec(null, namespacedCommand(subcommand, "-f", f.getAbsolutePath()), 0, false, false)); + } + } else if (f.isDirectory()) { + File[] children = f.listFiles(); + if (children != null) { + Arrays.sort(children, cmp); + execResults.putAll(execRecursive(subcommand, children, cmp)); + } + } else if (!f.exists()) { + throw new RuntimeException(new NoSuchFileException(f.getPath())); + } + } + return execResults; + } + + @Override + @SuppressWarnings("unchecked") + public K replace(File... files) { + try (Context context = defaultContext()) { + Map execResults = execRecursive(REPLACE, files, Comparator.comparing(File::getName)); + for (Map.Entry entry : execResults.entrySet()) { + if (!entry.getValue().exitStatus()) { + LOGGER.warn("Failed to replace {}!", entry.getKey().getAbsolutePath()); + LOGGER.debug(entry.getValue().err()); + } + } + return (K) this; + } + } + + @Override + @SuppressWarnings("unchecked") + public K applyContentInNamespace(String yamlContent) { + try (Context context = defaultContext()) { + Exec.exec(yamlContent, namespacedCommand(APPLY, "-f", "-")); + return (K) this; + } + } + + @Override + @SuppressWarnings("unchecked") + public K deleteContentInNamespace(String yamlContent) { + try (Context context = defaultContext()) { + Exec.exec(yamlContent, namespacedCommand(DELETE, "-f", "-"), 0, true, false); + return (K) this; + } + } + + @Override + @SuppressWarnings("unchecked") + public K applyContent(String yamlContent) { + try (Context context = defaultContext()) { + Exec.exec(yamlContent, command(Arrays.asList(APPLY, "-f", "-"), false), 0, true, true); + return (K) this; + } + } + + @Override + @SuppressWarnings("unchecked") + public K deleteContent(String yamlContent) { + try (Context context = defaultContext()) { + Exec.exec(yamlContent, command(Arrays.asList(DELETE, "-f", "-"), false), 0, true, false); + return (K) this; + } + } + + @Override + @SuppressWarnings("unchecked") + public K createNamespace(String name) { + try (Context context = adminContext()) { + Exec.exec(namespacedCommand(CREATE, "namespace", name)); + } + return (K) this; + } + + @Override + @SuppressWarnings("unchecked") + public K deleteNamespace(String name) { + try (Context context = adminContext()) { + Exec.exec(null, namespacedCommand(DELETE, "namespace", name), 0, true, false); + } + return (K) this; + } + + @Override + @SuppressWarnings("unchecked") + public K scaleByName(String kind, String name, int replicas) { + try (Context context = defaultContext()) { + Exec.exec(null, namespacedCommand("scale", kind, name, "--replicas", Integer.toString(replicas))); + return (K) this; + } + } + + @Override + public ExecResult execInPod(String pod, String... command) { + List cmd = namespacedCommand("exec", pod, "--"); + cmd.addAll(asList(command)); + return Exec.exec(cmd); + } + + @Override + public ExecResult execInPodContainer(String pod, String container, String... command) { + return execInPodContainer(true, pod, container, command); + } + + @Override + public ExecResult execInPodContainer(boolean logToOutput, String pod, String container, String... command) { + List cmd = namespacedCommand("exec", pod, "-c", container, "--"); + cmd.addAll(asList(command)); + return Exec.exec(null, cmd, 0, logToOutput); + } + + @Override + public ExecResult exec(String... command) { + return exec(true, command); + } + + @Override + public ExecResult exec(boolean throwError, String... command) { + return exec(throwError, true, command); + } + + @Override + public ExecResult exec(boolean throwError, boolean logToOutput, String... command) { + List cmd = command(asList(command), false); + return Exec.exec(null, cmd, 0, logToOutput, throwError); + } + + @Override + public ExecResult execInCurrentNamespace(String... commands) { + return Exec.exec(namespacedCommand(commands)); + } + + @Override + public ExecResult execInCurrentNamespace(boolean logToOutput, String... commands) { + return Exec.exec(null, namespacedCommand(commands), 0, logToOutput); + } + + enum ExType { + BREAK, + CONTINUE, + THROW + } + + @Override + public String toString() { + return cmd(); + } + + @Override + public List list(String resourceType) { + return Arrays.stream(Exec.exec(namespacedCommand("get", resourceType, "-o", "jsonpath={range .items[*]}{.metadata.name} ")).out().trim().split(" +")) + .filter(s -> !s.trim().isEmpty()).collect(Collectors.toList()); + } + + @Override + public String getResourceAsJson(String resourceType, String resourceName) { + return Exec.exec(namespacedCommand("get", resourceType, resourceName, "-o", "json")).out(); + } + + @Override + public String getResourceAsYaml(String resourceType, String resourceName) { + return Exec.exec(namespacedCommand("get", resourceType, resourceName, "-o", "yaml")).out(); + } + + @Override + public String getResourcesAsYaml(String resourceType) { + return Exec.exec(namespacedCommand("get", resourceType, "-o", "yaml")).out(); + } + + @Override + public void createResourceAndApply(String template, Map params) { + List cmd = namespacedCommand("process", template, "-l", "app=" + template, "-o", "yaml"); + for (Map.Entry entry : params.entrySet()) { + cmd.add("-p"); + cmd.add(entry.getKey() + "=" + entry.getValue()); + } + + String yaml = Exec.exec(cmd).out(); + applyContentInNamespace(yaml); + } + + @Override + public String describe(String resourceType, String resourceName) { + return Exec.exec(namespacedCommand("describe", resourceType, resourceName)).out(); + } + + @Override + public String logs(String pod, String container) { + String[] args; + if (container != null) { + args = new String[]{"logs", pod, "-c", container}; + } else { + args = new String[]{"logs", pod}; + } + return Exec.exec(namespacedCommand(args)).out(); + } + + @Override + public String searchInLog(String resourceType, String resourceName, long sinceSeconds, String... grepPattern) { + try { + return Exec.exec("bash", "-c", join(" ", namespacedCommand("logs", resourceType + "/" + resourceName, "--since=" + sinceSeconds + "s", + "|", "grep", " -e " + join(" -e ", grepPattern), "-B", "1"))).out(); + } catch (KubeClusterException e) { + if (e.result != null && e.result.returnCode() == 1) { + LOGGER.info("{} not found", Arrays.stream(grepPattern).toList()); + } else { + LOGGER.error("Caught exception while searching {} in logs", Arrays.stream(grepPattern).toList()); + } + } + return ""; + } + + @Override + public String searchInLog(String resourceType, String resourceName, String resourceContainer, long sinceSeconds, String... grepPattern) { + try { + return Exec.exec("bash", "-c", join(" ", namespacedCommand("logs", resourceType + "/" + resourceName, "-c " + resourceContainer, "--since=" + sinceSeconds + "s", + "|", "grep", " -e " + join(" -e ", grepPattern), "-B", "1"))).out(); + } catch (KubeClusterException e) { + if (e.result != null && e.result.exitStatus()) { + LOGGER.info("{} not found", Arrays.stream(grepPattern).toList()); + } else { + LOGGER.error("Caught exception while searching {} in logs", Arrays.stream(grepPattern).toList()); + } + } + return ""; + } + + @Override + public List listResourcesByLabel(String resourceType, String label) { + return asList(Exec.exec(namespacedCommand("get", resourceType, "-l", label, "-o", "jsonpath={range .items[*]}{.metadata.name} ")).out().split("\\s+")); + } + + private List command(List rest, boolean namespaced) { + List result = new ArrayList<>(); + result.add(cmd()); + if (config != null) { + result.add("--kubeconfig"); + result.add(config); + } + if (namespaced) { + result.add("--namespace"); + result.add(namespace); + } + result.addAll(rest); + return result; + } + + @Override + @SuppressWarnings("unchecked") + public K process(Map parameters, String file, Consumer c) { + List command = command(asList(PROCESS, "-f", file), false); + command.addAll(parameters.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).toList()); + ExecResult exec = Exec.builder() + .throwErrors(true) + .withCommand(command) + .exec(); + c.accept(exec.out()); + return (K) this; + } +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/KubeCmdClient.java b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/KubeCmdClient.java new file mode 100644 index 0000000..f63349f --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/KubeCmdClient.java @@ -0,0 +1,206 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.clients.cmdClient; + +import io.skodjob.testframe.executor.ExecResult; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toList; + +/** + * Abstraction for a kubernetes client. + * + * @param The subtype of KubeClient, for fluency. + */ +public interface KubeCmdClient> { + + String defaultNamespace(); + + String defaultOlmNamespace(); + + /** + * Deletes the resources by resource name. + */ + K deleteByName(String resourceType, String resourceName); + + KubeCmdClient namespace(String namespace); + + /** + * Returns namespace for cluster + */ + String namespace(); + + /** + * Creates the resources in the given files. + */ + K create(File... files); + + /** + * Creates the resources in the given files. + */ + K apply(File... files); + + /** + * Deletes the resources in the given files. + */ + K delete(File... files); + + default K create(String... files) { + return create(asList(files).stream().map(File::new).collect(toList()).toArray(new File[0])); + } + + default K apply(String... files) { + return apply(asList(files).stream().map(File::new).collect(toList()).toArray(new File[0])); + } + + default K delete(String... files) { + return delete(asList(files).stream().map(File::new).collect(toList()).toArray(new File[0])); + } + + /** + * Replaces the resources in the given files. + */ + K replace(File... files); + + K applyContentInNamespace(String yamlContent); + + K deleteContentInNamespace(String yamlContent); + + K applyContent(String yamlContent); + + K deleteContent(String yamlContent); + + K createNamespace(String name); + + K deleteNamespace(String name); + + /** + * Scale resource using the scale subresource + * + * @param kind Kind of the resource which should be scaled + * @param name Name of the resource which should be scaled + * @param replicas Number of replicas to which the resource should be scaled + * @return This kube client + */ + K scaleByName(String kind, String name, int replicas); + + /** + * Execute the given {@code command} in the given {@code pod}. + * + * @param pod The pod + * @param command The command + * @return The process result. + */ + ExecResult execInPod(String pod, String... command); + + ExecResult execInCurrentNamespace(String... commands); + + ExecResult execInCurrentNamespace(boolean logToOutput, String... commands); + + /** + * Execute the given {@code command} in the given {@code container} which is deployed in {@code pod}. + * + * @param pod The pod + * @param container The container + * @param command The command + * @return The process result. + */ + ExecResult execInPodContainer(String pod, String container, String... command); + + ExecResult execInPodContainer(boolean logToOutput, String pod, String container, String... command); + + /** + * Execute the given {@code command}. + * + * @param command The command + * @return The process result. + */ + ExecResult exec(String... command); + + /** + * Execute the given {@code command}. You can specify if potential failure will thrown the exception or not. + * + * @param throwError parameter which control thrown exception in case of failure + * @param command The command + * @return The process result. + */ + ExecResult exec(boolean throwError, String... command); + + /** + * Execute the given {@code command}. You can specify if potential failure will thrown the exception or not. + * + * @param throwError parameter which control thrown exception in case of failure + * @param command The command + * @param logToOutput determines if we want to print whole output of command + * @return The process result. + */ + ExecResult exec(boolean throwError, boolean logToOutput, String... command); + + /** + * Get the content of the given {@code resource} with the given {@code name} as YAML. + * + * @param resource The type of resource (e.g. "cm"). + * @param resourceName The name of the resource. + * @return The resource YAML. + */ + String get(String resource, String resourceName); + + /** + * Get a list of events in a given namespace + * + * @return List of events + */ + String getEvents(); + + List list(String resourceType); + + String getResourceAsYaml(String resourceType, String resourceName); + + String getResourcesAsYaml(String resourceType); + + void createResourceAndApply(String template, Map params); + + String describe(String resourceType, String resourceName); + + default String logs(String pod) { + return logs(pod, null); + } + + String logs(String pod, String container); + + /** + * @param resourceType The type of resource + * @param resourceName The name of resource + * @param sinceSeconds Return logs newer than a relative duration like 5s, 2m, or 3h. + * @param grepPattern Grep patterns for search + * @return Grep result as string + */ + String searchInLog(String resourceType, String resourceName, long sinceSeconds, String... grepPattern); + + /** + * @param resourceType The type of resource + * @param resourceName The name of resource + * @param resourceContainer The name of resource container + * @param sinceSeconds Return logs newer than a relative duration like 5s, 2m, or 3h. + * @param grepPattern Grep patterns for search + * @return Grep result as string + */ + String searchInLog(String resourceType, String resourceName, String resourceContainer, long sinceSeconds, String... grepPattern); + + String getResourceAsJson(String resourceType, String resourceName); + + List listResourcesByLabel(String resourceType, String label); + + String cmd(); + + K process(Map domain, String file, Consumer c); + + String getUsername(); +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Kubectl.java b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Kubectl.java new file mode 100644 index 0000000..5529294 --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Kubectl.java @@ -0,0 +1,57 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.clients.cmdClient; + +/** + * A {@link KubeCmdClient} wrapping {@code kubectl}. + */ +public class Kubectl extends BaseCmdKubeClient { + + public static final String KUBECTL = "kubectl"; + + public Kubectl() { + this(null); + } + + public Kubectl(String config) { + super(config); + } + + private Kubectl(String futureNamespace, String config) { + super(config); + namespace = futureNamespace; + } + + @Override + public Kubectl namespace(String namespace) { + return new Kubectl(namespace, config); + } + + @Override + public String namespace() { + return namespace; + } + + @Override + public String defaultNamespace() { + return "default"; + } + + @Override + public String defaultOlmNamespace() { + return "operators"; + } + + @Override + public String cmd() { + return KUBECTL; + } + + @Override + public String getUsername() { + // TODO - implement this! + return null; + } +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Oc.java b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Oc.java new file mode 100644 index 0000000..8295c69 --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/clients/cmdClient/Oc.java @@ -0,0 +1,80 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.clients.cmdClient; + +import io.skodjob.testframe.executor.Exec; + +import java.util.List; +import java.util.Map; + +/** + * A {@link KubeCmdClient} implementation wrapping {@code oc}. + */ +public class Oc extends BaseCmdKubeClient { + + private static final String OC = "oc"; + + public Oc() { + this(null); + } + + public Oc(String config) { + super(config); + } + + private Oc(String futureNamespace, String config) { + super(config); + namespace = futureNamespace; + } + + @Override + public String defaultNamespace() { + return "myproject"; + } + + @Override + public String defaultOlmNamespace() { + return "openshift-marketplace"; + } + + @Override + public Oc namespace(String namespace) { + return new Oc(namespace, config); + } + + @Override + public String namespace() { + return namespace; + } + + @Override + public Oc createNamespace(String name) { + try (Context context = defaultContext()) { + Exec.exec(cmd(), "new-project", name); + } + return this; + } + + public Oc newApp(String template, Map params) { + List cmd = namespacedCommand("new-app", template); + for (Map.Entry entry : params.entrySet()) { + cmd.add("-p"); + cmd.add(entry.getKey() + "=" + entry.getValue()); + } + + Exec.exec(cmd); + return this; + } + + @Override + public String cmd() { + return OC; + } + + @Override + public String getUsername() { + return Exec.exec(cmd(), "whoami").out(); + } +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/executor/Exec.java b/test-frame-common/src/main/java/io/skodjob/testframe/executor/Exec.java new file mode 100644 index 0000000..2048bb8 --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/executor/Exec.java @@ -0,0 +1,424 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.executor; + +import io.fabric8.kubernetes.api.model.EnvVar; +import io.skodjob.testframe.clients.KubeClusterException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Scanner; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.lang.String.join; + +public class Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(Exec.class); + + private static final Pattern ERROR_PATTERN = Pattern.compile("Error from server \\(([a-zA-Z0-9]+)\\):"); + private static final Pattern INVALID_PATTERN = Pattern.compile("The ([a-zA-Z0-9]+) \"([a-z0-9.-]+)\" is invalid:"); + private static final Pattern PATH_SPLITTER = Pattern.compile(System.getProperty("path.separator")); + private static final int MAXIMUM_EXEC_LOG_CHARACTER_SIZE = 2000; + private static final Object LOCK = new Object(); + + public Process process; + private String stdOut; + private String stdErr; + private StreamGobbler stdOutReader; + private StreamGobbler stdErrReader; + private Path logPath; + private final boolean appendLineSeparator; + + public Exec() { + this.appendLineSeparator = true; + } + + public Exec(Path logPath) { + this.appendLineSeparator = true; + this.logPath = logPath; + } + + public Exec(boolean appendLineSeparator) { + this.appendLineSeparator = appendLineSeparator; + } + + public static ExecBuilder builder() { + return new ExecBuilder(); + } + + + /** + * Getter for stdOutput + * + * @return string stdOut + */ + public String out() { + return stdOut; + } + + /** + * Getter for stdErrorOutput + * + * @return string stdErr + */ + public String err() { + return stdErr; + } + + public boolean isRunning() { + return process.isAlive(); + } + + public int getRetCode() { + LOGGER.info("Process: {}", process); + if (isRunning()) { + return -1; + } else { + return process.exitValue(); + } + } + + /** + * Method executes external command + * + * @param command arguments for command + * @return execution results + */ + public static ExecResult exec(String... command) { + return exec(Arrays.asList(command)); + } + + /** + * Method executes external command + * + * @param command arguments for command + * @return execution results + */ + public static ExecResult exec(boolean logToOutput, String... command) { + List commands = new ArrayList<>(Arrays.asList(command)); + return exec(null, commands, 0, logToOutput); + } + + /** + * Method executes external command + * + * @param command arguments for command + * @return execution results + */ + public static ExecResult exec(List command) { + return exec(null, command, 0, false); + } + + /** + * Method executes external command + * + * @param command arguments for command + * @return execution results + */ + public static ExecResult exec(String input, List command) { + return exec(input, command, 0, false); + } + + /** + * Method executes external command + * + * @param command arguments for command + * @param timeout timeout for execution + * @param logToOutput log output or not + * @return execution results + */ + public static ExecResult exec(String input, List command, int timeout, boolean logToOutput) { + return exec(input, command, Collections.emptySet(), timeout, logToOutput, true); + } + + /** + * @param input input + * @param command command + * @param timeout timeout for command + * @param logToOutput log to output + * @param throwErrors throw error if exec fail + * @return results + */ + public static ExecResult exec(String input, List command, int timeout, boolean logToOutput, boolean throwErrors) { + return exec(input, command, Collections.emptySet(), timeout, logToOutput, throwErrors); + } + + + /** + * Method executes external command + * + * @param command arguments for command + * @param envVars + * @param timeout timeout for execution + * @param logToOutput log output or not + * @param throwErrors look for errors in output and throws exception if true + * @return execution results + */ + public static ExecResult exec(String input, List command, Set envVars, int timeout, boolean logToOutput, boolean throwErrors) { + int ret = 1; + ExecResult execResult; + try { + Exec executor = new Exec(); + LOGGER.info("Command: {}", String.join(" ", command)); + ret = executor.execute(input, command, envVars, timeout); + synchronized (LOCK) { + if (logToOutput) { + LOGGER.info("RETURN code: {}", ret); + if (!executor.out().isEmpty()) { + LOGGER.info("======STDOUT START======="); + LOGGER.info("{}", cutExecutorLog(executor.out())); + LOGGER.info("======STDOUT END======"); + } + if (!executor.err().isEmpty()) { + LOGGER.info("======STDERR START======="); + LOGGER.info("{}", cutExecutorLog(executor.err())); + LOGGER.info("======STDERR END======"); + } + } + } + + execResult = new ExecResult(ret, executor.out(), executor.err()); + + if (throwErrors && ret != 0) { + String msg = "`" + join(" ", command) + "` got status code " + ret + " and stderr:\n------\n" + executor.stdErr + "\n------\nand stdout:\n------\n" + executor.stdOut + "\n------"; + + Matcher matcher = ERROR_PATTERN.matcher(executor.err()); + KubeClusterException t = null; + + if (matcher.find()) { + switch (matcher.group(1)) { + case "NotFound": + t = new KubeClusterException.NotFound(execResult, msg); + break; + case "AlreadyExists": + t = new KubeClusterException.AlreadyExists(execResult, msg); + break; + default: + break; + } + } + matcher = INVALID_PATTERN.matcher(executor.err()); + if (matcher.find()) { + t = new KubeClusterException.InvalidResource(execResult, msg); + } + if (t == null) { + t = new KubeClusterException(execResult, msg); + } + throw t; + } + return new ExecResult(ret, executor.out(), executor.err()); + + } catch (IOException | ExecutionException e) { + throw new KubeClusterException(e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new KubeClusterException(e); + } + } + + /** + * Method executes external command + * + * @param commands arguments for command + * @param envVars + * @param timeoutMs timeout in ms for kill + * @return returns ecode of execution + * @throws IOException + * @throws InterruptedException + * @throws ExecutionException + */ + public int execute(String input, List commands, Set envVars, long timeoutMs) throws IOException, InterruptedException, ExecutionException { + LOGGER.trace("Running command - " + join(" ", commands.toArray(new String[0]))); + ProcessBuilder builder = new ProcessBuilder(); + builder.command(commands); + if (envVars != null) { + envVars.forEach(e -> { + builder.environment().put(e.getName(), e.getValue()); + }); + } + builder.directory(new File(System.getProperty("user.dir"))); + process = builder.start(); + try (OutputStream outputStream = process.getOutputStream()) { + if (input != null) { + LOGGER.trace("With stdin {}", input); + outputStream.write(input.getBytes(Charset.defaultCharset())); + } + } + + Future output = readStdOutput(); + Future error = readStdError(); + + int retCode = 1; + if (timeoutMs > 0) { + if (process.waitFor(timeoutMs, TimeUnit.MILLISECONDS)) { + retCode = process.exitValue(); + } else { + process.destroyForcibly(); + } + } else { + retCode = process.waitFor(); + } + + try { + stdOut = output.get(500, TimeUnit.MILLISECONDS); + } catch (TimeoutException ex) { + output.cancel(true); + stdOut = stdOutReader.getData(); + } + + try { + stdErr = error.get(500, TimeUnit.MILLISECONDS); + } catch (TimeoutException ex) { + error.cancel(true); + stdErr = stdErrReader.getData(); + } + storeOutputsToFile(); + + return retCode; + } + + /** + * Method kills process + */ + public void stop() { + process.destroyForcibly(); + stdOut = stdOutReader.getData(); + stdErr = stdErrReader.getData(); + } + + /** + * Get standard output of execution + * + * @return future string output + */ + private Future readStdOutput() { + stdOutReader = new StreamGobbler(process.getInputStream()); + return stdOutReader.read(); + } + + /** + * Get standard error output of execution + * + * @return future string error output + */ + private Future readStdError() { + stdErrReader = new StreamGobbler(process.getErrorStream()); + return stdErrReader.read(); + } + + /** + * Get stdOut and stdErr and store it into files + */ + private void storeOutputsToFile() { + if (logPath != null) { + try { + Files.createDirectories(logPath); + Files.write(Paths.get(logPath.toString(), "stdOutput.log"), stdOut.getBytes(Charset.defaultCharset())); + Files.write(Paths.get(logPath.toString(), "stdError.log"), stdErr.getBytes(Charset.defaultCharset())); + } catch (Exception ex) { + LOGGER.warn("Cannot save output of execution: " + ex.getMessage()); + } + } + } + + /** + * Check if command is executable + * + * @param cmd command + * @return true.false + */ + public static boolean isExecutableOnPath(String cmd) { + for (String dir : PATH_SPLITTER.split(System.getenv("PATH"))) { + if (new File(dir, cmd).canExecute()) { + return true; + } + } + return false; + } + + /** + * This method check the size of executor output log and cut it if it's too long. + * + * @param log executor log + * @return updated log if size is too big + */ + public static String cutExecutorLog(String log) { + if (log.length() > MAXIMUM_EXEC_LOG_CHARACTER_SIZE) { + LOGGER.warn("Executor log is too long. Going to strip it and print only first {} characters", MAXIMUM_EXEC_LOG_CHARACTER_SIZE); + return log.substring(0, MAXIMUM_EXEC_LOG_CHARACTER_SIZE); + } + return log; + } + + /** + * Class represent async reader + */ + class StreamGobbler { + private final InputStream is; + private final StringBuffer data = new StringBuffer(); + + /** + * Constructor of StreamGobbler + * + * @param is input stream for reading + */ + StreamGobbler(InputStream is) { + this.is = is; + } + + /** + * Return data from stream sync + * + * @return string of data + */ + public String getData() { + return data.toString(); + } + + /** + * read method + * + * @return return future string of output + */ + public Future read() { + return CompletableFuture.supplyAsync(() -> { + try (Scanner scanner = new Scanner(is, StandardCharsets.UTF_8.name())) { + while (scanner.hasNextLine()) { + data.append(scanner.nextLine()); + if (appendLineSeparator) { + data.append(System.getProperty("line.separator")); + } + } + scanner.close(); + return data.toString(); + } catch (Exception e) { + throw new CompletionException(e); + } + }, runnable -> new Thread(runnable).start()); + } + } +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecBuilder.java b/test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecBuilder.java new file mode 100644 index 0000000..1c0e0c6 --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecBuilder.java @@ -0,0 +1,60 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.executor; + +import io.fabric8.kubernetes.api.model.EnvVar; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +public class ExecBuilder { + + private String input; + private List command; + private Set envVars; + private int timeout; + private boolean logToOutput; + private boolean throwErrors; + + public ExecBuilder withCommand(List command) { + this.command = command; + return this; + } + + public ExecBuilder withCommand(String... cmd) { + this.command = Arrays.asList(cmd); + return this; + } + + public ExecBuilder withEnvVars(Set envVars) { + this.envVars = envVars; + return this; + } + + public ExecBuilder withInput(String input) { + this.input = input; + return this; + } + + public ExecBuilder logToOutput(boolean logToOutput) { + this.logToOutput = logToOutput; + return this; + } + + public ExecBuilder throwErrors(boolean throwErrors) { + this.throwErrors = throwErrors; + return this; + } + + public ExecBuilder timeout(int timeout) { + this.timeout = timeout; + return this; + } + + public ExecResult exec() { + return Exec.exec(input, command, envVars, timeout, logToOutput, throwErrors); + } +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecResult.java b/test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecResult.java new file mode 100644 index 0000000..5220726 --- /dev/null +++ b/test-frame-common/src/main/java/io/skodjob/testframe/executor/ExecResult.java @@ -0,0 +1,48 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ +package io.skodjob.testframe.executor; + +import java.io.Serializable; + +public class ExecResult implements Serializable { + + private static final long serialVersionUID = 1L; + + private final int returnCode; + private final String stdOut; + private final String stdErr; + + ExecResult(int returnCode, String stdOut, String stdErr) { + this.returnCode = returnCode; + this.stdOut = stdOut; + this.stdErr = stdErr; + } + + public boolean exitStatus() { + return returnCode == 0; + } + + public int returnCode() { + return returnCode; + } + + public String out() { + return stdOut; + } + + public String err() { + return stdErr; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ExecResult{"); + sb.append("returnCode=").append(returnCode); + sb.append(", stdOut='").append(stdOut).append('\''); + sb.append(", stdErr='").append(stdErr).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/NamespacedResourceType.java b/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/NamespacedResourceType.java index d96c770..3cb394b 100644 --- a/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/NamespacedResourceType.java +++ b/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/NamespacedResourceType.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.interfaces; import io.fabric8.kubernetes.api.model.HasMetadata; diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/ResourceType.java b/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/ResourceType.java index 25b8c14..a114de1 100644 --- a/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/ResourceType.java +++ b/test-frame-common/src/main/java/io/skodjob/testframe/interfaces/ResourceType.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.interfaces; import io.fabric8.kubernetes.api.model.HasMetadata; diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/wait/Wait.java b/test-frame-common/src/main/java/io/skodjob/testframe/wait/Wait.java index dd4a9b7..b690215 100644 --- a/test-frame-common/src/main/java/io/skodjob/testframe/wait/Wait.java +++ b/test-frame-common/src/main/java/io/skodjob/testframe/wait/Wait.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.wait; import java.io.PrintWriter; diff --git a/test-frame-common/src/main/java/io/skodjob/testframe/wait/WaitException.java b/test-frame-common/src/main/java/io/skodjob/testframe/wait/WaitException.java index 0b6cb96..0b0b1d8 100644 --- a/test-frame-common/src/main/java/io/skodjob/testframe/wait/WaitException.java +++ b/test-frame-common/src/main/java/io/skodjob/testframe/wait/WaitException.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.wait; public class WaitException extends RuntimeException { diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleBindingResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleBindingResource.java index 8b83f6e..2be0e7c 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleBindingResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleBindingResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleResource.java index 32f8cce..8ba63d7 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ClusterRoleResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.rbac.ClusterRole; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ConfigMapResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ConfigMapResource.java index e5bf221..3a17989 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ConfigMapResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ConfigMapResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.ConfigMap; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/CustomResourceDefinitionResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/CustomResourceDefinitionResource.java index bc676a4..f7ccd0a 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/CustomResourceDefinitionResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/CustomResourceDefinitionResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/DeploymentResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/DeploymentResource.java index ed8a3f2..70719ad 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/DeploymentResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/DeploymentResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.apps.Deployment; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/JobResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/JobResource.java index 810d864..e3e19bf 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/JobResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/JobResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.batch.v1.Job; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/LeaseResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/LeaseResource.java index 97968c6..e4a3217 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/LeaseResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/LeaseResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.coordination.v1.Lease; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NamespaceResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NamespaceResource.java index f250df8..011190e 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NamespaceResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NamespaceResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.NamespaceBuilder; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NetworkPolicyResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NetworkPolicyResource.java index 6e3e6d0..981727e 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NetworkPolicyResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/NetworkPolicyResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleBindingResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleBindingResource.java index 2c6d64e..8779762 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleBindingResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleBindingResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.rbac.RoleBinding; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleResource.java index d6b6deb..497d6bb 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/RoleResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.rbac.Role; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/SecretResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/SecretResource.java index 860479e..a642f5b 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/SecretResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/SecretResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.Secret; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceAccountResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceAccountResource.java index 8a5e813..62de8bb 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceAccountResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceAccountResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.ServiceAccount; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceResource.java index e69e0b8..1fadadc 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ServiceResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.Service; diff --git a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ValidatingWebhookConfigurationResource.java b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ValidatingWebhookConfigurationResource.java index 41aec3d..defcb26 100644 --- a/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ValidatingWebhookConfigurationResource.java +++ b/test-frame-kubernetes/src/main/java/io/skodjob/testframe/resources/ValidatingWebhookConfigurationResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookConfiguration; diff --git a/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/OperatorGroupResource.java b/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/OperatorGroupResource.java index 01679af..c502b64 100644 --- a/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/OperatorGroupResource.java +++ b/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/OperatorGroupResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.client.dsl.MixedOperation; diff --git a/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/SubscriptionResource.java b/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/SubscriptionResource.java index 372c17b..34c0628 100644 --- a/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/SubscriptionResource.java +++ b/test-frame-openshift/src/main/java/io/skodjob/testframe/resources/SubscriptionResource.java @@ -1,3 +1,7 @@ +/* + * Copyright Skodjob authors. + * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html). + */ package io.skodjob.testframe.resources; import io.fabric8.kubernetes.client.dsl.MixedOperation;