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 container.appRoot config parameter #984

Merged
merged 33 commits into from
Sep 19, 2018
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
520222a
Add container.appRoot config parameter
chanseokoh Sep 13, 2018
971b95d
Merge branch 'master' into i964-container.appRoot
chanseokoh Sep 13, 2018
5b16ded
Change appRoot type from String to Path
chanseokoh Sep 14, 2018
5a7f486
List digests as Set
chanseokoh Sep 14, 2018
5024da9
Merge branch 'i985-list-cache-test-failure' into i964-container.appRoot
chanseokoh Sep 14, 2018
ee6fe2c
Add tests
chanseokoh Sep 14, 2018
d568f6e
listDigests --> fetchDigests
chanseokoh Sep 14, 2018
886d71d
Merge remote-tracking branch 'origin/i985-list-cache-test-failure' in…
chanseokoh Sep 14, 2018
d86d749
Fix typo
chanseokoh Sep 14, 2018
85c6132
Merge branch 'i985-list-cache-test-failure' into i964-container.appRoot
chanseokoh Sep 14, 2018
4ef4148
Add tests
chanseokoh Sep 14, 2018
a1ba423
Add tests
chanseokoh Sep 14, 2018
12face5
Add tests
chanseokoh Sep 14, 2018
93b42a7
Update Javadocs and copyright
chanseokoh Sep 14, 2018
35a64f6
Merge remote-tracking branch 'origin/master' into i964-container.appRoot
chanseokoh Sep 14, 2018
c16531d
Update CHANGLOG and README
chanseokoh Sep 14, 2018
a978e4c
Fix test
chanseokoh Sep 14, 2018
6330547
Set appRoot for JavaLayerConfigurations
chanseokoh Sep 14, 2018
0e8c597
Check appRoot early
chanseokoh Sep 14, 2018
6489596
Various fixes
chanseokoh Sep 14, 2018
713c673
Add tests
chanseokoh Sep 14, 2018
6dd49a9
Do not update docs yet
chanseokoh Sep 14, 2018
984f51c
Merge branch 'master' into i964-container.appRoot
chanseokoh Sep 17, 2018
c70f5a3
feedback
chanseokoh Sep 17, 2018
3b7c510
Accept default extraction path along with files
chanseokoh Sep 17, 2018
21bec72
Clean up
chanseokoh Sep 17, 2018
4d5154d
format
chanseokoh Sep 17, 2018
becad9c
Merge branch 'master' into i964-container.appRoot
chanseokoh Sep 17, 2018
e699be8
Move field / rename methods
chanseokoh Sep 18, 2018
99c7d1d
Rename argument
chanseokoh Sep 18, 2018
2d74e9d
Update Javadoc
chanseokoh Sep 18, 2018
28ac010
Fix typo
chanseokoh Sep 19, 2018
566561f
Fix typo
chanseokoh Sep 19, 2018
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
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ private BuildConfiguration.Builder getBuildConfigurationBuilder(
ContainerConfiguration.builder()
.setEntrypoint(
JavaEntrypointConstructor.makeDefaultEntrypoint(
Collections.emptyList(), "HelloWorld"))
"/app", Collections.emptyList(), "HelloWorld"))
.setProgramArguments(Collections.singletonList("An argument."))
.setEnvironment(ImmutableMap.of("env1", "envvalue1", "env2", "envvalue2"))
.setExposedPorts(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,35 +146,38 @@ private static String mapToDockerfileString(Map<String, String> map, String comm
* @param javaLayerConfigurations the {@link JavaLayerConfigurations}
*/
public JavaDockerContextGenerator(JavaLayerConfigurations javaLayerConfigurations) {
String appRoot = javaLayerConfigurations.getAppRoot();
appRoot = appRoot.endsWith("/") ? appRoot : appRoot + '/';
chanseokoh marked this conversation as resolved.
Show resolved Hide resolved

ImmutableList.Builder<CopyDirective> copyDirectivesBuilder = ImmutableList.builder();
addIfNotEmpty(
copyDirectivesBuilder,
javaLayerConfigurations.getDependencyLayerEntries(),
DEPENDENCIES_LAYER_DIRECTORY,
JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE);
appRoot + JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE);
addIfNotEmpty(
copyDirectivesBuilder,
javaLayerConfigurations.getSnapshotDependencyLayerEntries(),
SNAPSHOT_DEPENDENCIES_LAYER_DIRECTORY,
JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE);
appRoot + JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE);
addIfNotEmpty(
copyDirectivesBuilder,
javaLayerConfigurations.getResourceLayerEntries(),
RESOURCES_LAYER_DIRECTORY,
JavaEntrypointConstructor.DEFAULT_RESOURCES_PATH_ON_IMAGE);
appRoot + JavaEntrypointConstructor.DEFAULT_RESOURCES_PATH_ON_IMAGE);
addIfNotEmpty(
copyDirectivesBuilder,
javaLayerConfigurations.getClassLayerEntries(),
CLASSES_LAYER_DIRECTORY,
JavaEntrypointConstructor.DEFAULT_CLASSES_PATH_ON_IMAGE);
appRoot + JavaEntrypointConstructor.DEFAULT_CLASSES_PATH_ON_IMAGE);
// TODO: remove this once we put files in WAR into the relevant layers (i.e., dependencies,
// snapshot dependencies, resources, and classes layers). Should copy files in the right
// directories. (For example, "resources" will go into the webapp root.)
addIfNotEmpty(
copyDirectivesBuilder,
javaLayerConfigurations.getExplodedWarEntries(),
EXPLODED_WAR_LAYER_DIRECTORY,
JavaEntrypointConstructor.DEFAULT_JETTY_BASE_ON_IMAGE);
appRoot);
addIfNotEmpty(
copyDirectivesBuilder,
javaLayerConfigurations.getExtraFilesLayerEntries(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,29 @@

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

import com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/** Constructs an image entrypoint for the Java application. */
public class JavaEntrypointConstructor {

public static final String DEFAULT_RESOURCES_PATH_ON_IMAGE = "/app/resources/";
public static final String DEFAULT_CLASSES_PATH_ON_IMAGE = "/app/classes/";
public static final String DEFAULT_DEPENDENCIES_PATH_ON_IMAGE = "/app/libs/";
public static final String DEFAULT_JETTY_BASE_ON_IMAGE = "/jetty/webapps/ROOT/";
public static final String DEFAULT_RESOURCES_PATH_ON_IMAGE = "resources/";
public static final String DEFAULT_CLASSES_PATH_ON_IMAGE = "classes/";
public static final String DEFAULT_DEPENDENCIES_PATH_ON_IMAGE = "libs/";

public static List<String> makeDefaultEntrypoint(
String appRoot, List<String> jvmFlags, String mainClass) {
Preconditions.checkArgument(
chanseokoh marked this conversation as resolved.
Show resolved Hide resolved
appRoot.startsWith("/"), "appRoot should be an absolute path in Unix-style");
appRoot = appRoot.endsWith("/") ? appRoot : appRoot + '/';

public static List<String> makeDefaultEntrypoint(List<String> jvmFlags, String mainClass) {
return makeEntrypoint(
Arrays.asList(
DEFAULT_RESOURCES_PATH_ON_IMAGE,
DEFAULT_CLASSES_PATH_ON_IMAGE,
DEFAULT_DEPENDENCIES_PATH_ON_IMAGE + "*"),
appRoot + DEFAULT_RESOURCES_PATH_ON_IMAGE,
appRoot + DEFAULT_CLASSES_PATH_ON_IMAGE,
appRoot + DEFAULT_DEPENDENCIES_PATH_ON_IMAGE + "*"),
jvmFlags,
mainClass);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,47 +36,62 @@ public class JavaLayerConfigurations {
/** Represents the different types of layers for a Java application. */
@VisibleForTesting
enum LayerType {
DEPENDENCIES("dependencies", JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE),
DEPENDENCIES(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking more about this enum where we need to dynamically decide the final extraction path, I'm now leaning toward getting rid of static extraction path initialization with the appRootRelative switch. Instead, the JavaLayerConfiguration.Builder() could accept the extraction path when setting layer files. That is, change setClassFiles(List<Path> classFiles) to setClassFiles(String extractionPath, List<Path> classFiles). That seems more straightforward for our dynamic path requirement and simpler. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that sounds good. One nit is to have it be setClassFiles(List<Path> classFiles, String extractionPath) to be more like LayerEntry.

"dependencies", JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE, true),
SNAPSHOT_DEPENDENCIES(
"snapshot dependencies", JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE),
RESOURCES("resources", JavaEntrypointConstructor.DEFAULT_RESOURCES_PATH_ON_IMAGE),
CLASSES("classes", JavaEntrypointConstructor.DEFAULT_CLASSES_PATH_ON_IMAGE),
"snapshot dependencies",
JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE,
true),
RESOURCES("resources", JavaEntrypointConstructor.DEFAULT_RESOURCES_PATH_ON_IMAGE, true),
CLASSES("classes", JavaEntrypointConstructor.DEFAULT_CLASSES_PATH_ON_IMAGE, true),
// TODO: remove this once we put files in WAR into the relevant layers (i.e., dependencies,
// snapshot dependencies, resources, and classes layers). Should copy files in the right
EXPLODED_WAR("exploded war", JavaEntrypointConstructor.DEFAULT_JETTY_BASE_ON_IMAGE),
EXTRA_FILES("extra files", "/");
EXPLODED_WAR("exploded war", "", true),
EXTRA_FILES("extra files", "/", false);

private final String name;
private final Path extractionPath;

/** Initializes with a name for the layer and the layer files' default extraction path root. */
LayerType(String name, String extractionPath) {
private final boolean appRootRelative;

/**
* Initializes with a name for the layer and the layer files' default extraction path root.
*
* @param name name to set for the layer; does not affect the contents of the layer
* @param extractionPath root directory in the image where the files in the layer will be
* extracted; the final extraction path depends on the value of the {@code appRootRelative}
* parameter
* @param appRootRelative if {@code true}, {@code extractionPath} will additionally be relative
* to the app root directory configured when building {@link #JavaLayerConfigurations}
*/
LayerType(String name, String extractionPath, boolean appRootRelative) {
chanseokoh marked this conversation as resolved.
Show resolved Hide resolved
this.name = name;
this.extractionPath = Paths.get(extractionPath);
this.appRootRelative = appRootRelative;
}

@VisibleForTesting
String getName() {
return name;
}

@VisibleForTesting
Path getExtractionPath() {
return extractionPath;
}
}

/** Builds with each layer's files. */
public static class Builder {

private final Map<LayerType, List<Path>> layerFilesMap = new EnumMap<>(LayerType.class);
private String appRoot = DEFAULT_APP_ROOT;

private Builder() {
for (LayerType layerType : LayerType.values()) {
layerFilesMap.put(layerType, new ArrayList<>());
}
}

public Builder setAppRoot(String appRoot) {
this.appRoot = appRoot;
return this;
}

public Builder setDependencyFiles(List<Path> dependencyFiles) {
layerFilesMap.put(LayerType.DEPENDENCIES, dependencyFiles);
return this;
Expand Down Expand Up @@ -110,6 +125,11 @@ public Builder setExplodedWarFiles(List<Path> explodedWarFiles) {
}

public JavaLayerConfigurations build() throws IOException {
// Windows filenames cannot have "/", so this also blocks Windows-style path.
Preconditions.checkState(
appRoot.startsWith("/"), "appRoot should be an absolute path in Unix-style");
Path appRootPath = Paths.get(appRoot);

ImmutableMap.Builder<LayerType, LayerConfiguration> layerConfigurationsMap =
ImmutableMap.builderWithExpectedSize(LayerType.values().length);
for (LayerType layerType : LayerType.values()) {
Expand All @@ -119,25 +139,45 @@ public JavaLayerConfigurations build() throws IOException {
// Adds all the layer files recursively.
List<Path> layerFiles = Preconditions.checkNotNull(layerFilesMap.get(layerType));
for (Path layerFile : layerFiles) {
layerConfigurationBuilder.addEntryRecursive(
layerFile, layerType.getExtractionPath().resolve(layerFile.getFileName()));
Path extractTo = layerType.extractionPath.resolve(layerFile.getFileName());
Path pathInContainer =
layerType.appRootRelative ? appRootPath.resolve(extractTo) : extractTo;
layerConfigurationBuilder.addEntryRecursive(layerFile, pathInContainer);
}

layerConfigurationsMap.put(layerType, layerConfigurationBuilder.build());
}
return new JavaLayerConfigurations(layerConfigurationsMap.build());
return new JavaLayerConfigurations(appRoot, layerConfigurationsMap.build());
}
}

public static Builder builder() {
return new Builder();
}

/**
* The default app root in the image. For example, if this is set to {@code "/app"}, dependency
* JARs will be in {@code "/app/libs"}.
*/
public static final String DEFAULT_APP_ROOT = "/app";

private final ImmutableMap<LayerType, LayerConfiguration> layerConfigurationMap;
private final String appRoot;

private JavaLayerConfigurations(
ImmutableMap<LayerType, LayerConfiguration> layerConfigurationsMap) {
this.layerConfigurationMap = layerConfigurationsMap;
String appRoot, ImmutableMap<LayerType, LayerConfiguration> layerConfigurationsMap) {
this.appRoot = appRoot;
layerConfigurationMap = layerConfigurationsMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should name these both layerConfigurationsMap so this can be the more familiar this.x = x form?

}

/**
* Returns the Unix-style, absolute path for the application root in the container image. The path
* may or may not end with a forward slash ('/').
*
* @return Unix-style, absolute path for the application root
*/
public String getAppRoot() {
return appRoot;
}

public ImmutableList<LayerConfiguration> getLayerConfigurations() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class JavaDockerContextGeneratorTest {
private static final Path EXPECTED_DEPENDENCIES_PATH = Paths.get("/app/libs/");
private static final Path EXPECTED_RESOURCES_PATH = Paths.get("/app/resources/");
private static final Path EXPECTED_CLASSES_PATH = Paths.get("/app/classes/");
private static final Path EXPECTED_EXPLODED_WAR_PATH = Paths.get("/jetty/webapps/ROOT/");
private static final Path EXPECTED_EXPLODED_WAR_PATH = Paths.get("/app/");

private static void assertSameFiles(Path directory1, Path directory2) throws IOException {
ImmutableList<Path> directory1Files =
Expand Down Expand Up @@ -98,6 +98,7 @@ public void testGenerate() throws IOException, URISyntaxException {
*/
Files.delete(targetDirectory);

Mockito.when(mockJavaLayerConfigurations.getAppRoot()).thenReturn("/app");
Mockito.when(mockJavaLayerConfigurations.getDependencyLayerEntries())
.thenReturn(filesToLayerEntries(testDependencies, EXPECTED_DEPENDENCIES_PATH));
Mockito.when(mockJavaLayerConfigurations.getSnapshotDependencyLayerEntries())
Expand Down Expand Up @@ -141,6 +142,7 @@ public void testMakeDockerfile() throws IOException {
"key3",
"value3");

Mockito.when(mockJavaLayerConfigurations.getAppRoot()).thenReturn("/app");
Mockito.when(mockJavaLayerConfigurations.getDependencyLayerEntries())
.thenReturn(
ImmutableList.of(new LayerEntry(Paths.get("ignored"), EXPECTED_DEPENDENCIES_PATH)));
Expand All @@ -162,7 +164,7 @@ public void testMakeDockerfile() throws IOException {
.setBaseImage(expectedBaseImage)
.setEntrypoint(
JavaEntrypointConstructor.makeDefaultEntrypoint(
expectedJvmFlags, expectedMainClass))
"/app", expectedJvmFlags, expectedMainClass))
.setJavaArguments(expectedJavaArguments)
.setEnvironment(expectedEnv)
.setExposedPorts(exposedPorts)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.cloud.tools.jib.frontend;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
Expand Down Expand Up @@ -49,10 +50,69 @@ public void testMakeEntrypoint() {

// Checks that this is also the default entrypoint.
Assert.assertEquals(
JavaEntrypointConstructor.makeDefaultEntrypoint(expectedJvmFlags, expectedMainClass),
JavaEntrypointConstructor.makeDefaultEntrypoint(
"/app", expectedJvmFlags, expectedMainClass),
entrypoint);
}

@Test
public void testMakeDefaultEntrypoint_classpathString() {
// Checks that this is also the default entrypoint.
List<String> entrypoint =
JavaEntrypointConstructor.makeDefaultEntrypoint("/app", Collections.emptyList(), "MyMain");
Assert.assertEquals("/app/resources/:/app/classes/:/app/libs/*", entrypoint.get(2));
}

@Test
public void testMakeDefaultEntrypoint_classpathStringWithNonDefaultAppRoot() {
// Checks that this is also the default entrypoint.
List<String> entrypoint =
JavaEntrypointConstructor.makeDefaultEntrypoint("/my/app", Collections.emptyList(), "Main");
Assert.assertEquals("/my/app/resources/:/my/app/classes/:/my/app/libs/*", entrypoint.get(2));
}

@Test
public void testMakeDefaultEntrypoint_appRootWithTrailingSlash() {
// Checks that this is also the default entrypoint.
List<String> entrypoint =
JavaEntrypointConstructor.makeDefaultEntrypoint(
"/my/root/", Collections.emptyList(), "SomeMainClass");
Assert.assertEquals("/my/root/resources/:/my/root/classes/:/my/root/libs/*", entrypoint.get(2));
}

@Test
public void testMakeDefaultEntrypoint_nonAbsoluteAppRoot() {
try {
JavaEntrypointConstructor.makeDefaultEntrypoint(
"relative/path", Collections.emptyList(), "MainClass");
Assert.fail();
} catch (IllegalArgumentException ex) {
Assert.assertEquals("appRoot should be an absolute path in Unix-style", ex.getMessage());
}
}

@Test
public void testMakeDefaultEntrypoint_windowsAppRootPath() {
try {
JavaEntrypointConstructor.makeDefaultEntrypoint(
"\\windows\\path", Collections.emptyList(), "MyMain");
Assert.fail();
} catch (IllegalArgumentException ex) {
Assert.assertEquals("appRoot should be an absolute path in Unix-style", ex.getMessage());
}
}

@Test
public void testMakeDefaultEntrypoint_windowsPathWithDriveLetter() {
try {
JavaEntrypointConstructor.makeDefaultEntrypoint(
"D:\\windows\\path", Collections.emptyList(), "MyMain");
Assert.fail();
} catch (IllegalArgumentException ex) {
Assert.assertEquals("appRoot should be an absolute path in Unix-style", ex.getMessage());
}
}

@Test
public void testMakeDistrolessJettyEntrypoint() {
List<String> expected = Arrays.asList("java", "-jar", "/jetty/start.jar");
Expand Down
Loading