void clean(final Id id) {
   if (id == null) {
     throw new IllegalArgumentException("id is null");
   }
   stop(id);
   logger.info("Cleaning " + id);
   for (Container container : findAllContainers(id)) {
     logger.info("Removing container " + container.getId());
     try {
       removeContainer(container);
     } catch (DockerException e) {
       throw new OrchestrationException(e);
     }
   }
   String imageId = null;
   try {
     imageId = findImageId(id);
   } catch (NotFoundException e) {
     logger.warn("Image " + id + " not found");
   } catch (DockerException e) {
     throw new OrchestrationException(e);
   }
   if (imageId != null) {
     logger.info("Removing image " + imageId);
     try {
       docker.removeImageCmd(imageId).withForce().exec();
     } catch (DockerException e) {
       logger.warn(e.getMessage());
     }
   }
 }
  private void start(final Id id) {
    if (id == null) {
      throw new IllegalArgumentException("id is null");
    }
    final Pattern waitForPattern = compileWaitForPattern(id);

    logger.info("Starting " + id);

    try {
      if (!imageExists(id)) {
        logger.info("Image does not exist, so building it");
        build(id);
      }
    } catch (DockerException e) {
      throw new OrchestrationException(e);
    }

    try {
      Container existingContainer = findContainer(id);

      if (existingContainer == null) {
        logger.info("No existing container so creating and starting new one");
        startContainer(createNewContainer(id));

      } else if (!isImageIdFromContainerMatchingProvidedImageId(existingContainer.getId(), id)) {
        logger.info("Image IDs do not match, removing container and creating new one from image");
        removeContainer(existingContainer);
        startContainer(createNewContainer(id));

      } else if (isRunning(id)) {
        logger.info("Container already running");

      } else {
        logger.info("Starting existing container " + existingContainer.getId());
        startContainer(existingContainer.getId());
      }

      try (Tail tail = tailFactory.newTail(docker, findContainer(id), logger)) {
        tail.start();

        for (Plugin plugin : plugins) {
          plugin.started(id, conf(id));
        }

        healthCheck(id);

        sleep(id);

        tail.setMaxLines(conf(id).getMaxLogLines());
      }
      waitFor(id, waitForPattern);
    } catch (DockerException e) {
      throw new OrchestrationException(e);
    }
  }
 private boolean isRunning(Id id) {
   if (id == null) {
     throw new IllegalArgumentException("id is null");
   }
   boolean running = false;
   final Container candidate = findContainer(id);
   for (Container container : docker.listContainersCmd().withShowAll(false).exec()) {
     running |= candidate != null && candidate.getId().equals(container.getId());
   }
   return running;
 }
  private void waitFor(Id id, Pattern pattern) {
    if (pattern == null) {
      return;
    }

    final StopWatch watch = new StopWatch();
    watch.start();
    logger.info(String.format("Waiting for '%s' to appear in output", pattern.toString()));

    final Container container;

    try {
      container = findContainer(id);
    } catch (DockerException e) {
      throw new OrchestrationException(e);
    }

    if (container == null) {
      logger.warn(String.format("Can not find container %s, not waiting", id));
      return;
    }

    try {
      final LogContainerCmd logContainerCmd =
          docker
              .logContainerCmd(container.getId())
              .withStdErr()
              .withStdOut()
              .withFollowStream()
              .withTimestamps();

      final InputStream stream = logContainerCmd.exec();

      try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
        String line;
        while ((line = reader.readLine()) != null) {
          if (pattern.matcher(line).find()) {
            watch.stop();
            logger.info(String.format("Waited for %s", watch.toString()));
            return;
          }
        }
        throw new OrchestrationException("Container log ended before line appeared in output");
      }
    } catch (Exception e) {
      logger.warn(
          "Unable to obtain logs from container "
              + container.getId()
              + ", will continue without waiting: ",
          e);
    }
  }
 private List<Container> findContainers(Id id, boolean allContainers) {
   final List<Container> matchingContainers = new ArrayList<>();
   for (Container container : docker.listContainersCmd().withShowAll(allContainers).exec()) {
     boolean imageNameMatches = container.getImage().equals(repo.imageName(id));
     String[] containerNames = container.getNames();
     if (containerNames == null) {
       // Every container should have a name, but this is not the case
       // on Circle CI. Containers with no name are broken residues of
       // building the image and therefore will be ignored.
       continue;
     }
     boolean containerNameMatches = asList(containerNames).contains(containerName(id));
     if (imageNameMatches || containerNameMatches) {
       matchingContainers.add(container);
     }
   }
   return matchingContainers;
 }
  private void stop(final Id id) {
    if (id == null) {
      throw new IllegalArgumentException("id is null");
    }

    logger.info("Stopping " + id);

    for (Container container : findRunningContainers(id)) {
      logger.info("Stopping container " + Arrays.toString(container.getNames()));
      try {
        docker.stopContainerCmd(container.getId()).withTimeout(1).exec();
      } catch (DockerException e) {
        throw new OrchestrationException(e);
      }
    }
    for (Plugin plugin : plugins) {
      plugin.stopped(id, conf(id));
    }
  }
 private void removeContainer(Container existingContainer) {
   try {
     docker.removeContainerCmd(existingContainer.getId()).withForce().exec();
   } catch (InternalServerErrorException e) {
     if (permissionErrorTolerant && isPermissionError(e)) {
       logger.warn(
           String.format(
               "ignoring %s when removing container as we are configured to be permission error tolerant",
               e));
     } else {
       throw e;
     }
   }
 }