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

Separate out project dependencies, add integration test to verify #1785

Merged
merged 4 commits into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions jib-gradle-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ All notable changes to this project will be documented in this file.

### Changed

- Dependencies are now split into three layers: dependencies, snapshots dependencies, project dependencies ([#1724](https://github.com/GoogleContainerTools/jib/pull/1724))

### Fixed

## 1.3.0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright 2019 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.gradle;
loosebazooka marked this conversation as resolved.
Show resolved Hide resolved

import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;

public class GradleLayerConfigurationIntegrationTest {

@ClassRule
public static final TestProject multiTestProject = new TestProject("all-local-multi-service");

@Test
public void testGradleLayerConfiguration_multiModule() throws IOException {
multiTestProject.build(":complex-service:jibBuildTar");
chanseokoh marked this conversation as resolved.
Show resolved Hide resolved

Path jibTar = multiTestProject.getProjectRoot().resolve("complex-service/build/jib-image.tar");

List<List<String>> layers = getLayers(jibTar);

Assert.assertEquals(6, layers.size());

// the expected order is:
// no base image layers (scratch)
// extra-files (0)
// classes (1)
// resources (2)
// project dependencies (3)
// snapshot dependencies (4)
// dependencies (5)

Path complexServiceRoot = multiTestProject.getProjectRoot().resolve("complex-service");
// verify dependencies
List<String> dependencies = layers.get(5);
List<String> expectedDependencies =
ImmutableList.of("app/", "app/libs/", "app/libs/dependency-1.0.0.jar");
Assert.assertEquals(expectedDependencies, dependencies);

// verify snapshot dependencies
List<String> snapshotDependencies = layers.get(4);
List<String> expectedSnapshotDependencies =
ImmutableList.of("app/", "app/libs/", "app/libs/dependencyX-1.0.0-SNAPSHOT.jar");
Assert.assertEquals(expectedSnapshotDependencies, snapshotDependencies);

// verify project dependencies
List<String> projectDependencies = layers.get(3);
List<String> expectedProjectDependencies =
ImmutableList.of("app/", "app/libs/", "app/libs/lib.jar");
Assert.assertEquals(expectedProjectDependencies, projectDependencies);

// verify resources
List<String> resources = layers.get(2);
List<String> expectedResources =
ImmutableList.of(
"app/", "app/resources/", "app/resources/resource1.txt", "app/resources/resource2.txt");
Assert.assertEquals(expectedResources, resources);

// verify classes
List<String> classes = layers.get(1);
List<String> expectedClasses =
ImmutableList.of(
"app/",
"app/classes/",
"app/classes/com/",
"app/classes/com/test/",
"app/classes/com/test/HelloWorld.class");
Assert.assertEquals(expectedClasses, classes);

// verify extra files
List<String> extraFiles = layers.get(0);
List<String> expectedExtraFiles = ImmutableList.of("extra-file");
Assert.assertEquals(expectedExtraFiles, extraFiles);
}

@Test
public void testGradleLayerConfiguration_simpleModule() throws IOException {
multiTestProject.build(":simple-service:jibBuildTar");

Path jibTar = multiTestProject.getProjectRoot().resolve("simple-service/build/jib-image.tar");

List<List<String>> layers = getLayers(jibTar);

Assert.assertEquals(1, layers.size());

// the expected order is:
// no base image layers (scratch)
// classes (0)

// verify classes
List<String> classes = layers.get(0);
List<String> expectedClasses =
ImmutableList.of(
"app/",
"app/classes/",
"app/classes/com/",
"app/classes/com/test/",
"app/classes/com/test/HelloWorld.class");
Assert.assertEquals(expectedClasses, classes);
}

// returns all files in layers (*.tar.gz) in a image tar
private List<List<String>> getLayers(Path tar) throws IOException {
List<List<String>> layers = new ArrayList<>();

try (TarArchiveInputStream image = new TarArchiveInputStream(Files.newInputStream(tar))) {
TarArchiveEntry entry;
while ((entry = image.getNextTarEntry()) != null) {
System.out.println(entry.getName());
loosebazooka marked this conversation as resolved.
Show resolved Hide resolved
if (entry.getName().endsWith(".tar.gz")) {
@SuppressWarnings("resource") // must not close sub-streams
TarArchiveInputStream layer = new TarArchiveInputStream(new GZIPInputStream(image));
TarArchiveEntry layerEntry;
List<String> layerFiles = new ArrayList<>();
while ((layerEntry = layer.getNextTarEntry()) != null) {
layerFiles.add(layerEntry.getName());
}
layers.add(0, layerFiles);
}
}
}
return layers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@
import org.gradle.api.GradleException;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.file.FileCollection;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.WarPlugin;
import org.gradle.api.tasks.SourceSet;
Expand Down Expand Up @@ -149,21 +152,43 @@ public JibContainerBuilder createContainerBuilder(
Path resourcesOutputDirectory = mainSourceSet.getOutput().getResourcesDir().toPath();
FileCollection allFiles = mainSourceSet.getRuntimeClasspath().filter(File::exists);

FileCollection allDependencyFiles =
FileCollection projectDependencies =
project.files(
project
.getConfigurations()
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)
.getResolvedConfiguration()
.getResolvedArtifacts()
.stream()
.filter(
artifact ->
artifact.getId().getComponentIdentifier()
instanceof ProjectComponentIdentifier)
.map(ResolvedArtifact::getFile)
Copy link
Member

Choose a reason for hiding this comment

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

This is unclear, but should we do filter(File::exists)? For what I know, if do the filtering for allFiles and classesOutputDirectories. (JavaContainerBuilder does the check and throws NoSuchFileException, which might be a good thing actually. It's unclear to me if this can ever happen.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I think it's safer to not filter, if we have an issue in the configuration, it should pick it up. File::exists will just quietly ignore the error.

.collect(Collectors.toList()));

FileCollection nonProjectDependencies =
allFiles
.minus(classesOutputDirectories)
.minus(projectDependencies)
.filter(file -> !file.toPath().equals(resourcesOutputDirectory));

FileCollection snapshotDependencyFiles =
allDependencyFiles.filter(file -> file.getName().contains("SNAPSHOT"));
FileCollection dependencyFiles = allDependencyFiles.minus(snapshotDependencyFiles);
FileCollection snapshotDependencies =
nonProjectDependencies.filter(file -> file.getName().contains("SNAPSHOT"));
FileCollection dependencies = nonProjectDependencies.minus(snapshotDependencies);

// Adds dependency files
javaContainerBuilder
.addDependencies(
dependencyFiles.getFiles().stream().map(File::toPath).collect(Collectors.toList()))
dependencies.getFiles().stream().map(File::toPath).collect(Collectors.toList()))
.addSnapshotDependencies(
snapshotDependencyFiles
snapshotDependencies
.getFiles()
.stream()
.map(File::toPath)
.collect(Collectors.toList()))
.addProjectDependencies(
projectDependencies
.getFiles()
.stream()
.map(File::toPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
import org.gradle.StartParameter;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.file.AbstractFileCollection;
import org.gradle.api.internal.file.FileResolver;
Expand All @@ -60,6 +62,7 @@
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.configuration.ConsoleOutput;
import org.gradle.api.plugins.Convention;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.PluginContainer;
import org.gradle.api.plugins.WarPlugin;
Expand Down Expand Up @@ -198,6 +201,21 @@ public void setup() throws URISyntaxException, IOException {
Mockito.when(mockGradle.getStartParameter()).thenReturn(mockStartParameter);
Mockito.when(mockStartParameter.getConsoleOutput()).thenReturn(ConsoleOutput.Auto);

// mocking to complete ignore project dependency resolution
Mockito.when(mockProject.getConfigurations())
.thenReturn(Mockito.mock(ConfigurationContainer.class, Mockito.RETURNS_DEEP_STUBS));
Mockito.when(
mockProject
.getConfigurations()
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)
.getResolvedConfiguration()
.getResolvedArtifacts())
.thenReturn(ImmutableSet.of());
ConfigurableFileCollection emptyFileCollection = Mockito.mock(ConfigurableFileCollection.class);
Mockito.when(emptyFileCollection.getFiles()).thenReturn(ImmutableSet.of());
Mockito.when(mockProject.files(ImmutableList.of())).thenReturn(emptyFileCollection);
// done mocking project dependency resolution

Set<Path> classesFiles = ImmutableSet.of(getResource("gradle/application/classes"));
FileCollection classesFileCollection = new TestFileCollection(classesFiles);
Path resourcesOutputDir = getResource("gradle/application/resources");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// this file doesn't do anything
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
plugins {
id 'java'
id 'com.google.cloud.tools.jib'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
flatDir {
dirs "libs"
}
}

jib {
from {
image = "scratch"
}
extraDirectories {
paths = file('src/main/other-jib')
}
}

sourceSets {
main {
resources {
srcDirs "src/main/extra-resources-1", "src/main/extra-resources-2"
}
}
}

dependencies {
compile project(':lib')
compile name: "dependency-1.0.0"
compile name: "dependencyX-1.0.0-SNAPSHOT"
}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.test;

public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins {
id 'java'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
mavenCentral()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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.lib;

/** Shared Code! */
public class Lib {

public String getThing() {
return "thing";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hi
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.lib;

import org.junit.Assert;
import org.junit.Test;
loosebazooka marked this conversation as resolved.
Show resolved Hide resolved

/** Unit test for simple App. */
public class LibTest {
/** Rigorous Test :-) */
@Test
public void testGetThing() {
Assert.assertEquals("thing", new Lib().getThing());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include ':simple-service'
include ':complex-service'
include ':lib'
Loading