com.mesosphere.dcos.cassandra.common.tasks.CassandraDaemonTask.java Source code

Java tutorial

Introduction

Here is the source code for com.mesosphere.dcos.cassandra.common.tasks.CassandraDaemonTask.java

Source

/*
 * Copyright 2016 Mesosphere
 *
 * Licensed 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 com.mesosphere.dcos.cassandra.common.tasks;

import com.google.inject.Inject;
import com.mesosphere.dcos.cassandra.common.config.CassandraConfig;
import com.mesosphere.dcos.cassandra.common.config.ExecutorConfig;
import com.mesosphere.dcos.cassandra.common.util.TaskUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.mesos.Protos;
import org.apache.mesos.Protos.DiscoveryInfo;
import org.apache.mesos.Protos.Port;
import org.apache.mesos.dcos.Capabilities;
import org.apache.mesos.offer.VolumeRequirement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * CassandraDaemonTask extends CassandraTask to implement the task for a
 * Cassandra daemon. This is the task that starts and monitors the Cassandra
 * node. It is the first task launched on slave with a new executor, and it
 * must be running for additional tasks to run successfully on the slave. In
 * addition to the basic CassandraTask properties it contains the configuration
 * for the Cassandra node.
 */
public class CassandraDaemonTask extends CassandraTask {
    private static final Logger LOGGER = LoggerFactory.getLogger(CassandraDaemonTask.class);

    /**
     * String prefix for the CassandraDaemon task.
     */
    public static final String NAME_PREFIX = "node-";

    /**
     * Public node name used in VIP
     */
    public static final String VIP_NODE_NAME = "node";

    /**
     * Public port used in VIP
     */
    public static final int VIP_NODE_PORT = 9042; // cassandra's default native transport port

    public static CassandraDaemonTask parse(final Protos.TaskInfo info) {
        return new CassandraDaemonTask(info);
    }

    /**
     * Factory for {@link CassandraDaemonTask}s.
     */
    public static class Factory {

        private final Capabilities capabilities;

        @Inject
        public Factory(Capabilities capabilities) {
            this.capabilities = capabilities;
        }

        public CassandraDaemonTask create(final String name, final String configName,
                final CassandraTaskExecutor executor, final CassandraConfig config) {
            return new CassandraDaemonTask(name, configName, executor, config, capabilities);
        }

        public CassandraDaemonTask move(CassandraDaemonTask currentDaemon, CassandraTaskExecutor executor) {
            final List<Protos.Label> labelsList = currentDaemon.getTaskInfo().getLabels().getLabelsList();
            String configName = "";
            for (Protos.Label label : labelsList) {
                if ("config_target".equals(label.getKey())) {
                    configName = label.getValue();
                }
            }
            if (StringUtils.isBlank(configName)) {
                throw new IllegalStateException("Task should have 'config_target'");
            }
            CassandraDaemonTask replacementDaemon = new CassandraDaemonTask(currentDaemon.getName(), configName,
                    executor, currentDaemon.getConfig(), capabilities,
                    currentDaemon.getData().replacing(currentDaemon.getData().getHostname()));

            Protos.TaskInfo taskInfo = Protos.TaskInfo.newBuilder(replacementDaemon.getTaskInfo())
                    .setTaskId(currentDaemon.getTaskInfo().getTaskId()).build();

            return new CassandraDaemonTask(taskInfo);
        }

        public CassandraDaemonTask create(final String name, final String configName,
                final CassandraTaskExecutor executor, final CassandraConfig config, final CassandraData data) {
            return new CassandraDaemonTask(name, configName, executor, config, capabilities, data);
        }
    }

    private static CassandraConfig updateConfig(final CassandraMode mode, final CassandraConfig config) {
        // If the Cassandra daemon has transitioned to the NORMAL mode, remove the replaceIp so that the next
        // time Cassandra starts, it does not have the startup argument -Dcassandra.replace_address=<replaceIp>.
        if (CassandraMode.NORMAL.equals(mode)) {
            return config.mutable().setReplaceIp("").build();
        } else {
            return config;
        }
    }

    /**
     * Constructs a new CassandraDaemonTask.
     *
     * @param name     The name of the Cassandra node.
     * @param executor The exeuctor for the CassandraDaemonTask.
     * @param config   The configuration for the Cassandra node.
     */
    private CassandraDaemonTask(final String name, final String configName, final CassandraTaskExecutor executor,
            final CassandraConfig config, final Capabilities capabilities, final CassandraData data) {
        super(name, configName, executor, config.getCpus(), config.getMemoryMb(), config.getDiskMb(),
                VolumeRequirement.VolumeMode.CREATE, config.getDiskType(),
                Arrays.asList(config.getJmxPort(), config.getApplication().getStoragePort(),
                        config.getApplication().getSslStoragePort(), config.getApplication().getRpcPort(),
                        config.getApplication().getNativeTransportPort()),
                getDiscoveryInfo(config.getPublishDiscoveryInfo(), config.getApplication().getClusterName(),
                        capabilities, name, config.getApplication().getNativeTransportPort()),
                data);
    }

    private CassandraDaemonTask(final String name, final String configName, final CassandraTaskExecutor executor,
            final CassandraConfig config, final Capabilities capabilities) {
        this(name, configName, executor, config, capabilities,
                CassandraData.createDaemonData("", CassandraMode.STARTING, config));
    }

    private CassandraDaemonTask(final Protos.TaskInfo info) {
        super(info);
    }

    /**
     * Gets the CassandraConfig for the Cassandra daemon.
     *
     * @return The configuration object for the Cassandra daemon.
     */
    public CassandraConfig getConfig() {
        return getData().getConfig();
    }

    public CassandraMode getMode() {
        return getData().getMode();
    }

    @Override
    public CassandraDaemonTask update(CassandraTaskStatus status) {
        if (status.getType() == TYPE.CASSANDRA_DAEMON && status.getId().equals(getId())) {
            CassandraDaemonStatus daemonStatus = (CassandraDaemonStatus) status;
            return new CassandraDaemonTask(Protos.TaskInfo
                    .newBuilder(getTaskInfo()).setData(getData().updateDaemon(daemonStatus.getState(),
                            daemonStatus.getMode(), updateConfig(daemonStatus.getMode(), getConfig())).getBytes())
                    .build());
        }

        return this;
    }

    @Override
    public CassandraDaemonTask update(Protos.TaskState state) {
        return new CassandraDaemonTask(getBuilder().setData(getData().withState(state).getBytes()).build());
    }

    public CassandraDaemonStatus createStatus(Protos.TaskState state, CassandraMode mode,
            Optional<String> message) {
        return CassandraDaemonStatus.create(getStatusBuilder(state, message)
                .setData(CassandraData.createDaemonStatusData(mode).getBytes()).build());
    }

    public String getVolumePath() {
        return TaskUtils.getVolumePaths(getTaskInfo().getResourcesList()).get(0);
    }

    @Override
    public CassandraDaemonStatus createStatus(Protos.TaskState state, Optional<String> message) {
        return createStatus(state, getMode(), message);
    }

    @Override
    public CassandraDaemonTask update(Protos.Offer offer) {
        return new CassandraDaemonTask(getBuilder().setData(getData().withHostname(offer.getHostname()).getBytes())
                .setSlaveId(offer.getSlaveId()).build());
    }

    public CassandraDaemonTask updateConfig(CassandraConfig cassandraConfig, ExecutorConfig executorConfig,
            UUID targetConfigName) {
        LOGGER.info("Updating config for task: {} to config: {}", getTaskInfo().getName(),
                targetConfigName.toString());
        final Protos.Label label = Protos.Label.newBuilder().setKey("config_target")
                .setValue(targetConfigName.toString()).build();
        return new CassandraDaemonTask(getBuilder()
                .setExecutor(getExecutor().update(executorConfig).getExecutorInfo()).setTaskId(createId(getName()))
                .setData(getData().withNewConfig(cassandraConfig).getBytes()).clearResources()
                .addAllResources(TaskUtils.updateResources(cassandraConfig.getCpus(), cassandraConfig.getMemoryMb(),
                        getTaskInfo().getResourcesList()))
                .clearLabels().setLabels(Protos.Labels.newBuilder().addLabels(label).build()).build());
    }

    @Override
    public CassandraDaemonTask updateId() {
        return new CassandraDaemonTask(
                getBuilder().setTaskId(createId(getName())).setExecutor(getExecutor().clearId().getExecutorInfo())
                        .setData(getData().withState(Protos.TaskState.TASK_STAGING).getBytes()).build());
    }

    @Nullable
    private static DiscoveryInfo getDiscoveryInfo(boolean publishDiscoveryInfo, String clusterName,
            Capabilities capabilities, String nodeName, int nativePort) {

        // If the explicit configuration flag for publishing discovery info is set, include the cluster name in the
        // discovery info name and don't use labels.
        if (publishDiscoveryInfo) {
            DiscoveryInfo.Builder discoveryBuilder = DiscoveryInfo.newBuilder()
                    .setVisibility(DiscoveryInfo.Visibility.EXTERNAL).setName(clusterName + "." + nodeName);
            discoveryBuilder.getPortsBuilder().addPortsBuilder().setName("NativeTransport").setNumber(nativePort);
            return discoveryBuilder.build();
        }

        // Else, check if DC/OS has the right capabilities and publish the discovery info the DC/OS way.
        try {
            if (!capabilities.supportsNamedVips()) {
                return null;
            }
        } catch (Exception e) {
            LOGGER.warn("Unable to determine Named VIP support, assuming they are unavailable.", e);
            return null;
        }
        DiscoveryInfo.Builder discoveryBuilder = DiscoveryInfo.newBuilder()
                .setVisibility(DiscoveryInfo.Visibility.EXTERNAL).setName(nodeName);
        Port.Builder portBuilder = discoveryBuilder.getPortsBuilder().addPortsBuilder().setNumber(nativePort)
                .setProtocol("tcp");
        portBuilder.getLabelsBuilder().addLabelsBuilder().setKey("VIP_" + UUID.randomUUID())
                .setValue(String.format("%s:%d", VIP_NODE_NAME, VIP_NODE_PORT));
        return discoveryBuilder.build();
    }
}