org.jclouds.scriptbuilder.statements.chef.ChefSolo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jclouds-scriptbuilder Show documentation
Show all versions of jclouds-scriptbuilder Show documentation
creates scripts that can be used to manage services
/*
* 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.jclouds.scriptbuilder.statements.chef;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.transform;
import static org.jclouds.scriptbuilder.domain.Statements.createOrOverwriteFile;
import static org.jclouds.scriptbuilder.domain.Statements.exec;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.StatementList;
import org.jclouds.scriptbuilder.domain.Statements;
import org.jclouds.scriptbuilder.domain.chef.DataBag;
import org.jclouds.scriptbuilder.domain.chef.Role;
import org.jclouds.scriptbuilder.domain.chef.RunList;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
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.ImmutableSet;
import com.google.common.collect.Lists;
/**
* Bootstraps a node using Chef Solo.
*
*
* @see InstallChefGems
* @see InstallRuby
* @see InstallRubyGems
*/
public class ChefSolo implements Statement {
public static final String DEFAULT_SOLO_PATH = "/var/chef";
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String fileCachePath = DEFAULT_SOLO_PATH;
private String rolePath;
private String databagPath;
private ImmutableList.Builder cookbookPath = ImmutableList.builder();
private String cookbooksArchiveLocation;
private String jsonAttributes;
private String group;
private Integer interval;
private String logLevel;
private String logFile;
private String nodeName;
private Integer splay;
private String user;
private List roles = Lists.newArrayList();
private List databags = Lists.newArrayList();
private RunList runlist;
/**
* Directory where Chef Solo will store files.
*/
public Builder fileCachePath(String fileCachePath) {
this.fileCachePath = checkNotNull(fileCachePath, "fileCachePath");
return this;
}
/**
* Directory where Chef Solo will store roles.
*/
public Builder rolePath(String rolePath) {
this.rolePath = checkNotNull(rolePath, "rolePath");
return this;
}
/**
* Directory where Chef Solo will store data bags.
*/
public Builder dataBagPath(String dataBagPath) {
this.databagPath = checkNotNull(dataBagPath, "dataBagPath");
return this;
}
/**
* Directory where Chef Solo will look for cookbooks.
*/
public Builder cookbookPath(String cookbookPath) {
this.cookbookPath.add(checkNotNull(cookbookPath, "cookbookPath"));
return this;
}
/**
* Directories where Chef Solo will look for cookbooks.
*/
public Builder cookbookPaths(Iterable cookbookPaths) {
this.cookbookPath.addAll(checkNotNull(cookbookPaths, "cookbookPath"));
return this;
}
/**
* Local file path or remote URL of a cookbook tar file. Chef solo will
* download and unpack its contents in the {@link #fileCachePath}
* directory.
*/
public Builder cookbooksArchiveLocation(String cookbooksArchiveLocation) {
this.cookbooksArchiveLocation = checkNotNull(cookbooksArchiveLocation, "cookbooksArchiveLocation");
return this;
}
/**
* JSON attributes to customize cookbook values.
*/
public Builder jsonAttributes(String jsonAttributes) {
this.jsonAttributes = checkNotNull(jsonAttributes, "jsonAttributes");
return this;
}
/**
* The goup to set privilege to.
*/
public Builder group(String group) {
this.group = checkNotNull(group, "group");
return this;
}
/**
* Run chef-client periodically, in seconds.
*/
public Builder interval(Integer interval) {
this.interval = checkNotNull(interval, "interval");
return this;
}
/**
* Set he Log level (debug, info, warn, error, fatal).
*/
public Builder logLevel(String logLevel) {
this.logLevel = checkNotNull(logLevel, "logLevel");
return this;
}
/**
* Set the log file location, by default STDOUT.
*/
public Builder logFile(String logFile) {
this.logFile = checkNotNull(logFile, "logFile");
return this;
}
/**
* Set the name for the node. By default the hostname will be used.
*/
public Builder nodeName(String nodeName) {
this.nodeName = checkNotNull(nodeName, "nodeName");
return this;
}
/**
* The splay time for running at intervals, in seconds.
*/
public Builder splay(Integer splay) {
this.splay = checkNotNull(splay, "splay");
return this;
}
/**
* The user to set privilege to.
*/
public Builder user(String user) {
this.user = checkNotNull(user, "user");
return this;
}
/**
* Creates a role.
*/
public Builder defineRole(Role role) {
this.roles.add(checkNotNull(role, "role"));
return this;
}
/**
* Creates a set of roles.
*/
public Builder defineRoles(Iterable roles) {
this.roles = ImmutableList. copyOf(checkNotNull(roles, "roles"));
return this;
}
/**
* Creates a data bag.
*
* @since Chef 0.10.4
*/
public Builder defineDataBag(DataBag dataBag) {
this.databags.add(checkNotNull(dataBag, "dataBag"));
return this;
}
/**
* Creates a set of data bags.
*
* @since Chef 0.10.4
*/
public Builder defineDataBags(Iterable databags) {
this.databags = ImmutableList. copyOf(checkNotNull(databags, "databags"));
return this;
}
/**
* The run list to be executed in the Chef Solo run.
*/
public Builder runlist(RunList runlist) {
this.runlist = checkNotNull(runlist, "runlist");
return this;
}
public ChefSolo build() {
return new ChefSolo(Optional.of(fileCachePath), Optional.fromNullable(rolePath),
Optional.fromNullable(databagPath), Optional.of(cookbookPath.build()),
Optional.fromNullable(cookbooksArchiveLocation), Optional.fromNullable(jsonAttributes),
Optional.fromNullable(group), Optional.fromNullable(interval), Optional.fromNullable(logLevel),
Optional.fromNullable(logFile), Optional.fromNullable(nodeName), Optional.fromNullable(splay),
Optional.fromNullable(user), Optional.of(roles), Optional.of(databags), Optional.fromNullable(runlist));
}
}
private String fileCachePath;
private String rolePath;
private String databagPath;
private List cookbookPath;
private Optional cookbooksArchiveLocation;
private Optional jsonAttributes;
private Optional group;
private Optional interval;
private Optional logLevel;
private Optional logFile;
private Optional nodeName;
private Optional splay;
private Optional user;
private Optional> roles;
private Optional> databags;
private RunList runlist;
protected ChefSolo(Optional fileCachePath, Optional rolePath, Optional databagPath,
Optional> cookbookPath, Optional cookbooksArchiveLocation,
Optional jsonAttributes, Optional group, Optional interval,
Optional logLevel, Optional logFile, Optional nodeName, Optional splay,
Optional user, Optional> roles, Optional> databags, Optional runlist) {
this.fileCachePath = checkNotNull(fileCachePath, "fileCachePath must be set").or(DEFAULT_SOLO_PATH);
this.rolePath = checkNotNull(rolePath, "rolePath must be set").or(this.fileCachePath + "/roles");
this.databagPath = checkNotNull(databagPath, "databagPath must be set").or(this.fileCachePath + "/data_bags");
this.cookbooksArchiveLocation = checkNotNull(cookbooksArchiveLocation, "cookbooksArchiveLocation must be set");
this.jsonAttributes = checkNotNull(jsonAttributes, "jsonAttributes must be set");
this.group = checkNotNull(group, "group must be set");
this.interval = checkNotNull(interval, "interval must be set");
this.logLevel = checkNotNull(logLevel, "logLevel must be set");
this.logFile = checkNotNull(logFile, "logFile must be set");
this.nodeName = checkNotNull(nodeName, "nodeName must be set");
this.splay = checkNotNull(splay, "splay must be set");
this.user = checkNotNull(user, "user must be set");
this.roles = checkNotNull(roles, "roles must be set");
this.databags = checkNotNull(databags, "databags must be set");
this.runlist = checkNotNull(runlist, "runlist must be set").or(RunList.builder().build());
this.user = checkNotNull(user, "user must be set");
if (!checkNotNull(cookbookPath, "cookbookPath must be set").isPresent() || cookbookPath.get().isEmpty()) {
this.cookbookPath = ImmutableList. of(this.fileCachePath + "/cookbooks");
} else {
this.cookbookPath = ImmutableList. copyOf(cookbookPath.get());
}
}
@Override
public String render(OsFamily family) {
if (family == OsFamily.WINDOWS) {
throw new UnsupportedOperationException("windows not yet implemented");
}
ImmutableList.Builder statements = ImmutableList.builder();
checkChefIsInstalled(statements);
createSoloConfiguration(statements);
createRolesIfNecessary(statements);
createDatabagsIfNecessary(statements);
createNodeConfiguration(statements);
ImmutableMap.Builder options = ImmutableMap.builder();
options.put("-c", fileCachePath + "/solo.rb");
options.put("-j", fileCachePath + "/node.json");
options.put("-N", nodeName.or("`hostname`"));
if (group.isPresent()) {
options.put("-g", group.get());
}
if (interval.isPresent()) {
options.put("-i", interval.get().toString());
}
if (logLevel.isPresent()) {
options.put("-l", logLevel.get());
}
if (logFile.isPresent()) {
options.put("-L", logFile.get());
}
if (cookbooksArchiveLocation.isPresent()) {
options.put("-r", cookbooksArchiveLocation.get());
}
if (splay.isPresent()) {
options.put("-s", splay.get().toString());
}
if (user.isPresent()) {
options.put("-u", user.get());
}
String strOptions = Joiner.on(' ').withKeyValueSeparator(" ").join(options.build());
statements.add(Statements.exec(String.format("chef-solo %s", strOptions)));
return new StatementList(statements.build()).render(family);
}
@Override
public Iterable functionDependencies(OsFamily family) {
return Collections.emptyList();
}
private void checkChefIsInstalled(ImmutableList.Builder statements) {
statements.add(exec("if ! hash chef-solo 2>/dev/null; then"));
statements.add(exec("echo 'chef-solo not found. Please, install Chef first'"));
statements.add(exec("exit 1"));
statements.add(exec("fi"));
}
@VisibleForTesting
void createSoloConfiguration(ImmutableList.Builder statements) {
statements.add(exec("{md} " + fileCachePath));
for (String path : cookbookPath) {
statements.add(exec("{md} " + path));
}
String cookbookPathJoined = Joiner.on(',').join(transform(cookbookPath, quote()));
statements.add(createOrOverwriteFile(
fileCachePath + "/solo.rb",
ImmutableSet.of("file_cache_path \"" + fileCachePath + "\"", //
"cookbook_path [" + cookbookPathJoined + "]", "role_path \"" + rolePath + "\"", "data_bag_path \""
+ databagPath + "\"")));
}
@VisibleForTesting
void createNodeConfiguration(ImmutableList.Builder statements) {
StringBuilder json = new StringBuilder();
if (jsonAttributes.isPresent()) {
// Start the node configuration with the attributes, but remove the
// last bracket to append the run list to the json configuration
String attributes = jsonAttributes.get();
json.append(attributes, 0, attributes.lastIndexOf('}'));
json.append(",");
} else {
json.append("{");
}
json.append("\"run_list\":");
json.append(runlist.toString());
json.append("}");
statements.add(createOrOverwriteFile(fileCachePath + "/node.json", ImmutableSet.of(json.toString())));
}
@VisibleForTesting
void createRolesIfNecessary(ImmutableList.Builder statements) {
// The roles directory must contain one file for each role definition
if (roles.isPresent() && !roles.get().isEmpty()) {
statements.add(exec("{md} " + rolePath));
for (Role role : roles.get()) {
statements.add(createOrOverwriteFile(rolePath + "/" + role.getName() + ".json",
ImmutableSet.of(role.toJsonString())));
}
}
}
@VisibleForTesting
void createDatabagsIfNecessary(ImmutableList.Builder statements) {
// Each data bag item must be defined in a file inside the data bag
// directory, and each data bag item must have its own JSON file.
if (databags.isPresent() && !databags.get().isEmpty()) {
statements.add(exec("{md} " + databagPath));
for (DataBag databag : databags.get()) {
String databagFolder = databagPath + "/" + databag.getName();
statements.add(exec("{md} " + databagFolder));
for (Map.Entry item : databag.getItems().entrySet()) {
statements.add(createOrOverwriteFile(databagFolder + "/" + item.getKey() + ".json",
ImmutableSet.of(item.getValue())));
}
}
}
}
private static Function quote() {
return new Function() {
@Override
public String apply(String input) {
return "\"" + input + "\"";
}
};
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy