Skip to content

Commit

Permalink
Add support for pattern-matching to docker:stop and docker:remove.
Browse files Browse the repository at this point in the history
Separate name patterns are supported for each goal. For docker:stop, the patterns are applied to the container name and container image repo tag. While for docker:remove, the patterns are applied to the image repo tags.

Signed-off-by: William Rose <william.rose@nuance.com>
  • Loading branch information
wrosenuance committed Apr 20, 2019
1 parent ae654a0 commit ea84a13
Show file tree
Hide file tree
Showing 27 changed files with 2,460 additions and 322 deletions.
14 changes: 6 additions & 8 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: 2
jobs:
# Build Website
doc:
working_directory: ~/wrosenuance/dmp-docs
working_directory: ~/fabric8io/dmp-docs
docker:
- image: circleci/node:8-browsers
steps:
Expand All @@ -15,7 +15,7 @@ jobs:

# Run unit tests
build:
working_directory: ~/wrosenuance/docker-maven-plugin
working_directory: ~/fabric8io/docker-maven-plugin
docker:
- image: circleci/openjdk:8-node-browsers
steps:
Expand All @@ -30,7 +30,7 @@ jobs:
- ~/.m2

sonar-pr:
working_directory: ~/wrosenuance/dmp-sonar-pr
working_directory: ~/fabric8io/dmp-sonar-pr
docker:
- image: circleci/openjdk:8-node-browsers
steps:
Expand All @@ -41,7 +41,7 @@ jobs:
if [ -n "${CIRCLE_PR_NUMBER}" ]; then
mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar \
-Dsonar.github.pullRequest=${CIRCLE_PR_NUMBER} \
-Dsonar.github.repository=wrosenuance/docker-maven-plugin \
-Dsonar.github.repository=fabric8io/docker-maven-plugin \
-Dsonar.github.oauth=${GITHUB_COMMENT_TOKEN} \
-Dsonar.host.url=https://sonarcloud.io \
-Dsonar.login=${SONARQUBE_TOKEN}
Expand All @@ -55,7 +55,7 @@ jobs:
- ~/.m2

sonar:
working_directory: ~/wrosenuance/dmp-sonar
working_directory: ~/fabric8io/dmp-sonar
docker:
- image: circleci/openjdk:8-node-browsers
steps:
Expand All @@ -64,8 +64,6 @@ jobs:
key: dmp-sonar-{{ checksum "pom.xml" }}
- run: |
mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar \
-Dsonar.projectKey=wrosenuance_docker-maven-plugin \
-Dsonar.organization=wrosenuance-github \
-Dsonar.host.url=https://sonarcloud.io \
-Dsonar.login=${SONARQUBE_TOKEN}
- save_cache:
Expand All @@ -86,4 +84,4 @@ workflows:
- sonar:
filters:
branches:
only: local-master
only: master
22 changes: 22 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -629,5 +629,27 @@
<defaultGoal>generate-resources asciidoctor:process-asciidoc</defaultGoal>
</build>
</profile>
<profile>
<id>checkstyle</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.0.0</version>

<configuration>
<checkstyleRules>
<module name="Checker">
<module name="TreeWalker">
<module name="UnusedImports" />
</module>
</module>
</checkstyleRules>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
60 changes: 54 additions & 6 deletions src/main/java/io/fabric8/maven/docker/RemoveMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@
import io.fabric8.maven.docker.service.QueryService;
import io.fabric8.maven.docker.service.ServiceHub;
import io.fabric8.maven.docker.util.ImageName;
import io.fabric8.maven.docker.util.NamePatternUtil;

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.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* Mojo for removing images. By default only data images are removed. Data images are
Expand Down Expand Up @@ -58,19 +64,22 @@ public class RemoveMojo extends AbstractDockerMojo {
*/
@Parameter(property = "docker.skip.tag", defaultValue = "false")
private boolean skipTag;


@Parameter(property = "docker.removeNamePattern")
private String removeNamePattern;

@Override
protected void executeInternal(ServiceHub hub) throws DockerAccessException {
protected void executeInternal(ServiceHub hub) throws DockerAccessException, MojoExecutionException {
for (ImageConfiguration image : getResolvedImages()) {
String name = image.getName();

if (imageShouldBeRemoved(image)) {
removeImage(hub, name);
for(String name : getImageNamesToRemove(hub, image)) {
removeImage(hub, name);
}

if(!skipTag) {
// Remove any tagged images
for (String tag: getImageBuildTags(image)){
removeImage(hub, new ImageName(name, tag).getFullName());
removeImage(hub, new ImageName(image.getName(), tag).getFullName());
}
}
}
Expand All @@ -97,6 +106,45 @@ private boolean imageShouldBeRemoved(ImageConfiguration image) {
return image.getBuildConfiguration() != null;
}

private Collection<String> getImageNamesToRemove(ServiceHub hub, ImageConfiguration imageConfiguration)
throws MojoExecutionException, DockerAccessException {

String removeNamePattern = imageConfiguration.getRemoveNamePattern() == null
? this.removeNamePattern : imageConfiguration.getRemoveNamePattern();

if(removeNamePattern != null) {
Matcher imageNameMatcher = getImageNameMatcher(removeNamePattern);

if(imageNameMatcher == null) {
log.warn("There are no image name patterns in removeNamePattern for image %s: no images will be removed", imageConfiguration.getName());
return Collections.emptyList();
}

return hub.getQueryService().listImages(false)
.stream()
.flatMap(image -> image.getRepoTags().stream())
.filter(repoTag -> imageNameMatcher.reset(repoTag).matches())
.collect(Collectors.toList());
}

return Collections.singleton(imageConfiguration.getName());
}

private Matcher getImageNameMatcher(String removeNamePattern) throws MojoExecutionException {
try {
String imageNameRegex = NamePatternUtil.convertNamePatternList(removeNamePattern, NamePatternUtil.IMAGE_FIELD, true);
if(imageNameRegex == null) {
log.debug("No image name patterns in removeNamePattern %s", removeNamePattern);
return null;
} else {
log.debug("Converted removeNamePattern %s into image name regular expression %s", removeNamePattern, imageNameRegex);
return Pattern.compile(imageNameRegex).matcher("");
}
} catch(IllegalArgumentException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}

private void removeImage(ServiceHub hub, String name) throws DockerAccessException {
QueryService queryService = hub.getQueryService();
if (queryService.hasImage(name)) {
Expand Down
96 changes: 89 additions & 7 deletions src/main/java/io/fabric8/maven/docker/StopMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import io.fabric8.maven.docker.access.ExecException;
import io.fabric8.maven.docker.config.ImageConfiguration;
Expand All @@ -18,6 +21,8 @@
import io.fabric8.maven.docker.service.ServiceHub;
import io.fabric8.maven.docker.util.ContainerNamingUtil;
import io.fabric8.maven.docker.util.GavLabel;
import io.fabric8.maven.docker.util.NamePatternUtil;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
Expand Down Expand Up @@ -60,6 +65,9 @@ public class StopMojo extends AbstractDockerMojo {
@Parameter(property = "docker.containerNamePattern")
private String containerNamePattern = ContainerNamingUtil.DEFAULT_CONTAINER_NAME_PATTERN;

@Parameter(property = "docker.stopNamePattern")
private String stopNamePattern;

@Override
protected void executeInternal(ServiceHub hub) throws MojoExecutionException, IOException, ExecException {
QueryService queryService = hub.getQueryService();
Expand All @@ -80,15 +88,15 @@ protected void executeInternal(ServiceHub hub) throws MojoExecutionException, IO
dispatcher.untrackAllContainerLogs();
}

private void stopContainers(QueryService queryService, RunService runService, GavLabel gavLabel) throws IOException, ExecException {
private void stopContainers(QueryService queryService, RunService runService, GavLabel gavLabel)
throws MojoExecutionException, IOException, ExecException {

Collection<Network> networksToRemove = getNetworksToRemove(queryService, gavLabel);
for (ImageConfiguration image : getResolvedImages()) {

Collection<Container> existingContainers =
ContainerNamingUtil.getContainersToStop(image,
containerNamePattern,
getBuildTimestamp(),
queryService.getContainersForImage(image.getName(), false));
Collection<Container> existingContainers
= getContainersForImage(queryService, image);

for (Container container : existingContainers) {
if (shouldStopContainer(container, gavLabel)) {
runService.stopContainer(container.getId(), image, keepContainer, removeVolumes);
Expand All @@ -98,6 +106,80 @@ private void stopContainers(QueryService queryService, RunService runService, Ga
runService.removeCustomNetworks(networksToRemove);
}

private Collection<Container> getContainersForImage(QueryService queryService, ImageConfiguration image)
throws MojoExecutionException, IOException {

String stopNamePattern = getStopNamePatternForImage(image);

if(stopNamePattern != null) {
Matcher imageNameMatcher = getImageNameMatcher(stopNamePattern);

Matcher containerNameMatcher = getContainerNameMatcher(stopNamePattern);

if(imageNameMatcher == null && containerNameMatcher == null) {
log.warn("There are no image name or container name patterns in stopNamePattern for image %s: no containers will be stopped", image.getName());
return Collections.emptyList();
}

return queryService.listContainers(!keepContainer)
.stream()
.filter(c -> containerMatchesPattern(c, image.getName(), imageNameMatcher, containerNameMatcher))
.collect(Collectors.toList());
}

return ContainerNamingUtil.getContainersToStop(image,
containerNamePattern,
getBuildTimestamp(),
queryService.getContainersForImage(image.getName(), !keepContainer));
}

private String getStopNamePatternForImage(ImageConfiguration image) {
return image.getStopNamePattern() == null ? this.stopNamePattern : image.getStopNamePattern();
}

private Matcher getImageNameMatcher(String stopNamePattern) throws MojoExecutionException {
try {
String imageNameRegex = NamePatternUtil.convertNamePatternList(stopNamePattern, NamePatternUtil.IMAGE_FIELD, true);
if(imageNameRegex == null) {
log.debug("No image name patterns in stopNamePattern %s", stopNamePattern);
return null;
} else {
log.debug("Converted stopNamePattern %s into image name regular expression %s", stopNamePattern, imageNameRegex);
return Pattern.compile(imageNameRegex).matcher("");
}
} catch(IllegalArgumentException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}

private Matcher getContainerNameMatcher(String stopNamePattern) throws MojoExecutionException {
try {
String containerNameRegex = NamePatternUtil.convertNamePatternList(stopNamePattern, NamePatternUtil.NAME_FIELD, true);;
if(containerNameRegex == null) {
log.debug("No container name patterns in stopNamePattern %s", stopNamePattern);
return null;
} else {
log.debug("Converted stopNamePattern %s into container name regular expression %s", stopNamePattern, containerNameRegex);
return Pattern.compile(containerNameRegex).matcher("");
}
} catch(IllegalArgumentException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}

private boolean containerMatchesPattern(Container container, String imageName, Matcher imageNameMatcher, Matcher containerNameMatcher) {
if(imageNameMatcher != null && container.getImage() != null && imageNameMatcher.reset(container.getImage()).find()) {
log.debug("Container image %s matched stopNamePattern for image %s", container.getImage(), imageName);
return true;
} else if(containerNameMatcher != null && container.getName() != null && containerNameMatcher.reset(container.getName()).find()) {
log.debug("Container name %s matched stopNamePattern for image %s", container.getName(), imageName);
return true;
} else {
log.debug("Neither container image %s nor name %s matched stopNamePattern for image %s", container.getImage(), container.getName(), imageName);
return false;
}
}

private boolean shouldStopContainer(Container container, GavLabel gavLabel) {
if (isStopAllContainers()) {
return true;
Expand Down Expand Up @@ -138,7 +220,7 @@ private Set<Network> getNetworksToRemove(QueryService queryService, GavLabel gav
ContainerNamingUtil.getContainersToStop(image,
containerNamePattern,
getBuildTimestamp(),
queryService.getContainersForImage(image.getName(), false));
queryService.getContainersForImage(image.getName(), !keepContainer));

for (Container container : existingContainers) {
if (!shouldStopContainer(container, gavLabel)) {
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/io/fabric8/maven/docker/access/DockerAccess.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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.model.Image;
import io.fabric8.maven.docker.model.Network;

/**
Expand Down Expand Up @@ -64,6 +65,15 @@ public interface DockerAccess {
*/
String getImageId(String name) throws DockerAccessException;

/**
* List all containers from the Docker server.
*
* @param all whether to fetch also stopped containers. If false only running containers are returned
* @return list of <code>Container</code> objects or an empty list if none is found
* @throws DockerAccessException if the request fails
*/
List<Container> listContainers(boolean all) throws DockerAccessException;

/**
* 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.
Expand Down Expand Up @@ -162,6 +172,14 @@ void copyArchive(String containerId, File archive, String targetPath)
*/
void removeContainer(String containerId, boolean removeVolumes) throws DockerAccessException;

/**
* List the containers on the server
* @param all if true, return untagged images
* @return the images list (may be empty but never null)
* @throws DockerAccessException if the list couldn't be retrieved
*/
List<Image> listImages(boolean all) throws DockerAccessException;

/**
* Load an image from an archive.
*
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/io/fabric8/maven/docker/access/UrlBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public String inspectImage(String name) {
.build();
}

public String listImages(boolean all) {
return u("images/json")
.p("all", all)
.build();
}

public String containerLogs(String containerId, boolean follow) {
return u("containers/%s/logs", containerId)
.p("stdout",true)
Expand Down
Loading

0 comments on commit ea84a13

Please sign in to comment.