/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.plugin.docker.client.parser; import org.eclipse.che.plugin.docker.client.DockerFileException; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Parse docker image reference. * <p> * For example reference used in FROM instruction of Dockerfile.<br> * This class doesn't validate all components as Docker do.<br> * It was designed to extract base docker image reference from dockerfile. * * @author Alexander Garagatyi */ public class DockerImageIdentifierParser { // 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 || 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; // extract digest int index = workingCopyOfImage.lastIndexOf('@'); if (index != -1) { String digest = workingCopyOfImage.substring(index + 1); if (!digest.isEmpty()) { workingCopyOfImage = workingCopyOfImage.substring(0, index); identifierBuilder.setDigest(digest); } } // extract tag index = workingCopyOfImage.lastIndexOf(':'); if (index != -1) { if (workingCopyOfImage.lastIndexOf('/') < index) { String tag = workingCopyOfImage.substring(index + 1); if (!tag.isEmpty()) { workingCopyOfImage = workingCopyOfImage.substring(0, index); identifierBuilder.setTag(tag); } } } // 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 identifierBuilder.build(); } private DockerImageIdentifierParser() {} }