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

Fix docker image reference parsing by DockerImageIdentifierParser #1420

Merged
merged 1 commit into from
Jun 6, 2016
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@
*******************************************************************************/
package org.eclipse.che.plugin.docker.client.parser;

import org.eclipse.che.commons.annotation.Nullable;

import javax.validation.constraints.NotNull;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
* Describes specific docker image.
* <p>
Expand Down Expand Up @@ -51,6 +56,7 @@ public static DockerImageIdentifierBuilder builder() {
return new DockerImageIdentifierBuilder();
}

@Nullable
public String getRegistry() {
return registry;
}
Expand All @@ -59,10 +65,12 @@ public String getRepository() {
return repository;
}

@Nullable
public String getTag() {
return tag;
}

@Nullable
public String getDigest() {
return digest;
}
Expand Down Expand Up @@ -108,7 +116,8 @@ public DockerImageIdentifierBuilder setRegistry(String registry) {
return this;
}

public DockerImageIdentifierBuilder setRepository(String repository) {
public DockerImageIdentifierBuilder setRepository(@NotNull String repository) {
requireNonNull(repository);
this.repository = repository;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,59 +26,78 @@
*/
public class DockerImageIdentifierParser {

private static final String HOSTNAME_COMPONENT = "(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])";
private static final String REGISTRY = HOSTNAME_COMPONENT + "(?:\\." + HOSTNAME_COMPONENT + ")*([:][0-9]+)?";
private static final String CAPTURED_REGISTRY = "(?<registry>" + REGISTRY + ")";
private static final String SEPARATOR = "(?:[_.]|__|[-]*)";
private static final String ALPHA_NUMERIC = "[a-z0-9]+";
private static final String NAME_COMPONENT = ALPHA_NUMERIC + "(?:" + SEPARATOR + ALPHA_NUMERIC + ")*";
private static final String REPOSITORY = NAME_COMPONENT + "(?:[/]" + NAME_COMPONENT + ")*";
private static final String CAPTURED_REPOSITORY = "(?<repository>" + REPOSITORY + ")";
private static final String NAME = "(?:" + CAPTURED_REGISTRY + "[/])?" + CAPTURED_REPOSITORY;

private static final Pattern IMAGE_PATTERN = Pattern.compile(NAME);

// Validation rules are taken from https://github.com/docker/distribution/blob/master/reference/regexp.go
private static final String HOSTNAME_COMPONENT = "(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])";
private static final String REGISTRY = HOSTNAME_COMPONENT + "(?:\\." + HOSTNAME_COMPONENT + ")*(?::[0-9]+)?";
private static final String SEPARATOR = "(?:[._]|__|[-]*)";
private static final String ALPHA_NUMERIC = "[a-z0-9]+";
private static final String NAME_COMPONENT = ALPHA_NUMERIC + "(?:" + SEPARATOR + ALPHA_NUMERIC + ")*";
private static final String REPOSITORY = NAME_COMPONENT + "(?:/" + NAME_COMPONENT + ")*";
private static final String TAG = "[\\w][\\w.-]*";
private static final String DIGEST = "[\\w+.:-]+";
private static final String NAME = "(?:" + REGISTRY + "/)?" + REPOSITORY;
private static final String REFERENCE = NAME + "(?::" + TAG + ")?" + "(?:@" + DIGEST + ")?";
private static final Pattern IMAGE_PATTERN = Pattern.compile(REFERENCE);

/**
* Validates and parse docker image reference into object that holds reference components
*
* @param image image reference to parse
* @throws DockerFileException if validation fails
*/
public static DockerImageIdentifier parse(final String image) throws DockerFileException {
if (image == null) {
throw new IllegalArgumentException("Null argument value is forbidden");
if (image == null || image.isEmpty()) {
throw new DockerFileException("Null and empty argument value is forbidden");
}

Matcher matcher = IMAGE_PATTERN.matcher(image);
if (!matcher.matches()) {
throw new DockerFileException("Provided image reference is invalid");
}

DockerImageIdentifier.DockerImageIdentifierBuilder identifierBuilder = DockerImageIdentifier.builder();
String workingCopyOfImage = image;
String digest = "";
String tag = "";

// find digest
// extract digest
int index = workingCopyOfImage.lastIndexOf('@');
if (index != -1) {
digest = workingCopyOfImage.substring(index + 1);
String digest = workingCopyOfImage.substring(index + 1);
if (!digest.isEmpty()) {
workingCopyOfImage = workingCopyOfImage.substring(0, index);
identifierBuilder.setDigest(digest);
}
}

// find tag
// extract tag
index = workingCopyOfImage.lastIndexOf(':');
if (index != -1) {
if (workingCopyOfImage.lastIndexOf('/') < index) {
tag = workingCopyOfImage.substring(index + 1);
String tag = workingCopyOfImage.substring(index + 1);
if (!tag.isEmpty()) {
workingCopyOfImage = workingCopyOfImage.substring(0, index);
identifierBuilder.setTag(tag);
}
}
}

Matcher matcher = IMAGE_PATTERN.matcher(workingCopyOfImage);
if (!matcher.matches()) {
throw new DockerFileException("Provided image reference is invalid");
// find first part of the name that can be registry or first repository part
index = workingCopyOfImage.indexOf('/');
String beforeSlash = index > -1 ? workingCopyOfImage.substring(0, index) : "";
// consider first part of the name as registry if:
// - there is dot symbol in it (consider it as dot in the hostname, e.g. eclipse.com)
// - there is colon symbol in it (consider it as registry port mark)
// - it is equal to 'localhost'
if (!beforeSlash.isEmpty() && (beforeSlash.contains(".") ||
beforeSlash.contains(":") ||
"localhost".equals(beforeSlash))) {

identifierBuilder.setRegistry(beforeSlash)
.setRepository(workingCopyOfImage.substring(index + 1));
} else {
identifierBuilder.setRepository(workingCopyOfImage);
}

return DockerImageIdentifier.builder()
.setRepository(matcher.group("repository"))
.setRegistry(matcher.group("registry"))
.setTag(tag.isEmpty() ? null : tag)
.setDigest(digest.isEmpty() ? null : digest)
.build();
return identifierBuilder.build();
}

private DockerImageIdentifierParser() {}
Expand Down
Loading