use of com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer in project jib by google.
the class Layers method toLayers.
/**
* Convert a layer spec to a list of layer objects.
*
* <p>Does not handle missing directories for files added via this method. We can either prefill
* directories here, or allow passing of the file entry information directly to the reproducible
* layer builder
*
* @param buildRoot the directory to resolve relative paths, usually the directory where the build
* config file is located
* @param layersSpec a layersSpec containing configuration for all layers
* @return a {@link List} of {@link FileEntriesLayer} to use as part of a jib container build
* @throws IOException if traversing a directory fails
*/
static List<FileEntriesLayer> toLayers(Path buildRoot, LayersSpec layersSpec) throws IOException {
List<FileEntriesLayer> layers = new ArrayList<>();
FilePropertiesStack filePropertiesStack = new FilePropertiesStack();
// base properties
layersSpec.getProperties().ifPresent(filePropertiesStack::push);
for (LayerSpec entry : layersSpec.getEntries()) {
// each loop is a new layer
if (entry instanceof FileLayerSpec) {
FileEntriesLayer.Builder layerBuiler = FileEntriesLayer.builder();
FileLayerSpec fileLayer = (FileLayerSpec) entry;
layerBuiler.setName(fileLayer.getName());
// layer properties
fileLayer.getProperties().ifPresent(filePropertiesStack::push);
for (CopySpec copySpec : ((FileLayerSpec) entry).getFiles()) {
// copy spec properties
copySpec.getProperties().ifPresent(filePropertiesStack::push);
// relativize all paths to the buildRoot location
Path rawSrc = copySpec.getSrc();
Path src = rawSrc.isAbsolute() ? rawSrc : buildRoot.resolve(rawSrc);
AbsoluteUnixPath dest = copySpec.getDest();
if (!Files.isDirectory(src) && !Files.isRegularFile(src)) {
throw new UnsupportedOperationException("Cannot create FileLayers from non-file, non-directory: " + src.toString());
}
if (Files.isRegularFile(src)) {
// regular file
if (!copySpec.getExcludes().isEmpty() || !copySpec.getIncludes().isEmpty()) {
throw new UnsupportedOperationException("Cannot apply includes/excludes on single file copy directives.");
}
layerBuiler.addEntry(src, copySpec.isDestEndsWithSlash() ? dest.resolve(src.getFileName()) : dest, filePropertiesStack.getFilePermissions(), filePropertiesStack.getModificationTime(), filePropertiesStack.getOwnership());
} else if (Files.isDirectory(src)) {
// directory
List<PathMatcher> excludes = copySpec.getExcludes().stream().map(Layers::toPathMatcher).collect(Collectors.toList());
List<PathMatcher> includes = copySpec.getIncludes().stream().map(Layers::toPathMatcher).collect(Collectors.toList());
try (Stream<Path> dirWalk = Files.walk(src)) {
List<Path> filtered = dirWalk.filter(path -> excludes.stream().noneMatch(exclude -> exclude.matches(path))).filter(path -> {
// if there are no includes directives, include everything
if (includes.isEmpty()) {
return true;
}
// if there are includes directives, only include those specified
for (PathMatcher matcher : includes) {
if (matcher.matches(path)) {
return true;
}
}
return false;
}).collect(Collectors.toList());
BiFunction<Path, FilePermissions, FileEntry> newEntry = (file, permission) -> new FileEntry(file, dest.resolve(src.relativize(file)), permission, filePropertiesStack.getModificationTime(), filePropertiesStack.getOwnership());
Set<Path> addedDirectories = new HashSet<>();
for (Path path : filtered) {
if (!Files.isDirectory(path) && !Files.isRegularFile(path)) {
throw new UnsupportedOperationException("Cannot create FileLayers from non-file, non-directory: " + src.toString());
}
if (Files.isDirectory(path)) {
addedDirectories.add(path);
layerBuiler.addEntry(newEntry.apply(path, filePropertiesStack.getDirectoryPermissions()));
} else if (Files.isRegularFile(path)) {
if (!path.startsWith(src)) {
// be from a link scenario that we do not understand.
throw new IllegalStateException(src.toString() + " is not a parent of " + path.toString());
}
Path parent = Verify.verifyNotNull(path.getParent());
while (true) {
if (addedDirectories.contains(parent)) {
break;
}
layerBuiler.addEntry(newEntry.apply(parent, filePropertiesStack.getDirectoryPermissions()));
addedDirectories.add(parent);
if (parent.equals(src)) {
break;
}
parent = Verify.verifyNotNull(parent.getParent());
}
layerBuiler.addEntry(newEntry.apply(path, filePropertiesStack.getFilePermissions()));
}
}
}
}
copySpec.getProperties().ifPresent(ignored -> filePropertiesStack.pop());
}
fileLayer.getProperties().ifPresent(ignored -> filePropertiesStack.pop());
// TODO: add logging/handling for empty layers
layers.add(layerBuiler.build());
} else {
throw new UnsupportedOperationException("Only FileLayers are supported at this time.");
}
}
layersSpec.getProperties().ifPresent(ignored -> filePropertiesStack.pop());
return layers;
}
use of com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer in project jib by google.
the class JarFiles method toJibContainerBuilder.
/**
* Generates a {@link JibContainerBuilder} from contents of a jar file.
*
* @param processor jar processor
* @param jarOptions jar cli options
* @param commonCliOptions common cli options
* @param commonContainerConfigCliOptions common command line options shared between jar and war
* command
* @param logger console logger
* @return JibContainerBuilder
* @throws IOException if I/O error occurs when opening the jar file or if temporary directory
* provided doesn't exist
* @throws InvalidImageReferenceException if the base image reference is invalid
*/
public static JibContainerBuilder toJibContainerBuilder(ArtifactProcessor processor, Jar jarOptions, CommonCliOptions commonCliOptions, CommonContainerConfigCliOptions commonContainerConfigCliOptions, ConsoleLogger logger) throws IOException, InvalidImageReferenceException {
String imageReference = commonContainerConfigCliOptions.getFrom().orElseGet(() -> getDefaultBaseImage(processor));
JibContainerBuilder containerBuilder = ContainerBuilders.create(imageReference, Collections.emptySet(), commonCliOptions, logger);
List<FileEntriesLayer> layers = processor.createLayers();
List<String> customEntrypoint = commonContainerConfigCliOptions.getEntrypoint();
List<String> entrypoint = customEntrypoint.isEmpty() ? processor.computeEntrypoint(jarOptions.getJvmFlags()) : customEntrypoint;
containerBuilder.setEntrypoint(entrypoint).setFileEntriesLayers(layers).setExposedPorts(commonContainerConfigCliOptions.getExposedPorts()).setVolumes(commonContainerConfigCliOptions.getVolumes()).setEnvironment(commonContainerConfigCliOptions.getEnvironment()).setLabels(commonContainerConfigCliOptions.getLabels()).setProgramArguments(commonContainerConfigCliOptions.getProgramArguments());
commonContainerConfigCliOptions.getUser().ifPresent(containerBuilder::setUser);
commonContainerConfigCliOptions.getFormat().ifPresent(containerBuilder::setFormat);
commonContainerConfigCliOptions.getCreationTime().ifPresent(containerBuilder::setCreationTime);
return containerBuilder;
}
use of com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer in project jib by google.
the class JarLayers method getDependenciesLayers.
static List<FileEntriesLayer> getDependenciesLayers(Path jarPath, ProcessingMode mode) throws IOException {
// adding the dependencies layers.
try (JarFile jarFile = new JarFile(jarPath.toFile())) {
String classPath = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
if (classPath == null) {
return new ArrayList<>();
}
List<FileEntriesLayer> layers = new ArrayList<>();
Path jarParent = jarPath.getParent() == null ? Paths.get("") : jarPath.getParent();
Predicate<String> isSnapshot = name -> name.contains("SNAPSHOT");
List<String> allDependencies = Splitter.onPattern("\\s+").splitToList(classPath.trim());
List<Path> nonSnapshots = allDependencies.stream().filter(isSnapshot.negate()).map(Paths::get).collect(Collectors.toList());
List<Path> snapshots = allDependencies.stream().filter(isSnapshot).map(Paths::get).collect(Collectors.toList());
if (!nonSnapshots.isEmpty()) {
FileEntriesLayer.Builder nonSnapshotLayer = FileEntriesLayer.builder().setName(ArtifactLayers.DEPENDENCIES);
nonSnapshots.forEach(path -> addDependency(nonSnapshotLayer, jarParent.resolve(path), mode.equals(ProcessingMode.packaged) ? APP_ROOT.resolve(path) : APP_ROOT.resolve(ArtifactLayers.DEPENDENCIES).resolve(path.getFileName())));
layers.add(nonSnapshotLayer.build());
}
if (!snapshots.isEmpty()) {
FileEntriesLayer.Builder snapshotLayer = FileEntriesLayer.builder().setName(ArtifactLayers.SNAPSHOT_DEPENDENCIES);
snapshots.forEach(path -> addDependency(snapshotLayer, jarParent.resolve(path), mode.equals(ProcessingMode.packaged) ? APP_ROOT.resolve(path) : APP_ROOT.resolve(ArtifactLayers.DEPENDENCIES).resolve(path.getFileName())));
layers.add(snapshotLayer.build());
}
return layers;
}
}
use of com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer in project jib by google.
the class SpringBootExplodedProcessor method createLayers.
@Override
public List<FileEntriesLayer> createLayers() throws IOException {
// Clear the exploded-artifact root first
if (Files.exists(targetExplodedJarRoot)) {
MoreFiles.deleteRecursively(targetExplodedJarRoot, RecursiveDeleteOption.ALLOW_INSECURE);
}
try (JarFile jarFile = new JarFile(jarPath.toFile())) {
ZipUtil.unzip(jarPath, targetExplodedJarRoot, true);
ZipEntry layerIndex = jarFile.getEntry(BOOT_INF + "/layers.idx");
if (layerIndex != null) {
return createLayersForLayeredSpringBootJar(targetExplodedJarRoot);
}
Predicate<Path> isFile = Files::isRegularFile;
// Non-snapshot layer
Predicate<Path> isInBootInfLib = path -> path.startsWith(targetExplodedJarRoot.resolve(BOOT_INF).resolve("lib"));
Predicate<Path> isSnapshot = path -> path.getFileName().toString().contains("SNAPSHOT");
Predicate<Path> isInBootInfLibAndIsNotSnapshot = isInBootInfLib.and(isSnapshot.negate());
Predicate<Path> nonSnapshotPredicate = isFile.and(isInBootInfLibAndIsNotSnapshot);
FileEntriesLayer nonSnapshotLayer = ArtifactLayers.getDirectoryContentsAsLayer(ArtifactLayers.DEPENDENCIES, targetExplodedJarRoot, nonSnapshotPredicate, JarLayers.APP_ROOT);
// Snapshot layer
Predicate<Path> isInBootInfLibAndIsSnapshot = isInBootInfLib.and(isSnapshot);
Predicate<Path> snapshotPredicate = isFile.and(isInBootInfLibAndIsSnapshot);
FileEntriesLayer snapshotLayer = ArtifactLayers.getDirectoryContentsAsLayer(ArtifactLayers.SNAPSHOT_DEPENDENCIES, targetExplodedJarRoot, snapshotPredicate, JarLayers.APP_ROOT);
// Spring-boot-loader layer.
Predicate<Path> isLoader = path -> path.startsWith(targetExplodedJarRoot.resolve("org"));
Predicate<Path> loaderPredicate = isFile.and(isLoader);
FileEntriesLayer loaderLayer = ArtifactLayers.getDirectoryContentsAsLayer("spring-boot-loader", targetExplodedJarRoot, loaderPredicate, JarLayers.APP_ROOT);
// Classes layer.
Predicate<Path> isClass = path -> path.getFileName().toString().endsWith(".class");
Predicate<Path> isInBootInfClasses = path -> path.startsWith(targetExplodedJarRoot.resolve(BOOT_INF).resolve("classes"));
Predicate<Path> classesPredicate = isInBootInfClasses.and(isClass);
FileEntriesLayer classesLayer = ArtifactLayers.getDirectoryContentsAsLayer(ArtifactLayers.CLASSES, targetExplodedJarRoot, classesPredicate, JarLayers.APP_ROOT);
// Resources layer.
Predicate<Path> isInMetaInf = path -> path.startsWith(targetExplodedJarRoot.resolve("META-INF"));
Predicate<Path> isResource = isInMetaInf.or(isInBootInfClasses.and(isClass.negate()));
Predicate<Path> resourcesPredicate = isFile.and(isResource);
FileEntriesLayer resourcesLayer = ArtifactLayers.getDirectoryContentsAsLayer(ArtifactLayers.RESOURCES, targetExplodedJarRoot, resourcesPredicate, JarLayers.APP_ROOT);
return Arrays.asList(nonSnapshotLayer, loaderLayer, snapshotLayer, resourcesLayer, classesLayer);
}
}
use of com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer in project jib by google.
the class StandardExplodedProcessor method createLayers.
@Override
public List<FileEntriesLayer> createLayers() throws IOException {
// Clear the exploded-artifact root first
if (Files.exists(targetExplodedJarRoot)) {
MoreFiles.deleteRecursively(targetExplodedJarRoot, RecursiveDeleteOption.ALLOW_INSECURE);
}
// Add dependencies layers.
List<FileEntriesLayer> layers = JarLayers.getDependenciesLayers(jarPath, ProcessingMode.exploded);
// Determine class and resource files in the directory containing jar contents and create
// FileEntriesLayer for each type of layer (classes or resources).
ZipUtil.unzip(jarPath, targetExplodedJarRoot, true);
Predicate<Path> isClassFile = path -> path.getFileName().toString().endsWith(".class");
Predicate<Path> isResourceFile = isClassFile.negate().and(Files::isRegularFile);
FileEntriesLayer classesLayer = ArtifactLayers.getDirectoryContentsAsLayer(ArtifactLayers.CLASSES, targetExplodedJarRoot, isClassFile, JarLayers.APP_ROOT.resolve("explodedJar"));
FileEntriesLayer resourcesLayer = ArtifactLayers.getDirectoryContentsAsLayer(ArtifactLayers.RESOURCES, targetExplodedJarRoot, isResourceFile, JarLayers.APP_ROOT.resolve("explodedJar"));
if (!resourcesLayer.getEntries().isEmpty()) {
layers.add(resourcesLayer);
}
if (!classesLayer.getEntries().isEmpty()) {
layers.add(classesLayer);
}
return layers;
}
Aggregations