
com.oracle.bedrock.runtime.k8s.Pod Maven / Gradle / Ivy
Show all versions of bedrock-runtime-k8s Show documentation
/*
* File: Pod.java
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* The contents of this file are subject to the terms and conditions of
* the Common Development and Distribution License 1.0 (the "License").
*
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License by consulting the LICENSE.txt file
* distributed with this file, or by consulting https://oss.oracle.com/licenses/CDDL
*
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file LICENSE.txt.
*
* MODIFICATIONS:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*/
package com.oracle.bedrock.runtime.k8s;
import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.options.LaunchLogging;
import com.oracle.bedrock.runtime.Application;
import com.oracle.bedrock.runtime.ApplicationConsole;
import com.oracle.bedrock.runtime.console.CapturingApplicationConsole;
import com.oracle.bedrock.runtime.console.FileWriterApplicationConsole;
import com.oracle.bedrock.runtime.console.InputFromIntputStreamRedirector;
import com.oracle.bedrock.runtime.console.OutputToOutputStreamRedirector;
import com.oracle.bedrock.runtime.options.Arguments;
import com.oracle.bedrock.runtime.options.Console;
import com.oracle.bedrock.runtime.options.ConsoleInputRedirector;
import com.oracle.bedrock.runtime.options.ConsoleOutputRedirector;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* A representation of a Kubernetes Pod.
*
* Copyright (c) 2019. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
*
* @author Jonathan Knight
*/
public class Pod
{
/**
* The {@link K8sCluster} containing this {@link Pod}.
*/
private K8sCluster k8s;
/**
* The name of this {@link Pod}.
*/
private String podName;
/**
* The namespace that this {@link Pod} is running in.
*/
private String namespace;
/**
* The containers in this Pod.
*/
private Map containers;
/**
* Create a {@link Pod} in the default namespace.
*
* @param k8s the {@link K8sCluster} containing this {@link Pod}
* @param podName the name of this {@link Pod}
*/
public Pod(K8sCluster k8s, String podName)
{
this(k8s, podName, null);
}
/**
* Create a {@link Pod}.
*
* @param k8s the {@link K8sCluster} containing this {@link Pod}
* @param podName the name of this {@link Pod}
* @param namespace the namespace that this {@link Pod} is running in
*/
public Pod(K8sCluster k8s, String podName, String namespace)
{
if (podName == null || podName.trim().isEmpty())
{
throw new IllegalArgumentException("Pod name cannot be null or blank");
}
this.k8s = k8s == null ? new K8sCluster() : k8s;
this.podName = podName;
this.namespace = namespace == null || namespace.trim().isEmpty() ? K8sCluster.DEFAULT_NAMESPACE : namespace;
}
/**
* Obtain the {@link K8sCluster} containing this {@link Pod}
*
* @return the {@link K8sCluster} containing this {@link Pod}
*/
public K8sCluster getK8sCluster()
{
return k8s;
}
/**
* Obtain the name of this {@link Pod}.
*
* @return the name of this {@link Pod}
*/
public String getPodName()
{
return podName;
}
/**
* Obtain the namespace that this {@link Pod} is running in.
*
* @return the namespace that this {@link Pod} is running in
*/
public String getNamespace()
{
return namespace;
}
/**
* Copy a local file to this Pod.
*
* @param source the location of the file to copy
* @param destination the destination to copy to in the Pod
*
* @throws Exception if an error occurs copying the file
*/
public void copyTo(Path source, Path destination) throws Exception
{
if (!Files.exists(source))
{
throw new IOException("Source does not exists " + source);
}
List listArgs = getExecArgs(true, "cat > " + destination.toString());
try (FileInputStream in = new FileInputStream(source.toFile()))
{
InputFromIntputStreamRedirector redirector = new InputFromIntputStreamRedirector(in);
k8s.kubectl(Arguments.of(listArgs),
LaunchLogging.disabled(),
ConsoleInputRedirector.of(redirector));
redirector.join();
}
}
/**
* Copy a file from this Pod to a local destination.
*
* @param source the source file to copy from this Pod
* @param destination the destination to copy to
*
* @throws Exception if an error occurs copying the file
*/
public void copyFrom(Path source, Path destination) throws Exception
{
List listArgs = getExecArgs(false, "cat " + source.toString());
try (OutputStream out = new FileOutputStream(destination.toFile()))
{
OutputToOutputStreamRedirector redirector = new OutputToOutputStreamRedirector(out);
try (Application app = k8s.kubectl(Arguments.of(listArgs),
LaunchLogging.disabled(),
ConsoleOutputRedirector.of(redirector)))
{
redirector.join();
}
}
}
/**
* Write this Pod's log to the specified file.
*
* @param file the file to write the log to
*
* @throws IOException if an error occurs obtaining the log
*/
public void log(File file) throws IOException
{
try(FileWriter writer = new FileWriter(file))
{
log(new FileWriterApplicationConsole(writer));
}
}
/**
* Capture this Pod's log.
*
* @param console the {@link ApplicationConsole} to write the log to
*
* @throws IOException if an error occurs obtaining the log
*/
public void log(ApplicationConsole console) throws IOException
{
List listArgs = getExecArgs(false, "log");
if (k8s.kubectlAndWait(Arguments.of(listArgs), Console.of(console), LaunchLogging.disabled()) != 0)
{
throw new IOException("Error obtaining logs for Pod " + podName);
}
}
/**
* Tail and follow this Pod's log.
*
* @param file the file to write the log to
*
* @return the {@link Application} wrapping the kubectl log command that allows
* stopping of log following by calling {@link Application#close()}
*
* @throws IOException if an error occurs obtaining the log
*/
public Application followLog(File file) throws IOException
{
return followLog(new FileWriterApplicationConsole(new FileWriter(file)));
}
/**
* Tail and follow this Pod's log.
*
* @param console the {@link ApplicationConsole} to write the log to
*
* @return the {@link Application} wrapping the kubectl log command that allows
* stopping of log following by calling {@link Application#close()}
*/
public Application followLog(ApplicationConsole console)
{
List listArgs = getExecArgs(false, "log");
return k8s.kubectl(Arguments.of(listArgs), Console.of(console), LaunchLogging.disabled());
}
/**
* Exec a command on this Pod.
*
* @param args the arguments of the command to exec
* @param options extra {@link Option}s to use when exec'ing the command
*/
public Application exec(Arguments args, Option... options)
{
List listArgs = getExecArgs(false, "log");
OptionsByType opts = OptionsByType.of(options).add(Arguments.of(listArgs).with(args));
return k8s.kubectl(opts.asArray());
}
/**
* Execute the ls command in this Pod and return the list of files.
*
* @return the list of files in the path
*
* @throws IOException if an error occurs executing the ls command
*/
public List ls() throws IOException
{
return ls(null);
}
/**
* Execute the ls command in this Pod and return the list of files.
*
* @param path the path to perform the ls command in, or null to use the root directory
*
* @return the list of files in the path
*
* @throws IOException if an error occurs executing the ls command
*/
public List ls(Path path) throws IOException
{
String dir = path == null ? "/" : path.toString();
List listArgs = getExecArgs(true, "ls " + dir);
CapturingApplicationConsole console = new CapturingApplicationConsole();
if (k8s.kubectlAndWait(Arguments.of(listArgs), Console.of(console), LaunchLogging.disabled()) != 0)
{
throw new IOException("Error executing ls " + dir + "\n"
+ String.join("", console.getCapturedErrorLines()));
}
List list = new ArrayList<>(console.getCapturedOutputLines());
list.remove(list.size() - 1);
return list;
}
/**
* Execute the mkdir command in this Pod to create a directory or directory path
*
* @param path the path of the directory to create
*
* @throws IOException if an error occurs executing the ls command
*/
public void mkdir(Path path) throws IOException
{
List listArgs = getExecArgs(true, "mkdir -p " + path);
CapturingApplicationConsole console = new CapturingApplicationConsole();
if (k8s.kubectlAndWait(Arguments.of(listArgs), Console.of(console), LaunchLogging.disabled()) != 0)
{
throw new IOException("Error executing mkdir -p " + path + "\n"
+ String.join("", console.getCapturedErrorLines()));
}
}
/**
* Execute the rm command in this Pod to remove a file or directory
*
* @param path the path to the file or directory to remove
*
* @throws IOException if an error occurs executing the ls command
*/
public void rm(Path path) throws IOException
{
List listArgs = getExecArgs(true, "rm -rf " + path);
CapturingApplicationConsole console = new CapturingApplicationConsole();
if (k8s.kubectlAndWait(Arguments.of(listArgs), Console.of(console), LaunchLogging.disabled()) != 0)
{
throw new IOException("Error executing rm " + path + "\n"
+ String.join("", console.getCapturedErrorLines()));
}
}
/**
* Obtain the containers for this Pod.
*
* @return a {@link Map} of containers for this Pod keyed by container name
*/
public Map getContainers()
{
if (containers == null)
{
synchronized (this)
{
if (containers == null)
{
CapturingApplicationConsole console = new CapturingApplicationConsole();
List args = getArgs("get");
args.add("pod");
args.add(podName);
args.add("-o");
args.add("jsonpath={range .spec.containers[*]}{.name}{\"\\n\"}{end}");
int exitCode = k8s.kubectlAndWait(Arguments.of(args), Console.of(console), LaunchLogging.disabled());
if (exitCode == 0)
{
containers = console.getCapturedOutputLines()
.stream()
.filter(s -> !"(terminated)".equals(s))
.filter(s -> !s.trim().isEmpty())
.map(name -> new PodContainer(k8s, podName, name, namespace))
.collect(Collectors.toMap(PodContainer::getContainerName, container -> container));
}
else
{
String msg = String.join("\n", console.getCapturedErrorLines());
throw new RuntimeException("Error obtaining Pod containers\n" + msg);
}
}
}
}
return Collections.unmodifiableMap(containers);
}
/**
* Obtain the kubectl exec command line to use for this Pod.
*
* @param termainal {@link true} to include {@code -i} in the command
* @param args the additional arguments to append to the command
*
* @return the kubectl exec command line to use for this Pod
*/
protected List getExecArgs(boolean termainal, String... args)
{
List list = getArgs("exec");
if (termainal)
{
list.add("-i");
}
list.add(podName);
list.add("--");
list.add("/bin/sh");
list.add("-c");
list.addAll(Arrays.asList(args));
return list;
}
/**
* Obtain the base kubectl arguments.
*
* @param command the kubectl command
*
* @return the base kubectl arguments
*/
protected List getArgs(String command)
{
List list = new ArrayList<>();
if (namespace != null)
{
list.add("-n");
list.add(namespace);
}
list.add(command);
return list;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
Pod pod = (Pod) o;
return Objects.equals(podName, pod.podName) &&
Objects.equals(namespace, pod.namespace);
}
@Override
public int hashCode()
{
return Objects.hash(podName, namespace);
}
}