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

Find the name of an image in an archive using a pattern #1208

Merged
merged 1 commit into from
Apr 21, 2019
Merged
Show file tree
Hide file tree
Changes from all 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 doc/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Restore ANSI color to Maven logging if disabled during plugin execution and enable color for Windows with Maven 3.5.0 or later. Color logging is enabled by default, but disabled if the Maven CLI disables color (e.g. in batch mode) ([#1108](https://github.com/fabric8io/docker-maven-plugin/issues/1108))
- Fix NPE if docker:save is called with -Dfile=file-name-only.tar ([#1203](https://github.com/fabric8io/docker-maven-plugin/issues/1203))
- Improve GZIP compression performance for docker:save ([#1205](https://github.com/fabric8io/docker-maven-plugin/issues/1205))
- Use pattern to detect image name in archive loaded during build and tag with image name from the project configuration ([#1207](https://github.com/fabric8io/docker-maven-plugin/issues/1207))

* **0.29.0** (2019-04-08)
- Avoid failing docker:save when no images with build configuration are present ([#1185](https://github.com/fabric8io/docker-maven-plugin/issues/1185))
Expand Down
12 changes: 12 additions & 0 deletions src/main/asciidoc/inc/build/_configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ A provided `<from>` takes precedence over the name given here. This tag is usefu
| *imagePullPolicy*
| Specific pull policy for the base image. This overwrites any global pull policy. See the globale configuration option <<image-pull-policy, imagePullPolicy>> for the possible values and the default.

| *loadNamePattern*
a| Scan the images in the archive specified in `dockerArchive` and match the associated repository and tag information against this pattern. When a matching repository and tag is found, create a tag linking the `name` for this image to the repository and tag that matched the pattern.

The wildcards are:

* `?` matches a single character
* `*` matches within one component, where components are separated by slashes, or the final colon that separates the repository from the tag
* `**` matches multiple components, stopping at the final colon
* `**/` matches multiple components, but must stop at a slash, or the final colon

When matching multiple components, `$$**/$$` is likely to be more useful than `$$**$$`. The pattern `$$**image-name:*$$` will match `my-group/my-image-name:some-tag`, whereas `$$**/image-name:*$$` will not, because the wildcard has to stop at a slash. Note that `$$**/image-name:*$$` will also match 'image-name:some-tag', since the `$$**/$$` wildcard can be empty.
Copy link
Collaborator

Choose a reason for hiding this comment

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

happy with it for now, but would favor to move it to an extra section (maybe right after the table) to which we add only a reference here. Otherwise it will blow up the table quite a bit.

Also, I think the explanation is a not 100% clear, so would love to see some more elaborate examples explainng why the one is more useful than the other. E.g. describing the use case. In the example above its not clear to me why **/ is preferable (maybe because I don't understand what you want to achieve ;).


| <<misc-env, *labels*>>
| Labels as described in <<misc-env,Setting Environment Variables and Labels>>.

Expand Down
2 changes: 1 addition & 1 deletion src/main/asciidoc/inc/build/_overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Alternatively an external Dockerfile template or Docker archive can be used. Thi

* *contextDir* specifies docker build context if an external dockerfile is located outside of Docker build context. If not specified, Dockerfile's parent directory is used as build context.
* *dockerFile* specifies a specific Dockerfile path. The Docker build context directory is set to `contextDir` if given. If not the directory by default is the directory in which the Dockerfile is stored.
* *dockerArchive* specifies a previously saved image archive to load directly. Such a tar archive can be created with `docker save`. If a `dockerArchive` is provided, no `dockerFile` or `dockerFileDir` must be given.
* *dockerArchive* specifies a previously saved image archive to load directly. Such a tar archive can be created with `docker save` or the <<{plugin}:save>> goal. If a `dockerArchive` is provided, no `dockerFile` or `dockerFileDir` must be given.
* *dockerFileDir* (_deprecated_, use *contextDir*) specifies a directory containing a Dockerfile that will be used to create the image. The name of the Dockerfile is `Dockerfile` by default but can be also set with the option `dockerFile` (see below).

All paths can be either absolute or relative paths (except when both `dockerFileDir` and `dockerFile` are provided in which case `dockerFile` must not be absolute). A relative path is looked up in `${project.basedir}/src/main/docker` by default. You can make it easily an absolute path by using `${project.basedir}` in your configuration.
Expand Down
3 changes: 3 additions & 0 deletions src/main/asciidoc/inc/external/_property_configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ when a `docker.from` or a `docker.fromExt` is set.
| *docker.labels.LABEL*
| Sets a label which works similarly like setting environment variables.

| *docker.loadNamePattern*
| Search the archive specified in `docker.dockerArchive` for the specified image name and creates a tag from the matched name to the build image name specified in `docker.name`.

| *docker.log.enabled*
| Use logging (default: `true`)

Expand Down
2 changes: 1 addition & 1 deletion src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Roland Huß;

ifndef::ebook-format[:leveloffset: 1]

(C) 2015 - 2018 The original authors.
(C) 2015 - 2019 The original authors.

ifdef::basebackend-html[toc::[]]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ public class DockerAccessWithHcClient implements DockerAccess {
// Minimal API version, independent of any feature used
public static final String API_VERSION = "1.18";

// Copy buffer size when saving images
private static final int COPY_BUFFER_SIZE = 65536;

// Logging
private final Logger log;

Expand Down Expand Up @@ -465,7 +468,7 @@ private ResponseHandler<Object> getImageResponseHandler(final String filename, f
public Object handleResponse(HttpResponse response) throws IOException {
try (InputStream stream = response.getEntity().getContent();
OutputStream out = compression.wrapOutputStream(new FileOutputStream(filename))) {
IOUtils.copy(stream, out, 65536);
IOUtils.copy(stream, out, COPY_BUFFER_SIZE);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,14 @@ public static ArchiveCompression fromFileName(String filename) {
return ArchiveCompression.none;
}

private static final int GZIP_BUFFER_SIZE = 65536;
// According to https://bugs.openjdk.java.net/browse/JDK-8142920, 3 is a better default
private static final int GZIP_COMPRESSION_LEVEL = 3;

private static class GZIPOutputStream extends java.util.zip.GZIPOutputStream {
private GZIPOutputStream(OutputStream out) throws IOException {
super(out, 65536);
// According to https://bugs.openjdk.java.net/browse/JDK-8142920, 3 is a better default
def.setLevel(3);
super(out, GZIP_BUFFER_SIZE);
def.setLevel(GZIP_COMPRESSION_LEVEL);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package io.fabric8.maven.docker.config;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

import io.fabric8.maven.docker.util.*;
Expand Down Expand Up @@ -52,6 +49,17 @@ public class BuildImageConfiguration implements Serializable {
@Parameter
private String dockerArchive;

/**
* Pattern for the image name we expect to find in the dockerArchive.
*
* If set, the archive is scanned prior to sending to Docker and checked to
* ensure a matching name is found linked to one of the images in the archive.
* After loading, the image with the matching name will be tagged with the
* image name configured in this project.
*/
@Parameter
private String loadNamePattern;

/**
* How interpolation of a dockerfile should be performed
*/
Expand Down Expand Up @@ -161,6 +169,10 @@ public boolean isDockerFileMode() {
return dockerFileFile != null;
}

public String getLoadNamePattern() {
return loadNamePattern;
}

public File getContextDir() {
return contextDir != null ? new File(contextDir) : getDockerFile().getParentFile();
}
Expand Down Expand Up @@ -372,6 +384,11 @@ public Builder dockerArchive(String archive) {
return this;
}

public Builder loadNamePattern(String archiveEntryRepoTagPattern) {
config.loadNamePattern = archiveEntryRepoTagPattern;
return this;
}

public Builder filter(String filter) {
config.filter = filter;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public enum ConfigKey {
IMAGE_PULL_POLICY_RUN("imagePullPolicy.run"),
LABELS(ValueCombinePolicy.Merge),
LINKS,
LOAD_NAME_PATTERN,
LOG_ENABLED("log.enabled"),
LOG_PREFIX("log.prefix"),
LOG_DATE("log.date"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ private BuildImageConfiguration extractBuildConfiguration(ImageConfiguration fro
.imagePullPolicy(valueProvider.getString(IMAGE_PULL_POLICY_BUILD, config == null ? null : config.getImagePullPolicy()))
.contextDir(valueProvider.getString(CONTEXT_DIR, config == null ? null : config.getContextDirRaw()))
.dockerArchive(valueProvider.getString(DOCKER_ARCHIVE, config == null ? null : config.getDockerArchiveRaw()))
.loadNamePattern(valueProvider.getString(LOAD_NAME_PATTERN, config == null ? null : config.getLoadNamePattern()))
.dockerFile(valueProvider.getString(DOCKER_FILE, config == null ? null : config.getDockerFileRaw()))
.dockerFileDir(valueProvider.getString(DOCKER_FILE_DIR, config == null ? null : config.getDockerFileDirRaw()))
.buildOptions(valueProvider.getMap(BUILD_OPTIONS, config == null ? null : config.getBuildOptions()))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.fabric8.maven.docker.model;

import java.util.List;

import com.google.gson.JsonObject;

public interface ImageArchiveManifest {
/**
* @return the list of images in the archive.
*/
List<ImageArchiveManifestEntry> getEntries();

/**
* Return the JSON object for the named config
* @param configName
* @return
*/
JsonObject getConfig(String configName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.fabric8.maven.docker.model;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

public class ImageArchiveManifestAdapter implements ImageArchiveManifest {
private List<ImageArchiveManifestEntry> entries;

private Map<String, JsonObject> config;

public ImageArchiveManifestAdapter(JsonElement json) {
this.entries = new ArrayList<>();

if(json.isJsonArray()) {
for(JsonElement entryJson : json.getAsJsonArray()) {
if(entryJson.isJsonObject()) {
this.entries.add(new ImageArchiveManifestEntryAdapter(entryJson.getAsJsonObject()));
}
}
}

this.config = new LinkedHashMap<>();
}

@Override
public List<ImageArchiveManifestEntry> getEntries() {
return this.entries;
}

@Override
public JsonObject getConfig(String configName) {
return this.config.get(configName);
}

public JsonObject putConfig(String configName, JsonObject config) {
return this.config.put(configName, config);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.fabric8.maven.docker.model;

import java.util.List;

/**
* Interface representing an entry in an image archive manifest.
*/
public interface ImageArchiveManifestEntry {
/**
* @return the image id for this manifest entry
*/
String getId();

/**
* @return the configuration JSON path for this manifest entry
*/
String getConfig();

/**
* @return the repository tags associated with this manifest entry
*/
List<String> getRepoTags();

/**
* @return the layer archive paths for this manifest entry
*/
List<String> getLayers();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.fabric8.maven.docker.model;

import java.util.ArrayList;
import java.util.List;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

/**
* Adapter to convert from JSON representation to model.
*/
public class ImageArchiveManifestEntryAdapter implements ImageArchiveManifestEntry {
public static final String CONFIG = "Config";
public static final String REPO_TAGS = "RepoTags";
public static final String LAYERS = "Layers";
public static final String CONFIG_JSON_SUFFIX = ".json";

private String config;
private List<String> repoTags;
private List<String> layers;

public ImageArchiveManifestEntryAdapter(JsonObject json) {
JsonElement field;

if((field = json.get(CONFIG)) != null && field.isJsonPrimitive()) {
this.config = field.getAsString();
}

this.repoTags = new ArrayList<>();
if ((field = json.get(REPO_TAGS)) != null && field.isJsonArray()) {
for(JsonElement item : field.getAsJsonArray()) {
if(item.isJsonPrimitive()) {
this.repoTags.add(item.getAsString());
}
}
}

this.layers = new ArrayList<>();
if ((field = json.get(LAYERS)) != null && field.isJsonArray()) {
for(JsonElement item : field.getAsJsonArray()) {
if(item.isJsonPrimitive()) {
this.layers.add(item.getAsString());
}
}
}
}

@Override
public String getConfig() {
return config;
}

@Override
public String getId() {
return this.config == null || !this.config.endsWith(CONFIG_JSON_SUFFIX) ? this.config : this.config.substring(0, this.config.length() - CONFIG_JSON_SUFFIX.length());
}

@Override
public List<String> getRepoTags() {
return repoTags;
}

@Override
public List<String> getLayers() {
return layers;
}
}
Loading