From ca6e4cb951338eaaa2e44540361af09ee02500a8 Mon Sep 17 00:00:00 2001 From: Marcus Thiesen Date: Wed, 18 Apr 2018 12:00:44 +0200 Subject: [PATCH 1/8] Initial implementation of containerNamePattern --- .../io/fabric8/maven/docker/StartMojo.java | 44 ++++- .../io/fabric8/maven/docker/StopMojo.java | 56 ++---- .../io/fabric8/maven/docker/WatchMojo.java | 1 + .../docker/config/ImageConfiguration.java | 16 +- .../docker/config/NamingConfiguration.java | 178 ++++++++++++++++++ .../docker/config/RunImageConfiguration.java | 34 +++- .../maven/docker/service/RunService.java | 56 +++--- .../maven/docker/service/WatchService.java | 39 ++-- .../config/NamingConfigurationTest.java | 86 +++++++++ .../handler/AbstractConfigHandlerTest.java | 7 +- .../DockerComposeConfigHandlerTest.java | 21 +-- .../property/PropertyConfigHandlerTest.java | 44 +++-- 12 files changed, 461 insertions(+), 121 deletions(-) create mode 100644 src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java create mode 100644 src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java diff --git a/src/main/java/io/fabric8/maven/docker/StartMojo.java b/src/main/java/io/fabric8/maven/docker/StartMojo.java index dc8ad5ae1..b6a5319fa 100644 --- a/src/main/java/io/fabric8/maven/docker/StartMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StartMojo.java @@ -8,21 +8,44 @@ * the License. */ -import java.io.IOException; -import java.util.*; -import java.util.concurrent.*; - import io.fabric8.maven.docker.access.*; -import io.fabric8.maven.docker.config.*; +import io.fabric8.maven.docker.config.ConfigHelper; +import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.config.LogConfiguration; +import io.fabric8.maven.docker.config.NamingConfiguration; +import io.fabric8.maven.docker.config.NetworkConfig; +import io.fabric8.maven.docker.config.RunImageConfiguration; +import io.fabric8.maven.docker.config.WaitConfiguration; import io.fabric8.maven.docker.log.LogDispatcher; import io.fabric8.maven.docker.model.Container; -import io.fabric8.maven.docker.service.*; +import io.fabric8.maven.docker.service.ImagePullManager; +import io.fabric8.maven.docker.service.QueryService; +import io.fabric8.maven.docker.service.RegistryService; +import io.fabric8.maven.docker.service.RunService; +import io.fabric8.maven.docker.service.ServiceHub; import io.fabric8.maven.docker.util.StartOrderResolver; import com.google.common.util.concurrent.MoreExecutors; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.*; import org.codehaus.plexus.util.StringUtils; +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + /** * Goal for creating and starting a docker container. This goal evaluates the image configuration @@ -138,7 +161,7 @@ public synchronized void executeInternal(final ServiceHub hub) throws DockerAcce // Move from waiting to starting status imagesStarting.add(image); imagesWaitingToStart.remove(image); - + if (!startParallel) { waitForStartedContainer(hub, containerStartupService, startedContainerAliases, imagesStarting); } @@ -241,18 +264,21 @@ private void updateAliasesSet(Set aliasesSet, String alias) { private void startImage(final ImageConfiguration image, final ServiceHub hub, final ExecutorCompletionService startingContainers, - final PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper) { + final PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper) throws MojoExecutionException, DockerAccessException { final RunService runService = hub.getRunService(); + final QueryService queryService = hub.getQueryService(); final Properties projProperties = project.getProperties(); final RunImageConfiguration runConfig = image.getRunConfiguration(); + final NamingConfiguration namingConfiguration = image.calculateNamingConfiguration(getBuildTimestamp(), + queryService.getContainersForImage(image.getName())); final PortMapping portMapping = runService.createPortMapping(runConfig, projProperties); final LogDispatcher dispatcher = getLogDispatcher(hub); startingContainers.submit(new Callable() { @Override public StartedContainer call() throws Exception { - final String containerId = runService.createAndStartContainer(image, portMapping, getPomLabel(), projProperties, project.getBasedir()); + final String containerId = runService.createAndStartContainer(image, namingConfiguration, portMapping, getPomLabel(), projProperties, project.getBasedir()); // Update port-mapping writer portMappingPropertyWriteHelper.add(portMapping, runConfig.getPortPropertyFile()); diff --git a/src/main/java/io/fabric8/maven/docker/StopMojo.java b/src/main/java/io/fabric8/maven/docker/StopMojo.java index 51a7d9b6f..2f9b5ea17 100644 --- a/src/main/java/io/fabric8/maven/docker/StopMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StopMojo.java @@ -1,17 +1,10 @@ package io.fabric8.maven.docker; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.access.ExecException; import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.config.NamingConfiguration; import io.fabric8.maven.docker.config.NetworkConfig; -import io.fabric8.maven.docker.config.RunImageConfiguration; import io.fabric8.maven.docker.log.LogDispatcher; import io.fabric8.maven.docker.model.Container; import io.fabric8.maven.docker.model.Network; @@ -19,12 +12,16 @@ import io.fabric8.maven.docker.service.RunService; import io.fabric8.maven.docker.service.ServiceHub; import io.fabric8.maven.docker.util.PomLabel; - import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + /** * Mojo for stopping containers. If called together with docker:start (i.e. * when configured for integration testing in a lifefcycle phase), then only the container @@ -76,11 +73,14 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do dispatcher.untrackAllContainerLogs(); } - private void stopContainers(QueryService queryService, RunService runService, PomLabel pomLabel) throws DockerAccessException, ExecException { + private void stopContainers(QueryService queryService, RunService runService, PomLabel pomLabel) throws DockerAccessException, ExecException, MojoExecutionException { Collection networksToRemove = getNetworksToRemove(queryService, pomLabel); for (ImageConfiguration image : getResolvedImages()) { - for (Container container : getContainersToStop(queryService, image)) { - if (shouldStopContainer(container, pomLabel, image)) { + final NamingConfiguration namingConfiguration = image.calculateNamingConfiguration(getBuildTimestamp(), + queryService.getContainersForImage(image.getName())); + + for (Container container : queryService.getContainersForImage(image.getName())) { + if (shouldStopContainer(container, namingConfiguration)) { runService.stopContainer(container.getId(), image, keepContainer, removeVolumes); } } @@ -88,31 +88,12 @@ private void stopContainers(QueryService queryService, RunService runService, Po runService.removeCustomNetworks(networksToRemove); } - // If naming strategy is alias stop a container with this name, otherwise get all containers with this image's name - private List getContainersToStop(QueryService queryService, ImageConfiguration image) throws DockerAccessException { - RunImageConfiguration.NamingStrategy strategy = image.getRunConfiguration().getNamingStrategy(); - - if (strategy == RunImageConfiguration.NamingStrategy.alias) { - Container container = queryService.getContainer(image.getAlias()); - return container != null ? Collections.singletonList(container) : Collections.emptyList(); - } else { - return queryService.getContainersForImage(image.getName()); - } - } - - private boolean shouldStopContainer(Container container, PomLabel pomLabel, ImageConfiguration image) { + private boolean shouldStopContainer(Container container, NamingConfiguration namingConfiguration) { if (isStopAllContainers()) { return true; } - RunImageConfiguration.NamingStrategy strategy = image.getRunConfiguration().getNamingStrategy(); - if (RunImageConfiguration.NamingStrategy.alias.equals(strategy)) { - return container.getName().equals(image.getAlias()); - } - - String key = pomLabel.getKey(); - Map labels = container.getLabels(); - return labels.containsKey(key) && pomLabel.equals(new PomLabel(labels.get(key))); + return container.getName().equals(namingConfiguration.calculateLastContainerName()); } private boolean isStopAllContainers() { @@ -124,7 +105,7 @@ private boolean invokedTogetherWithDockerStart() { return startCalled != null && startCalled; } - private Set getNetworksToRemove(QueryService queryService, PomLabel pomLabel) throws DockerAccessException { + private Set getNetworksToRemove(QueryService queryService, PomLabel pomLabel) throws DockerAccessException, MojoExecutionException { if (!autoCreateCustomNetworks) { return Collections.emptySet(); } @@ -132,13 +113,16 @@ private Set getNetworksToRemove(QueryService queryService, PomLabel pom Set networks = queryService.getNetworks(); for (ImageConfiguration image : getResolvedImages()) { + final NamingConfiguration namingConfiguration = image.calculateNamingConfiguration(getBuildTimestamp(), + queryService.getContainersForImage(image.getName())); + final NetworkConfig config = image.getRunConfiguration().getNetworkingConfig(); if (config.isCustomNetwork()) { Network network = getNetworkByName(networks, config.getCustomNetwork()); if (network != null) { customNetworks.add(network); - for (Container container : getContainersToStop(queryService, image)) { - if (!shouldStopContainer(container, pomLabel, image)) { + for (Container container : queryService.getContainersForImage(image.getName())) { + if (!shouldStopContainer(container, namingConfiguration)) { // it's sill in use don't collect it customNetworks.remove(network); } diff --git a/src/main/java/io/fabric8/maven/docker/WatchMojo.java b/src/main/java/io/fabric8/maven/docker/WatchMojo.java index aff249d6b..2cf49f8d7 100644 --- a/src/main/java/io/fabric8/maven/docker/WatchMojo.java +++ b/src/main/java/io/fabric8/maven/docker/WatchMojo.java @@ -86,6 +86,7 @@ protected WatchService.WatchContext getWatchContext() throws MojoExecutionExcept .keepContainer(keepContainer) .keepRunning(keepRunning) .removeVolumes(removeVolumes) + .buildTimestamp(getBuildTimestamp()) .pomLabel(getPomLabel()) .mojoParameters(createMojoParameters()) .build(); diff --git a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java index 1ea2e1839..2e521ddba 100644 --- a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java @@ -1,10 +1,6 @@ package io.fabric8.maven.docker.config; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - +import io.fabric8.maven.docker.model.Container; import io.fabric8.maven.docker.util.DeepCopy; import io.fabric8.maven.docker.util.EnvUtil; import io.fabric8.maven.docker.util.ImageName; @@ -12,6 +8,12 @@ import io.fabric8.maven.docker.util.StartOrderResolver; import org.apache.maven.plugins.annotations.Parameter; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + /** * @author roland * @since 02.09.14 @@ -149,6 +151,10 @@ public String getRegistry() { return registry; } + public NamingConfiguration calculateNamingConfiguration(final Date buildTimestamp, final List existingContainers) { + return getRunConfiguration().calcualteNamingConfiguration(buildTimestamp, existingContainers, getName(), getAlias()); + } + @Override public String toString() { return String.format("ImageConfiguration {name='%s', alias='%s'}", name, alias); diff --git a/src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java new file mode 100644 index 000000000..051289889 --- /dev/null +++ b/src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java @@ -0,0 +1,178 @@ +package io.fabric8.maven.docker.config; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import io.fabric8.maven.docker.model.Container; +import io.fabric8.maven.docker.util.ImageName; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; +import java.util.Date; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.regex.Matcher.quoteReplacement; + +/** + * @author marcus + * @since 1.0.0 + */ +public class NamingConfiguration { + + private static final String DEFAULT_NAMING_CONFIGURATION = "%n-%i"; + + private static final String ALIAS_PLACEHOLDER = "%a"; + private static final String IMAGE_NAME_PLACEHOLDER = "%n"; + private static final String TIMESTAMP_PLACEHOLDER = "%t"; + private static final String INDEX_PLACEHOLDER = "%i"; + + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("(" + + Joiner.on('|').join( + ALIAS_PLACEHOLDER, + IMAGE_NAME_PLACEHOLDER, + TIMESTAMP_PLACEHOLDER, + INDEX_PLACEHOLDER) + + ")"); + + + private final String containerNamePattern; + private final String imageName; + private final String nameAlias; + private final String buildTimestamp; + private final Set existingContainerNames; + + @VisibleForTesting + NamingConfiguration(final String containerNamePattern, + final String imageName, + final String nameAlias, + final String buildTimestamp, + final Set existingContainerNames) { + this.containerNamePattern = containerNamePattern; + this.imageName = imageName; + this.nameAlias = nameAlias; + this.buildTimestamp = buildTimestamp; + this.existingContainerNames = existingContainerNames; + } + + public static NamingConfiguration create(final String containerNamePattern, + final RunImageConfiguration.NamingStrategy namingStrategy, + final String imageName, + final String nameAlias, + final Date buildTimestamp, + final Collection existingContainers) { + + final String timestamp = String.valueOf(buildTimestamp.getTime()); + final Set containerNames = extractContainerNames(existingContainers); + + // explicitly set containerNamePattern wins + if (StringUtils.isNotBlank(containerNamePattern)) { + return new NamingConfiguration(containerNamePattern, imageName, nameAlias, timestamp, containerNames); + } + + // backward compatibility code + if (namingStrategy != null) { + switch (namingStrategy) { + case alias: + return new NamingConfiguration(ALIAS_PLACEHOLDER, imageName, nameAlias, timestamp, containerNames); + case none: + return new NamingConfiguration(DEFAULT_NAMING_CONFIGURATION, imageName, nameAlias, timestamp, containerNames); + default: + throw new IllegalStateException("Naming strategy " + namingStrategy + "not covered in comptatibility code"); + } + } + + return new NamingConfiguration(DEFAULT_NAMING_CONFIGURATION, imageName, nameAlias, timestamp, containerNames); + } + + private static Set extractContainerNames(final Collection existingContainers) { + final ImmutableSet.Builder containerNamesBuilder = ImmutableSet.builder(); + for (final Container container : existingContainers) { + containerNamesBuilder.add(container.getName()); + } + return containerNamesBuilder.build(); + } + + public String calculateContainerName() { + final String partiallyApplied = calculateWithoutIndex(); + + if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { + return applyIndexReplacement(partiallyApplied); + } else { + return partiallyApplied; + } + } + + private String calculateWithoutIndex() { + final Matcher matcher = PLACEHOLDER_PATTERN.matcher(containerNamePattern); + final StringBuffer sb = new StringBuffer(); + + while (matcher.find()) { + final String placeholder = matcher.group(1); + + switch (placeholder) { + case ALIAS_PLACEHOLDER: + matcher.appendReplacement(sb, quoteReplacement(nameAlias)); + break; + case IMAGE_NAME_PLACEHOLDER: + matcher.appendReplacement(sb, quoteReplacement(cleanImageName(imageName))); + break; + case TIMESTAMP_PLACEHOLDER: + matcher.appendReplacement(sb, quoteReplacement(buildTimestamp)); + break; + case INDEX_PLACEHOLDER: + // noop, we apply the index later + matcher.appendReplacement(sb, quoteReplacement(INDEX_PLACEHOLDER)); + break; + default: + throw new IllegalArgumentException("Matched an unexpected placeholder " + placeholder); + } + } + matcher.appendTail(sb); + + return sb.toString(); + } + + private String applyIndexReplacement(final String partiallyApplied) { + for (long i = 1; i < Long.MAX_VALUE; i++) { + final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); + if (!existingContainerNames.contains(withIndexApplied)) { + return withIndexApplied; + } + } + throw new IllegalStateException("Could not find any free container name for pattern " + partiallyApplied); + } + + private String applyLastIndex(final String partiallyApplied) { + String last = null; + for (long i = 1; i < Long.MAX_VALUE; i++) { + final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); + if (last == null) { + last = withIndexApplied; + } + + if (!existingContainerNames.contains(withIndexApplied)) { + return last; + } else { + last = withIndexApplied; + } + } + + return last; + } + + private String cleanImageName(final String imageName) { + return new ImageName(imageName).getSimpleName().replaceAll("[^a-zA-Z0-9_.-]+", "_"); + } + + public String calculateLastContainerName() { + final String partiallyApplied = calculateWithoutIndex(); + + if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { + return applyLastIndex(partiallyApplied); + } else { + return partiallyApplied; + } + } +} diff --git a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java index 8a967ce08..bf2644695 100644 --- a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java @@ -1,14 +1,16 @@ package io.fabric8.maven.docker.config; -import java.io.Serializable; -import java.util.List; -import java.util.Map; - +import io.fabric8.maven.docker.model.Container; import io.fabric8.maven.docker.util.DeepCopy; import io.fabric8.maven.docker.util.EnvUtil; import org.apache.maven.plugins.annotations.Parameter; import javax.annotation.Nonnull; +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; /** * @author roland @@ -113,8 +115,21 @@ public boolean isDefault() { private List ports; @Parameter + @Deprecated private NamingStrategy namingStrategy; + /** + * A pattern to define the naming of the container where + * + * - %a for the "alias" mode + * - %n for the image name + * - %t for a timestamp + * - %i for an increasing index of container names + * + */ + @Parameter + private String containerNamePattern; + /** * Property key part used to expose the container ip when running. */ @@ -301,6 +316,7 @@ public List getTmpfs() { } // Naming scheme for how to name container + @Deprecated // for backward compatibility public enum NamingStrategy { /** * No extra naming @@ -312,13 +328,15 @@ public enum NamingStrategy { alias } - public NamingStrategy getNamingStrategy() { - return namingStrategy == null ? NamingStrategy.none : namingStrategy; + public NamingConfiguration calcualteNamingConfiguration(final Date buildTimestamp, + final Collection existingContainers, + final String imageName, + final String nameAlias) { + return NamingConfiguration.create(containerNamePattern, namingStrategy, imageName, nameAlias, buildTimestamp, existingContainers); } public NamingStrategy getNamingStrategyRaw() { return namingStrategy; - } public String getExposedPropertyKey() { @@ -520,6 +538,7 @@ public Builder log(LogConfiguration log) { return this; } + @Deprecated public Builder namingStrategy(String namingStrategy) { config.namingStrategy = namingStrategy == null ? NamingStrategy.none : @@ -527,6 +546,7 @@ public Builder namingStrategy(String namingStrategy) { return this; } + @Deprecated public Builder namingStrategy(NamingStrategy namingStrategy) { config.namingStrategy = namingStrategy; return this; diff --git a/src/main/java/io/fabric8/maven/docker/service/RunService.java b/src/main/java/io/fabric8/maven/docker/service/RunService.java index 86da86fda..3c1e0b64e 100644 --- a/src/main/java/io/fabric8/maven/docker/service/RunService.java +++ b/src/main/java/io/fabric8/maven/docker/service/RunService.java @@ -17,22 +17,45 @@ * limitations under the License. */ -import java.io.File; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; - +import io.fabric8.maven.docker.access.ContainerCreateConfig; +import io.fabric8.maven.docker.access.ContainerHostConfig; +import io.fabric8.maven.docker.access.ContainerNetworkingConfig; +import io.fabric8.maven.docker.access.DockerAccess; +import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.access.ExecException; +import io.fabric8.maven.docker.access.NetworkCreateConfig; +import io.fabric8.maven.docker.access.PortMapping; +import io.fabric8.maven.docker.config.Arguments; +import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.config.NamingConfiguration; +import io.fabric8.maven.docker.config.NetworkConfig; +import io.fabric8.maven.docker.config.RestartPolicy; +import io.fabric8.maven.docker.config.RunImageConfiguration; +import io.fabric8.maven.docker.config.RunVolumeConfiguration; +import io.fabric8.maven.docker.log.LogOutputSpecFactory; import io.fabric8.maven.docker.model.Container; import io.fabric8.maven.docker.model.ContainerDetails; import io.fabric8.maven.docker.model.ExecDetails; -import io.fabric8.maven.docker.log.LogOutputSpecFactory; -import io.fabric8.maven.docker.access.*; -import io.fabric8.maven.docker.config.*; import io.fabric8.maven.docker.model.Network; -import io.fabric8.maven.docker.util.*; -import io.fabric8.maven.docker.wait.WaitUtil; +import io.fabric8.maven.docker.util.EnvUtil; +import io.fabric8.maven.docker.util.Logger; +import io.fabric8.maven.docker.util.PomLabel; +import io.fabric8.maven.docker.util.StartOrderResolver; import io.fabric8.maven.docker.wait.WaitTimeoutException; +import io.fabric8.maven.docker.wait.WaitUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import static io.fabric8.maven.docker.util.VolumeBindingUtil.resolveRelativeVolumeBindings; @@ -107,13 +130,14 @@ public String execInContainer(String containerId, String command, ImageConfigura * @throws DockerAccessException if access to the docker backend fails */ public String createAndStartContainer(ImageConfiguration imageConfig, + NamingConfiguration namingConfiguration, PortMapping portMapping, PomLabel pomLabel, Properties mavenProps, File baseDir) throws DockerAccessException { RunImageConfiguration runConfig = imageConfig.getRunConfiguration(); String imageName = imageConfig.getName(); - String containerName = calculateContainerName(imageConfig.getAlias(), runConfig.getNamingStrategy()); + String containerName = namingConfiguration.calculateContainerName(); ContainerCreateConfig config = createContainerConfig(imageName, runConfig, portMapping, pomLabel, mavenProps, baseDir); String id = docker.createContainer(config, containerName); @@ -381,16 +405,6 @@ private List findVolumesFromContainers(List images) throws Docke } - private String calculateContainerName(String alias, RunImageConfiguration.NamingStrategy namingStrategy) { - if (namingStrategy == RunImageConfiguration.NamingStrategy.none) { - return null; - } - if (alias == null) { - throw new IllegalArgumentException("A naming scheme 'alias' requires an image alias to be set"); - } - return alias; - } - // checkAllContainers: false = only running containers are considered private String findContainerId(String imageNameOrAlias, boolean checkAllContainers) throws DockerAccessException { String id = lookupContainer(imageNameOrAlias); diff --git a/src/main/java/io/fabric8/maven/docker/service/WatchService.java b/src/main/java/io/fabric8/maven/docker/service/WatchService.java index 40c1de25e..3e0afd1de 100644 --- a/src/main/java/io/fabric8/maven/docker/service/WatchService.java +++ b/src/main/java/io/fabric8/maven/docker/service/WatchService.java @@ -1,21 +1,12 @@ package io.fabric8.maven.docker.service; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - import io.fabric8.maven.docker.access.DockerAccess; import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.access.ExecException; import io.fabric8.maven.docker.access.PortMapping; import io.fabric8.maven.docker.assembly.AssemblyFiles; import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.config.NamingConfiguration; import io.fabric8.maven.docker.config.WatchImageConfiguration; import io.fabric8.maven.docker.config.WatchMode; import io.fabric8.maven.docker.util.Logger; @@ -23,11 +14,21 @@ import io.fabric8.maven.docker.util.PomLabel; import io.fabric8.maven.docker.util.StartOrderResolver; import io.fabric8.maven.docker.util.Task; - import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.codehaus.plexus.util.StringUtils; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + /** * Watch service for monitoring changes and restarting containers */ @@ -235,8 +236,11 @@ public void execute(ImageWatcher watcher) throws Exception { } runService.stopPreviouslyStartedContainer(id, false, false); + final NamingConfiguration namingConfiguration = imageConfig.calculateNamingConfiguration(watcher.getWatchContext().getBuildTimestamp(), + queryService.getContainersForImage(imageConfig.getName())); + // Start new one - watcher.setContainerId(runService.createAndStartContainer(imageConfig, mappedPorts, watcher.getWatchContext().getPomLabel(), + watcher.setContainerId(runService.createAndStartContainer(imageConfig, namingConfiguration, mappedPorts, watcher.getWatchContext().getPomLabel(), watcher.getWatchContext().getMojoParameters().getProject().getProperties(), watcher.getWatchContext().getMojoParameters().getProject().getBasedir())); } @@ -397,6 +401,8 @@ public static class WatchContext implements Serializable { private Task containerRestarter; + private Date buildTimestamp; + public WatchContext() { } @@ -448,6 +454,10 @@ public Task getContainerRestarter() { return containerRestarter; } + public Date getBuildTimestamp() { + return buildTimestamp; + } + public static class Builder { private WatchContext context = new WatchContext(); @@ -520,6 +530,11 @@ public Builder autoCreateCustomNetworks(boolean autoCreateCustomNetworks) { return this; } + public Builder buildTimestamp(Date buildTimestamp) { + context.buildTimestamp = buildTimestamp; + return this; + } + public WatchContext build() { return context; } diff --git a/src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java b/src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java new file mode 100644 index 000000000..06936dbb7 --- /dev/null +++ b/src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java @@ -0,0 +1,86 @@ +package io.fabric8.maven.docker.config; + +import com.google.common.collect.ImmutableSet; +import io.fabric8.maven.docker.model.Container; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; +import java.util.Date; + +/** + * @author marcus + * @since 1.0.0 + */ +public class NamingConfigurationTest { + + @Test + public void testDefault() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create(null, null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("jolokia_demo-1", namingConfiguration.calculateContainerName()); + } + + @Test + public void testBackwardCompatibilityAlias() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create(null, RunImageConfiguration.NamingStrategy.alias, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("nameAlias", namingConfiguration.calculateContainerName()); + } + + @Test + public void testBackwardCompatibilityNone() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create(null, RunImageConfiguration.NamingStrategy.none, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("jolokia_demo-1", namingConfiguration.calculateContainerName()); + } + + @Test + public void testAlias() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create("%a", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("nameAlias", namingConfiguration.calculateContainerName()); + } + + @Test + public void testTimestamp() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create("%t", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("123456", namingConfiguration.calculateContainerName()); + } + + @Test + public void testImageName() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create("%n", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("jolokia_demo", namingConfiguration.calculateContainerName()); + } + + @Test + public void testIndex() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create("%i", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("1", namingConfiguration.calculateContainerName()); + } + + @Test + public void testAll() { + final NamingConfiguration namingConfiguration = NamingConfiguration.create("prefix-%i-%a-%n-%t-postfix", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); + Assert.assertEquals("prefix-1-nameAlias-jolokia_demo-123456-postfix", namingConfiguration.calculateContainerName()); + } + + @Test + public void testIndexAdvanced() { + final NamingConfiguration namingConfiguration = new NamingConfiguration("container-%i", "jolokia/jolokia_demo", "nameAlias", "123456", + Collections.singleton("container-1")); + Assert.assertEquals("container-2", namingConfiguration.calculateContainerName()); + } + + @Test + public void testCalculateLastContainerName() { + final NamingConfiguration namingConfiguration = new NamingConfiguration("container-%i", "jolokia/jolokia_demo", "nameAlias", "123456", + Collections.singleton("container-1")); + Assert.assertEquals("container-1", namingConfiguration.calculateLastContainerName()); + } + + @Test + public void testCalculateLastContainerNameSecond() { + final NamingConfiguration namingConfiguration = new NamingConfiguration("container-%i", "jolokia/jolokia_demo", "nameAlias", "123456", + ImmutableSet.of("container-1", "container-2")); + Assert.assertEquals("container-2", namingConfiguration.calculateLastContainerName()); + } + +} diff --git a/src/test/java/io/fabric8/maven/docker/config/handler/AbstractConfigHandlerTest.java b/src/test/java/io/fabric8/maven/docker/config/handler/AbstractConfigHandlerTest.java index 3bc016ac9..0d927f92d 100644 --- a/src/test/java/io/fabric8/maven/docker/config/handler/AbstractConfigHandlerTest.java +++ b/src/test/java/io/fabric8/maven/docker/config/handler/AbstractConfigHandlerTest.java @@ -1,13 +1,13 @@ package io.fabric8.maven.docker.config.handler; -import static org.junit.Assert.assertEquals; +import io.fabric8.maven.docker.config.RestartPolicy; +import io.fabric8.maven.docker.config.RunImageConfiguration; import java.util.Arrays; import java.util.List; import java.util.Map; -import io.fabric8.maven.docker.config.RestartPolicy; -import io.fabric8.maven.docker.config.RunImageConfiguration; +import static org.junit.Assert.assertEquals; public abstract class AbstractConfigHandlerTest { @@ -35,7 +35,6 @@ protected void validateRunConfiguration(RunImageConfiguration runConfig) { assertEquals(a("redis"), runConfig.getLinks()); assertEquals((Long) 1L, runConfig.getMemory()); assertEquals((Long) 1L, runConfig.getMemorySwap()); - assertEquals(getRunNamingStrategy(), runConfig.getNamingStrategy()); assertEquals(getEnvPropertyFile(),runConfig.getEnvPropertyFile()); assertEquals("/tmp/props.txt", runConfig.getPortPropertyFile()); diff --git a/src/test/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeConfigHandlerTest.java b/src/test/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeConfigHandlerTest.java index 001c63544..a823f5d6b 100644 --- a/src/test/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeConfigHandlerTest.java +++ b/src/test/java/io/fabric8/maven/docker/config/handler/compose/DockerComposeConfigHandlerTest.java @@ -1,15 +1,5 @@ package io.fabric8.maven.docker.config.handler.compose; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import io.fabric8.maven.docker.config.ImageConfiguration; import io.fabric8.maven.docker.config.NetworkConfig; import io.fabric8.maven.docker.config.RestartPolicy; @@ -28,6 +18,16 @@ import org.junit.Before; import org.junit.Test; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -162,7 +162,6 @@ void validateRunConfiguration(RunImageConfiguration runConfig) { assertEquals(a("redis","link1"), runConfig.getLinks()); assertEquals((Long) 1L, runConfig.getMemory()); assertEquals((Long) 1L, runConfig.getMemorySwap()); - assertEquals(RunImageConfiguration.NamingStrategy.none, runConfig.getNamingStrategy()); assertEquals(null,runConfig.getEnvPropertyFile()); assertEquals(null, runConfig.getPortPropertyFile()); diff --git a/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java b/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java index d0220180d..48a2dc7e5 100644 --- a/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java +++ b/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java @@ -15,20 +15,41 @@ * limitations under the License. */ -import java.io.File; -import java.util.*; - -import io.fabric8.maven.docker.config.*; +import io.fabric8.maven.docker.config.AssemblyConfiguration; +import io.fabric8.maven.docker.config.BuildImageConfiguration; +import io.fabric8.maven.docker.config.CleanupMode; +import io.fabric8.maven.docker.config.ConfigHelper; +import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.config.LogConfiguration; +import io.fabric8.maven.docker.config.RestartPolicy; +import io.fabric8.maven.docker.config.RunImageConfiguration; +import io.fabric8.maven.docker.config.UlimitConfig; +import io.fabric8.maven.docker.config.WaitConfiguration; import io.fabric8.maven.docker.config.handler.AbstractConfigHandlerTest; import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit4.JMockit; import org.apache.maven.project.MavenProject; -import org.junit.*; +import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; -import static io.fabric8.maven.docker.config.BuildImageConfiguration.*; -import static org.junit.Assert.*; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static io.fabric8.maven.docker.config.BuildImageConfiguration.DEFAULT_CLEANUP; +import static io.fabric8.maven.docker.config.BuildImageConfiguration.DEFAULT_FILTER; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; /** * @author roland @@ -590,14 +611,6 @@ public void testAssembly() throws Exception { assertTrue(config.isIgnorePermissions()); } - @Test - public void testNamingScheme() throws Exception { - String[] testData = new String[] {k(ConfigKey.NAME), "image", k(ConfigKey.NAMING_STRATEGY), RunImageConfiguration.NamingStrategy.alias.toString() }; - - ImageConfiguration config = resolveExternalImageConfig(testData); - assertEquals(RunImageConfiguration.NamingStrategy.alias, config.getRunConfiguration().getNamingStrategy()); - } - @Test public void testNoCleanup() throws Exception { String[] testData = new String[] {k(ConfigKey.NAME), "image", k(ConfigKey.CLEANUP), "none", k(ConfigKey.FROM), "base" }; @@ -892,7 +905,6 @@ protected void validateRunConfiguration(RunImageConfiguration runConfig) { assertEquals(a("redis"), runConfig.getLinks()); assertEquals((Long) 1L, runConfig.getMemory()); assertEquals((Long) 1L, runConfig.getMemorySwap()); - Assert.assertEquals(RunImageConfiguration.NamingStrategy.none, runConfig.getNamingStrategy()); assertEquals("/tmp/envProps.txt",runConfig.getEnvPropertyFile()); assertEquals("/tmp/props.txt", runConfig.getPortPropertyFile()); assertEquals(a("8081:8080"), runConfig.getPorts()); From fab0ceebc802452033f40e46e1125719f6a7b030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Wed, 16 May 2018 12:15:17 -0700 Subject: [PATCH 2/8] chore: Rebased and added missing import --- .../config/handler/property/PropertyConfigHandlerTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java b/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java index 48a2dc7e5..cca0b626a 100644 --- a/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java +++ b/src/test/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandlerTest.java @@ -15,6 +15,7 @@ * limitations under the License. */ +import io.fabric8.maven.docker.config.Arguments; import io.fabric8.maven.docker.config.AssemblyConfiguration; import io.fabric8.maven.docker.config.BuildImageConfiguration; import io.fabric8.maven.docker.config.CleanupMode; From e4b1f8ce9940ca4689e79e1981a2061bc1e67f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Wed, 16 May 2018 14:25:32 -0700 Subject: [PATCH 3/8] chore: Refactored a bit * Moved most logic into an utility class with a single static method * Moved calculation of the names to the service layer, Mojos should stay as slim as possible. * Reused already existing "FormatParameterReplacer" (and introduced lambda for the first time) --- .../io/fabric8/maven/docker/StartMojo.java | 6 +- .../io/fabric8/maven/docker/StopMojo.java | 17 +- .../docker/config/ImageConfiguration.java | 5 +- .../docker/config/NamingConfiguration.java | 178 ------------------ .../docker/config/RunImageConfiguration.java | 16 +- .../maven/docker/service/QueryService.java | 43 +++++ .../maven/docker/service/RunService.java | 11 +- .../maven/docker/service/WatchService.java | 19 +- .../docker/util/ContainerNamingUtil.java | 104 ++++++++++ .../config/NamingConfigurationTest.java | 86 --------- .../docker/util/ContainerNamingUtilTest.java | 119 ++++++++++++ 11 files changed, 303 insertions(+), 301 deletions(-) delete mode 100644 src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java create mode 100644 src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java delete mode 100644 src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java create mode 100644 src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java diff --git a/src/main/java/io/fabric8/maven/docker/StartMojo.java b/src/main/java/io/fabric8/maven/docker/StartMojo.java index b6a5319fa..0f56ef5f4 100644 --- a/src/main/java/io/fabric8/maven/docker/StartMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StartMojo.java @@ -12,7 +12,7 @@ import io.fabric8.maven.docker.config.ConfigHelper; import io.fabric8.maven.docker.config.ImageConfiguration; import io.fabric8.maven.docker.config.LogConfiguration; -import io.fabric8.maven.docker.config.NamingConfiguration; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.config.NetworkConfig; import io.fabric8.maven.docker.config.RunImageConfiguration; import io.fabric8.maven.docker.config.WaitConfiguration; @@ -270,15 +270,13 @@ private void startImage(final ImageConfiguration image, final QueryService queryService = hub.getQueryService(); final Properties projProperties = project.getProperties(); final RunImageConfiguration runConfig = image.getRunConfiguration(); - final NamingConfiguration namingConfiguration = image.calculateNamingConfiguration(getBuildTimestamp(), - queryService.getContainersForImage(image.getName())); final PortMapping portMapping = runService.createPortMapping(runConfig, projProperties); final LogDispatcher dispatcher = getLogDispatcher(hub); startingContainers.submit(new Callable() { @Override public StartedContainer call() throws Exception { - final String containerId = runService.createAndStartContainer(image, namingConfiguration, portMapping, getPomLabel(), projProperties, project.getBasedir()); + final String containerId = runService.createAndStartContainer(image, portMapping, getPomLabel(), projProperties, project.getBasedir(), getBuildTimestamp()); // Update port-mapping writer portMappingPropertyWriteHelper.add(portMapping, runConfig.getPortPropertyFile()); diff --git a/src/main/java/io/fabric8/maven/docker/StopMojo.java b/src/main/java/io/fabric8/maven/docker/StopMojo.java index 2f9b5ea17..ce12e1465 100644 --- a/src/main/java/io/fabric8/maven/docker/StopMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StopMojo.java @@ -3,7 +3,7 @@ import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.access.ExecException; import io.fabric8.maven.docker.config.ImageConfiguration; -import io.fabric8.maven.docker.config.NamingConfiguration; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.config.NetworkConfig; import io.fabric8.maven.docker.log.LogDispatcher; import io.fabric8.maven.docker.model.Container; @@ -76,11 +76,10 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do private void stopContainers(QueryService queryService, RunService runService, PomLabel pomLabel) throws DockerAccessException, ExecException, MojoExecutionException { Collection networksToRemove = getNetworksToRemove(queryService, pomLabel); for (ImageConfiguration image : getResolvedImages()) { - final NamingConfiguration namingConfiguration = image.calculateNamingConfiguration(getBuildTimestamp(), - queryService.getContainersForImage(image.getName())); for (Container container : queryService.getContainersForImage(image.getName())) { - if (shouldStopContainer(container, namingConfiguration)) { + String lastContainerName = queryService.calculateLastContainerName(image, getBuildTimestamp(), null); + if (shouldStopContainer(container, lastContainerName)) { runService.stopContainer(container.getId(), image, keepContainer, removeVolumes); } } @@ -88,12 +87,11 @@ private void stopContainers(QueryService queryService, RunService runService, Po runService.removeCustomNetworks(networksToRemove); } - private boolean shouldStopContainer(Container container, NamingConfiguration namingConfiguration) { + private boolean shouldStopContainer(Container container, String lastContainerName) { if (isStopAllContainers()) { return true; } - - return container.getName().equals(namingConfiguration.calculateLastContainerName()); + return container.getName().equals(lastContainerName); } private boolean isStopAllContainers() { @@ -113,8 +111,6 @@ private Set getNetworksToRemove(QueryService queryService, PomLabel pom Set networks = queryService.getNetworks(); for (ImageConfiguration image : getResolvedImages()) { - final NamingConfiguration namingConfiguration = image.calculateNamingConfiguration(getBuildTimestamp(), - queryService.getContainersForImage(image.getName())); final NetworkConfig config = image.getRunConfiguration().getNetworkingConfig(); if (config.isCustomNetwork()) { @@ -122,7 +118,8 @@ private Set getNetworksToRemove(QueryService queryService, PomLabel pom if (network != null) { customNetworks.add(network); for (Container container : queryService.getContainersForImage(image.getName())) { - if (!shouldStopContainer(container, namingConfiguration)) { + String lastContainerName = queryService.calculateLastContainerName(image, getBuildTimestamp(), null); + if (!shouldStopContainer(container, lastContainerName)) { // it's sill in use don't collect it customNetworks.remove(network); } diff --git a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java index 2e521ddba..bf728c05c 100644 --- a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java @@ -1,6 +1,7 @@ package io.fabric8.maven.docker.config; import io.fabric8.maven.docker.model.Container; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.util.DeepCopy; import io.fabric8.maven.docker.util.EnvUtil; import io.fabric8.maven.docker.util.ImageName; @@ -151,10 +152,6 @@ public String getRegistry() { return registry; } - public NamingConfiguration calculateNamingConfiguration(final Date buildTimestamp, final List existingContainers) { - return getRunConfiguration().calcualteNamingConfiguration(buildTimestamp, existingContainers, getName(), getAlias()); - } - @Override public String toString() { return String.format("ImageConfiguration {name='%s', alias='%s'}", name, alias); diff --git a/src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java deleted file mode 100644 index 051289889..000000000 --- a/src/main/java/io/fabric8/maven/docker/config/NamingConfiguration.java +++ /dev/null @@ -1,178 +0,0 @@ -package io.fabric8.maven.docker.config; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableSet; -import io.fabric8.maven.docker.model.Container; -import io.fabric8.maven.docker.util.ImageName; -import org.apache.commons.lang3.StringUtils; - -import java.util.Collection; -import java.util.Date; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.util.regex.Matcher.quoteReplacement; - -/** - * @author marcus - * @since 1.0.0 - */ -public class NamingConfiguration { - - private static final String DEFAULT_NAMING_CONFIGURATION = "%n-%i"; - - private static final String ALIAS_PLACEHOLDER = "%a"; - private static final String IMAGE_NAME_PLACEHOLDER = "%n"; - private static final String TIMESTAMP_PLACEHOLDER = "%t"; - private static final String INDEX_PLACEHOLDER = "%i"; - - private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("(" + - Joiner.on('|').join( - ALIAS_PLACEHOLDER, - IMAGE_NAME_PLACEHOLDER, - TIMESTAMP_PLACEHOLDER, - INDEX_PLACEHOLDER) - + ")"); - - - private final String containerNamePattern; - private final String imageName; - private final String nameAlias; - private final String buildTimestamp; - private final Set existingContainerNames; - - @VisibleForTesting - NamingConfiguration(final String containerNamePattern, - final String imageName, - final String nameAlias, - final String buildTimestamp, - final Set existingContainerNames) { - this.containerNamePattern = containerNamePattern; - this.imageName = imageName; - this.nameAlias = nameAlias; - this.buildTimestamp = buildTimestamp; - this.existingContainerNames = existingContainerNames; - } - - public static NamingConfiguration create(final String containerNamePattern, - final RunImageConfiguration.NamingStrategy namingStrategy, - final String imageName, - final String nameAlias, - final Date buildTimestamp, - final Collection existingContainers) { - - final String timestamp = String.valueOf(buildTimestamp.getTime()); - final Set containerNames = extractContainerNames(existingContainers); - - // explicitly set containerNamePattern wins - if (StringUtils.isNotBlank(containerNamePattern)) { - return new NamingConfiguration(containerNamePattern, imageName, nameAlias, timestamp, containerNames); - } - - // backward compatibility code - if (namingStrategy != null) { - switch (namingStrategy) { - case alias: - return new NamingConfiguration(ALIAS_PLACEHOLDER, imageName, nameAlias, timestamp, containerNames); - case none: - return new NamingConfiguration(DEFAULT_NAMING_CONFIGURATION, imageName, nameAlias, timestamp, containerNames); - default: - throw new IllegalStateException("Naming strategy " + namingStrategy + "not covered in comptatibility code"); - } - } - - return new NamingConfiguration(DEFAULT_NAMING_CONFIGURATION, imageName, nameAlias, timestamp, containerNames); - } - - private static Set extractContainerNames(final Collection existingContainers) { - final ImmutableSet.Builder containerNamesBuilder = ImmutableSet.builder(); - for (final Container container : existingContainers) { - containerNamesBuilder.add(container.getName()); - } - return containerNamesBuilder.build(); - } - - public String calculateContainerName() { - final String partiallyApplied = calculateWithoutIndex(); - - if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { - return applyIndexReplacement(partiallyApplied); - } else { - return partiallyApplied; - } - } - - private String calculateWithoutIndex() { - final Matcher matcher = PLACEHOLDER_PATTERN.matcher(containerNamePattern); - final StringBuffer sb = new StringBuffer(); - - while (matcher.find()) { - final String placeholder = matcher.group(1); - - switch (placeholder) { - case ALIAS_PLACEHOLDER: - matcher.appendReplacement(sb, quoteReplacement(nameAlias)); - break; - case IMAGE_NAME_PLACEHOLDER: - matcher.appendReplacement(sb, quoteReplacement(cleanImageName(imageName))); - break; - case TIMESTAMP_PLACEHOLDER: - matcher.appendReplacement(sb, quoteReplacement(buildTimestamp)); - break; - case INDEX_PLACEHOLDER: - // noop, we apply the index later - matcher.appendReplacement(sb, quoteReplacement(INDEX_PLACEHOLDER)); - break; - default: - throw new IllegalArgumentException("Matched an unexpected placeholder " + placeholder); - } - } - matcher.appendTail(sb); - - return sb.toString(); - } - - private String applyIndexReplacement(final String partiallyApplied) { - for (long i = 1; i < Long.MAX_VALUE; i++) { - final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); - if (!existingContainerNames.contains(withIndexApplied)) { - return withIndexApplied; - } - } - throw new IllegalStateException("Could not find any free container name for pattern " + partiallyApplied); - } - - private String applyLastIndex(final String partiallyApplied) { - String last = null; - for (long i = 1; i < Long.MAX_VALUE; i++) { - final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); - if (last == null) { - last = withIndexApplied; - } - - if (!existingContainerNames.contains(withIndexApplied)) { - return last; - } else { - last = withIndexApplied; - } - } - - return last; - } - - private String cleanImageName(final String imageName) { - return new ImageName(imageName).getSimpleName().replaceAll("[^a-zA-Z0-9_.-]+", "_"); - } - - public String calculateLastContainerName() { - final String partiallyApplied = calculateWithoutIndex(); - - if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { - return applyLastIndex(partiallyApplied); - } else { - return partiallyApplied; - } - } -} diff --git a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java index bf2644695..aa7bac39f 100644 --- a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java @@ -1,6 +1,7 @@ package io.fabric8.maven.docker.config; import io.fabric8.maven.docker.model.Container; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.util.DeepCopy; import io.fabric8.maven.docker.util.EnvUtil; import org.apache.maven.plugins.annotations.Parameter; @@ -328,13 +329,6 @@ public enum NamingStrategy { alias } - public NamingConfiguration calcualteNamingConfiguration(final Date buildTimestamp, - final Collection existingContainers, - final String imageName, - final String nameAlias) { - return NamingConfiguration.create(containerNamePattern, namingStrategy, imageName, nameAlias, buildTimestamp, existingContainers); - } - public NamingStrategy getNamingStrategyRaw() { return namingStrategy; } @@ -367,6 +361,14 @@ public String getImagePullPolicy() { return imagePullPolicy; } + public String getContainerNamePattern() { + return containerNamePattern; + } + + public NamingStrategy getNamingStrategy() { + return namingStrategy; + } + // ====================================================================================== public static class Builder { diff --git a/src/main/java/io/fabric8/maven/docker/service/QueryService.java b/src/main/java/io/fabric8/maven/docker/service/QueryService.java index 45f5dff1d..1f73051ea 100644 --- a/src/main/java/io/fabric8/maven/docker/service/QueryService.java +++ b/src/main/java/io/fabric8/maven/docker/service/QueryService.java @@ -1,10 +1,16 @@ package io.fabric8.maven.docker.service; +import java.util.Collection; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import com.google.common.collect.ImmutableSet; import io.fabric8.maven.docker.access.DockerAccess; +import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.util.ContainerNamingUtil; +import io.fabric8.maven.docker.config.RunImageConfiguration; import io.fabric8.maven.docker.model.Container; import io.fabric8.maven.docker.model.Network; import io.fabric8.maven.docker.access.DockerAccessException; @@ -86,6 +92,18 @@ public String getContainerName(String containerId) throws DockerAccessException return getMandatoryContainer(containerId).getName(); } + public String calculateContainerName(ImageConfiguration image, Date timestamp, String defaultContainerNamePattern) throws DockerAccessException { + String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern); + Collection existingContainers = getContainersForImage(image.getName()); + return ContainerNamingUtil.calculateContainerName(containerNamePattern, image.getName(), image.getAlias(), timestamp, extractContainerNames(existingContainers)); + } + + public String calculateLastContainerName(ImageConfiguration image, Date timestamp, String defaultContainerNamePattern) throws DockerAccessException { + String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern); + Collection existingContainers = getContainersForImage(image.getName()); + return ContainerNamingUtil.calculateLastContainerName(containerNamePattern, image.getName(), image.getAlias(), timestamp, extractContainerNames(existingContainers)); + } + /** * Get all containers which are build from an image. By default only the last containers are considered but this * can be tuned with a global parameters. @@ -167,4 +185,29 @@ public boolean hasNetwork(String networkName) throws DockerAccessException { public boolean hasImage(String name) throws DockerAccessException { return docker.hasImage(name); } + + + // ============================================================================== + + private Set extractContainerNames(final Collection existingContainers) { + final ImmutableSet.Builder containerNamesBuilder = ImmutableSet.builder(); + for (final Container container : existingContainers) { + containerNamesBuilder.add(container.getName()); + } + return containerNamesBuilder.build(); + } + + private String extractContainerNamePattern(ImageConfiguration image, String defaultContainerNamePattern) { + RunImageConfiguration runConfig = image.getRunConfiguration(); + if (runConfig != null) { + if (runConfig.getContainerNamePattern() != null) { + return runConfig.getContainerNamePattern(); + } + if (runConfig.getNamingStrategy() == RunImageConfiguration.NamingStrategy.alias) { + return "%a"; + } + } + return defaultContainerNamePattern; + } + } diff --git a/src/main/java/io/fabric8/maven/docker/service/RunService.java b/src/main/java/io/fabric8/maven/docker/service/RunService.java index 3c1e0b64e..cea851a87 100644 --- a/src/main/java/io/fabric8/maven/docker/service/RunService.java +++ b/src/main/java/io/fabric8/maven/docker/service/RunService.java @@ -27,7 +27,6 @@ import io.fabric8.maven.docker.access.PortMapping; import io.fabric8.maven.docker.config.Arguments; import io.fabric8.maven.docker.config.ImageConfiguration; -import io.fabric8.maven.docker.config.NamingConfiguration; import io.fabric8.maven.docker.config.NetworkConfig; import io.fabric8.maven.docker.config.RestartPolicy; import io.fabric8.maven.docker.config.RunImageConfiguration; @@ -48,6 +47,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -122,22 +122,23 @@ public String execInContainer(String containerId, String command, ImageConfigura * Create and start a container with the given image configuration. * @param imageConfig image configuration holding the run information and the image name * @param portMapping container port mapping - * @param mavenProps properties to fill in with dynamically assigned ports * @param pomLabel label to tag the started container with * + * @param mavenProps properties to fill in with dynamically assigned ports + * @param buildTimestamp date which should be used as the timestamp when calculating container names * @return the container id * * @throws DockerAccessException if access to the docker backend fails */ public String createAndStartContainer(ImageConfiguration imageConfig, - NamingConfiguration namingConfiguration, PortMapping portMapping, PomLabel pomLabel, Properties mavenProps, - File baseDir) throws DockerAccessException { + File baseDir, + Date buildTimestamp) throws DockerAccessException { RunImageConfiguration runConfig = imageConfig.getRunConfiguration(); String imageName = imageConfig.getName(); - String containerName = namingConfiguration.calculateContainerName(); + String containerName = queryService.calculateContainerName(imageConfig, buildTimestamp, null); ContainerCreateConfig config = createContainerConfig(imageName, runConfig, portMapping, pomLabel, mavenProps, baseDir); String id = docker.createContainer(config, containerName); diff --git a/src/main/java/io/fabric8/maven/docker/service/WatchService.java b/src/main/java/io/fabric8/maven/docker/service/WatchService.java index 3e0afd1de..d2a30c360 100644 --- a/src/main/java/io/fabric8/maven/docker/service/WatchService.java +++ b/src/main/java/io/fabric8/maven/docker/service/WatchService.java @@ -6,7 +6,7 @@ import io.fabric8.maven.docker.access.PortMapping; import io.fabric8.maven.docker.assembly.AssemblyFiles; import io.fabric8.maven.docker.config.ImageConfiguration; -import io.fabric8.maven.docker.config.NamingConfiguration; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.config.WatchImageConfiguration; import io.fabric8.maven.docker.config.WatchMode; import io.fabric8.maven.docker.util.Logger; @@ -16,6 +16,7 @@ import io.fabric8.maven.docker.util.Task; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.StringUtils; import java.io.File; @@ -236,13 +237,17 @@ public void execute(ImageWatcher watcher) throws Exception { } runService.stopPreviouslyStartedContainer(id, false, false); - final NamingConfiguration namingConfiguration = imageConfig.calculateNamingConfiguration(watcher.getWatchContext().getBuildTimestamp(), - queryService.getContainersForImage(imageConfig.getName())); - // Start new one - watcher.setContainerId(runService.createAndStartContainer(imageConfig, namingConfiguration, mappedPorts, watcher.getWatchContext().getPomLabel(), - watcher.getWatchContext().getMojoParameters().getProject().getProperties(), - watcher.getWatchContext().getMojoParameters().getProject().getBasedir())); + WatchContext ctx = watcher.getWatchContext(); + MavenProject project = ctx.getMojoParameters().getProject(); + watcher.setContainerId( + runService.createAndStartContainer( + imageConfig, + mappedPorts, + ctx.getPomLabel(), + project.getProperties(), + project.getBasedir(), + ctx.getBuildTimestamp())); } }; } diff --git a/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java b/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java new file mode 100644 index 000000000..1d8c44590 --- /dev/null +++ b/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java @@ -0,0 +1,104 @@ +package io.fabric8.maven.docker.util; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import com.google.common.base.Joiner; + +/** + * @author marcus + * @since 1.0.0 + */ +public class ContainerNamingUtil { + + private static final String DEFAULT_NAMING_CONFIGURATION = "%n-%i"; + + private static final String INDEX_PLACEHOLDER = "%i"; + + public static String calculateContainerName(final String containerNamePattern, + final String imageName, + final String nameAlias, + final Date buildTimestamp, + final Set existingContainers) { + + final String partiallyApplied = + replacePlaceholders( + containerNamePattern != null ? containerNamePattern : DEFAULT_NAMING_CONFIGURATION, + imageName, + nameAlias, + buildTimestamp); + + + if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { + for (long i = 1; i < Long.MAX_VALUE; i++) { + final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); + if (!existingContainers.contains(withIndexApplied)) { + return withIndexApplied; + } + } + throw new IllegalStateException("Could not find any free container name for pattern " + partiallyApplied); + } else { + return partiallyApplied; + } + } + + public static String calculateLastContainerName(final String containerNamePattern, + final String imageName, + final String nameAlias, + final Date buildTimestamp, + final Set existingContainers) { + + final String partiallyApplied = + replacePlaceholders(containerNamePattern, + imageName, + nameAlias, + buildTimestamp); + + if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { + return applyLastIndex(existingContainers, partiallyApplied); + } else { + return partiallyApplied; + } + } + + // ======================================================================================================== + + private static String replacePlaceholders(String containerNamePattern, String imageName, String nameAlias, Date buildTimestamp) { + + Map lookups = new HashMap<>(); + + lookups.put("a", () -> nameAlias); + lookups.put("n", () -> cleanImageName(imageName)); + lookups.put("t", () -> String.valueOf(buildTimestamp.getTime())); + lookups.put("i", () -> INDEX_PLACEHOLDER); + + return new FormatParameterReplacer(lookups).replace(containerNamePattern); + } + + + private static String applyLastIndex(Set existingContainerNames, final String partiallyApplied) { + + String last = null; + for (long i = 1; i < Long.MAX_VALUE; i++) { + final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); + if (last == null) { + last = withIndexApplied; + } + + if (!existingContainerNames.contains(withIndexApplied)) { + return last; + } else { + last = withIndexApplied; + } + } + + return last; + } + + private static String cleanImageName(final String imageName) { + return new ImageName(imageName).getSimpleName().replaceAll("[^a-zA-Z0-9_.-]+", "_"); + } +} diff --git a/src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java b/src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java deleted file mode 100644 index 06936dbb7..000000000 --- a/src/test/java/io/fabric8/maven/docker/config/NamingConfigurationTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.fabric8.maven.docker.config; - -import com.google.common.collect.ImmutableSet; -import io.fabric8.maven.docker.model.Container; -import org.junit.Assert; -import org.junit.Test; - -import java.util.Collections; -import java.util.Date; - -/** - * @author marcus - * @since 1.0.0 - */ -public class NamingConfigurationTest { - - @Test - public void testDefault() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create(null, null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("jolokia_demo-1", namingConfiguration.calculateContainerName()); - } - - @Test - public void testBackwardCompatibilityAlias() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create(null, RunImageConfiguration.NamingStrategy.alias, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("nameAlias", namingConfiguration.calculateContainerName()); - } - - @Test - public void testBackwardCompatibilityNone() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create(null, RunImageConfiguration.NamingStrategy.none, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("jolokia_demo-1", namingConfiguration.calculateContainerName()); - } - - @Test - public void testAlias() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create("%a", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("nameAlias", namingConfiguration.calculateContainerName()); - } - - @Test - public void testTimestamp() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create("%t", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("123456", namingConfiguration.calculateContainerName()); - } - - @Test - public void testImageName() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create("%n", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("jolokia_demo", namingConfiguration.calculateContainerName()); - } - - @Test - public void testIndex() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create("%i", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("1", namingConfiguration.calculateContainerName()); - } - - @Test - public void testAll() { - final NamingConfiguration namingConfiguration = NamingConfiguration.create("prefix-%i-%a-%n-%t-postfix", null, "jolokia/jolokia_demo", "nameAlias", new Date(123456), Collections.emptyList()); - Assert.assertEquals("prefix-1-nameAlias-jolokia_demo-123456-postfix", namingConfiguration.calculateContainerName()); - } - - @Test - public void testIndexAdvanced() { - final NamingConfiguration namingConfiguration = new NamingConfiguration("container-%i", "jolokia/jolokia_demo", "nameAlias", "123456", - Collections.singleton("container-1")); - Assert.assertEquals("container-2", namingConfiguration.calculateContainerName()); - } - - @Test - public void testCalculateLastContainerName() { - final NamingConfiguration namingConfiguration = new NamingConfiguration("container-%i", "jolokia/jolokia_demo", "nameAlias", "123456", - Collections.singleton("container-1")); - Assert.assertEquals("container-1", namingConfiguration.calculateLastContainerName()); - } - - @Test - public void testCalculateLastContainerNameSecond() { - final NamingConfiguration namingConfiguration = new NamingConfiguration("container-%i", "jolokia/jolokia_demo", "nameAlias", "123456", - ImmutableSet.of("container-1", "container-2")); - Assert.assertEquals("container-2", namingConfiguration.calculateLastContainerName()); - } - -} diff --git a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java new file mode 100644 index 000000000..04721707e --- /dev/null +++ b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java @@ -0,0 +1,119 @@ +package io.fabric8.maven.docker.util; + +import com.google.common.collect.ImmutableSet; +import io.fabric8.maven.docker.config.RunImageConfiguration; +import io.fabric8.maven.docker.model.Container; +import io.fabric8.maven.docker.util.ContainerNamingUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; +import java.util.Date; + +/** + * @author marcus + * @since 1.0.0 + */ +public class ContainerNamingUtilTest { + + @Test + public void testDefault() { + Assert.assertEquals("jolokia_demo-1", + ContainerNamingUtil.calculateContainerName( + null, + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.emptySet())); + } + + @Test + public void testAlias() { + Assert.assertEquals("nameAlias", + ContainerNamingUtil.calculateContainerName( + "%a", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.emptySet())); + } + + @Test + public void testTimestamp() { + Assert.assertEquals("123456", + ContainerNamingUtil.calculateContainerName( + "%t", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.emptySet())); + } + + @Test + public void testImageName() { + Assert.assertEquals("jolokia_demo", + ContainerNamingUtil.calculateContainerName( + "%n", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.emptySet())); + } + + @Test + public void testIndex() { + Assert.assertEquals("1", + ContainerNamingUtil.calculateContainerName( + "%i", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.emptySet())); + } + + @Test + public void testAll() { + Assert.assertEquals("prefix-1-nameAlias-jolokia_demo-123456-postfix", + ContainerNamingUtil.calculateContainerName( + "prefix-%i-%a-%n-%t-postfix", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.emptySet())); + } + + @Test + public void testIndexAdvanced() { + Assert.assertEquals("container-2", + ContainerNamingUtil.calculateContainerName( + "container-%i", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.singleton("container-1"))); + } + + @Test + public void testCalculateLastContainerName() { + Assert.assertEquals("container-1", + ContainerNamingUtil.calculateLastContainerName( + "container-%i", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + Collections.singleton("container-1"))); + } + + @Test + public void testCalculateLastContainerNameSecond() { + Assert.assertEquals("container-2", + ContainerNamingUtil.calculateLastContainerName( + "container-%i", + "jolokia/jolokia_demo", + "nameAlias", + new Date(123456), + ImmutableSet.of("container-1","container-2"))); + + } + +} From 2dd64c7a5891ac98bd7a70a7eb1ae78b7a127ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Sun, 23 Sep 2018 18:52:58 +0200 Subject: [PATCH 4/8] chore: pepperbot fixes --- .../io/fabric8/maven/docker/StartMojo.java | 48 ++++++++++--------- .../io/fabric8/maven/docker/StopMojo.java | 39 ++++++++------- .../docker/config/ImageConfiguration.java | 13 ++--- .../docker/config/RunImageConfiguration.java | 31 +++++++----- .../property/PropertyConfigHandler.java | 2 +- .../maven/docker/service/WatchService.java | 23 +++++---- .../docker/util/ContainerNamingUtil.java | 6 +-- 7 files changed, 82 insertions(+), 80 deletions(-) diff --git a/src/main/java/io/fabric8/maven/docker/StartMojo.java b/src/main/java/io/fabric8/maven/docker/StartMojo.java index 0f56ef5f4..6694f9bab 100644 --- a/src/main/java/io/fabric8/maven/docker/StartMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StartMojo.java @@ -8,27 +8,6 @@ * the License. */ -import io.fabric8.maven.docker.access.*; -import io.fabric8.maven.docker.config.ConfigHelper; -import io.fabric8.maven.docker.config.ImageConfiguration; -import io.fabric8.maven.docker.config.LogConfiguration; -import io.fabric8.maven.docker.util.ContainerNamingUtil; -import io.fabric8.maven.docker.config.NetworkConfig; -import io.fabric8.maven.docker.config.RunImageConfiguration; -import io.fabric8.maven.docker.config.WaitConfiguration; -import io.fabric8.maven.docker.log.LogDispatcher; -import io.fabric8.maven.docker.model.Container; -import io.fabric8.maven.docker.service.ImagePullManager; -import io.fabric8.maven.docker.service.QueryService; -import io.fabric8.maven.docker.service.RegistryService; -import io.fabric8.maven.docker.service.RunService; -import io.fabric8.maven.docker.service.ServiceHub; -import io.fabric8.maven.docker.util.StartOrderResolver; -import com.google.common.util.concurrent.MoreExecutors; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.*; -import org.codehaus.plexus.util.StringUtils; - import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; @@ -46,6 +25,30 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import com.google.common.util.concurrent.MoreExecutors; +import io.fabric8.maven.docker.access.DockerAccessException; +import io.fabric8.maven.docker.access.ExecException; +import io.fabric8.maven.docker.access.PortMapping; +import io.fabric8.maven.docker.config.ConfigHelper; +import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.config.LogConfiguration; +import io.fabric8.maven.docker.config.NetworkConfig; +import io.fabric8.maven.docker.config.RunImageConfiguration; +import io.fabric8.maven.docker.config.WaitConfiguration; +import io.fabric8.maven.docker.log.LogDispatcher; +import io.fabric8.maven.docker.model.Container; +import io.fabric8.maven.docker.service.ImagePullManager; +import io.fabric8.maven.docker.service.QueryService; +import io.fabric8.maven.docker.service.RegistryService; +import io.fabric8.maven.docker.service.RunService; +import io.fabric8.maven.docker.service.ServiceHub; +import io.fabric8.maven.docker.util.StartOrderResolver; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.codehaus.plexus.util.StringUtils; + /** * Goal for creating and starting a docker container. This goal evaluates the image configuration @@ -264,10 +267,9 @@ private void updateAliasesSet(Set aliasesSet, String alias) { private void startImage(final ImageConfiguration image, final ServiceHub hub, final ExecutorCompletionService startingContainers, - final PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper) throws MojoExecutionException, DockerAccessException { + final PortMapping.PropertyWriteHelper portMappingPropertyWriteHelper) { final RunService runService = hub.getRunService(); - final QueryService queryService = hub.getQueryService(); final Properties projProperties = project.getProperties(); final RunImageConfiguration runConfig = image.getRunConfiguration(); final PortMapping portMapping = runService.createPortMapping(runConfig, projProperties); diff --git a/src/main/java/io/fabric8/maven/docker/StopMojo.java b/src/main/java/io/fabric8/maven/docker/StopMojo.java index ce12e1465..d6dbe3f67 100644 --- a/src/main/java/io/fabric8/maven/docker/StopMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StopMojo.java @@ -1,9 +1,13 @@ package io.fabric8.maven.docker; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.access.ExecException; import io.fabric8.maven.docker.config.ImageConfiguration; -import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.config.NetworkConfig; import io.fabric8.maven.docker.log.LogDispatcher; import io.fabric8.maven.docker.model.Container; @@ -17,11 +21,6 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - /** * Mojo for stopping containers. If called together with docker:start (i.e. * when configured for integration testing in a lifefcycle phase), then only the container @@ -64,7 +63,7 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do if (invokedTogetherWithDockerStart()) { runService.stopStartedContainers(keepContainer, removeVolumes, autoCreateCustomNetworks, pomLabel); } else { - stopContainers(queryService, runService, pomLabel); + stopContainers(queryService, runService); } } @@ -73,8 +72,8 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do dispatcher.untrackAllContainerLogs(); } - private void stopContainers(QueryService queryService, RunService runService, PomLabel pomLabel) throws DockerAccessException, ExecException, MojoExecutionException { - Collection networksToRemove = getNetworksToRemove(queryService, pomLabel); + private void stopContainers(QueryService queryService, RunService runService) throws DockerAccessException, ExecException, MojoExecutionException { + Collection networksToRemove = getNetworksToRemove(queryService); for (ImageConfiguration image : getResolvedImages()) { for (Container container : queryService.getContainersForImage(image.getName())) { @@ -103,7 +102,7 @@ private boolean invokedTogetherWithDockerStart() { return startCalled != null && startCalled; } - private Set getNetworksToRemove(QueryService queryService, PomLabel pomLabel) throws DockerAccessException, MojoExecutionException { + private Set getNetworksToRemove(QueryService queryService) throws DockerAccessException, MojoExecutionException { if (!autoCreateCustomNetworks) { return Collections.emptySet(); } @@ -113,22 +112,22 @@ private Set getNetworksToRemove(QueryService queryService, PomLabel pom for (ImageConfiguration image : getResolvedImages()) { final NetworkConfig config = image.getRunConfiguration().getNetworkingConfig(); - if (config.isCustomNetwork()) { - Network network = getNetworkByName(networks, config.getCustomNetwork()); - if (network != null) { - customNetworks.add(network); - for (Container container : queryService.getContainersForImage(image.getName())) { - String lastContainerName = queryService.calculateLastContainerName(image, getBuildTimestamp(), null); - if (!shouldStopContainer(container, lastContainerName)) { - // it's sill in use don't collect it - customNetworks.remove(network); - } + final Network network = getNetworkByName(networks, config.getCustomNetwork()); + + if (config.isCustomNetwork() && network != null) { + customNetworks.add(network); + for (Container container : queryService.getContainersForImage(image.getName())) { + String lastContainerName = queryService.calculateLastContainerName(image, getBuildTimestamp(), null); + if (!shouldStopContainer(container, lastContainerName)) { + // it's sill in use don't collect it + customNetworks.remove(network); } } } } return customNetworks; } + private Network getNetworkByName(Set networks, String networkName) { for (Network network : networks) { if (networkName.equals(network.getName())) { diff --git a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java index bf728c05c..1ea2e1839 100644 --- a/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/ImageConfiguration.java @@ -1,7 +1,10 @@ package io.fabric8.maven.docker.config; -import io.fabric8.maven.docker.model.Container; -import io.fabric8.maven.docker.util.ContainerNamingUtil; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import io.fabric8.maven.docker.util.DeepCopy; import io.fabric8.maven.docker.util.EnvUtil; import io.fabric8.maven.docker.util.ImageName; @@ -9,12 +12,6 @@ import io.fabric8.maven.docker.util.StartOrderResolver; import org.apache.maven.plugins.annotations.Parameter; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; - /** * @author roland * @since 02.09.14 diff --git a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java index aa7bac39f..0926391ca 100644 --- a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java @@ -1,18 +1,15 @@ package io.fabric8.maven.docker.config; -import io.fabric8.maven.docker.model.Container; -import io.fabric8.maven.docker.util.ContainerNamingUtil; -import io.fabric8.maven.docker.util.DeepCopy; -import io.fabric8.maven.docker.util.EnvUtil; -import org.apache.maven.plugins.annotations.Parameter; - -import javax.annotation.Nonnull; import java.io.Serializable; -import java.util.Collection; -import java.util.Date; import java.util.List; import java.util.Map; +import javax.annotation.Nonnull; + +import io.fabric8.maven.docker.util.DeepCopy; +import io.fabric8.maven.docker.util.EnvUtil; +import org.apache.maven.plugins.annotations.Parameter; + /** * @author roland * @since 02.09.14 @@ -115,6 +112,9 @@ public boolean isDefault() { @Parameter private List ports; + /** + * @deprecated + */ @Parameter @Deprecated private NamingStrategy namingStrategy; @@ -316,6 +316,9 @@ public List getTmpfs() { return tmpfs; } + /** + * @deprecated + */ // Naming scheme for how to name container @Deprecated // for backward compatibility public enum NamingStrategy { @@ -329,10 +332,6 @@ public enum NamingStrategy { alias } - public NamingStrategy getNamingStrategyRaw() { - return namingStrategy; - } - public String getExposedPropertyKey() { return exposedPropertyKey; } @@ -540,6 +539,9 @@ public Builder log(LogConfiguration log) { return this; } + /** + * @deprecated + */ @Deprecated public Builder namingStrategy(String namingStrategy) { config.namingStrategy = namingStrategy == null ? @@ -548,6 +550,9 @@ public Builder namingStrategy(String namingStrategy) { return this; } + /** + * @deprecated + */ @Deprecated public Builder namingStrategy(NamingStrategy namingStrategy) { config.namingStrategy = namingStrategy; diff --git a/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java b/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java index 4f9fcd817..b4a9c70bd 100644 --- a/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java +++ b/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java @@ -188,7 +188,7 @@ private RunImageConfiguration extractRunConfiguration(ImageConfiguration fromCon .links(valueProvider.getList(LINKS, config == null ? null : config.getLinks())) .memory(valueProvider.getLong(MEMORY, config == null ? null : config.getMemory())) .memorySwap(valueProvider.getLong(MEMORY_SWAP, config == null ? null : config.getMemorySwap())) - .namingStrategy(valueProvider.getString(NAMING_STRATEGY, config == null || config.getNamingStrategyRaw() == null ? null : config.getNamingStrategyRaw().name())) + .namingStrategy(valueProvider.getString(NAMING_STRATEGY, config == null || config.getNamingStrategy() == null ? null : config.getNamingStrategy().name())) .exposedPropertyKey(valueProvider.getString(EXPOSED_PROPERTY_KEY, config == null ? null : config.getExposedPropertyKey())) .portPropertyFile(valueProvider.getString(PORT_PROPERTY_FILE, config == null ? null : config.getPortPropertyFile())) .ports(valueProvider.getList(PORTS, config == null ? null : config.getPorts())) diff --git a/src/main/java/io/fabric8/maven/docker/service/WatchService.java b/src/main/java/io/fabric8/maven/docker/service/WatchService.java index d2a30c360..ebcf4ad0d 100644 --- a/src/main/java/io/fabric8/maven/docker/service/WatchService.java +++ b/src/main/java/io/fabric8/maven/docker/service/WatchService.java @@ -1,12 +1,22 @@ package io.fabric8.maven.docker.service; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + import io.fabric8.maven.docker.access.DockerAccess; import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.access.ExecException; import io.fabric8.maven.docker.access.PortMapping; import io.fabric8.maven.docker.assembly.AssemblyFiles; import io.fabric8.maven.docker.config.ImageConfiguration; -import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.config.WatchImageConfiguration; import io.fabric8.maven.docker.config.WatchMode; import io.fabric8.maven.docker.util.Logger; @@ -19,17 +29,6 @@ import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.StringUtils; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - /** * Watch service for monitoring changes and restarting containers */ diff --git a/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java b/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java index 1d8c44590..c451130c6 100644 --- a/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java +++ b/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java @@ -4,9 +4,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.regex.Pattern; - -import com.google.common.base.Joiner; /** * @author marcus @@ -18,6 +15,9 @@ public class ContainerNamingUtil { private static final String INDEX_PLACEHOLDER = "%i"; + // class with only static methods + private ContainerNamingUtil() { } + public static String calculateContainerName(final String containerNamePattern, final String imageName, final String nameAlias, From b4d61a839ad3028c9c09949e22ca8d87a504c725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Mon, 24 Sep 2018 11:26:38 +0200 Subject: [PATCH 5/8] chore(container-name): Refactored a bit and tested also for non-running containers --- .../io/fabric8/maven/docker/LogsMojo.java | 2 +- .../io/fabric8/maven/docker/StartMojo.java | 9 +- .../io/fabric8/maven/docker/StopMojo.java | 43 ++++-- .../io/fabric8/maven/docker/WatchMojo.java | 8 ++ .../maven/docker/access/DockerAccess.java | 3 +- .../maven/docker/access/UrlBuilder.java | 4 +- .../access/hc/DockerAccessWithHcClient.java | 6 +- .../docker/config/RunImageConfiguration.java | 17 ++- .../maven/docker/service/QueryService.java | 43 +----- .../maven/docker/service/RunService.java | 8 +- .../maven/docker/service/WatchService.java | 15 ++- .../docker/util/ContainerNamingUtil.java | 124 +++++++++++++----- .../fabric8/maven/docker/UrlBuilderTest.java | 8 +- .../docker/util/ContainerNamingUtilTest.java | 110 +++++++--------- 14 files changed, 235 insertions(+), 165 deletions(-) diff --git a/src/main/java/io/fabric8/maven/docker/LogsMojo.java b/src/main/java/io/fabric8/maven/docker/LogsMojo.java index 9ba802d41..55864409e 100644 --- a/src/main/java/io/fabric8/maven/docker/LogsMojo.java +++ b/src/main/java/io/fabric8/maven/docker/LogsMojo.java @@ -43,7 +43,7 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do for (ImageConfiguration image : getResolvedImages()) { String imageName = image.getName(); if (logAll) { - for (Container container : queryService.getContainersForImage(imageName)) { + for (Container container : queryService.getContainersForImage(imageName, false)) { doLogging(logDispatcher, image, container.getId()); } } else { diff --git a/src/main/java/io/fabric8/maven/docker/StartMojo.java b/src/main/java/io/fabric8/maven/docker/StartMojo.java index 6694f9bab..abd4e933d 100644 --- a/src/main/java/io/fabric8/maven/docker/StartMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StartMojo.java @@ -42,6 +42,7 @@ import io.fabric8.maven.docker.service.RegistryService; import io.fabric8.maven.docker.service.RunService; import io.fabric8.maven.docker.service.ServiceHub; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.util.StartOrderResolver; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -89,6 +90,12 @@ public class StartMojo extends AbstractDockerMojo { @Parameter(property = "docker.exposeContainerInfo") private String exposeContainerProps = "docker.container"; + /** + * Naming pattern for how to name containers when started + */ + @Parameter(property = "docker.containerNamePattern") + private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN;; + /** * Whether to create the customs networks (user-defined bridge networks) before starting automatically */ @@ -278,7 +285,7 @@ private void startImage(final ImageConfiguration image, startingContainers.submit(new Callable() { @Override public StartedContainer call() throws Exception { - final String containerId = runService.createAndStartContainer(image, portMapping, getPomLabel(), projProperties, project.getBasedir(), getBuildTimestamp()); + final String containerId = runService.createAndStartContainer(image, portMapping, getPomLabel(), projProperties, project.getBasedir(), containerNamePattern, getBuildTimestamp()); // Update port-mapping writer portMappingPropertyWriteHelper.add(portMapping, runConfig.getPortPropertyFile()); diff --git a/src/main/java/io/fabric8/maven/docker/StopMojo.java b/src/main/java/io/fabric8/maven/docker/StopMojo.java index d6dbe3f67..211c6e5c2 100644 --- a/src/main/java/io/fabric8/maven/docker/StopMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StopMojo.java @@ -3,6 +3,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; import io.fabric8.maven.docker.access.DockerAccessException; @@ -15,6 +16,7 @@ import io.fabric8.maven.docker.service.QueryService; import io.fabric8.maven.docker.service.RunService; import io.fabric8.maven.docker.service.ServiceHub; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.util.PomLabel; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -52,6 +54,12 @@ public class StopMojo extends AbstractDockerMojo { @Parameter( property = "docker.sledgeHammer", defaultValue = "false" ) private boolean sledgeHammer; + /** + * Naming pattern for how to name containers when started + */ + @Parameter(property = "docker.containerNamePattern") + private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN; + @Override protected void executeInternal(ServiceHub hub) throws MojoExecutionException, DockerAccessException, ExecException { QueryService queryService = hub.getQueryService(); @@ -63,7 +71,7 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do if (invokedTogetherWithDockerStart()) { runService.stopStartedContainers(keepContainer, removeVolumes, autoCreateCustomNetworks, pomLabel); } else { - stopContainers(queryService, runService); + stopContainers(queryService, runService, pomLabel); } } @@ -72,13 +80,17 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, Do dispatcher.untrackAllContainerLogs(); } - private void stopContainers(QueryService queryService, RunService runService) throws DockerAccessException, ExecException, MojoExecutionException { - Collection networksToRemove = getNetworksToRemove(queryService); + private void stopContainers(QueryService queryService, RunService runService, PomLabel pomLabel) throws DockerAccessException, ExecException, MojoExecutionException { + Collection networksToRemove = getNetworksToRemove(queryService, pomLabel); for (ImageConfiguration image : getResolvedImages()) { - for (Container container : queryService.getContainersForImage(image.getName())) { - String lastContainerName = queryService.calculateLastContainerName(image, getBuildTimestamp(), null); - if (shouldStopContainer(container, lastContainerName)) { + Collection existingContainers = + ContainerNamingUtil.getContainersToStop(image, + containerNamePattern, + getBuildTimestamp(), + queryService.getContainersForImage(image.getName(), false)); + for (Container container : existingContainers) { + if (shouldStopContainer(container, pomLabel)) { runService.stopContainer(container.getId(), image, keepContainer, removeVolumes); } } @@ -86,11 +98,13 @@ private void stopContainers(QueryService queryService, RunService runService) th runService.removeCustomNetworks(networksToRemove); } - private boolean shouldStopContainer(Container container, String lastContainerName) { + private boolean shouldStopContainer(Container container, PomLabel pomLabel) { if (isStopAllContainers()) { return true; } - return container.getName().equals(lastContainerName); + String key = pomLabel.getKey(); + Map labels = container.getLabels(); + return labels.containsKey(key) && pomLabel.equals(new PomLabel(labels.get(key))); } private boolean isStopAllContainers() { @@ -102,7 +116,7 @@ private boolean invokedTogetherWithDockerStart() { return startCalled != null && startCalled; } - private Set getNetworksToRemove(QueryService queryService) throws DockerAccessException, MojoExecutionException { + private Set getNetworksToRemove(QueryService queryService, PomLabel pomLabel) throws DockerAccessException, MojoExecutionException { if (!autoCreateCustomNetworks) { return Collections.emptySet(); } @@ -116,9 +130,14 @@ private Set getNetworksToRemove(QueryService queryService) throws Docke if (config.isCustomNetwork() && network != null) { customNetworks.add(network); - for (Container container : queryService.getContainersForImage(image.getName())) { - String lastContainerName = queryService.calculateLastContainerName(image, getBuildTimestamp(), null); - if (!shouldStopContainer(container, lastContainerName)) { + Collection existingContainers = + ContainerNamingUtil.getContainersToStop(image, + containerNamePattern, + getBuildTimestamp(), + queryService.getContainersForImage(image.getName(), false)); + + for (Container container : existingContainers) { + if (!shouldStopContainer(container, pomLabel)) { // it's sill in use don't collect it customNetworks.remove(network); } diff --git a/src/main/java/io/fabric8/maven/docker/WatchMojo.java b/src/main/java/io/fabric8/maven/docker/WatchMojo.java index 2cf49f8d7..9bee8b619 100644 --- a/src/main/java/io/fabric8/maven/docker/WatchMojo.java +++ b/src/main/java/io/fabric8/maven/docker/WatchMojo.java @@ -21,6 +21,7 @@ import io.fabric8.maven.docker.service.ServiceHub; import io.fabric8.maven.docker.service.WatchService; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; @@ -60,6 +61,12 @@ public class WatchMojo extends AbstractBuildSupportMojo { @Parameter(property = "docker.watchPostExec") private String watchPostExec; + /** + * Naming pattern for how to name containers when started + */ + @Parameter(property = "docker.containerNamePattern") + private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN; + /** * Whether to create the customs networks (user-defined bridge networks) before starting automatically */ @@ -86,6 +93,7 @@ protected WatchService.WatchContext getWatchContext() throws MojoExecutionExcept .keepContainer(keepContainer) .keepRunning(keepRunning) .removeVolumes(removeVolumes) + .containerNamePattern(containerNamePattern) .buildTimestamp(getBuildTimestamp()) .pomLabel(getPomLabel()) .mojoParameters(createMojoParameters()) diff --git a/src/main/java/io/fabric8/maven/docker/access/DockerAccess.java b/src/main/java/io/fabric8/maven/docker/access/DockerAccess.java index 9fce39430..c37eea437 100644 --- a/src/main/java/io/fabric8/maven/docker/access/DockerAccess.java +++ b/src/main/java/io/fabric8/maven/docker/access/DockerAccess.java @@ -69,10 +69,11 @@ public interface DockerAccess { * can be tuned with a global parameters. * * @param image for which its container are looked up + * @param all whether to fetch also stopped containers. If false only running containers are returned * @return list of Container objects or an empty list if none is found * @throws DockerAccessException if the request fails */ - List getContainersForImage(String image) throws DockerAccessException; + List getContainersForImage(String image, boolean all) throws DockerAccessException; /** * Starts a previously set up exec instance (via {@link #createExecContainer(String, Arguments)} container diff --git a/src/main/java/io/fabric8/maven/docker/access/UrlBuilder.java b/src/main/java/io/fabric8/maven/docker/access/UrlBuilder.java index 9f772c1fd..151c3ace8 100644 --- a/src/main/java/io/fabric8/maven/docker/access/UrlBuilder.java +++ b/src/main/java/io/fabric8/maven/docker/access/UrlBuilder.java @@ -80,8 +80,8 @@ public String inspectExecContainer(String containerId) { .build(); } - public String listContainers(String ... filter) { - Builder builder = u("containers/json"); + public String listContainers(boolean all, String ... filter) { + Builder builder = u("containers/json").p("all", all); addFilters(builder, filter); return builder.build(); } diff --git a/src/main/java/io/fabric8/maven/docker/access/hc/DockerAccessWithHcClient.java b/src/main/java/io/fabric8/maven/docker/access/hc/DockerAccessWithHcClient.java index f54b61b8e..df2a2e6e8 100644 --- a/src/main/java/io/fabric8/maven/docker/access/hc/DockerAccessWithHcClient.java +++ b/src/main/java/io/fabric8/maven/docker/access/hc/DockerAccessWithHcClient.java @@ -247,15 +247,15 @@ public LogGetHandle getLogAsync(String containerId, LogCallback callback) { } @Override - public List getContainersForImage(String image) throws DockerAccessException { + public List getContainersForImage(String image, boolean all) throws DockerAccessException { String url; String serverApiVersion = getServerApiVersion(); if (EnvUtil.greaterOrEqualsVersion(serverApiVersion, "1.23")) { // For Docker >= 1.11 we can use a new filter when listing containers - url = urlBuilder.listContainers("ancestor",image); + url = urlBuilder.listContainers(all, "ancestor",image); } else { // For older versions (< Docker 1.11) we need to iterate over the containers. - url = urlBuilder.listContainers(); + url = urlBuilder.listContainers(all); } try { diff --git a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java index 0926391ca..ea2d76c6d 100644 --- a/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java +++ b/src/main/java/io/fabric8/maven/docker/config/RunImageConfiguration.java @@ -113,7 +113,7 @@ public boolean isDefault() { private List ports; /** - * @deprecated + * @deprecated */ @Parameter @Deprecated @@ -320,7 +320,7 @@ public List getTmpfs() { * @deprecated */ // Naming scheme for how to name container - @Deprecated // for backward compatibility + @Deprecated // for backward compatibility, us containerNamePattern instead public enum NamingStrategy { /** * No extra naming @@ -364,6 +364,10 @@ public String getContainerNamePattern() { return containerNamePattern; } + /** + * @deprecated use {@link #getContainerNamePattern} instead + */ + @Deprecated public NamingStrategy getNamingStrategy() { return namingStrategy; } @@ -539,8 +543,13 @@ public Builder log(LogConfiguration log) { return this; } + public Builder containerNamePattern(String pattern) { + config.containerNamePattern = pattern; + return this; + } + /** - * @deprecated + * @deprecated use {@link #containerNamePattern} instead */ @Deprecated public Builder namingStrategy(String namingStrategy) { @@ -551,7 +560,7 @@ public Builder namingStrategy(String namingStrategy) { } /** - * @deprecated + * @deprecated use {@link #containerNamePattern} instead */ @Deprecated public Builder namingStrategy(NamingStrategy namingStrategy) { diff --git a/src/main/java/io/fabric8/maven/docker/service/QueryService.java b/src/main/java/io/fabric8/maven/docker/service/QueryService.java index 1f73051ea..3c7598b79 100644 --- a/src/main/java/io/fabric8/maven/docker/service/QueryService.java +++ b/src/main/java/io/fabric8/maven/docker/service/QueryService.java @@ -92,28 +92,17 @@ public String getContainerName(String containerId) throws DockerAccessException return getMandatoryContainer(containerId).getName(); } - public String calculateContainerName(ImageConfiguration image, Date timestamp, String defaultContainerNamePattern) throws DockerAccessException { - String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern); - Collection existingContainers = getContainersForImage(image.getName()); - return ContainerNamingUtil.calculateContainerName(containerNamePattern, image.getName(), image.getAlias(), timestamp, extractContainerNames(existingContainers)); - } - - public String calculateLastContainerName(ImageConfiguration image, Date timestamp, String defaultContainerNamePattern) throws DockerAccessException { - String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern); - Collection existingContainers = getContainersForImage(image.getName()); - return ContainerNamingUtil.calculateLastContainerName(containerNamePattern, image.getName(), image.getAlias(), timestamp, extractContainerNames(existingContainers)); - } - /** * Get all containers which are build from an image. By default only the last containers are considered but this * can be tuned with a global parameters. * * @param image for which its container are looked up + * @param all whether to fetch all containers * @return list of Container objects * @throws DockerAccessException if the request fails */ - public List getContainersForImage(final String image) throws DockerAccessException { - return docker.getContainersForImage(image); + public List getContainersForImage(final String image, final boolean all) throws DockerAccessException { + return docker.getContainersForImage(image, all); } /** @@ -138,7 +127,7 @@ public Container getLatestContainerForImage(String image) throws DockerAccessExc long newest = 0; Container result = null; - for (Container container : getContainersForImage(image)) { + for (Container container : getContainersForImage(image, false)) { long timestamp = container.getCreated(); if (timestamp < newest) { @@ -186,28 +175,4 @@ public boolean hasImage(String name) throws DockerAccessException { return docker.hasImage(name); } - - // ============================================================================== - - private Set extractContainerNames(final Collection existingContainers) { - final ImmutableSet.Builder containerNamesBuilder = ImmutableSet.builder(); - for (final Container container : existingContainers) { - containerNamesBuilder.add(container.getName()); - } - return containerNamesBuilder.build(); - } - - private String extractContainerNamePattern(ImageConfiguration image, String defaultContainerNamePattern) { - RunImageConfiguration runConfig = image.getRunConfiguration(); - if (runConfig != null) { - if (runConfig.getContainerNamePattern() != null) { - return runConfig.getContainerNamePattern(); - } - if (runConfig.getNamingStrategy() == RunImageConfiguration.NamingStrategy.alias) { - return "%a"; - } - } - return defaultContainerNamePattern; - } - } diff --git a/src/main/java/io/fabric8/maven/docker/service/RunService.java b/src/main/java/io/fabric8/maven/docker/service/RunService.java index cea851a87..193b08b1b 100644 --- a/src/main/java/io/fabric8/maven/docker/service/RunService.java +++ b/src/main/java/io/fabric8/maven/docker/service/RunService.java @@ -36,6 +36,7 @@ import io.fabric8.maven.docker.model.ContainerDetails; import io.fabric8.maven.docker.model.ExecDetails; import io.fabric8.maven.docker.model.Network; +import io.fabric8.maven.docker.util.ContainerNamingUtil; import io.fabric8.maven.docker.util.EnvUtil; import io.fabric8.maven.docker.util.Logger; import io.fabric8.maven.docker.util.PomLabel; @@ -125,6 +126,7 @@ public String execInContainer(String containerId, String command, ImageConfigura * @param pomLabel label to tag the started container with * * @param mavenProps properties to fill in with dynamically assigned ports + * @param defaultContainerNamePattern pattern to use for naming containers. Can be null in which case a default pattern is used * @param buildTimestamp date which should be used as the timestamp when calculating container names * @return the container id * @@ -135,10 +137,14 @@ public String createAndStartContainer(ImageConfiguration imageConfig, PomLabel pomLabel, Properties mavenProps, File baseDir, + String defaultContainerNamePattern, Date buildTimestamp) throws DockerAccessException { RunImageConfiguration runConfig = imageConfig.getRunConfiguration(); String imageName = imageConfig.getName(); - String containerName = queryService.calculateContainerName(imageConfig, buildTimestamp, null); + + Collection existingContainers = queryService.getContainersForImage(imageName, true); + String containerName = ContainerNamingUtil.formatContainerName(imageConfig, defaultContainerNamePattern, buildTimestamp, existingContainers); + ContainerCreateConfig config = createContainerConfig(imageName, runConfig, portMapping, pomLabel, mavenProps, baseDir); String id = docker.createContainer(config, containerName); diff --git a/src/main/java/io/fabric8/maven/docker/service/WatchService.java b/src/main/java/io/fabric8/maven/docker/service/WatchService.java index ebcf4ad0d..8148f0a6d 100644 --- a/src/main/java/io/fabric8/maven/docker/service/WatchService.java +++ b/src/main/java/io/fabric8/maven/docker/service/WatchService.java @@ -246,6 +246,7 @@ public void execute(ImageWatcher watcher) throws Exception { ctx.getPomLabel(), project.getProperties(), project.getBasedir(), + ctx.getContainerNamePattern(), ctx.getBuildTimestamp())); } }; @@ -407,6 +408,8 @@ public static class WatchContext implements Serializable { private Date buildTimestamp; + private String containerNamePattern; + public WatchContext() { } @@ -462,9 +465,13 @@ public Date getBuildTimestamp() { return buildTimestamp; } + public String getContainerNamePattern() { + return containerNamePattern; + } + public static class Builder { - private WatchContext context = new WatchContext(); + private WatchContext context; public Builder() { this.context = new WatchContext(); @@ -539,6 +546,12 @@ public Builder buildTimestamp(Date buildTimestamp) { return this; } + public Builder containerNamePattern(String containerNamePattern) { + context.containerNamePattern = containerNamePattern; + return this; + } + + public WatchContext build() { return context; } diff --git a/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java b/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java index c451130c6..d8e0c90c0 100644 --- a/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java +++ b/src/main/java/io/fabric8/maven/docker/util/ContainerNamingUtil.java @@ -1,9 +1,18 @@ package io.fabric8.maven.docker.util; +import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableSet; +import io.fabric8.maven.docker.config.ImageConfiguration; +import io.fabric8.maven.docker.config.RunImageConfiguration; +import io.fabric8.maven.docker.model.Container; /** * @author marcus @@ -11,31 +20,33 @@ */ public class ContainerNamingUtil { - private static final String DEFAULT_NAMING_CONFIGURATION = "%n-%i"; - private static final String INDEX_PLACEHOLDER = "%i"; + public static final String DEFAULT_CONTAINER_NAME_PATTERN = "%n-%i"; + // class with only static methods private ContainerNamingUtil() { } - public static String calculateContainerName(final String containerNamePattern, - final String imageName, - final String nameAlias, - final Date buildTimestamp, - final Set existingContainers) { + public static String formatContainerName(final ImageConfiguration image, + final String defaultContainerNamePattern, + final Date buildTimestamp, + final Collection existingContainers) { + + String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern); + Set existingContainersNames = extractContainerNames(existingContainers); final String partiallyApplied = replacePlaceholders( - containerNamePattern != null ? containerNamePattern : DEFAULT_NAMING_CONFIGURATION, - imageName, - nameAlias, + containerNamePattern, + image.getName(), + image.getAlias(), buildTimestamp); if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { for (long i = 1; i < Long.MAX_VALUE; i++) { final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); - if (!existingContainers.contains(withIndexApplied)) { + if (!existingContainersNames.contains(withIndexApplied)) { return withIndexApplied; } } @@ -45,23 +56,34 @@ public static String calculateContainerName(final String containerNamePattern, } } - public static String calculateLastContainerName(final String containerNamePattern, - final String imageName, - final String nameAlias, - final Date buildTimestamp, - final Set existingContainers) { + /** + * Keep only the entry with the higest index if an indexed naming scheme for container has been chosen. + * @param image the image from which to the the container pattern + * @param buildTimestamp the timestamp for the build + * @param containers the list of existing containers + * @return a list with potentially lower indexed entries removed + */ + public static Collection getContainersToStop(final ImageConfiguration image, + final String defaultContainerNamePattern, + final Date buildTimestamp, + final Collection containers) { + + String containerNamePattern = extractContainerNamePattern(image, defaultContainerNamePattern); + + // Only special treatment for indexed container names + if (!containerNamePattern.contains(INDEX_PLACEHOLDER)) { + return containers; + } final String partiallyApplied = - replacePlaceholders(containerNamePattern, - imageName, - nameAlias, - buildTimestamp); + replacePlaceholders( + containerNamePattern, + image.getName(), + image.getAlias(), + buildTimestamp); + + return keepOnlyLastIndexedContainer(containers, partiallyApplied); - if (partiallyApplied.contains(INDEX_PLACEHOLDER)) { - return applyLastIndex(existingContainers, partiallyApplied); - } else { - return partiallyApplied; - } } // ======================================================================================================== @@ -79,23 +101,55 @@ private static String replacePlaceholders(String containerNamePattern, String im } - private static String applyLastIndex(Set existingContainerNames, final String partiallyApplied) { + // Filter out any older indexed containernames, keeping only the last one (i.e. with the highest index) + private static Collection keepOnlyLastIndexedContainer(Collection existingContainers, final String partiallyApplied) { + + Collection result = new ArrayList<>(existingContainers); - String last = null; + // No index place holder, so nothing to filters + if (!partiallyApplied.contains(INDEX_PLACEHOLDER)) { + return result; + } + + Map containerMap = existingContainers.stream().collect(Collectors.toMap(Container::getName, Function.identity())); + + Container last = null; for (long i = 1; i < Long.MAX_VALUE; i++) { final String withIndexApplied = partiallyApplied.replaceAll(INDEX_PLACEHOLDER, String.valueOf(i)); - if (last == null) { - last = withIndexApplied; - } - - if (!existingContainerNames.contains(withIndexApplied)) { - return last; + Container mapped = containerMap.get(withIndexApplied); + if (mapped != null) { + result.remove(mapped); + last = mapped; } else { - last = withIndexApplied; + // Readd last one removed (if any) + if (last != null) { + result.add(last); + } + return result; } } + throw new IllegalStateException("Internal error: Cannot find a free container index slot in " + existingContainers); + } - return last; + private static Set extractContainerNames(final Collection existingContainers) { + final ImmutableSet.Builder containerNamesBuilder = ImmutableSet.builder(); + for (final Container container : existingContainers) { + containerNamesBuilder.add(container.getName()); + } + return containerNamesBuilder.build(); + } + + private static String extractContainerNamePattern(ImageConfiguration image, String defaultContainerNamePattern) { + RunImageConfiguration runConfig = image.getRunConfiguration(); + if (runConfig != null) { + if (runConfig.getContainerNamePattern() != null) { + return runConfig.getContainerNamePattern(); + } + if (runConfig.getNamingStrategy() == RunImageConfiguration.NamingStrategy.alias) { + return "%a"; + } + } + return defaultContainerNamePattern != null ? defaultContainerNamePattern : DEFAULT_CONTAINER_NAME_PATTERN; } private static String cleanImageName(final String imageName) { diff --git a/src/test/java/io/fabric8/maven/docker/UrlBuilderTest.java b/src/test/java/io/fabric8/maven/docker/UrlBuilderTest.java index 1bfe49d16..728e8a10e 100644 --- a/src/test/java/io/fabric8/maven/docker/UrlBuilderTest.java +++ b/src/test/java/io/fabric8/maven/docker/UrlBuilderTest.java @@ -89,12 +89,12 @@ public void getImage() throws URISyntaxException { public void listContainers() throws MalformedURLException, UnsupportedEncodingException, URISyntaxException { UrlBuilder builder = new UrlBuilder("","1.0"); - assertEquals(new URI("/1.0/containers/json"), new URI(builder.listContainers())); - assertEquals(new URI("/1.0/containers/json?filters=" + URLEncoder.encode("{\"ancestor\":[\"nginx\"]}","UTF8")), - new URI(builder.listContainers("ancestor", "nginx"))); + assertEquals(new URI("/1.0/containers/json?all=0"), new URI(builder.listContainers(false))); + assertEquals(new URI("/1.0/containers/json?all=1&filters=" + URLEncoder.encode("{\"ancestor\":[\"nginx\"]}","UTF8")), + new URI(builder.listContainers(true, "ancestor", "nginx"))); try { - builder.listContainers("ancestor"); + builder.listContainers(false,"ancestor"); fail(); } catch (IllegalArgumentException exp) { assertTrue(exp.getMessage().contains("pair")); diff --git a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java index 04721707e..db5ec0ff2 100644 --- a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java @@ -1,119 +1,107 @@ package io.fabric8.maven.docker.util; -import com.google.common.collect.ImmutableSet; +import java.util.Collections; +import java.util.Date; + +import io.fabric8.maven.docker.config.ImageConfiguration; import io.fabric8.maven.docker.config.RunImageConfiguration; import io.fabric8.maven.docker.model.Container; -import io.fabric8.maven.docker.util.ContainerNamingUtil; +import mockit.Expectations; +import mockit.Mocked; +import mockit.integration.junit4.JMockit; import org.junit.Assert; import org.junit.Test; - -import java.util.Collections; -import java.util.Date; +import org.junit.runner.RunWith; /** * @author marcus * @since 1.0.0 */ +@RunWith(JMockit.class) public class ContainerNamingUtilTest { @Test public void testDefault() { Assert.assertEquals("jolokia_demo-1", - ContainerNamingUtil.calculateContainerName( + ContainerNamingUtil.formatContainerName( + imageConfiguration("jolokia/jolokia_demo","nameAlias", null), null, - "jolokia/jolokia_demo", - "nameAlias", new Date(123456), - Collections.emptySet())); + Collections.emptySet())); } @Test public void testAlias() { Assert.assertEquals("nameAlias", - ContainerNamingUtil.calculateContainerName( - "%a", - "jolokia/jolokia_demo", - "nameAlias", + ContainerNamingUtil.formatContainerName( + imageConfiguration("jolokia/jolokia_demo","nameAlias", "%a"), + null, new Date(123456), - Collections.emptySet())); + Collections.emptySet())); } @Test public void testTimestamp() { Assert.assertEquals("123456", - ContainerNamingUtil.calculateContainerName( - "%t", - "jolokia/jolokia_demo", - "nameAlias", + ContainerNamingUtil.formatContainerName( + imageConfiguration("jolokia/jolokia_demo","nameAlias", "%t"), + null, new Date(123456), - Collections.emptySet())); + Collections.emptySet())); } @Test public void testImageName() { Assert.assertEquals("jolokia_demo", - ContainerNamingUtil.calculateContainerName( - "%n", - "jolokia/jolokia_demo", - "nameAlias", + ContainerNamingUtil.formatContainerName( + imageConfiguration("jolokia/jolokia_demo","nameAlias", "%n"), + null, new Date(123456), - Collections.emptySet())); + Collections.emptySet())); } @Test public void testIndex() { Assert.assertEquals("1", - ContainerNamingUtil.calculateContainerName( - "%i", - "jolokia/jolokia_demo", - "nameAlias", + ContainerNamingUtil.formatContainerName( + imageConfiguration("jolokia/jolokia_demo","nameAlias", "%i"), + null, new Date(123456), - Collections.emptySet())); + Collections.emptySet())); } @Test public void testAll() { Assert.assertEquals("prefix-1-nameAlias-jolokia_demo-123456-postfix", - ContainerNamingUtil.calculateContainerName( - "prefix-%i-%a-%n-%t-postfix", - "jolokia/jolokia_demo", - "nameAlias", + ContainerNamingUtil.formatContainerName( + imageConfiguration("jolokia/jolokia_demo","nameAlias", "prefix-%i-%a-%n-%t-postfix"), + null, new Date(123456), - Collections.emptySet())); + Collections.emptySet())); } + @Mocked + Container container; + @Test public void testIndexAdvanced() { + new Expectations() {{ + container.getName(); result = "container-1"; + }}; Assert.assertEquals("container-2", - ContainerNamingUtil.calculateContainerName( - "container-%i", - "jolokia/jolokia_demo", - "nameAlias", + ContainerNamingUtil.formatContainerName( + imageConfiguration("jolokia/jolokia_demo","nameAlias", "container-%i"), + null, new Date(123456), - Collections.singleton("container-1"))); + Collections.singleton(container))); } - @Test - public void testCalculateLastContainerName() { - Assert.assertEquals("container-1", - ContainerNamingUtil.calculateLastContainerName( - "container-%i", - "jolokia/jolokia_demo", - "nameAlias", - new Date(123456), - Collections.singleton("container-1"))); + private ImageConfiguration imageConfiguration(String name, String alias, String containerNamePattern) { + ImageConfiguration.Builder builder = new ImageConfiguration.Builder().name(name).alias(alias); + if (containerNamePattern != null) { + RunImageConfiguration runConfig = new RunImageConfiguration.Builder().containerNamePattern(containerNamePattern).build(); + builder.runConfig(runConfig); + } + return builder.build(); } - - @Test - public void testCalculateLastContainerNameSecond() { - Assert.assertEquals("container-2", - ContainerNamingUtil.calculateLastContainerName( - "container-%i", - "jolokia/jolokia_demo", - "nameAlias", - new Date(123456), - ImmutableSet.of("container-1","container-2"))); - - } - } From 63425ac8787fbd666feb203432c987b4eff7f905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Mon, 24 Sep 2018 12:40:59 +0200 Subject: [PATCH 6/8] chore: pepperbot fixes + extra test --- .../asciidoc/inc/start/_configuration.adoc | 2 +- .../io/fabric8/maven/docker/StartMojo.java | 2 +- .../maven/docker/service/QueryService.java | 8 +---- .../docker/util/ContainerNamingUtilTest.java | 29 +++++++++++++++++-- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/main/asciidoc/inc/start/_configuration.adoc b/src/main/asciidoc/inc/start/_configuration.adoc index 7cc37086a..84953d336 100644 --- a/src/main/asciidoc/inc/start/_configuration.adoc +++ b/src/main/asciidoc/inc/start/_configuration.adoc @@ -80,7 +80,7 @@ The `` configuration element knows the following sub elements: | Total memory usage (memory + swap); use -1 to disable swap. | *namingStrategy* -a| Naming strategy for how the container name is created: +a| *This option is deprecated, please use a `containerNamePattern` instead* Naming strategy for how the container name is created: * *none* : uses randomly assigned names from docker (default) * *alias* : uses the `alias` specified in the `image` configuration. An error is thrown, if a container already exists with this name. diff --git a/src/main/java/io/fabric8/maven/docker/StartMojo.java b/src/main/java/io/fabric8/maven/docker/StartMojo.java index abd4e933d..21480f7eb 100644 --- a/src/main/java/io/fabric8/maven/docker/StartMojo.java +++ b/src/main/java/io/fabric8/maven/docker/StartMojo.java @@ -94,7 +94,7 @@ public class StartMojo extends AbstractDockerMojo { * Naming pattern for how to name containers when started */ @Parameter(property = "docker.containerNamePattern") - private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN;; + private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN; /** * Whether to create the customs networks (user-defined bridge networks) before starting automatically diff --git a/src/main/java/io/fabric8/maven/docker/service/QueryService.java b/src/main/java/io/fabric8/maven/docker/service/QueryService.java index 3c7598b79..13baca94c 100644 --- a/src/main/java/io/fabric8/maven/docker/service/QueryService.java +++ b/src/main/java/io/fabric8/maven/docker/service/QueryService.java @@ -1,19 +1,13 @@ package io.fabric8.maven.docker.service; -import java.util.Collection; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; -import com.google.common.collect.ImmutableSet; import io.fabric8.maven.docker.access.DockerAccess; -import io.fabric8.maven.docker.config.ImageConfiguration; -import io.fabric8.maven.docker.util.ContainerNamingUtil; -import io.fabric8.maven.docker.config.RunImageConfiguration; +import io.fabric8.maven.docker.access.DockerAccessException; import io.fabric8.maven.docker.model.Container; import io.fabric8.maven.docker.model.Network; -import io.fabric8.maven.docker.access.DockerAccessException; /** * Query service for getting image and container information from the docker dameon diff --git a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java index db5ec0ff2..2b94209fd 100644 --- a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java @@ -1,5 +1,7 @@ package io.fabric8.maven.docker.util; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -81,19 +83,40 @@ public void testAll() { } @Mocked - Container container; + Container container1; + + @Mocked + Container container2; @Test public void testIndexAdvanced() { new Expectations() {{ - container.getName(); result = "container-1"; + container1.getName();result = "container-1"; }}; Assert.assertEquals("container-2", ContainerNamingUtil.formatContainerName( imageConfiguration("jolokia/jolokia_demo","nameAlias", "container-%i"), null, new Date(123456), - Collections.singleton(container))); + Collections.singleton(container1))); + } + + + @Test + public void testContainersToStop() { + new Expectations() {{ + container1.getName();result = "container-1"; + container2.getName();result = "container-2"; + + }}; + Collection containers = Arrays.asList(container1, container2); + Collection filtered = ContainerNamingUtil.getContainersToStop( + imageConfiguration("jolokia/jolokia_demo","nameAlias", "container-%i"), + null, + new Date(123456), + containers); + Assert.assertEquals(filtered.size(),1); + Assert.assertEquals(filtered.iterator().next(),container2); } private ImageConfiguration imageConfiguration(String name, String alias, String containerNamePattern) { From 1a80b9eb1da95eb12d817b5bb838552be23eaf8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Mon, 24 Sep 2018 14:34:22 +0200 Subject: [PATCH 7/8] chore: Added documentation for container naming --- doc/changelog.md | 1 + src/main/asciidoc/inc/_docker-stop.adoc | 12 ++++-- .../asciidoc/inc/_image-configuration.adoc | 2 + src/main/asciidoc/inc/build/_overview.adoc | 2 +- .../asciidoc/inc/image/_configuration.adoc | 25 ----------- .../asciidoc/inc/start/_configuration.adoc | 7 ++++ .../asciidoc/inc/watch/_configuration.adoc | 42 ++++++++++--------- .../docker/util/ContainerNamingUtilTest.java | 4 +- 8 files changed, 44 insertions(+), 51 deletions(-) diff --git a/doc/changelog.md b/doc/changelog.md index a7b892e2f..0e3c1e601 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -6,6 +6,7 @@ - Fix NPE when no networks are configured (#1055) - Fix Base64 encoding for X-Registry-Auth used for Docker authentication (#1084) - Fix property configuration based's build detection (#1078) + - Introduce container name patterns for naming containers (#931) * **0.26.1** (2018-07-20) - Simple Dockerfile triggered also when only a single run section is given diff --git a/src/main/asciidoc/inc/_docker-stop.adoc b/src/main/asciidoc/inc/_docker-stop.adoc index 2010a68a3..35bbfb847 100644 --- a/src/main/asciidoc/inc/_docker-stop.adoc +++ b/src/main/asciidoc/inc/_docker-stop.adoc @@ -19,6 +19,14 @@ For tuning what should happen when stopping there are four global parameters whi |=== | Element | Description | Parameter +| *allContainers* +| Stops and removes any container that matches an image defined in the current project's configuration. This was the default behavior of the plugin prior up to version 0.13.6 +| `docker.allContainers` + +| <> +| Default pattern for naming all containers when they are created. See <> for details. +| `docker.containerNamePattern` + | *keepContainer* | If set to `true` not destroy container after they have been stopped. Default is false. | `docker.keepContainer` @@ -30,10 +38,6 @@ For tuning what should happen when stopping there are four global parameters whi | *removeVolumes* | If set to `true` will remove any volumes associated to the container as well. This option will be ignored if either `keepContainer` or `keepRunning` are true. | `docker.removeVolumes` - -| *allContainers* -| Stops and removes any container that matches an image defined in the current project's configuration. This was the default behavior of the plugin prior up to version 0.13.6 -| `docker.allContainers` |=== .Example diff --git a/src/main/asciidoc/inc/_image-configuration.adoc b/src/main/asciidoc/inc/_image-configuration.adoc index 1d634816c..e19498d27 100644 --- a/src/main/asciidoc/inc/_image-configuration.adoc +++ b/src/main/asciidoc/inc/_image-configuration.adoc @@ -12,3 +12,5 @@ include::image/_configuration.adoc[] Either a `` or `` section must be present (except when you are using the <> mode). These are explained in details in the corresponding goal sections. include::image/_example.adoc[] + +include::image/_naming.adoc[] diff --git a/src/main/asciidoc/inc/build/_overview.adoc b/src/main/asciidoc/inc/build/_overview.adoc index 51fb08ecf..aab4c96ad 100644 --- a/src/main/asciidoc/inc/build/_overview.adoc +++ b/src/main/asciidoc/inc/build/_overview.adoc @@ -46,7 +46,7 @@ When only a single image should be built with a Dockerfile no XML configuration All what need to be done is to place a `Dockerfile` into the top-level module directory, alongside to `pom.xml`. You can still configure <> in the plugin configuration, but as soon as you add an `` in the XML configuration, you need to configure also the build explicitly. -The image name is by default set from the Maven coordinates (`%g/%a:%l`, see <> for an explanation of the params which are essentially the Maven GAV) +The image name is by default set from the Maven coordinates (`%g/%a:%l`, see <> for an explanation of the params which are essentially the Maven GAV) This name can be set with the property `docker.name`. If you want to add some `` configuration to this image for starting it with `docker:run` then you can add an image configuration but without a `` section in which case the Dockerfile will be picked up, too. This works only for a single image, though. diff --git a/src/main/asciidoc/inc/image/_configuration.adoc b/src/main/asciidoc/inc/image/_configuration.adoc index 1ace4fa95..b70db0efa 100644 --- a/src/main/asciidoc/inc/image/_configuration.adoc +++ b/src/main/asciidoc/inc/image/_configuration.adoc @@ -29,28 +29,3 @@ created and run when <<{plugin}:start>> is called. If this image is only used a | Specification of external configuration as an alternative to this XML based configuration with `` and ``. It contains a `` element specifying the handler for getting the configuration. See <> for details. endif::[] |=== - -[[image-name-placeholders]] -.Name placeholders -When specifying the name you can use several placeholders which are replaced during runtime by this plugin. In addition you can use regular Maven properties which are resolved by Maven itself. - -.Placeholders -[cols="1,5"] -|=== -| Placeholder | Description - -| *%g* -| The last part of the Maven group name, sanitized so that it can be used as username on GitHub. Only the part after the last dot is used. E.g. for a group id `io.fabric8` this placeholder would insert `fabric8` - -| *%a* -| A sanitized version of the artefact id so that it can be used as part of an Docker image name. I.e. it is converted to all lower case (as required by Docker) - -| *%v* -| The project version. Synonym to `${project.version}` - -| *%l* -| If the project version ends with `-SNAPSHOT` then this placeholder is `latest`, otherwise its the full version (same as `%v`) - -| *%t* -| If the project version ends with `-SNAPSHOT` this placeholder resolves to `snapshot-` where timestamp has the date format `yyMMdd-HHmmss-SSSS` (eg `snapshot-`). This feature is especially useful during development in oder to avoid conflicts when images are to be updated which are still in use. You need to take care yourself of cleaning up old images afterwards, though. -|=== diff --git a/src/main/asciidoc/inc/start/_configuration.adoc b/src/main/asciidoc/inc/start/_configuration.adoc index 84953d336..b5cdcf2f1 100644 --- a/src/main/asciidoc/inc/start/_configuration.adoc +++ b/src/main/asciidoc/inc/start/_configuration.adoc @@ -5,6 +5,10 @@ In addition to the <>, this goal supports the following gl |=== | Element | Description | Property +| <> +| Default pattern for naming all containers when they are created. See <> for details. +| `docker.containerNamePattern` + | *showLogs* | In order to switch on globally the logs *showLogs* can be used as global configuration (i.e. outside of ``). If set it will print out all standard output and standard error messages for all containers started. As value the images for which logs should be shown can be given as a comma separated list. @@ -34,6 +38,9 @@ The `` configuration element knows the following sub elements: | <> | Command which should be executed at the end of the container's startup. If not given, the image's default command is used. See <> for details. +| <> +| Pattern for naming the container when it is created. See <> for details. + | *domainname* | Domain name for the container diff --git a/src/main/asciidoc/inc/watch/_configuration.adoc b/src/main/asciidoc/inc/watch/_configuration.adoc index b0ad8edd0..99ee7e2b6 100644 --- a/src/main/asciidoc/inc/watch/_configuration.adoc +++ b/src/main/asciidoc/inc/watch/_configuration.adoc @@ -7,6 +7,26 @@ This maven goal can be configured with the following top-level parameters: |=== | Element | Description | Property +| <> +| Default pattern for naming all containers when they are created. See <> for details. +| `docker.containerNamePattern` + +| *keepContainer* +| As for `{plugin}:stop`, if this is set to `true` (and `keepRunning` is disabled) then all container will be removed after they have been stopped. The default is `true`. +| `docker.keepContainer` + +| *keepRunning* +| If set to `true` all container will be kept running after `{plugin}:watch` has been stopped. By default this is set to `false`. +| `docker.keepRunning` + +| *removeVolumes* +| if set to `true` will remove any volumes associated to the container as well. This option will be ignored if either `keepContainer` or `keepRunning` are `true`. +| `docker.removeVolumes` + +| *watchInterval* +| Interval in milliseconds how often to check for changes, which must be larger than 100ms. The default is 5 seconds. +| `docker.watchInterval` + | *watchMode* a| Watch mode specifies what should be watched @@ -22,29 +42,13 @@ need any watching. `none` is best used on an per image level, see below how this can be specified. | `docker.watchMode` -| *watchInterval* -| Interval in milliseconds how often to check for changes, which must be larger than 100ms. The default is 5 seconds. -| `docker.watchInterval` - -| *watchPostGoal* -| A maven goal which should be called if a rebuild or a restart has been performed. This goal must have the format `::` and the plugin must be configured in the `pom.xml`. For example a post-goal `io.fabric8:fabric8:delete-pods` will trigger the deletion of PODs in Kubernetes which in turn triggers are new start of a POD within the Kubernetes cluster. The value specified here is the the default post goal which can be overridden by `` in a `` configuration. -| - | *watchPostExec* | A command which is executed within the container after files are copied into this container when `watchMode` is `copy`. Note that this container must be running. | -| *keepRunning* -| If set to `true` all container will be kept running after `{plugin}:watch` has been stopped. By default this is set to `false`. -| `docker.keepRunning` - -| *keepContainer* -| As for `{plugin}:stop`, if this is set to `true` (and `keepRunning` is disabled) then all container will be removed after they have been stopped. The default is `true`. -| `docker.keepContainer` - -| *removeVolumes* -| if set to `true` will remove any volumes associated to the container as well. This option will be ignored if either `keepContainer` or `keepRunning` are `true`. -| `docker.removeVolumes` +| *watchPostGoal* +| A maven goal which should be called if a rebuild or a restart has been performed. This goal must have the format `::` and the plugin must be configured in the `pom.xml`. For example a post-goal `io.fabric8:fabric8:delete-pods` will trigger the deletion of PODs in Kubernetes which in turn triggers are new start of a POD within the Kubernetes cluster. The value specified here is the the default post goal which can be overridden by `` in a `` configuration. +| |=== Image specific watch configuration goes into an extra image-level `` section (i.e. `+...+`). The following parameters are recognized: diff --git a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java index 2b94209fd..4a9e5316c 100644 --- a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java @@ -115,8 +115,8 @@ public void testContainersToStop() { null, new Date(123456), containers); - Assert.assertEquals(filtered.size(),1); - Assert.assertEquals(filtered.iterator().next(),container2); + Assert.assertEquals(2, filtered.size()); + Assert.assertEquals(container2, filtered.iterator().next()); } private ImageConfiguration imageConfiguration(String name, String alias, String containerNamePattern) { From d978282cd43f4d6a4a66db2a10574946f2d671de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Mon, 24 Sep 2018 14:40:23 +0200 Subject: [PATCH 8/8] fix: fix test + missing doc --- src/main/asciidoc/inc/image/_naming.adoc | 65 +++++++++++++++++++ .../docker/util/ContainerNamingUtilTest.java | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/asciidoc/inc/image/_naming.adoc diff --git a/src/main/asciidoc/inc/image/_naming.adoc b/src/main/asciidoc/inc/image/_naming.adoc new file mode 100644 index 000000000..9938ac6c8 --- /dev/null +++ b/src/main/asciidoc/inc/image/_naming.adoc @@ -0,0 +1,65 @@ +[[image-name]] +## Image Names +When specifying the image name in the configuration with the `` field you can use several placeholders which are replaced during runtime by this plugin. In addition you can use regular Maven properties which are resolved by Maven itself. + +[cols="1,5"] +|=== +| Placeholder | Description + +| *%g* +| The last part of the Maven group name, sanitized so that it can be used as username on GitHub. Only the part after the last dot is used. E.g. for a group id `io.fabric8` this placeholder would insert `fabric8` + +| *%a* +| A sanitized version of the artefact id so that it can be used as part of an Docker image name. I.e. it is converted to all lower case (as required by Docker) + +| *%v* +| The project version. Synonym to `${project.version}` + +| *%l* +| If the project version ends with `-SNAPSHOT` then this placeholder is `latest`, otherwise its the full version (same as `%v`) + +| *%t* +| If the project version ends with `-SNAPSHOT` this placeholder resolves to `snapshot-` where timestamp has the date format `yyMMdd-HHmmss-SSSS` (eg `snapshot-`). This feature is especially useful during development in oder to avoid conflicts when images are to be updated which are still in use. You need to take care yourself of cleaning up old images afterwards, though. +|=== + +ifeval::["{plugin}" == "docker"] +[[container-name]] +## Container Names + +Similar to image name placeholders, for starting and stopping containers and alternate set of placeholders can be configured in order to the name the containers to create. + +These placeholders can be used in the top-level configuration value `containerNamePattern` which is used globally for every container that is created. +This global pattern can be overwritten individually by each image's <> configuration. +If neither is given, then by default the pattern `%n-%a` is used. + +When specifying the container name pattern the following placeholders can be used: + +[cols="1,5"] +|=== +| Placeholder | Description + +| *%a* +| The `` of an image which must be set. The alias is set in the top-level image configuration + +| *%n* +| A sanitized version of the imag's short name from which this container is created. "Sanitized" means that any non letter, digit, dot or dash is replaced by an underscore. + +| *%t* +| The build time stamp. This is the timestamp which created during the building of an image and locally cached. A rebuild of the image will update the timestamp. + +| *%i* +| An index which is incremented if a container has already been created. With this parameter it is easily possible to have multiple, similar containers. See the example below for more details. + +|=== + +You can combine the placeholders in any combination and will be resolved during `docker:start`, `docker:stop` and `docker:watch`. + +The following example is using a container name pattern of `%n-%i` which is also the default. +Given an image `fabric8io/dmp-sample-jolokia:latest`, then during `mvn docker:start` a container with the name `dmp-sample-jolokia-1` is first tried. +If there is already a container with this name, then `dmp-sample-jolokia-2` is the second attempt. +This goes on until a "free" name is found. + +Similar, when stopping containers with `mvn docker:stop` then only the container with the highest index is stopped. +However, if you don't use an index via `%i` then _all_ containers started with `docker:start` are stopped. +Use `mvn docker:stop -Ddocker.allContainers` to also stop every container named via a `%i` pattern. +endif::[] diff --git a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java index 4a9e5316c..1ddac502b 100644 --- a/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java +++ b/src/test/java/io/fabric8/maven/docker/util/ContainerNamingUtilTest.java @@ -115,7 +115,7 @@ public void testContainersToStop() { null, new Date(123456), containers); - Assert.assertEquals(2, filtered.size()); + Assert.assertEquals(1, filtered.size()); Assert.assertEquals(container2, filtered.iterator().next()); }