org.apache.brooklyn.entity.nosql.riak.RiakNodeSshDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.entity.nosql.riak.RiakNodeSshDriver.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.brooklyn.entity.nosql.riak;

import static java.lang.String.format;
import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL;
import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR;
import static org.apache.brooklyn.util.ssh.BashCommands.addSbinPathCommand;
import static org.apache.brooklyn.util.ssh.BashCommands.sbinPath;
import static org.apache.brooklyn.util.ssh.BashCommands.alternatives;
import static org.apache.brooklyn.util.ssh.BashCommands.chainGroup;
import static org.apache.brooklyn.util.ssh.BashCommands.commandToDownloadUrlAs;
import static org.apache.brooklyn.util.ssh.BashCommands.ifExecutableElse;
import static org.apache.brooklyn.util.ssh.BashCommands.ifNotExecutable;
import static org.apache.brooklyn.util.ssh.BashCommands.ok;
import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
import static org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes.escapeLiteralForDoubleQuotedBash;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.brooklyn.api.location.OsDetails;
import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.entity.java.JavaSoftwareProcessSshDriver;
import org.apache.brooklyn.entity.software.base.lifecycle.ScriptHelper;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.ssh.SshTasks;
import org.apache.brooklyn.util.net.Urls;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.ssh.BashCommands;
import org.apache.brooklyn.util.text.Strings;

import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;

// TODO: Alter -env ERL_CRASH_DUMP path in vm.args
public class RiakNodeSshDriver extends JavaSoftwareProcessSshDriver implements RiakNodeDriver {

    private static final Logger LOG = LoggerFactory.getLogger(RiakNodeSshDriver.class);
    private static final String INSTALLING_FALLBACK = INSTALLING + "_fallback";

    public RiakNodeSshDriver(final RiakNodeImpl entity, final SshMachineLocation machine) {
        super(entity, machine);
    }

    @Override
    protected String getLogFileLocation() {
        return "/var/log/riak/solr.log";
    }

    @Override
    public RiakNodeImpl getEntity() {
        return RiakNodeImpl.class.cast(super.getEntity());
    }

    @Override
    public Map<String, String> getShellEnvironment() {
        MutableMap<String, String> result = MutableMap.copyOf(super.getShellEnvironment());
        // how to change epmd port, according to
        // http://serverfault.com/questions/582787/how-to-change-listening-interface-of-rabbitmqs-epmd-port-4369
        if (getEntity().getEpmdListenerPort() != null) {
            result.put("ERL_EPMD_PORT", Integer.toString(getEntity().getEpmdListenerPort()));
        }
        result.put("WAIT_FOR_ERLANG", "60");
        return result;
    }

    @Override
    public void preInstall() {
        resolver = Entities.newDownloader(this);
        setExpandedInstallDir(
                Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(format("riak-%s", getVersion()))));

        // Set package install attribute
        OsDetails osDetails = getMachine().getMachineDetails().getOsDetails();
        if (osDetails.isLinux()) {
            entity.sensors().set(RiakNode.RIAK_PACKAGE_INSTALL, true);
        } else if (osDetails.isMac()) {
            entity.sensors().set(RiakNode.RIAK_PACKAGE_INSTALL, false);
        }
    }

    @Override
    public void install() {
        if (entity.getConfig(Attributes.DOWNLOAD_URL) != null) {
            LOG.warn("Ignoring download.url {}, use download.url.rhelcentos or download.url.mac",
                    entity.getConfig(Attributes.DOWNLOAD_URL));
        }

        OsDetails osDetails = getMachine().getMachineDetails().getOsDetails();
        List<String> commands = Lists.newLinkedList();
        if (osDetails.isLinux()) {
            if (getEntity().isPackageDownloadUrlProvided()) {
                commands.addAll(installLinuxFromPackageUrl());
            } else {
                commands.addAll(installFromPackageCloud());
            }
        } else if (osDetails.isMac()) {
            commands.addAll(installMac());
        } else if (osDetails.isWindows()) {
            throw new UnsupportedOperationException("RiakNode not supported on Windows instances");
        } else {
            throw new IllegalStateException(
                    "Machine was not detected as linux, mac or windows! Installation does not know how to proceed with "
                            + getMachine() + ". Details: " + getMachine().getMachineDetails().getOsDetails());
        }

        int result = newScript(INSTALLING).body.append(commands).failIfBodyEmpty().execute();

        if (result != 0 && osDetails.isLinux()) {
            result = newScript(INSTALLING_FALLBACK).body.append(installLinuxFromPackageUrl()).execute();
        }

        if (result != 0) {
            throw new IllegalStateException(String.format("Install failed with result %d", result));
        }
    }

    private List<String> installLinuxFromPackageUrl() {
        DynamicTasks.queueIfPossible(SshTasks.dontRequireTtyForSudo(getMachine(),
                SshTasks.OnFailingTask.WARN_OR_IF_DYNAMIC_FAIL_MARKING_INESSENTIAL)).orSubmitAndBlock();

        String expandedInstallDir = getExpandedInstallDir();
        String installBin = Urls.mergePaths(expandedInstallDir, "bin");
        String saveAsYum = "riak.rpm";
        String saveAsApt = "riak.deb";
        OsDetails osDetails = getMachine().getOsDetails();

        String downloadUrl;
        String osReleaseCmd;
        if ("debian".equalsIgnoreCase(osDetails.getName())) {
            // TODO osDetails.getName() is returning "linux", instead of debian/ubuntu on AWS with jenkins image,
            //      running as integration test targetting localhost.
            // TODO Debian support (default debian image fails with 'sudo: command not found')
            downloadUrl = (String) entity.getAttribute(RiakNode.DOWNLOAD_URL_DEBIAN);
            osReleaseCmd = osDetails.getVersion().substring(0, osDetails.getVersion().indexOf("."));
        } else {
            // assume Ubuntu
            downloadUrl = (String) entity.getAttribute(RiakNode.DOWNLOAD_URL_UBUNTU);
            osReleaseCmd = "`lsb_release -sc` && "
                    + "export OS_RELEASE=`([[ \"lucid natty precise\" =~ (^| )\\$OS_RELEASE($| ) ]] && echo $OS_RELEASE || echo precise)`";
        }
        String apt = chainGroup(
                //debian fix
                addSbinPathCommand(), "which apt-get",
                ok(sudo("apt-get -y --allow-unauthenticated install logrotate libpam0g-dev libssl0.9.8")),
                "export OS_NAME=" + Strings.toLowerCase(osDetails.getName()), "export OS_RELEASE=" + osReleaseCmd,
                String.format("wget -O %s %s", saveAsApt, downloadUrl),
                sudo(String.format("dpkg -i %s", saveAsApt)));
        String yum = chainGroup("which yum", ok(sudo("yum -y install openssl")),
                String.format("wget -O %s %s", saveAsYum, entity.getAttribute(RiakNode.DOWNLOAD_URL_RHEL_CENTOS)),
                sudo(String.format("yum localinstall -y %s", saveAsYum)));
        return ImmutableList.<String>builder().add("mkdir -p " + installBin).add(INSTALL_CURL)
                .add(alternatives(apt, yum)).add("ln -s `which riak` " + Urls.mergePaths(installBin, "riak"))
                .add("ln -s `which riak-admin` " + Urls.mergePaths(installBin, "riak-admin")).build();
    }

    private List<String> installFromPackageCloud() {
        OsDetails osDetails = getMachine().getMachineDetails().getOsDetails();
        return ImmutableList.<String>builder()
                .add(osDetails.getName().toLowerCase().contains("debian") ? addSbinPathCommand() : "")
                .add(ifNotExecutable("curl", INSTALL_CURL))
                .addAll(ifExecutableElse("yum", installDebianBased(), installRpmBased())).build();
    }

    private ImmutableList<String> installDebianBased() {
        return ImmutableList.<String>builder()
                .add("curl https://packagecloud.io/install/repositories/basho/riak/script.deb.sh | "
                        + BashCommands.sudo("bash"))
                .add(BashCommands.sudo("apt-get install --assume-yes riak=" + getEntity().getFullVersion() + "-1"))
                .build();
    }

    private ImmutableList<String> installRpmBased() {
        return ImmutableList.<String>builder()
                .add("curl https://packagecloud.io/install/repositories/basho/riak/script.rpm.sh | "
                        + BashCommands.sudo("bash"))
                .add(BashCommands.sudo("yum install -y riak-" + getEntity().getFullVersion() + "*")).build();
    }

    protected List<String> installMac() {
        String saveAs = resolver.getFilename();
        String url = entity.getAttribute(RiakNode.DOWNLOAD_URL_MAC);
        return ImmutableList.<String>builder().add(INSTALL_TAR).add(INSTALL_CURL)
                .add(commandToDownloadUrlAs(url, saveAs)).add("tar xzvf " + saveAs).build();
    }

    @Override
    public void customize() {
        checkRiakOnPath();

        //create entity's runDir
        newScript(CUSTOMIZING).execute();

        OsDetails osDetails = getMachine().getMachineDetails().getOsDetails();

        List<String> commands = Lists.newLinkedList();
        commands.add(sudo("mkdir -p " + getRiakEtcDir()));

        if (isVersion1()) {
            String vmArgsTemplate = processTemplate(entity.getConfig(RiakNode.RIAK_VM_ARGS_TEMPLATE_URL));
            String saveAsVmArgs = Urls.mergePaths(getRunDir(), "vm.args");
            DynamicTasks.queue(SshEffectorTasks.put(saveAsVmArgs).contents(vmArgsTemplate));
            commands.add(sudo("mv " + saveAsVmArgs + " " + getRiakEtcDir()));

            String appConfigTemplate = processTemplate(entity.getConfig(RiakNode.RIAK_APP_CONFIG_TEMPLATE_URL));
            String saveAsAppConfig = Urls.mergePaths(getRunDir(), "app.config");
            DynamicTasks.queue(SshEffectorTasks.put(saveAsAppConfig).contents(appConfigTemplate));
            commands.add(sudo("mv " + saveAsAppConfig + " " + getRiakEtcDir()));
        } else {
            String templateUrl = osDetails.isMac() ? entity.getConfig(RiakNode.RIAK_CONF_TEMPLATE_URL_MAC)
                    : entity.getConfig(RiakNode.RIAK_CONF_TEMPLATE_URL_LINUX);
            String riakConfContent = processTemplate(templateUrl);
            String saveAsRiakConf = Urls.mergePaths(getRunDir(), "riak.conf");

            if (Strings.isNonBlank(entity.getConfig(RiakNode.RIAK_CONF_ADDITIONAL_CONTENT))) {
                String additionalConfigContent = processTemplateContents(
                        entity.getConfig(RiakNode.RIAK_CONF_ADDITIONAL_CONTENT));
                riakConfContent += "\n## Brooklyn note: additional config\n";
                riakConfContent += additionalConfigContent;
            }

            DynamicTasks.queue(SshEffectorTasks.put(saveAsRiakConf).contents(riakConfContent));
            commands.add(sudo("mv " + saveAsRiakConf + " " + getRiakEtcDir()));
        }

        //increase open file limit (default min for riak is: 4096)
        //TODO: detect the actual limit then do the modification.
        //TODO: modify ulimit for linux distros
        //    commands.add(sudo("launchctl limit maxfiles 4096 32768"));
        if (osDetails.isMac()) {
            commands.add("ulimit -n 4096");
        }

        if (osDetails.isLinux() && isVersion1()) {
            commands.add(sudo("chown -R riak:riak " + getRiakEtcDir()));
        }

        // TODO platform_*_dir
        // TODO riak config log

        ScriptHelper customizeScript = newScript(CUSTOMIZING).failOnNonZeroResultCode().body.append(commands);

        if (!isRiakOnPath()) {
            addRiakOnPath(customizeScript);
        }
        customizeScript.failOnNonZeroResultCode().execute();

        if (osDetails.isLinux()) {
            ImmutableMap<String, String> sysctl = ImmutableMap.<String, String>builder().put("vm.swappiness", "0")
                    .put("net.core.somaxconn", "40000").put("net.ipv4.tcp_max_syn_backlog", "40000")
                    .put("net.ipv4.tcp_sack", "1").put("net.ipv4.tcp_window_scaling", "15")
                    .put("net.ipv4.tcp_fin_timeout", "1").put("net.ipv4.tcp_keepalive_intvl", "30")
                    .put("net.ipv4.tcp_tw_reuse", "1").put("net.ipv4.tcp_moderate_rcvbuf", "1").build();

            ScriptHelper optimize = newScript(CUSTOMIZING + "network").body
                    .append(sudo("sysctl " + Joiner.on(' ').withKeyValueSeparator("=").join(sysctl)));

            Optional<Boolean> enable = Optional.fromNullable(entity.getConfig(RiakNode.OPTIMIZE_HOST_NETWORKING));
            if (!enable.isPresent())
                optimize.inessential();
            if (enable.or(true))
                optimize.execute();
        }

        //set the riak node name
        entity.sensors().set(RiakNode.RIAK_NODE_NAME, format("riak@%s", getSubnetHostname()));
    }

    @Override
    public void launch() {
        List<String> commands = Lists.newLinkedList();

        if (isPackageInstall()) {
            commands.add(addSbinPathCommand());
            commands.add(sudo(format("sh -c \"ulimit -n %s && service riak start\"", maxOpenFiles())));
        } else {
            // NOTE: See instructions at http://superuser.com/questions/433746/is-there-a-fix-for-the-too-many-open-files-in-system-error-on-os-x-10-7-1
            // for increasing the system limit for number of open files
            commands.add("ulimit -n 65536 || true"); // `BashCommands.ok` will put this in parentheses, which will set ulimit -n in the subshell
            commands.add(format("%s start >/dev/null 2>&1 < /dev/null &", getRiakCmd()));
        }

        ScriptHelper launchScript = newScript(LAUNCHING).body.append(commands);

        if (!isRiakOnPath()) {
            addRiakOnPath(launchScript);
        }
        launchScript.failOnNonZeroResultCode().execute();

        String mainUri = String.format("http://%s:%s/admin", entity.getAttribute(Attributes.HOSTNAME),
                entity.getAttribute(RiakNode.RIAK_WEB_PORT));
        entity.sensors().set(Attributes.MAIN_URI, URI.create(mainUri));
    }

    @Override
    public void stop() {
        leaveCluster();

        String command = format("%s stop", getRiakCmd());
        command = isPackageInstall() ? sudo(command) : command;

        ScriptHelper stopScript = newScript(ImmutableMap.of(USE_PID_FILE, false), STOPPING).body.append(command);

        if (!isRiakOnPath()) {
            addRiakOnPath(stopScript);
        }

        int result = stopScript.failOnNonZeroResultCode().execute();
        if (result != 0) {
            newScript(ImmutableMap.of(USE_PID_FILE, false), STOPPING).execute();
        }
    }

    @Override
    public boolean isRunning() {
        // Version 2.0.0 requires sudo for `riak ping`
        ScriptHelper checkRunningScript = newScript(CHECK_RUNNING).body
                .append(sudo(format("%s ping", getRiakCmd())));

        if (!isRiakOnPath()) {
            addRiakOnPath(checkRunningScript);
        }
        return (checkRunningScript.execute() == 0);
    }

    public boolean isPackageInstall() {
        return entity.getAttribute(RiakNode.RIAK_PACKAGE_INSTALL);
    }

    public boolean isRiakOnPath() {
        return entity.getAttribute(RiakNode.RIAK_ON_PATH);
    }

    public String getRiakEtcDir() {
        return isPackageInstall() ? "/etc/riak" : Urls.mergePaths(getExpandedInstallDir(), "etc");
    }

    protected String getRiakCmd() {
        return isPackageInstall() ? "riak" : Urls.mergePaths(getExpandedInstallDir(), "bin/riak");
    }

    protected String getRiakAdminCmd() {
        return isPackageInstall() ? "riak-admin" : Urls.mergePaths(getExpandedInstallDir(), "bin/riak-admin");
    }

    // TODO find a way to batch commit the changes, instead of committing for every operation.

    @Override
    public void joinCluster(String nodeName) {
        if (getRiakName().equals(nodeName)) {
            log.warn("Cannot join Riak node: {} to itself", nodeName);
        } else {
            if (!hasJoinedCluster()) {
                ScriptHelper joinClusterScript = newScript("joinCluster").body
                        .append(sudo(format("%s cluster join %s", getRiakAdminCmd(), nodeName))).body
                                .append(sudo(format("%s cluster plan", getRiakAdminCmd()))).body
                                        .append(sudo(format("%s cluster commit", getRiakAdminCmd())))
                                        .failOnNonZeroResultCode();

                if (!isRiakOnPath()) {
                    addRiakOnPath(joinClusterScript);
                }

                joinClusterScript.execute();

                entity.sensors().set(RiakNode.RIAK_NODE_HAS_JOINED_CLUSTER, Boolean.TRUE);
            } else {
                log.warn("entity {}: is already in the riak cluster", entity.getId());
            }
        }
    }

    @Override
    public void leaveCluster() {
        if (hasJoinedCluster()) {
            ScriptHelper leaveClusterScript = newScript("leaveCluster").body
                    .append(sudo(format("%s cluster leave", getRiakAdminCmd()))).body
                            .append(sudo(format("%s cluster plan", getRiakAdminCmd()))).body
                                    .append(sudo(format("%s cluster commit", getRiakAdminCmd())))
                                    .failOnNonZeroResultCode();

            if (!isRiakOnPath()) {
                addRiakOnPath(leaveClusterScript);
            }

            leaveClusterScript.execute();

            entity.sensors().set(RiakNode.RIAK_NODE_HAS_JOINED_CLUSTER, Boolean.FALSE);
        } else {
            log.warn("entity {}: has already left the riak cluster", entity.getId());
        }
    }

    @Override
    public void removeNode(String nodeName) {
        ScriptHelper removeNodeScript = newScript("removeNode").body
                .append(sudo(format("%s cluster force-remove %s", getRiakAdminCmd(), nodeName))).body
                        .append(sudo(format("%s down %s", getRiakAdminCmd(), nodeName))).body
                                .append(sudo(format("%s cluster plan", getRiakAdminCmd()))).body
                                        .append(sudo(format("%s cluster commit", getRiakAdminCmd())))
                                        .failOnNonZeroResultCode();

        if (!isRiakOnPath()) {
            addRiakOnPath(removeNodeScript);
        }

        removeNodeScript.execute();
    }

    @Override
    public void bucketTypeCreate(String bucketTypeName, String bucketTypeProperties) {
        ScriptHelper bucketTypeCreateScript = newScript("bucket-type_create " + bucketTypeName).body
                .append(sudo(format("%s bucket-type create %s %s", getRiakAdminCmd(), bucketTypeName,
                        escapeLiteralForDoubleQuotedBash(bucketTypeProperties))));
        if (!isRiakOnPath()) {
            addRiakOnPath(bucketTypeCreateScript);
        }
        bucketTypeCreateScript.body
                .append(sudo(format("%s bucket-type activate %s", getRiakAdminCmd(), bucketTypeName)))
                .failOnNonZeroResultCode();

        bucketTypeCreateScript.execute();
    }

    @Override
    public List<String> bucketTypeList() {
        ScriptHelper bucketTypeListScript = newScript("bucket-types_list").body
                .append(sudo(format("%s bucket-type list", getRiakAdminCmd()))).gatherOutput().noExtraOutput()
                .failOnNonZeroResultCode();
        if (!isRiakOnPath()) {
            addRiakOnPath(bucketTypeListScript);
        }
        bucketTypeListScript.execute();
        String stdout = bucketTypeListScript.getResultStdout();
        return Arrays.asList(stdout.split("[\\r\\n]+"));
    }

    @Override
    public List<String> bucketTypeStatus(String bucketTypeName) {
        ScriptHelper bucketTypeStatusScript = newScript("bucket-type_status").body
                .append(sudo(format("%s bucket-type status %s", getRiakAdminCmd(), bucketTypeName))).gatherOutput()
                .noExtraOutput().failOnNonZeroResultCode();
        if (!isRiakOnPath()) {
            addRiakOnPath(bucketTypeStatusScript);
        }
        bucketTypeStatusScript.execute();
        String stdout = bucketTypeStatusScript.getResultStdout();
        return Arrays.asList(stdout.split("[\\r\\n]+"));
    }

    @Override
    public void bucketTypeUpdate(String bucketTypeName, String bucketTypeProperties) {
        ScriptHelper bucketTypeStatusScript = newScript("bucket-type_update").body
                .append(sudo(format("%s bucket-type update %s %s", getRiakAdminCmd(), bucketTypeName,
                        escapeLiteralForDoubleQuotedBash(bucketTypeProperties))))
                .failOnNonZeroResultCode();
        if (!isRiakOnPath()) {
            addRiakOnPath(bucketTypeStatusScript);
        }
        bucketTypeStatusScript.execute();
    }

    @Override
    public void bucketTypeActivate(String bucketTypeName) {
        ScriptHelper bucketTypeStatusScript = newScript("bucket-type_activate").body
                .append(sudo(format("%s bucket-type activate %s", getRiakAdminCmd(), bucketTypeName)))
                .failOnNonZeroResultCode();
        if (!isRiakOnPath()) {
            addRiakOnPath(bucketTypeStatusScript);
        }
        bucketTypeStatusScript.execute();
    }

    @Override
    public void recoverFailedNode(String nodeName) {
        //TODO find ways to detect a faulty/failed node
        //argument passed 'node' is any working node in the riak cluster
        //following the instruction from: http://docs.basho.com/riak/latest/ops/running/recovery/failed-node/

        if (hasJoinedCluster()) {
            String failedNodeName = getRiakName();

            String stopCommand = format("%s stop", getRiakCmd());
            stopCommand = isPackageInstall() ? sudo(stopCommand) : stopCommand;

            String startCommand = format("%s start > /dev/null 2>&1 < /dev/null &", getRiakCmd());
            startCommand = isPackageInstall() ? sudo(startCommand) : startCommand;

            ScriptHelper recoverNodeScript = newScript("recoverNode").body.append(stopCommand).body
                    .append(format("%s down %s", getRiakAdminCmd(), failedNodeName)).body
                            .append(sudo(format("rm -rf %s", getRingStateDir()))).body.append(startCommand).body
                                    .append(sudo(format("%s cluster join %s", getRiakAdminCmd(), nodeName))).body
                                            .append(sudo(format("%s cluster plan", getRiakAdminCmd()))).body
                                                    .append(sudo(format("%s cluster commit", getRiakAdminCmd())))
                                                    .failOnNonZeroResultCode();

            if (!isRiakOnPath()) {
                addRiakOnPath(recoverNodeScript);
            }

            recoverNodeScript.execute();

        } else {
            log.warn("entity {}: is not in the riak cluster", entity.getId());
        }
    }

    @Override
    public void setup() {
        if (entity.getConfig(RiakNode.SEARCH_ENABLED)) {
            // JavaSoftwareProcessSshDriver.setup() is called in order to install java
            super.setup();
        }
    }

    private Boolean hasJoinedCluster() {
        return Boolean.TRUE.equals(entity.getAttribute(RiakNode.RIAK_NODE_HAS_JOINED_CLUSTER));
    }

    protected void checkRiakOnPath() {
        boolean riakOnPath = newScript("riakOnPath").body.append("which riak").execute() == 0;
        entity.sensors().set(RiakNode.RIAK_ON_PATH, riakOnPath);
    }

    private String getRiakName() {
        return entity.getAttribute(RiakNode.RIAK_NODE_NAME);
    }

    private String getRingStateDir() {
        //TODO: check for non-package install.
        return isPackageInstall() ? "/var/lib/riak/ring" : Urls.mergePaths(getExpandedInstallDir(), "lib/ring");
    }

    protected boolean isVersion1() {
        return getVersion().startsWith("1.");
    }

    @Override
    public String getOsMajorVersion() {
        OsDetails osDetails = getMachine().getMachineDetails().getOsDetails();
        String osVersion = osDetails.getVersion();
        return osVersion.contains(".") ? osVersion.substring(0, osVersion.indexOf(".")) : osVersion;
    }

    private void addRiakOnPath(ScriptHelper scriptHelper) {
        Map<String, String> newPathVariable = ImmutableMap.of("PATH", sbinPath());
        //        log.warn("riak command not found on PATH. Altering future commands' environment variables from {} to {}", getShellEnvironment(), newPathVariable);
        scriptHelper.environmentVariablesReset(newPathVariable);
    }

    public Integer maxOpenFiles() {
        return entity.getConfig(RiakNode.RIAK_MAX_OPEN_FILES);
    }
}