org.apache.whirr.ClusterController.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.whirr.ClusterController.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.whirr;

import static org.apache.whirr.RolePredicates.withIds;
import static org.jclouds.compute.options.RunScriptOptions.Builder.overrideLoginCredentials;

import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.apache.whirr.actions.BootstrapClusterAction;
import org.apache.whirr.actions.CleanupClusterAction;
import org.apache.whirr.actions.ConfigureServicesAction;
import org.apache.whirr.actions.DestroyClusterAction;
import org.apache.whirr.actions.StartServicesAction;
import org.apache.whirr.actions.StopServicesAction;
import org.apache.whirr.state.ClusterStateStore;
import org.apache.whirr.state.ClusterStateStoreFactory;
import org.apache.whirr.service.ComputeCache;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.RunScriptOnNodesException;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeState;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.scriptbuilder.domain.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * This class is used to start and stop clusters.
 */
public class ClusterController {

    private static final Logger LOG = LoggerFactory.getLogger(ClusterController.class);

    private static final ImmutableSet<String> EMPTYSET = ImmutableSet.of();

    private final Function<ClusterSpec, ComputeServiceContext> getCompute;
    private final ClusterStateStoreFactory stateStoreFactory;

    protected HandlerMapFactory handlerMapFactory = new HandlerMapFactory();

    public ClusterController() {
        this(ComputeCache.INSTANCE, new ClusterStateStoreFactory());
    }

    public ClusterController(Function<ClusterSpec, ComputeServiceContext> getCompute,
            ClusterStateStoreFactory stateStoreFactory) {
        this.getCompute = getCompute;
        this.stateStoreFactory = stateStoreFactory;
    }

    /**
     * @return the unique name of the service.
     */
    public String getName() {
        throw new UnsupportedOperationException("No service name");
    }

    /**
     * @return compute service contexts for use in managing the service
     */
    public Function<ClusterSpec, ComputeServiceContext> getCompute() {
        return getCompute;
    }

    /**
     * Start the cluster described by <code>clusterSpec</code> and block until the
     * cluster is
     * available. It is not guaranteed that the service running on the cluster
     * has started when this method returns.
     *
     * @param clusterSpec
     * @return an object representing the running cluster
     * @throws IOException          if there is a problem while starting the cluster. The
     *                              cluster may or may not have started.
     * @throws InterruptedException if the thread is interrupted.
     */
    public Cluster launchCluster(ClusterSpec clusterSpec) throws IOException, InterruptedException {
        try {
            Cluster cluster = bootstrapCluster(clusterSpec);
            cluster = configureServices(clusterSpec, cluster);
            return startServices(clusterSpec, cluster);

        } catch (Throwable e) {

            if (clusterSpec.isTerminateAllOnLaunchFailure()) {
                LOG.error("Unable to start the cluster. Terminating all nodes.", e);
                destroyCluster(clusterSpec);

            } else {
                LOG.error("*CRITICAL* the cluster failed to launch and the automated node termination"
                        + " option was not selected, there might be orphaned nodes.", e);
            }

            throw new RuntimeException(e);
        }
    }

    /**
     * Provision the hardware resources needed for running services
     */
    public Cluster bootstrapCluster(ClusterSpec clusterSpec) throws IOException, InterruptedException {
        BootstrapClusterAction bootstrapper = new BootstrapClusterAction(getCompute(), handlerMapFactory.create());
        Cluster cluster = bootstrapper.execute(clusterSpec, null);
        getClusterStateStore(clusterSpec).save(cluster);
        return cluster;
    }

    /**
     * Configure cluster services
     */
    @Beta
    public Cluster configureServices(ClusterSpec spec) throws IOException, InterruptedException {
        return configureServices(spec, new Cluster(getInstances(spec, getClusterStateStore(spec))));
    }

    @Beta
    public Cluster configureServices(ClusterSpec clusterSpec, Cluster cluster)
            throws IOException, InterruptedException {
        return configureServices(clusterSpec, cluster, EMPTYSET, EMPTYSET);
    }

    @Beta
    public Cluster configureServices(ClusterSpec clusterSpec, Cluster cluster, Set<String> targetRoles,
            Set<String> targetInstanceIds) throws IOException, InterruptedException {
        ConfigureServicesAction configurer = new ConfigureServicesAction(getCompute(), handlerMapFactory.create(),
                targetRoles, targetInstanceIds);
        return configurer.execute(clusterSpec, cluster);
    }

    /**
     * Start the cluster services
     */
    public Cluster startServices(ClusterSpec spec) throws IOException, InterruptedException {
        return startServices(spec, new Cluster(getInstances(spec, getClusterStateStore(spec))));
    }

    public Cluster startServices(ClusterSpec clusterSpec, Cluster cluster)
            throws IOException, InterruptedException {
        return startServices(clusterSpec, cluster, EMPTYSET, EMPTYSET);
    }

    public Cluster startServices(ClusterSpec clusterSpec, Cluster cluster, Set<String> targetRoles,
            Set<String> targetInstanceIds) throws IOException, InterruptedException {
        StartServicesAction starter = new StartServicesAction(getCompute(), handlerMapFactory.create(), targetRoles,
                targetInstanceIds);
        return starter.execute(clusterSpec, cluster);
    }

    /**
     * Stop the cluster services
     */
    public Cluster stopServices(ClusterSpec spec) throws IOException, InterruptedException {
        return stopServices(spec, new Cluster(getInstances(spec, getClusterStateStore(spec))));
    }

    public Cluster stopServices(ClusterSpec clusterSpec, Cluster cluster) throws IOException, InterruptedException {
        return stopServices(clusterSpec, cluster, EMPTYSET, EMPTYSET);

    }

    public Cluster stopServices(ClusterSpec clusterSpec, Cluster cluster, Set<String> targetRoles,
            Set<String> targetInstanceIds) throws IOException, InterruptedException {
        StopServicesAction stopper = new StopServicesAction(getCompute(), handlerMapFactory.create(), targetRoles,
                targetInstanceIds);
        return stopper.execute(clusterSpec, cluster);
    }

    /**
     * Remove the cluster services
     */
    @Beta
    public Cluster cleanupCluster(ClusterSpec spec) throws IOException, InterruptedException {
        return cleanupCluster(spec, new Cluster(getInstances(spec, getClusterStateStore(spec))));
    }

    @Beta
    public Cluster cleanupCluster(ClusterSpec clusterSpec, Cluster cluster)
            throws IOException, InterruptedException {
        CleanupClusterAction cleanner = new CleanupClusterAction(getCompute(), handlerMapFactory.create());
        return cleanner.execute(clusterSpec, cluster);
    }

    /**
     * Stop the cluster and destroy all resources associated with it.
     *
     * @throws IOException          if there is a problem while stopping the cluster. The cluster may
     *                              or may not have been stopped.
     * @throws InterruptedException if the thread is interrupted.
     */
    public void destroyCluster(ClusterSpec clusterSpec) throws IOException, InterruptedException {
        ClusterStateStore stateStore = getClusterStateStore(clusterSpec);
        destroyCluster(clusterSpec, stateStore.tryLoadOrEmpty());
        stateStore.destroy();
    }

    public void destroyCluster(ClusterSpec clusterSpec, Cluster cluster) throws IOException, InterruptedException {
        DestroyClusterAction destroyer = new DestroyClusterAction(getCompute(), handlerMapFactory.create());
        destroyer.execute(clusterSpec, cluster);
    }

    public void destroyInstance(ClusterSpec clusterSpec, String instanceId) throws IOException {
        LOG.info("Destroying instance {}", instanceId);

        /* Destroy the instance */
        ComputeService computeService = getCompute().apply(clusterSpec).getComputeService();
        computeService.destroyNode(instanceId);

        /* .. and update the cluster state storage */
        ClusterStateStore store = getClusterStateStore(clusterSpec);
        Cluster cluster = store.load();
        cluster.removeInstancesMatching(withIds(instanceId));
        store.save(cluster);

        LOG.info("Instance {} destroyed", instanceId);
    }

    public ClusterStateStore getClusterStateStore(ClusterSpec clusterSpec) {
        return stateStoreFactory.create(clusterSpec);
    }

    public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(ClusterSpec spec,
            Predicate<NodeMetadata> condition, Statement statement) throws IOException, RunScriptOnNodesException {
        return runScriptOnNodesMatching(spec, condition, statement, null);
    }

    public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(ClusterSpec spec,
            Predicate<NodeMetadata> condition, Statement statement, RunScriptOptions options)
            throws IOException, RunScriptOnNodesException {

        LoginCredentials credentials = LoginCredentials.builder().user(spec.getClusterUser())
                .privateKey(spec.getPrivateKey()).build();

        if (options == null) {
            options = defaultRunScriptOptionsForSpec(spec);
        } else if (options.getLoginUser() == null) {
            options = options.overrideLoginCredentials(credentials);
        }
        condition = Predicates.and(runningInGroup(spec.getClusterName()), condition);

        ComputeServiceContext context = getCompute().apply(spec);
        return context.getComputeService().runScriptOnNodesMatching(condition, statement, options);
    }

    public RunScriptOptions defaultRunScriptOptionsForSpec(ClusterSpec spec) {
        LoginCredentials credentials = LoginCredentials.builder().user(spec.getClusterUser())
                .privateKey(spec.getPrivateKey()).build();
        return overrideLoginCredentials(credentials).wrapInInitScript(false).runAsRoot(false);
    }

    @Deprecated
    public Set<? extends NodeMetadata> getNodes(ClusterSpec clusterSpec) throws IOException, InterruptedException {
        ComputeService computeService = getCompute().apply(clusterSpec).getComputeService();
        return computeService.listNodesDetailsMatching(runningInGroup(clusterSpec.getClusterName()));
    }

    public Set<Cluster.Instance> getInstances(ClusterSpec spec) throws IOException, InterruptedException {
        return getInstances(spec, null);
    }

    public Set<Cluster.Instance> getInstances(ClusterSpec spec, ClusterStateStore stateStore)
            throws IOException, InterruptedException {

        Set<Cluster.Instance> instances = Sets.newLinkedHashSet();
        Cluster cluster = (stateStore != null) ? stateStore.load() : null;

        for (NodeMetadata node : getNodes(spec)) {
            instances.add(toInstance(node, cluster, spec));
        }

        return instances;
    }

    public HandlerMapFactory getHandlerMapFactory() {
        return handlerMapFactory;
    }

    public void setHandlerMapFactory(HandlerMapFactory handlerMapFactory) {
        this.handlerMapFactory = handlerMapFactory;
    }

    private Cluster.Instance toInstance(NodeMetadata metadata, Cluster cluster, ClusterSpec spec) {
        Credentials credentials = new Credentials(spec.getClusterUser(), spec.getPrivateKey());

        Set<String> roles = Sets.newHashSet();
        try {
            if (cluster != null) {
                roles = cluster.getInstanceMatching(withIds(metadata.getId())).getRoles();
            }
        } catch (NoSuchElementException e) {
        }

        return new Cluster.Instance(credentials, roles, Iterables.getFirst(metadata.getPublicAddresses(), null),
                Iterables.getFirst(metadata.getPrivateAddresses(), null), metadata.getId(), metadata);
    }

    public static Predicate<ComputeMetadata> runningInGroup(final String group) {
        return new Predicate<ComputeMetadata>() {
            @Override
            public boolean apply(ComputeMetadata computeMetadata) {
                // Not all list calls return NodeMetadata (e.g. VCloud)
                if (computeMetadata instanceof NodeMetadata) {
                    NodeMetadata nodeMetadata = (NodeMetadata) computeMetadata;
                    return group.equals(nodeMetadata.getGroup()) && nodeMetadata.getState() == NodeState.RUNNING;
                }
                return false;
            }

            @Override
            public String toString() {
                return "runningInGroup(" + group + ")";
            }
        };
    }

}