org.apache.brooklyn.entity.chef.KnifeTaskFactory Maven / Gradle / Ivy
Show all versions of brooklyn-software-base Show documentation
/*
* 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 java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.core.internal.ssh.process.ProcessTool;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
import org.apache.brooklyn.util.core.task.system.internal.SystemProcessTaskFactory;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes;
import com.google.common.base.Function;
/** A factory which acts like {@link ProcessTaskFactory} with special options for knife.
* Typical usage is to {@link #addKnifeParameters(String)}s for the knife command to be run.
* You can also {@link #add(String...)} commands as needed; these will run *before* knife,
* unless you addKnifeCommandHere().
*
* This impl will use sensible defaults, including {@link ConfigKey}s on the context entity,
* for general knife config but not specific commands etc. It supports:
*
{@link ChefConfig#KNIFE_EXECUTABLE}
* {@link ChefConfig#KNIFE_CONFIG_FILE}
*
* (Other fields will typically be used by methods calling to this factory.)
* */
// see e.g. http://docs.opscode.com/knife_bootstrap.html
public class KnifeTaskFactory extends SystemProcessTaskFactory, RET>{
private static String KNIFE_PLACEHOLDER = "";
public final String taskName;
protected String knifeExecutable;
protected List knifeParameters = new ArrayList();
protected String knifeConfigFile;
protected String knifeSetupCommands;
protected Boolean throwOnCommonKnifeErrors;
public KnifeTaskFactory(String taskName) {
this.taskName = taskName;
summary(taskName);
// knife setup usually requires a login shell
config.put(ProcessTool.PROP_LOGIN_SHELL, true);
}
@Override
public List, Void>> getCompletionListeners() {
MutableList, Void>> result = MutableList.copyOf(super.getCompletionListeners());
if (throwOnCommonKnifeErrors != Boolean.FALSE)
insertKnifeCompletionListenerIntoCompletionListenersList(result);
return result.asUnmodifiable();
}
public KnifeTaskFactory notThrowingOnCommonKnifeErrors() {
throwOnCommonKnifeErrors = false;
return self();
}
protected void insertKnifeCompletionListenerIntoCompletionListenersList(List, Void>> listeners) {
// give a nice warning if chef/knife not set up correctly
Function, Void> propagateIfKnifeConfigFileMissing = new Function, Void>() {
@Override
public Void apply(@Nullable ProcessTaskWrapper> input) {
if (input.getExitCode()!=0 && input.getStderr().indexOf("WARNING: No knife configuration file found")>=0) {
String myConfig = knifeConfigFileOption();
if (Strings.isEmpty(myConfig))
throw new IllegalStateException("Config file for Chef knife must be specified in "+ChefConfig.KNIFE_CONFIG_FILE+" (or valid knife default set up)");
else
throw new IllegalStateException("Error reading config file for Chef knife ("+myConfig+") -- does it exist?");
}
return null;
}
};
listeners.add(propagateIfKnifeConfigFileMissing);
}
@Override
public ProcessTaskWrapper newTask() {
return new SystemProcessTaskWrapper("Knife");
}
/** Inserts the knife command at the current place in the list.
* Can be run multiple times. The knife command added at the end of the list
* if this is not invoked (and it is the only command if nothing is {@link #add(String...)}ed.
*/
public KnifeTaskFactory addKnifeCommandToScript() {
add(KNIFE_PLACEHOLDER);
return self();
}
@Override
public List getCommands() {
MutableList result = new MutableList();
String setupCommands = knifeSetupCommands();
if (setupCommands != null && Strings.isNonBlank(setupCommands))
result.add(setupCommands);
int numKnifes = 0;
for (String c: super.getCommands()) {
if (c==KNIFE_PLACEHOLDER)
result.add(buildKnifeCommand(numKnifes++));
else
result.add(c);
}
if (numKnifes==0)
result.add(buildKnifeCommand(numKnifes++));
return result.asUnmodifiable();
}
/** creates the command for running knife.
* in some cases knife may be added multiple times,
* and in that case the parameter here tells which time it is being added,
* on a single run. */
protected String buildKnifeCommand(int knifeCommandIndex) {
MutableList words = new MutableList();
words.add(knifeExecutable());
words.addAll(initialKnifeParameters());
words.addAll(knifeParameters());
String x = knifeConfigFileOption();
if (Strings.isNonBlank(x)) words.add(knifeConfigFileOption());
return Strings.join(words, " ");
}
/** allows a way for subclasses to build up parameters at the start */
protected List initialKnifeParameters() {
return new MutableList();
}
@Nullable /** callers should allow this to be null so task can be used outside of an entity */
protected Entity entity() {
return BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
}
protected T entityConfig(ConfigKey key) {
Entity entity = entity();
if (entity!=null)
return entity.getConfig(key);
return null;
}
public KnifeTaskFactory knifeExecutable(String knifeExecutable) {
this.knifeExecutable = knifeExecutable;
return this;
}
protected String knifeExecutable() {
if (knifeExecutable!=null) return knifeExecutable;
String knifeExecFromConfig = entityConfig(ChefConfig.KNIFE_EXECUTABLE);
if (knifeExecFromConfig!=null) return BashStringEscapes.wrapBash(knifeExecFromConfig);
// assume on the path, if executable not set
return "knife";
}
protected List knifeParameters() {
return knifeParameters;
}
public KnifeTaskFactory knifeAddParameters(String word1, String ...words) {
knifeParameters.add(word1);
for (String w: words)
knifeParameters.add(w);
return self();
}
public KnifeTaskFactory knifeConfigFile(String knifeConfigFile) {
this.knifeConfigFile = knifeConfigFile;
return self();
}
@Nullable
protected String knifeConfigFileOption() {
if (knifeConfigFile!=null) return "-c "+knifeConfigFile;
String knifeConfigFileFromConfig = entityConfig(ChefConfig.KNIFE_CONFIG_FILE);
if (knifeConfigFileFromConfig!=null) return "-c "+BashStringEscapes.wrapBash(knifeConfigFileFromConfig);
// if not supplied will use global config
return null;
}
public KnifeTaskFactory knifeSetupCommands(String knifeSetupCommands) {
this.knifeSetupCommands = knifeSetupCommands;
return self();
}
@Nullable
protected String knifeSetupCommands() {
if (knifeSetupCommands!=null) return knifeSetupCommands;
String knifeSetupCommandsFromConfig = entityConfig(ChefConfig.KNIFE_SETUP_COMMANDS);
if (knifeSetupCommandsFromConfig!=null) return knifeSetupCommandsFromConfig;
// if not supplied will use global config
return null;
}
@Override
public KnifeTaskFactory returning(ScriptReturnType type) {
return (KnifeTaskFactory) super.returning(type);
}
@Override
public KnifeTaskFactory returning(Function, RET2> resultTransformation) {
return (KnifeTaskFactory) super.returning(resultTransformation);
}
@Override
public KnifeTaskFactory returningIsExitCodeZero() {
return (KnifeTaskFactory) super.returningIsExitCodeZero();
}
@Override
public KnifeTaskFactory requiringZeroAndReturningStdout() {
return (KnifeTaskFactory) super.requiringZeroAndReturningStdout();
}
}