/* * 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.mongodb; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.api.location.OsDetails; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver; 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.internal.ssh.SshTool; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.net.Networking; import org.apache.brooklyn.util.os.Os; import org.apache.brooklyn.util.ssh.BashCommands; import org.apache.brooklyn.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; public abstract class AbstractMongoDBSshDriver extends AbstractSoftwareProcessSshDriver { private static final Logger LOG = LoggerFactory.getLogger(AbstractMongoDBSshDriver.class); public AbstractMongoDBSshDriver(EntityLocal entity, SshMachineLocation machine) { super(entity, machine); } @Override public void preInstall() { resolver = Entities.newDownloader(this); setExpandedInstallDir(Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(getBaseName()))); } @Override public void install() { List<String> urls = resolver.getTargets(); String saveAs = resolver.getFilename(); List<String> commands = new LinkedList<>(); commands.addAll(BashCommands.commandsToDownloadUrlsAs(urls, saveAs)); commands.add(BashCommands.INSTALL_TAR); commands.add("tar xzfv " + saveAs); newScript(INSTALLING) .failOnNonZeroResultCode() .body.append(commands).execute(); } @Override public void customize() { Map<?,?> ports = ImmutableMap.of("port", getServerPort()); Networking.checkPortsValid(ports); List<String> commands = new LinkedList<>(); commands.add(String.format("mkdir -p %s", getDataDirectory())); if (MongoDBAuthenticationUtils.usesAuthentication(entity)) { String destinationLocation = Os.mergePaths(getRunDir(), "mongodb-keyfile"); entity.sensors().set(AbstractMongoDBServer.MONGODB_KEYFILE_DESTINATION, destinationLocation); String keyfileContents = entity.config().get(AbstractMongoDBServer.MONGODB_KEYFILE_CONTENTS); if (Strings.isNullOrEmpty(keyfileContents)) { String keyfileUrl = entity.config().get(AbstractMongoDBServer.MONGODB_KEYFILE_URL); if (Strings.isNullOrEmpty(keyfileUrl)) { throw new IllegalStateException("MongoDBAuthenticationUtils.usesAuthentication returned true, but neither keyfileContents nor keyfileUrl are set"); } copyResource(keyfileUrl, destinationLocation); } else { commands.add(BashCommands.pipeTextToFile(keyfileContents, destinationLocation)); } commands.add("chmod 600 " + destinationLocation); } newScript(CUSTOMIZING) .updateTaskAndFailOnNonZeroResultCode() .body.append(commands).execute(); String templateUrl = entity.getConfig(MongoDBServer.MONGODB_CONF_TEMPLATE_URL); if (!Strings.isNullOrEmpty(templateUrl)) copyTemplate(templateUrl, getConfFile()); if (MongoDBAuthenticationUtils.usesAuthentication(entity)) { launch(getArgsBuilderWithNoAuthentication((AbstractMongoDBServer) getEntity()) .add("--dbpath", getDataDirectory())); newScript("create-user") .body.append(String.format("%s --port %s" + " --host localhost admin --eval \"db.createUser({user: '%s',pwd: '%s',roles: [ 'root' ]})\"", Os.mergePaths(getExpandedInstallDir(), "bin/mongo"), getServerPort(), getRootUsername(), MongoDBAuthenticationUtils.getRootPassword(entity))) .updateTaskAndFailOnNonZeroResultCode() .execute(); stop(); } } @Override public boolean isRunning() { try { if (entity instanceof MongoDBServerImpl && !((MongoDBServerImpl)entity).clientAccessEnabled()) { // No direct access via MongoDB port; only use ssh-port return newScript(MutableMap.of(USE_PID_FILE, getPidFile()), CHECK_RUNNING).execute() == 0; } else { return MongoDBClientSupport.forServer((AbstractMongoDBServer) entity).ping(); } } catch (Exception e) { Exceptions.propagateIfFatal(e); return false; } } /** * Kills the server with SIGINT. Sending SIGKILL is likely to result in data corruption. * @see <a href="http://docs.mongodb.org/manual/tutorial/manage-mongodb-processes/#sending-a-unix-int-or-term-signal">http://docs.mongodb.org/manual/tutorial/manage-mongodb-processes/#sending-a-unix-int-or-term-signal</a> */ @Override public void stop() { // TODO: Wait for process to terminate. Currently, this will send the signal and then immediately continue with next steps, // which could involve stopping VM etc. // We could also use SIGTERM (15) new ScriptHelper(this, "Send SIGINT to MongoDB server") .body.append("MONGO_PID=$(cat " + getPidFile() + ")\n") .body.append("kill -2 $MONGO_PID\n") .body.append("for i in {1..10}\n" + "do\n" + " kill -0 $MONGO_PID || exit \n" + " sleep 1\n" + "done\n" + "echo \"mongoDB process still running after 10 seconds; continuing but may subsequently fail\"") .execute(); } protected String getBaseName() { return getOsTag() + "-" + entity.getConfig(AbstractMongoDBServer.SUGGESTED_VERSION); } // IDE note: This is used by MongoDBServer.DOWNLOAD_URL public String getOsDir() { return (getLocation().getOsDetails().isMac()) ? "osx" : "linux"; } public String getOsTag() { OsDetails os = getLocation().getOsDetails(); if (os == null) { // Default to generic linux return "mongodb-linux-x86_64"; } else if (os.isMac()) { // Mac is 64bit only return "mongodb-osx-x86_64"; } else { String arch = os.is64bit() ? "x86_64" : "i686"; return "mongodb-linux-" + arch; } } public String getDataDirectory() { String result = entity.getConfig(MongoDBServer.DATA_DIRECTORY); if (result!=null) return result; return getRunDir() + "/data"; } protected String getLogFile() { return getRunDir() + "/log.txt"; } protected String getPidFile() { return getRunDir() + "/pid"; } protected Integer getServerPort() { return entity.getAttribute(MongoDBServer.PORT); } protected String getConfFile() { return getRunDir() + "/mongo.conf"; } protected String getRootUsername() { return entity.config().get(AbstractMongoDBServer.ROOT_USERNAME); } protected ImmutableList.Builder<String> getArgsBuilderWithDefaults(AbstractMongoDBServer server) { ImmutableList.Builder<String> builder = getArgsBuilderWithNoAuthentication(server); if (MongoDBAuthenticationUtils.usesAuthentication(entity)) { builder.add("--keyFile", entity.getAttribute(AbstractMongoDBServer.MONGODB_KEYFILE_DESTINATION)); } return builder; } protected ImmutableList.Builder<String> getArgsBuilderWithNoAuthentication(AbstractMongoDBServer server) { Integer port = server.getAttribute(MongoDBServer.PORT); ImmutableList.Builder<String> builder = ImmutableList.builder(); builder.add("--config", getConfFile()); builder.add("--pidfilepath", getPidFile()); builder.add("--logpath", getLogFile()); builder.add("--port", port.toString()); builder.add("--fork"); return builder; } protected void launch(ImmutableList.Builder<String> argsBuilder) { String args = Joiner.on(" ").join(argsBuilder.build()); String command = String.format("%s/bin/mongod %s >> out.log 2>> err.log < /dev/null", getExpandedInstallDir(), args); newScript(LAUNCHING) .setFlag(SshTool.PROP_CONNECT_TIMEOUT, Duration.TEN_SECONDS.toMilliseconds()) .updateTaskAndFailOnNonZeroResultCode() .body.append(command).execute(); } }