diff options
Diffstat (limited to 'src/main/java/org/openslx/virtualization/configuration/container')
4 files changed, 445 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/virtualization/configuration/container/ContainerBindMount.java b/src/main/java/org/openslx/virtualization/configuration/container/ContainerBindMount.java new file mode 100644 index 0000000..0c1788e --- /dev/null +++ b/src/main/java/org/openslx/virtualization/configuration/container/ContainerBindMount.java @@ -0,0 +1,74 @@ +package org.openslx.virtualization.configuration.container; + +import java.util.Objects; + +/** + * This class implements a model for a bind mount entry in the docker context + * (eg. docker run ... --mount type=bind,source=source,target=target,options ... ). A list of objects of this class is stored in + * {@link ContainerMeta}. + */ +public class ContainerBindMount { + + public enum ContainerMountType { + DEFAULT, + CONTAINER_IMAGE + } + + private ContainerMountType mount_type = ContainerMountType.DEFAULT; + private String source = ""; + private String target = ""; + private String options = ""; + + public ContainerBindMount() { + } + + public ContainerBindMount(String source, String target, String options) { + this(ContainerMountType.DEFAULT,source,target,options); + } + + public ContainerBindMount(ContainerMountType mount_type, String source, String target, String options) { + this.mount_type = mount_type; + this.source = source; + this.target = target; + this.options = options; + } + + public String getSource() { + return source; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public String getOptions() { + return options; + } + + public void setOptions(String options) { + this.options = options; + } + + public ContainerMountType getMountType() { + return this.mount_type; + } + + + @Override public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ContainerBindMount that = (ContainerBindMount) o; + return Objects.equals(source, that.source) && Objects.equals(mount_type, that.mount_type) + && Objects.equals(target, that.target) && Objects.equals(options, that.options); + } + + @Override public int hashCode() { + return Objects.hash(source, target, options); + } +} diff --git a/src/main/java/org/openslx/virtualization/configuration/container/ContainerDefinition.java b/src/main/java/org/openslx/virtualization/configuration/container/ContainerDefinition.java new file mode 100644 index 0000000..825d0c3 --- /dev/null +++ b/src/main/java/org/openslx/virtualization/configuration/container/ContainerDefinition.java @@ -0,0 +1,195 @@ +package org.openslx.virtualization.configuration.container; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonReader; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.openslx.util.Util; +import org.openslx.util.TarArchiveUtil.TarArchiveReader; +import org.openslx.util.TarArchiveUtil.TarArchiveWriter; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class ContainerDefinition { + + // TODO database needs a refactoring to store container details + // TODO refatoring: tar.gz of this object is not useful, for smaller dockerfiles it makes the package lager. + // remove the containerRecipe, ContainerMeta holds in build_context the dockerfile. + + protected static final Logger LOGGER = LogManager.getLogger(ContainerDefinition.class); + + protected static final String CONTAINER_FILE = "dockerfile"; + protected static final String CONTAINER_META_FILE = "container_meta.json"; + + /** + * The file to construct a real container image, could be an dockerfile or a singularity recipe. + */ + public String containerRecipe = ""; + + /** + * Further container information, see {@link ContainerMeta}. + */ + public ContainerMeta containerMeta; + + public ContainerDefinition() { + containerMeta = new ContainerMeta(); + } + + /** + * Copy Constructor + * + * @param containerDef {@link ContainerDefinition} from which to make a deep copy. + */ + public ContainerDefinition(ContainerDefinition containerDef) { + containerRecipe = String.valueOf(containerDef.getContainerRecipe()); + containerMeta = new ContainerMeta(containerDef.getContainerMeta()); + } + + /** + * Utility function to create a {@link ContainerDefinition} object for a byte array downloaded from the server. + * + * @param rawTarData Downloaded tar.gz file from the server as a byte array. + * @return New object of ContainerDefinition. + */ + public static ContainerDefinition fromByteArray(byte[] rawTarData) { + + ContainerDefinition containerDef = new ContainerDefinition(); + + try { + TarArchiveReader tarReader = new TarArchiveReader(new ByteArrayInputStream(rawTarData), true, true); + + while (tarReader.hasNextEntry()) { + if (tarReader.getEntryName().equals(CONTAINER_FILE)) + containerDef.setContainerRecipe(tarReader.readCurrentEntry()); + if (tarReader.getEntryName().equals(CONTAINER_META_FILE)) + containerDef.setContainerMeta(tarReader.readCurrentEntry()); + } + tarReader.close(); + + } catch (IOException e) { + LOGGER.error("Could not create a ContainerDefinition Object for rawTarData", e); + } + + return containerDef; + } + + public String getContainerRecipe() { + return containerRecipe; + } + + public void setContainerRecipe(String containerRecipe) { + this.containerRecipe = containerRecipe; + } + + public void setContainerRecipe(File containerRecipeFile) { + this.containerRecipe = readContainerRecipe(containerRecipeFile); + } + + public void setContainerRecipe(byte[] rawContainerRecipe) { + this.containerRecipe = new String(rawContainerRecipe, StandardCharsets.UTF_8); + } + + public ContainerMeta getContainerMeta() { + return containerMeta; + } + + public void setContainerMeta(byte[] containerMeta) { + Gson gson = new GsonBuilder().create(); + this.containerMeta = gson.fromJson(new JsonReader( + new InputStreamReader(new ByteArrayInputStream(containerMeta), StandardCharsets.UTF_8)), + ContainerMeta.class); + } + + /** + * Serializes the ContainerMeta and Container Description (e.g. dockerfile) into an tar.gz archive. + * + * @return A ByteBuffer object of the container definition. Can be uploaded so satellite server. + */ + public ByteBuffer toByteBuffer() { + + ByteBuffer containerDef = null; + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + TarArchiveWriter tarWriter = new TarArchiveWriter(baos); + tarWriter.writeFile(CONTAINER_META_FILE, gson.toJson(containerMeta)); + tarWriter.writeFile(CONTAINER_FILE, containerRecipe); + Util.safeClose(tarWriter); + + containerDef = ByteBuffer.wrap(baos.toByteArray()); + } catch (IOException e) { + LOGGER.warn("Could not create a tar file", e); + } + + return containerDef; + } + + private String readContainerRecipe(File file) { + String recipe = null; + try { + + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); + ByteArrayOutputStream rawFile = new ByteArrayOutputStream(); + int count; + byte[] data = new byte[1024]; + while ((count = bis.read(data)) != -1) { + rawFile.write(data, 0, count); + } + + String rawRecipe = new String(rawFile.toByteArray(), StandardCharsets.UTF_8); + + // replace windows by unix EOL + recipe = rawRecipe.replaceAll("\\r\\n", "\n"); + + bis.close(); + + } catch (IOException e) { + LOGGER.error("Could not read Container Recipe", e); + } + return recipe; + } + + /** + * Saves containerRecipe and containerMeta at the provided location. + * + * @param destDir destination directory for containerRecipe and containerMeta. + */ + public void saveLocal(File destDir) { + writeFile(destDir, containerRecipe, CONTAINER_FILE); + } + + private void writeFile(File destDir, String fileContent, String filename) { + File output = new File(destDir, filename); + try { + FileWriter fw = new FileWriter(output); + fw.write(fileContent); + fw.flush(); + fw.close(); + } catch (IOException e) { + e.printStackTrace(); + LOGGER.error("Could not write File", e); + } + } + + public ContainerImageContext getContainerImageContext() { + return ContainerImageContext.fromInt(containerMeta.getContainerImageContext()); + } + + @Override public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ContainerDefinition that = (ContainerDefinition) o; + return containerRecipe.equals(that.containerRecipe) && containerMeta.equals(that.containerMeta); + } + + @Override public int hashCode() { + return Objects.hash(containerRecipe, containerMeta); + } +} diff --git a/src/main/java/org/openslx/virtualization/configuration/container/ContainerImageContext.java b/src/main/java/org/openslx/virtualization/configuration/container/ContainerImageContext.java new file mode 100644 index 0000000..f19d419 --- /dev/null +++ b/src/main/java/org/openslx/virtualization/configuration/container/ContainerImageContext.java @@ -0,0 +1,10 @@ +package org.openslx.virtualization.configuration.container; + +public enum ContainerImageContext { + + DOCKERFILE, GIT_REPOSITORY, IMAGE_REPOSITORY, DOCKER_ARCHIVE; + + public static ContainerImageContext fromInt(int index) { + return values()[index]; + } +} diff --git a/src/main/java/org/openslx/virtualization/configuration/container/ContainerMeta.java b/src/main/java/org/openslx/virtualization/configuration/container/ContainerMeta.java new file mode 100644 index 0000000..4dbf64b --- /dev/null +++ b/src/main/java/org/openslx/virtualization/configuration/container/ContainerMeta.java @@ -0,0 +1,166 @@ +package org.openslx.virtualization.configuration.container; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * ContainerMeta is used to store container specific information. An object of + * this class will be serialized with gson to a json file. + * <p> + * TODO rename build_context_method to container_image_context, requires update + * in database(json) + * <p> + * TODO rename build_context_url to build_context TODO refactoring build_context + * is either a dockerfile or a git url with a dockerfile. + */ +public class ContainerMeta { + + private int build_context_method; + private String image_repo; + private String build_context_url; + private String image_name; + private String run_options; + private String run_command; + private String image_type; + private List<ContainerBindMount> bind_mount_config = new ArrayList<>(); + + public ContainerMeta() { + + image_repo = ""; + build_context_method = ContainerImageContext.DOCKERFILE.ordinal(); + build_context_url = ""; + image_name = ""; + run_options = ""; + run_command = ""; + image_type = ContainerImageType.LECTURE.toString(); + bind_mount_config = new ArrayList<>(); + } + + public ContainerMeta(ContainerMeta containerMeta) { + build_context_method = containerMeta.build_context_method; + build_context_url = containerMeta.build_context_url; + image_name = containerMeta.image_name; + run_options = containerMeta.run_options; + run_command = containerMeta.run_command; + image_repo = containerMeta.image_repo; + + for (ContainerBindMount bm : containerMeta.bind_mount_config) + bind_mount_config.add(new ContainerBindMount(bm.getSource(), bm.getTarget(), bm.getOptions())); + + } + + public int getContainerImageContext() { + return build_context_method; + } + + public void setContainerImageContext(int buildContextMethod) { + this.build_context_method = buildContextMethod; + } + + public String getBuildContextUrl() { + return build_context_url; + } + + public void setBuildContextUrl(String buildContextUrl) { + this.build_context_url = buildContextUrl; + } + + public String getRunOptions() { + return run_options; + } + + public void setRunOptions(String run_options) { + this.run_options = run_options; + } + + public String getRunCommand() { + return this.run_command; + } + + public void setRunCommand(String run_command) { + this.run_command = run_command; + } + + public String getImageName() { + return image_name; + } + + public void setImageName(String image_name) { + this.image_name = image_name; + } + + public List<ContainerBindMount> getBindMountConfig() { + return bind_mount_config; + } + + public void setBindMountConfig(List<ContainerBindMount> bindMountConfig) { + this.bind_mount_config = bindMountConfig; + } + + public String getImageRepo() { + return image_repo; + } + + public void setImageRepo(String from_image) { + this.image_repo = from_image; + } + + public ContainerImageType getImageType() { + if (image_type == null || image_type.length() == 0) + return ContainerImageType.LECTURE; + + // turn string representation into enum-var 'LECTURE' -> + // ContainerImageType.LECTURE + return ContainerImageType.valueOf(image_type); + } + + public void setImageType(ContainerImageType image_type) { + // set constant representation of the enum-var e.g. ContainerImageType.LECTURE + // -> 'LECTURE' + this.image_type = image_type.name(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ContainerMeta that = (ContainerMeta) o; + return Objects.equals(build_context_url, that.build_context_url) && Objects.equals(image_name, that.image_name) + && Objects.equals(run_options, that.run_options) && Objects.equals(run_command, that.run_command) + && Objects.equals(bind_mount_config, that.bind_mount_config) + && Objects.equals(image_repo, that.image_repo) && Objects.equals(image_type, that.image_type); + } + + @Override + public int hashCode() { + return Objects.hash(build_context_url, image_name, run_options, run_command, bind_mount_config, image_repo, + image_type); + } + + public enum ContainerImageType implements org.apache.thrift.TEnum { + LECTURE("Lecture"), BATCH("Batch"), DATA("Data"); + + private final String displayLable; + + ContainerImageType(String name) { + this.displayLable = name; + } + + public boolean equalNames(String other) { + return displayLable.equals(other); + } + + @Override + public String toString() { + return this.displayLable; + } + + @Override + public int getValue() { + return this.ordinal(); + } + } +} |