Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JavaContainerBuilder to jib core #1347

Merged
merged 22 commits into from
Dec 20, 2018
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jib-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
### Added

- Adds support for configuring volumes ([#1121](https://github.com/GoogleContainerTools/jib/issues/1121))
- Adds `JavaContainerBuilder` for building opinionated containers for Java applications ([#1212](https://github.com/GoogleContainerTools/jib/issues/1212))

### Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
/*
* Copyright 2018 Google LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.google.cloud.tools.jib.api;

import com.google.cloud.tools.jib.filesystem.AbsoluteUnixPath;
import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor;
import com.google.cloud.tools.jib.frontend.JavaLayerConfigurations;
import com.google.cloud.tools.jib.frontend.JavaLayerConfigurations.LayerType;
import com.google.cloud.tools.jib.image.ImageReference;
import com.google.cloud.tools.jib.image.InvalidImageReferenceException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import javax.annotation.Nullable;

/** Creates a {@link JibContainerBuilder} for containerizing Java applications. */
public class JavaContainerBuilder {

/** The default root directory of the application on the container. */
private static final AbsoluteUnixPath APP_ROOT = AbsoluteUnixPath.get("/app");
TadCordle marked this conversation as resolved.
Show resolved Hide resolved

/** Absolute path of directory containing application resources on container. */
private static final AbsoluteUnixPath RESOURCES_PATH =
APP_ROOT.resolve(JavaEntrypointConstructor.DEFAULT_RELATIVE_RESOURCES_PATH_ON_IMAGE);

/** Absolute path of classes on container. */
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
private static final AbsoluteUnixPath CLASSES_PATH =
APP_ROOT.resolve(JavaEntrypointConstructor.DEFAULT_RELATIVE_CLASSES_PATH_ON_IMAGE);

/** Absolute path of dependencies on container. */
private static final AbsoluteUnixPath DEPENDENCIES_PATH =
APP_ROOT.resolve(JavaEntrypointConstructor.DEFAULT_RELATIVE_DEPENDENCIES_PATH_ON_IMAGE);

/** The classpath element corresponding to dependencies in the entrypoint. */
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
private static final AbsoluteUnixPath DEPENDENCIES_CLASSPATH =
APP_ROOT
.resolve(JavaEntrypointConstructor.DEFAULT_RELATIVE_DEPENDENCIES_PATH_ON_IMAGE)
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
.resolve("*");

/** Absolute path of additional classpath files on container. */
private static final AbsoluteUnixPath OTHERS_PATH = APP_ROOT.resolve("classpath");

/**
* Creates a new {@link JavaContainerBuilder} that uses distroless java as the base image. For
* more information on {@code gcr.io/distroless/java}, see <a
* href="https://github.com/GoogleContainerTools/distroless">the distroless repository</a>.
*
* @return a new {@link JavaContainerBuilder}
* @throws InvalidImageReferenceException if creating the base image reference fails
* @see <a href="https://github.com/GoogleContainerTools/distroless">The distroless repository</a>
*/
public static JavaContainerBuilder fromDistroless() throws InvalidImageReferenceException {
return from(RegistryImage.named("gcr.io/distroless/java"));
}

/**
* Creates a new {@link JavaContainerBuilder} with the specified base image reference.
*
* @param baseImageReference the base image reference
* @return a new {@link JavaContainerBuilder}
* @throws InvalidImageReferenceException if {@code baseImageReference} is invalid
*/
public static JavaContainerBuilder from(String baseImageReference)
throws InvalidImageReferenceException {
return from(RegistryImage.named(baseImageReference));
}

/**
* Creates a new {@link JavaContainerBuilder} with the specified base image reference.
*
* @param baseImageReference the base image reference
* @return a new {@link JavaContainerBuilder}
*/
public static JavaContainerBuilder from(ImageReference baseImageReference) {
return from(RegistryImage.named(baseImageReference));
}

/**
* Creates a new {@link JavaContainerBuilder} with the specified base image.
*
* @param registryImage the {@link RegistryImage} that defines base container registry and
* credentials
* @return a new {@link JavaContainerBuilder}
*/
public static JavaContainerBuilder from(RegistryImage registryImage) {
return new JavaContainerBuilder(Jib.from(registryImage));
}

private final JibContainerBuilder jibContainerBuilder;
private final JavaLayerConfigurations.Builder layerConfigurationsBuilder =
JavaLayerConfigurations.builder();
private final List<String> jvmFlags = new ArrayList<>();
private final LinkedHashSet<String> classpath = new LinkedHashSet<>(4);
coollog marked this conversation as resolved.
Show resolved Hide resolved

@Nullable private String mainClass;

private JavaContainerBuilder(JibContainerBuilder jibContainerBuilder) {
this.jibContainerBuilder = jibContainerBuilder;
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Adds dependency JARs to the image.
*
* @param dependencyFiles the list of dependency JARs to add to the image
* @return this
* @throws FileNotFoundException if adding the layer fails
*/
public JavaContainerBuilder addDependencies(List<Path> dependencyFiles)
throws FileNotFoundException {
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
// Make sure all files exist before adding any
for (Path file : dependencyFiles) {
if (!Files.exists(file)) {
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
throw new FileNotFoundException("File '" + file + "' does not exist.");
}
}

for (Path file : dependencyFiles) {
layerConfigurationsBuilder.addFile(
file.getFileName().toString().contains("SNAPSHOT")
? LayerType.SNAPSHOT_DEPENDENCIES
: LayerType.DEPENDENCIES,
file,
DEPENDENCIES_PATH.resolve(file.getFileName()));
}
classpath.add(DEPENDENCIES_CLASSPATH.toString());
return this;
}

/**
* Adds dependency JARs to the image.
*
* @param dependencyFiles the list of dependency JARs to add to the image
* @return this
* @throws FileNotFoundException if adding the layer fails
*/
public JavaContainerBuilder addDependencies(Path... dependencyFiles)
throws FileNotFoundException {
return addDependencies(Arrays.asList(dependencyFiles));
}

/**
* Adds the contents of a resources directory to the image.
*
* @param resourceFilesDirectory the directory containing the project's resources
* @return this
* @throws IOException if adding the layer fails
*/
public JavaContainerBuilder addResources(Path resourceFilesDirectory) throws IOException {
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
if (!Files.exists(resourceFilesDirectory)) {
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
throw new FileNotFoundException("Directory '" + resourceFilesDirectory + "' does not exist.");
}
if (!Files.isDirectory(resourceFilesDirectory)) {
throw new NotDirectoryException(
"Adding resources failed: '" + resourceFilesDirectory + "' is not a directory");
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
}
layerConfigurationsBuilder.addDirectoryContents(
LayerType.RESOURCES, resourceFilesDirectory, path -> true, RESOURCES_PATH);
classpath.add(RESOURCES_PATH.toString());
return this;
}

/**
* Adds the contents of a classes directory to the image.
*
* @param classFilesDirectory the directory containing the class files
* @return this
* @throws IOException if adding the layer fails
*/
public JavaContainerBuilder addClasses(Path classFilesDirectory) throws IOException {
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
if (!Files.exists(classFilesDirectory)) {
throw new FileNotFoundException("Directory '" + classFilesDirectory + "' does not exist.");
}
if (!Files.isDirectory(classFilesDirectory)) {
throw new NotDirectoryException(
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
"Adding classes failed: '" + classFilesDirectory + "' is not a directory");
}
layerConfigurationsBuilder.addDirectoryContents(
LayerType.CLASSES, classFilesDirectory, path -> true, CLASSES_PATH);
classpath.add(CLASSES_PATH.toString());
return this;
}

/**
* Adds additional files to the classpath.
*
* @param otherFiles the list of files to add. If {@code otherFiles} contains a directory, files
* within are added recursively.
* @return this
* @throws IOException if adding the layer fails
*/
public JavaContainerBuilder addToClasspath(List<Path> otherFiles) throws IOException {
// Make sure all files exist before adding any
for (Path file : otherFiles) {
if (!Files.exists(file)) {
throw new FileNotFoundException("File '" + file + "' does not exist.");
}
}

for (Path file : otherFiles) {
if (Files.isDirectory(file)) {
layerConfigurationsBuilder.addDirectoryContents(
LayerType.EXTRA_FILES, file, path -> true, OTHERS_PATH);
} else {
layerConfigurationsBuilder.addFile(
LayerType.EXTRA_FILES, file, OTHERS_PATH.resolve(file.getFileName()));
}
}
classpath.add(OTHERS_PATH.toString());
return this;
}

/**
* Adds additional files to the classpath.
*
* @param otherFiles the list of files to add. If {@code otherFiles} contains a directory, files
* within are added recursively.
* @return this
* @throws IOException if adding the layer fails
*/
public JavaContainerBuilder addToClasspath(Path... otherFiles) throws IOException {
return addToClasspath(Arrays.asList(otherFiles));
}

/**
* Adds a JVM flag to use when starting the application.
*
* @param jvmFlag the JVM flag to add
* @return this
*/
public JavaContainerBuilder addJvmFlag(String jvmFlag) {
jvmFlags.add(jvmFlag);
return this;
}

/**
* Adds JVM flags to use when starting the application.
*
* @param jvmFlags the list of JVM flags to add
* @return this
*/
public JavaContainerBuilder addJvmFlags(List<String> jvmFlags) {
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
this.jvmFlags.addAll(jvmFlags);
return this;
}

/**
* Adds JVM flags to use when starting the application.
*
* @param jvmFlags the list of JVM flags to add
* @return this
*/
public JavaContainerBuilder addJvmFlags(String... jvmFlags) {
this.jvmFlags.addAll(Arrays.asList(jvmFlags));
return this;
}

/**
* Sets the main class used to start the application on the image. To find the main class from
* {@code .class} files, use {@link com.google.cloud.tools.jib.frontend.MainClassFinder}.
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
*
* @param mainClass the main class used to start the application
* @return this
* @see com.google.cloud.tools.jib.frontend.MainClassFinder
*/
public JavaContainerBuilder setMainClass(String mainClass) {
this.mainClass = mainClass;
return this;
}

/**
* Returns a new {@link JibContainerBuilder} using the parameters specified on the {@link
* JavaContainerBuilder}.
*
* @return a new {@link JibContainerBuilder} using the parameters specified on the {@link
* JavaContainerBuilder}
*/
public JibContainerBuilder toContainerBuilder() {
if (mainClass == null) {
throw new IllegalArgumentException(
TadCordle marked this conversation as resolved.
Show resolved Hide resolved
"mainClass is null on JavaContainerBuilder; specify the main class using "
+ "JavaContainerBuilder#setMainClass(String), or consider using a "
+ "jib.frontend.MainClassFinder to infer the main class");
}
if (classpath.isEmpty()) {
throw new IllegalArgumentException(
"Failed to construct entrypoint because no files were added to the JavaContainerBuilder");
}

jibContainerBuilder.setEntrypoint(
JavaEntrypointConstructor.makeEntrypoint(new ArrayList<>(classpath), jvmFlags, mainClass));
jibContainerBuilder.setLayers(layerConfigurationsBuilder.build().getLayerConfigurations());
return jibContainerBuilder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ public Builder addDirectoryContents(
* @throws IOException error while listing directories
* @throws NotDirectoryException if {@code sourceRoot} is not a directory
*/
// TODO: Use in plugins
public Builder addDirectoryContents(
LayerType layerType,
Path sourceRoot,
Expand Down
Loading