blob: 68a1d50a536166cd517c02b5968c5a442196c808 [file] [log] [blame]
/*
* 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.chef;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes.wrapBash;
import java.io.File;
import java.util.List;
import java.util.Map;
import com.google.common.base.Strings;
import com.google.common.net.HostAndPort;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.core.effector.EffectorTasks;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.collections.Jsonya;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.ssh.BashCommands;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.gson.GsonBuilder;
public class KnifeConvergeTaskFactory<RET> extends KnifeTaskFactory<RET> {
private static final Logger log = LoggerFactory.getLogger(KnifeConvergeTaskFactory.class);
protected Function<? super Entity,String> runList;
protected Map<Object, Object> knifeAttributes = new MutableMap<Object, Object>();
protected List<String> extraBootstrapParameters = MutableList.of();
protected Boolean sudo;
protected Boolean runTwice;
protected String nodeName;
protected Integer port;
/** null means nothing specified, use user supplied or machine default;
* false means use machine default (disallow user supplied);
* true means use knife default (omit the argument and disallow user supplied)
*/
protected Boolean portOmittedToUseKnifeDefault;
public KnifeConvergeTaskFactory(String taskName) {
super(taskName);
}
@Override
protected KnifeConvergeTaskFactory<RET> self() {
return this;
}
/** construct the knife command, based on the settings on other methods
* (called when instantiating the script, after all parameters sent)
*/
@Override
protected List<String> initialKnifeParameters() {
// runs inside the task so can detect entity/machine at runtime
MutableList<String> result = new MutableList<String>();
SshMachineLocation machine = EffectorTasks.findSshMachine();
result.add("bootstrap");
result.addAll(extraBootstrapParameters);
HostAndPort hostAndPort = machine.getSshHostAndPort();
result.add(wrapBash(hostAndPort.getHostText()));
Integer whichPort = knifeWhichPort(hostAndPort);
if (whichPort!=null)
result.add("-p "+whichPort);
result.add("-x "+wrapBash(checkNotNull(machine.getUser(), "user")));
File keyfile = ChefServerTasks.extractKeyFile(machine);
if (keyfile!=null) result.add("-i "+keyfile.getPath());
else result.add("-P "+checkNotNull(machine.findPassword(), "No password or private key data for "+machine));
result.add("--no-host-key-verify");
if (sudo != Boolean.FALSE) result.add("--sudo");
if (!Strings.isNullOrEmpty(nodeName)) {
result.add("--node-name");
result.add(nodeName);
}
result.add("-r "+wrapBash(runList.apply(entity())));
if (!knifeAttributes.isEmpty())
result.add("-j "+wrapBash(new GsonBuilder().create()
.toJson(knifeAttributes)));
return result;
}
/** whether knife should attempt to run twice;
* see {@link ChefConfig#CHEF_RUN_CONVERGE_TWICE} */
public KnifeConvergeTaskFactory<RET> knifeRunTwice(boolean runTwice) {
this.runTwice = runTwice;
return self();
}
/** whether to pass --sudo to knife; default true */
public KnifeConvergeTaskFactory<RET> knifeSudo(boolean sudo) {
this.sudo = sudo;
return self();
}
/** what node name to pass to knife; default = null, meaning chef-client will pick the node name */
public KnifeConvergeTaskFactory<RET> knifeNodeName(String nodeName) {
this.nodeName = nodeName;
return self();
}
/** tell knife to use an explicit port */
public KnifeConvergeTaskFactory<RET> knifePort(int port) {
if (portOmittedToUseKnifeDefault!=null) {
log.warn("Port "+port+" specified to "+this+" for when already explicitly told to use a default (overriding previous); see subsequent warning for more details");
}
this.port = port;
return self();
}
/** omit the port parameter altogether (let knife use its default) */
public KnifeConvergeTaskFactory<RET> knifePortUseKnifeDefault() {
if (port!=null) {
log.warn("knifePortUseKnifeDefault specified to "+this+" when already told to use "+port+" explicitly (overriding previous); see subsequent warning for more details");
port = -1;
}
portOmittedToUseKnifeDefault = true;
return self();
}
/** use the default port known to brooklyn for the target machine for ssh */
public KnifeConvergeTaskFactory<RET> knifePortUseMachineSshPort() {
if (port!=null) {
log.warn("knifePortUseMachineSshPort specified to "+this+" when already told to use "+port+" explicitly (overriding previous); see subsequent warning for more details");
port = -1;
}
portOmittedToUseKnifeDefault = false;
return self();
}
protected Integer knifeWhichPort(HostAndPort hostAndPort) {
if (port==null) {
if (Boolean.TRUE.equals(portOmittedToUseKnifeDefault))
// user has explicitly said to use knife default, omitting port here
return null;
// default is to use the machine port
return hostAndPort.getPort();
}
if (port==-1) {
// port was supplied by user, then portDefault (true or false)
port = null;
Integer whichPort = knifeWhichPort(hostAndPort);
log.warn("knife port conflicting instructions for "+this+" at entity "+entity()+" on "+hostAndPort+"; using default ("+whichPort+")");
return whichPort;
}
if (portOmittedToUseKnifeDefault!=null) {
// portDefault was specified (true or false), then overridden with a port
log.warn("knife port conflicting instructions for "+this+" at entity "+entity()+" on "+hostAndPort+"; using supplied port "+port);
}
// port was supplied by user, use that
return port;
}
/** parameters to pass to knife after the bootstrap command */
public KnifeConvergeTaskFactory<RET> knifeAddExtraBootstrapParameters(String extraBootstrapParameter1, String ...extraBootstrapParameters) {
this.extraBootstrapParameters.add(extraBootstrapParameter1);
for (String p: extraBootstrapParameters)
this.extraBootstrapParameters.add(p);
return self();
}
/** function supplying the run list to be passed to knife, evaluated at the last moment */
public KnifeConvergeTaskFactory<RET> knifeRunList(Function<? super Entity, String> runList) {
this.runList = runList;
return self();
}
public KnifeConvergeTaskFactory<RET> knifeRunList(String runList) {
this.runList = Functions.constant(runList);
return self();
}
/** includes the given attributes in the attributes to be passed to chef;
* when combining with other attributes, this uses {@link Jsonya} semantics to add
* (a deep add, combining lists and maps) */
public KnifeConvergeTaskFactory<RET> knifeAddAttributes(Map<? extends Object, ? extends Object> attributes) {
if (attributes!=null && !attributes.isEmpty()) {
Jsonya.of(knifeAttributes).add(attributes);
}
return self();
}
@Override
protected String buildKnifeCommand(int knifeCommandIndex) {
String result = super.buildKnifeCommand(knifeCommandIndex);
if (Boolean.TRUE.equals(runTwice))
result = BashCommands.alternatives(result, result);
return result;
}
@Override
public <T2> KnifeConvergeTaskFactory<T2> returning(ScriptReturnType type) {
return (KnifeConvergeTaskFactory<T2>) super.<T2>returning(type);
}
@Override
public <RET2> KnifeConvergeTaskFactory<RET2> returning(Function<ProcessTaskWrapper<?>, RET2> resultTransformation) {
return (KnifeConvergeTaskFactory<RET2>) super.returning(resultTransformation);
}
@Override
public KnifeConvergeTaskFactory<Boolean> returningIsExitCodeZero() {
return (KnifeConvergeTaskFactory<Boolean>) super.returningIsExitCodeZero();
}
@Override
public KnifeConvergeTaskFactory<String> requiringZeroAndReturningStdout() {
return (KnifeConvergeTaskFactory<String>) super.requiringZeroAndReturningStdout();
}
@Override
public KnifeConvergeTaskFactory<RET> knifeAddParameters(String word1, String ...words) {
super.knifeAddParameters(word1, words);
return self();
}
// TODO other methods from KnifeTaskFactory will return KTF class not KCTF;
// should make it generic so it returns the right type...
}