All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fabric8.maven.docker.util.StartOrderResolver Maven / Gradle / Ivy

There is a newer version: 0.45.0
Show newest version
package io.fabric8.maven.docker.util;

import java.util.*;

import io.fabric8.maven.docker.access.DockerAccessException;
import io.fabric8.maven.docker.service.QueryService;
import org.codehaus.plexus.util.StringUtils;

/**
 * @author roland
 * @since 16.10.14
 */
public class StartOrderResolver {

    public static final int MAX_RESOLVE_RETRIES = 10;

    private final QueryService queryService;

    private final List secondPass;
    private final Set processedImages;

    public static List resolve(QueryService queryService, List convertToResolvables) {
        return new StartOrderResolver(queryService).resolve(convertToResolvables);
    }

    private StartOrderResolver(QueryService queryService) {
        this.queryService = queryService;

        this.secondPass = new ArrayList<>();
        this.processedImages = new HashSet<>();
    }


    // Check images for volume / link dependencies and return it in the right order.
    // Only return images which should be run
    // Images references via volumes but with no run configuration are started once to create
    // an appropriate container which can be linked into the image
    private List resolve(List images) {
        List resolved = new ArrayList<>();
        // First pass: Pick all data images and all without dependencies
        for (Resolvable config : images) {
            List volumesOrLinks = extractDependentImagesFor(config);
            if (volumesOrLinks == null) {
                // A data image only or no dependency. Add it to the list of data image which can be always
                // created first.
                updateProcessedImages(config);
                resolved.add(config);
            } else {
                secondPass.add(config);
            }
        }

        // Next passes: Those with dependencies are checked whether they already have been visited.
        return secondPass.size() > 0 ? resolveRemaining(resolved) : resolved;
    }

    private List resolveRemaining(List ret) {
        int retries = MAX_RESOLVE_RETRIES;
        String error = null;
        try {
            do {
                resolveImageDependencies(ret);
            } while (secondPass.size() > 0  && retries-- > 0);
        } catch (DockerAccessException | ResolveSteadyStateException e) {
            error = "Cannot resolve image dependencies for start order\n" + remainingImagesDescription();
        }
        if (retries == 0 && secondPass.size() > 0) {
            error = "Cannot resolve image dependencies after " + MAX_RESOLVE_RETRIES + " passes\n"
                    + remainingImagesDescription();
        }
        if (error != null) {
            throw new IllegalStateException(error);
        }
        return ret;
    }

    private void updateProcessedImages(Resolvable config) {
        processedImages.add(config.getName());
        if (config.getAlias() != null) {
            processedImages.add(config.getAlias());
        }
    }

    private String remainingImagesDescription() {
        StringBuffer ret = new StringBuffer();
        ret.append("Unresolved images:\n");
        for (Resolvable config : secondPass) {
            ret.append("* ")
               .append(config.getAlias())
               .append(" depends on ")
               .append(StringUtils.join(config.getDependencies().toArray(), ","))
               .append("\n");
        }
        return ret.toString();
    }

    private void resolveImageDependencies(List resolved) throws DockerAccessException, ResolveSteadyStateException {
        boolean changed = false;
        Iterator iterator = secondPass.iterator();

        while (iterator.hasNext()) {
            Resolvable config = iterator.next();
            if (hasRequiredDependencies(config)) {
                updateProcessedImages(config);
                resolved.add(config);
                changed = true;
                iterator.remove();
            }
        }

        if (!changed) {
            throw new ResolveSteadyStateException();
        }
    }

    private boolean hasRequiredDependencies(Resolvable config) throws DockerAccessException {
        List dependencies = extractDependentImagesFor(config);
        if (dependencies == null) {
            return false;
        }

        for (String dependency : dependencies) {
            // make sure the container exists, it's state will be verified elsewhere
            if (processedImages.contains(dependency) ||
                // Check also whether a *container* with this name exists in which case it is interpreted
                // as an external dependency which is already running
                queryService.hasContainer(dependency)) {
                continue;
            }
            return false;
        }

        return true;
    }

    private List extractDependentImagesFor(Resolvable config) {
        LinkedHashSet ret = new LinkedHashSet<>(config.getDependencies());
        return ret.isEmpty() ? null : new ArrayList<>(ret);
    }

    // Exception indicating a steady state while resolving start order of images
    private static class ResolveSteadyStateException extends Throwable { }

    public interface Resolvable {
        String getName();
        String getAlias();
        List getDependencies();
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy