JCLOUDS-792: Configure chef-client security attributes

This commit also improves some internal behavior related to the
bootstrap configuration generation, and deprecated some methods that
will be removed in upcoming versions.

* Removes all methods that return the bootstrap DataBag or the raw
  configuration json to return the BootstrapConfig object instead.
* Deprecates all methods that provide support for old Chef versions.
* Formats the Enterprise Chef provider according to the jclouds
  formatting guidelines.
* Deprecates the ChefContext view. That view is not an abstraction and
  only provides access to the ChefService. It can be obtained from the
  ChefApi itself.
This commit is contained in:
Ignasi Barrera 2014-12-05 16:51:53 +01:00
parent 38ae98e409
commit 6b350fb6b7
28 changed files with 668 additions and 553 deletions

View File

@ -85,6 +85,8 @@ import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.annotations.WrapWith;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.inject.Provides;
/**
* Provides synchronous access to Chef.
*/
@ -92,6 +94,12 @@ import org.jclouds.rest.binders.BindToJsonPayload;
@Headers(keys = "X-Chef-Version", values = "{" + Constants.PROPERTY_API_VERSION + "}")
@Consumes(MediaType.APPLICATION_JSON)
public interface ChefApi extends Closeable {
/**
* Provides access to high level Chef features.
*/
@Provides
ChefService chefService();
// Clients

View File

@ -25,8 +25,13 @@ import com.google.inject.ImplementedBy;
/**
* Provides an entry point to Chef features.
*
* @deprecated Will be removed in next version. Directly create the
* {@link ChefApi} instead and access the {@link ChefService} from
* it.
*/
@ImplementedBy(ChefContextImpl.class)
@Deprecated
public interface ChefContext extends View, Closeable {
/**

View File

@ -16,23 +16,26 @@
*/
package org.jclouds.chef;
import com.google.common.io.InputSupplier;
import com.google.inject.ImplementedBy;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.ExecutorService;
import org.jclouds.chef.config.ChefProperties;
import org.jclouds.chef.domain.BootstrapConfig;
import org.jclouds.chef.domain.Client;
import org.jclouds.chef.domain.CookbookVersion;
import org.jclouds.chef.domain.Environment;
import org.jclouds.chef.domain.Node;
import org.jclouds.chef.internal.BaseChefService;
import org.jclouds.domain.JsonBall;
import org.jclouds.chef.util.ChefUtils;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.ohai.config.OhaiModule;
import org.jclouds.rest.annotations.SinceApiVersion;
import org.jclouds.scriptbuilder.domain.Statement;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.ExecutorService;
import com.google.common.io.InputSupplier;
import com.google.inject.ImplementedBy;
/**
* Provides high level Chef operations.
@ -105,19 +108,18 @@ public interface ChefService {
*
* @param The group to get the configured run list for.
* @return run list for all nodes bootstrapped with a certain group
* @deprecated USe {{@link #getBootstrapConfigForGroup(String)}.
*/
@Deprecated
List<String> getRunListForGroup(String group);
/**
* Gets the bootstrap configuration for a given group.
* <p/>
* The bootstrap configuration is a Json object containing the run list and
* the configured attributes.
*
* @param group The name of the group.
* @return The bootstrap configuration for the given group.
*/
JsonBall getBootstrapConfigForGroup(String group);
BootstrapConfig getBootstrapConfigForGroup(String group);
// Nodes / Clients

View File

@ -27,17 +27,16 @@ import static org.jclouds.crypto.Pems.privateKeySpec;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.chef.domain.BootstrapConfig;
import org.jclouds.chef.domain.Client;
import org.jclouds.chef.functions.BootstrapConfigForGroup;
import org.jclouds.chef.functions.ClientForGroup;
import org.jclouds.chef.functions.RunListForGroup;
import org.jclouds.chef.handlers.ChefApiErrorRetryHandler;
import org.jclouds.chef.handlers.ChefErrorHandler;
import org.jclouds.crypto.Crypto;
@ -45,7 +44,6 @@ import org.jclouds.crypto.Pems;
import org.jclouds.date.DateService;
import org.jclouds.date.TimeStamp;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.JsonBall;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.annotation.ClientError;
@ -177,13 +175,7 @@ public abstract class BaseChefHttpApiModule<S> extends HttpApiModule<S> {
@Provides
@Singleton
CacheLoader<String, List<String>> runListForGroup(RunListForGroup runListForGroup) {
return CacheLoader.from(runListForGroup);
}
@Provides
@Singleton
CacheLoader<String, ? extends JsonBall> bootstrapConfigForGroup(BootstrapConfigForGroup bootstrapConfigForGroup) {
CacheLoader<String, BootstrapConfig> bootstrapConfigForGroup(BootstrapConfigForGroup bootstrapConfigForGroup) {
return CacheLoader.from(bootstrapConfigForGroup);
}

View File

@ -288,7 +288,7 @@ public class ChefParserModule extends AbstractModule {
return (TypeAdapter<T>) new KeepLastRepeatedKeyMapTypeAdapter<K, V>(keyAdapter, valueAdapter);
}
}
@Provides
@Singleton
public Map<Type, Object> provideCustomAdapterBindings(DataBagItemAdapter adapter, PrivateKeyAdapter privateAdapter,

View File

@ -32,10 +32,12 @@ import javax.inject.Qualifier;
* Chef Server version 0.9 and 0.10 return a different Json when rquesting the
* cookbook definitions. This annotation can be used to setup the cookbook
* parser.
* @deprecated Support for Chef 0.9 and 0.10 will be removed in upcoming versions.
*/
@Target({ METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Qualifier
@Deprecated
public @interface CookbookParser {
}

View File

@ -32,10 +32,12 @@ import javax.inject.Qualifier;
* Chef Server version 0.9 and 0.10 return a different Json when rquesting the
* cookbook versions. This annotation can be used to setup the cookbook versions
* parser.
* @deprecated Support for Chef 0.9 and 0.10 will be removed in upcoming versions.
*/
@Target({ METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Qualifier
@Deprecated
public @interface CookbookVersionsParser {
}

View File

@ -18,12 +18,14 @@ package org.jclouds.chef.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.beans.ConstructorProperties;
import java.util.List;
import org.jclouds.domain.JsonBall;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.gson.annotations.SerializedName;
/**
* Configures how the nodes in a group will bootstrap.
@ -31,6 +33,16 @@ import com.google.common.collect.ImmutableList;
* @since 1.7
*/
public class BootstrapConfig {
public static enum SSLVerifyMode {
NONE, PEER;
@Override
public String toString() {
return ":verify_" + name().toLowerCase();
}
}
public static Builder builder() {
return new Builder();
}
@ -39,6 +51,10 @@ public class BootstrapConfig {
private ImmutableList.Builder<String> runList = ImmutableList.builder();
private String environment;
private JsonBall attribtues;
private String sslCAFile;
private String sslCAPath;
private SSLVerifyMode sslVerifyMode;
private Boolean verifyApiCert;
/**
* Sets the run list that will be executed in the nodes of the group.
@ -64,32 +80,118 @@ public class BootstrapConfig {
return this;
}
/**
* The file in which the OpenSSL key is saved. To be used by the Chef
* client to verify the certificate of the Chef Server.
*/
public Builder sslCAFile(String sslCAFile) {
this.sslCAFile = checkNotNull(sslCAFile, "sslCAFile");
return this;
}
/**
* The path to where the OpenSSL keys that are used by the Chef client are
* located.
*/
public Builder sslCAPath(String sslCAPath) {
this.sslCAPath = checkNotNull(sslCAPath, "sslCAPath");
return this;
}
/**
* The verify mode for HTTPS requests.
* <ul>
* <li>NONE - to do no validation of SSL certificates.</li>
* <li>PEER - to do validation of all SSL certificate, including the Chef
* server connections</li>
* </ul>
*/
public Builder sslVerifyMode(SSLVerifyMode sslVerifyMode) {
this.sslVerifyMode = checkNotNull(sslVerifyMode, "sslVerifyMode");
return this;
}
/**
* Use to only do SSL validation of the Chef server connection; may be
* needed if the Chef client needs to talk to other services that have
* broken SSL certificates.
*/
public Builder verifyApiCert(boolean verifyApiCert) {
this.verifyApiCert = verifyApiCert;
return this;
}
public BootstrapConfig build() {
return new BootstrapConfig(runList.build(), Optional.fromNullable(environment),
Optional.fromNullable(attribtues));
return new BootstrapConfig(runList.build(), environment, attribtues, sslCAFile, sslCAPath, sslVerifyMode,
verifyApiCert);
}
}
@SerializedName("run_list")
private final List<String> runList;
private final Optional<String> environment;
private final Optional<JsonBall> attribtues;
@Nullable
private final String environment;
@Nullable
private final JsonBall attributes;
@SerializedName("ssl_ca_file")
@Nullable
private final String sslCAFile;
@SerializedName("ssl_ca_path")
@Nullable
private final String sslCAPath;
@SerializedName("ssl_verify_mode")
@Nullable
private final SSLVerifyMode sslVerifyMode;
@SerializedName("verify_api_cert")
@Nullable
private final Boolean verifyApiCert;
protected BootstrapConfig(List<String> runList, Optional<String> environment, Optional<JsonBall> attribtues) {
this.runList = checkNotNull(runList, "runList");
this.environment = checkNotNull(environment, "environment");
this.attribtues = checkNotNull(attribtues, "attributes");
@ConstructorProperties({ "run_list", "environment", "attributes", "ssl_ca_file", "ssl_ca_path", "ssl_verify_mode",
"verify_api_cert" })
protected BootstrapConfig(List<String> runList, @Nullable String environment, @Nullable JsonBall attributes,
@Nullable String sslCAFile, @Nullable String sslCAPath, @Nullable SSLVerifyMode sslVerifyMode,
@Nullable Boolean verifyApiCert) {
this.runList = ImmutableList.copyOf(checkNotNull(runList, "runList"));
this.environment = environment;
this.attributes = attributes;
this.sslCAFile = sslCAFile;
this.sslCAPath = sslCAPath;
this.sslVerifyMode = sslVerifyMode;
this.verifyApiCert = verifyApiCert;
}
public List<String> getRunList() {
return runList;
}
public Optional<String> getEnvironment() {
@Nullable
public String getEnvironment() {
return environment;
}
public Optional<JsonBall> getAttribtues() {
return attribtues;
@Nullable
public JsonBall getAttributes() {
return attributes;
}
@Nullable
public String getSslCAFile() {
return sslCAFile;
}
@Nullable
public String getSslCAPath() {
return sslCAPath;
}
@Nullable
public SSLVerifyMode getSslVerifyMode() {
return sslVerifyMode;
}
@Nullable
public Boolean getVerifyApiCert() {
return verifyApiCert;
}
}

View File

@ -16,46 +16,43 @@
*/
package org.jclouds.chef.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.chef.config.ChefProperties.CHEF_BOOTSTRAP_DATABAG;
import java.lang.reflect.Type;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.chef.ChefApi;
import org.jclouds.chef.domain.BootstrapConfig;
import org.jclouds.chef.domain.DatabagItem;
import org.jclouds.domain.JsonBall;
import org.jclouds.json.Json;
import com.google.common.base.Function;
import com.google.inject.TypeLiteral;
/**
*
* Retrieves the bootstrap configuration for a specific group
*/
@Singleton
public class BootstrapConfigForGroup implements Function<String, DatabagItem> {
public static final Type BOOTSTRAP_CONFIG_TYPE = new TypeLiteral<Map<String, JsonBall>>() {
}.getType();
public class BootstrapConfigForGroup implements Function<String, BootstrapConfig> {
private final ChefApi api;
private final String databag;
private final Json json;
@Inject
public BootstrapConfigForGroup(@Named(CHEF_BOOTSTRAP_DATABAG) String databag, ChefApi api) {
this.databag = checkNotNull(databag, "databag");
this.api = checkNotNull(api, "api");
BootstrapConfigForGroup(@Named(CHEF_BOOTSTRAP_DATABAG) String databag, ChefApi api, Json json) {
this.databag = databag;
this.api = api;
this.json = json;
}
@Override
public DatabagItem apply(String from) {
public BootstrapConfig apply(String from) {
DatabagItem bootstrapConfig = api.getDatabagItem(databag, from);
checkState(bootstrapConfig != null, "databag item %s/%s not found", databag, from);
return bootstrapConfig;
// A DatabagItem is already a JsonBall, to we can easily deserialize it
return json.fromJson(bootstrapConfig.toString(), BootstrapConfig.class);
}
}

View File

@ -17,17 +17,13 @@
package org.jclouds.chef.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import static org.jclouds.scriptbuilder.domain.Statements.appendFile;
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 static org.jclouds.scriptbuilder.domain.Statements.newStatementList;
import java.lang.reflect.Type;
import java.net.URI;
import java.security.PrivateKey;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.inject.Inject;
@ -35,100 +31,119 @@ import javax.inject.Singleton;
import org.jclouds.chef.config.InstallChef;
import org.jclouds.chef.config.Validator;
import org.jclouds.chef.domain.BootstrapConfig;
import org.jclouds.crypto.Pems;
import org.jclouds.domain.JsonBall;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.Json;
import org.jclouds.location.Provider;
import org.jclouds.scriptbuilder.ExitInsteadOfReturn;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.StatementList;
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.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.TypeLiteral;
/**
*
* Generates a bootstrap script relevant for a particular group
*/
@Singleton
public class GroupToBootScript {
private static final Pattern newLinePattern = Pattern.compile("(\\r\\n)|(\\n)");
@VisibleForTesting
static final Type RUN_LIST_TYPE = new TypeLiteral<Map<String, List<String>>>() {
}.getType();
private final Supplier<URI> endpoint;
private final Json json;
private final CacheLoader<String, ? extends JsonBall> bootstrapConfigForGroup;
private final CacheLoader<String, BootstrapConfig> bootstrapConfigForGroup;
private final Statement installChef;
private final Optional<String> validatorName;
private final Optional<PrivateKey> validatorCredential;
@Inject
public GroupToBootScript(@Provider Supplier<URI> endpoint, Json json,
CacheLoader<String, ? extends JsonBall> bootstrapConfigForGroup,
GroupToBootScript(@Provider Supplier<URI> endpoint, CacheLoader<String, BootstrapConfig> bootstrapConfigForGroup,
@InstallChef Statement installChef, @Validator Optional<String> validatorName,
@Validator Optional<PrivateKey> validatorCredential) {
this.endpoint = checkNotNull(endpoint, "endpoint");
this.json = checkNotNull(json, "json");
this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup");
this.installChef = checkNotNull(installChef, "installChef");
this.validatorName = checkNotNull(validatorName, "validatorName");
this.validatorCredential = checkNotNull(validatorCredential, validatorCredential);
this.endpoint = endpoint;
this.bootstrapConfigForGroup = bootstrapConfigForGroup;
this.installChef = installChef;
this.validatorName = validatorName;
this.validatorCredential = validatorCredential;
}
public Statement apply(String group, @Nullable String nodeName) {
checkNotNull(group, "group");
String validatorClientName = validatorName.get();
PrivateKey validatorKey = validatorCredential.get();
JsonBall bootstrapConfig = null;
BootstrapConfig config = null;
try {
bootstrapConfig = bootstrapConfigForGroup.load(group);
} catch (Exception e) {
throw propagate(e);
config = bootstrapConfigForGroup.load(checkNotNull(group, "group"));
} catch (Exception ex) {
throw Throwables.propagate(ex);
}
Map<String, JsonBall> config = json.fromJson(bootstrapConfig.toString(),
BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
Optional<JsonBall> environment = Optional.fromNullable(config.get("environment"));
String chefConfigDir = "{root}etc{fs}chef";
Statement createChefConfigDir = exec("{md} " + chefConfigDir);
String createNodeName;
if (nodeName != null) {
createNodeName = String.format("node_name \"%s\"", nodeName);
} else {
createNodeName = String.format("node_name \"%s-\" + o[:ipaddress]", group);
}
Statement createClientRb = appendFile(chefConfigDir + "{fs}client.rb", ImmutableList.of("require 'rubygems'",
"require 'ohai'", "o = Ohai::System.new", "o.all_plugins", createNodeName, "log_level :info", "log_location STDOUT",
String.format("validation_client_name \"%s\"", validatorClientName),
String.format("chef_server_url \"%s\"", endpoint.get())));
Statement createValidationPem = appendFile(chefConfigDir + "{fs}validation.pem",
Splitter.on(newLinePattern).split(Pems.pem(validatorKey)));
String chefBootFile = chefConfigDir + "{fs}first-boot.json";
Statement createFirstBoot = appendFile(chefBootFile, Collections.singleton(json.toJson(bootstrapConfig)));
ImmutableMap.Builder<String, String> options = ImmutableMap.builder();
options.put("-j", chefBootFile);
if (environment.isPresent()) {
options.put("-E", environment.get().toString());
ImmutableList.Builder<Statement> statements = ImmutableList.builder();
statements.add(new ExitInsteadOfReturn(installChef));
statements.add(exec("{md} " + chefConfigDir));
if (config.getSslCAFile() != null) {
statements.add(createOrOverwriteFile(chefConfigDir + "{fs}chef-server.crt",
Splitter.on(newLinePattern).split(config.getSslCAFile())));
}
String strOptions = Joiner.on(' ').withKeyValueSeparator(" ").join(options.build());
Statement runChef = exec("chef-client " + strOptions);
statements.add(createClientRbFile(chefConfigDir + "{fs}client.rb", group, nodeName, config));
statements.add(createOrOverwriteFile(chefConfigDir + "{fs}validation.pem",
Splitter.on(newLinePattern).split(Pems.pem(validatorCredential.get()))));
statements.add(createAttributesFile(chefBootFile, config));
statements.add(exec("chef-client -j " + chefBootFile));
return newStatementList(new ExitInsteadOfReturn(installChef), createChefConfigDir, createClientRb, createValidationPem,
createFirstBoot, runChef);
return new StatementList(statements.build());
}
private Statement createClientRbFile(String clientRbFile, String group, String nodeName, BootstrapConfig config) {
ImmutableList.Builder<String> clientRb = ImmutableList.builder();
clientRb.add("require 'rubygems'");
clientRb.add("require 'ohai'");
clientRb.add("o = Ohai::System.new");
clientRb.add("o.all_plugins");
clientRb.add("node_name \"" + (nodeName != null ? nodeName + "\"" : group + "-\" + o[:ipaddress]"));
clientRb.add("log_level :info");
clientRb.add("log_location STDOUT");
clientRb.add(String.format("validation_client_name \"%s\"", validatorName.get()));
clientRb.add(String.format("chef_server_url \"%s\"", endpoint.get()));
addIfPresent(clientRb, "environment", config.getEnvironment());
if (config.getSslCAFile() != null) {
addIfPresent(clientRb, "ssl_ca_file", "/etc/chef/chef-server.crt");
}
addIfPresent(clientRb, "ssl_ca_path", config.getSslCAPath());
addIfPresent(clientRb, "ssl_verify_mode", config.getSslVerifyMode());
addIfPresent(clientRb, "verify_api_cert", config.getVerifyApiCert());
return createOrOverwriteFile(clientRbFile, clientRb.build());
}
private Statement createAttributesFile(String chefBootFile, BootstrapConfig config) {
String attributes = config.getAttributes().toString();
String runlist = Joiner.on(',').join(transform(config.getRunList(), new Function<String, String>() {
@Override
public String apply(String input) {
return "\"" + input + "\"";
}
}));
// Append the runlist to the json attributes
StringBuilder sb = new StringBuilder();
// Strip the json ending character
sb.append(attributes.trim().substring(0, attributes.length() - 1));
sb.append(",\"run_list\":[").append(runlist).append("]");
sb.append("}");
return createOrOverwriteFile(chefBootFile, Collections.singleton(sb.toString()));
}
private void addIfPresent(ImmutableList.Builder<String> lines, String key, Object value) {
if (value != null) {
// Quote the value if it is a String
lines.add(String.format("%s %s", key, value instanceof String ? "\"" + value + "\"" : value.toString()));
}
}
}

View File

@ -30,8 +30,10 @@ import com.google.common.base.Function;
/**
* Parses a cookbook definition from a Json response, taking care of using the
* appropriate parser.
* @deprecated Support for Chef 0.9 and 0.10 will be removed in upcoming verions.
*/
@Singleton
@Deprecated
public class ParseCookbookDefinitionCheckingChefVersion implements Function<HttpResponse, Set<String>> {
@VisibleForTesting

View File

@ -30,8 +30,10 @@ import com.google.common.base.Function;
/**
* Parses a cookbook versions from a Json response, taking care of using the
* appropriate parser.
* @deprecated Support for Chef 0.9 and 0.10 will be removed in upcoming verions.
*/
@Singleton
@Deprecated
public class ParseCookbookVersionsCheckingChefVersion implements Function<HttpResponse, Set<String>> {
@VisibleForTesting

View File

@ -30,8 +30,10 @@ import static com.google.common.collect.Iterables.getFirst;
/**
* Parses the cookbook versions in a Chef Server <= 0.9.8.
* @deprecated Support for Chef 0.9 and 0.10 will be removed in upcoming versions.
*/
@Singleton
@Deprecated
public class ParseCookbookVersionsV09FromJson implements Function<HttpResponse, Set<String>> {
private final ParseJson<Map<String, Set<String>>> json;

View File

@ -1,61 +0,0 @@
/*
* 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.chef.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.chef.domain.DatabagItem;
import org.jclouds.domain.JsonBall;
import org.jclouds.json.Json;
import com.google.common.base.Function;
import com.google.inject.TypeLiteral;
/**
* Retrieves the run-list for a specific group
*/
@Singleton
public class RunListForGroup implements Function<String, List<String>> {
public static final Type RUN_LIST_TYPE = new TypeLiteral<List<String>>() {
}.getType();
private final BootstrapConfigForGroup bootstrapConfigForGroup;
private final Json json;
@Inject
public RunListForGroup(BootstrapConfigForGroup bootstrapConfigForGroup, Json json) {
this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup");
this.json = checkNotNull(json, "json");
}
@Override
public List<String> apply(String from) {
DatabagItem bootstrapConfig = bootstrapConfigForGroup.apply(from);
Map<String, JsonBall> config = json.fromJson(bootstrapConfig.toString(),
BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
JsonBall runlist = config.get("run_list");
return json.fromJson(runlist.toString(), RUN_LIST_TYPE);
}
}

View File

@ -43,7 +43,6 @@ import org.jclouds.chef.domain.Environment;
import org.jclouds.chef.domain.Node;
import org.jclouds.chef.functions.BootstrapConfigForGroup;
import org.jclouds.chef.functions.GroupToBootScript;
import org.jclouds.chef.functions.RunListForGroup;
import org.jclouds.chef.strategy.CleanupStaleNodesAndClients;
import org.jclouds.chef.strategy.CreateNodeAndPopulateAutomaticAttributes;
import org.jclouds.chef.strategy.DeleteAllClientsInList;
@ -56,7 +55,6 @@ import org.jclouds.chef.strategy.ListNodes;
import org.jclouds.chef.strategy.ListNodesInEnvironment;
import org.jclouds.chef.strategy.UpdateAutomaticAttributesOnNode;
import org.jclouds.crypto.Crypto;
import org.jclouds.domain.JsonBall;
import org.jclouds.io.ByteStreams2;
import org.jclouds.io.Payloads;
import org.jclouds.io.payloads.RSADecryptingPayload;
@ -67,8 +65,8 @@ import org.jclouds.logging.Logger;
import org.jclouds.scriptbuilder.domain.Statement;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps;
import com.google.common.io.InputSupplier;
@Singleton
@ -87,7 +85,6 @@ public class BaseChefService implements ChefService {
private final GroupToBootScript groupToBootScript;
private final String databag;
private final BootstrapConfigForGroup bootstrapConfigForGroup;
private final RunListForGroup runListForGroup;
private final ListCookbookVersions listCookbookVersions;
private final ListCookbookVersionsInEnvironment listCookbookVersionsInEnvironment;
private final ListEnvironments listEnvironments;
@ -100,38 +97,34 @@ public class BaseChefService implements ChefService {
protected Logger logger = Logger.NULL;
@Inject
protected BaseChefService(ChefContext chefContext, ChefApi api,
CleanupStaleNodesAndClients cleanupStaleNodesAndClients,
BaseChefService(ChefContext chefContext, ChefApi api, CleanupStaleNodesAndClients cleanupStaleNodesAndClients,
CreateNodeAndPopulateAutomaticAttributes createNodeAndPopulateAutomaticAttributes,
DeleteAllNodesInList deleteAllNodesInList, ListNodes listNodes, DeleteAllClientsInList deleteAllClientsInList,
ListClients listClients, ListCookbookVersions listCookbookVersions,
UpdateAutomaticAttributesOnNode updateAutomaticAttributesOnNode, Supplier<PrivateKey> privateKey,
@Named(CHEF_BOOTSTRAP_DATABAG) String databag, GroupToBootScript groupToBootScript,
BootstrapConfigForGroup bootstrapConfigForGroup, RunListForGroup runListForGroup,
ListEnvironments listEnvironments, ListNodesInEnvironment listNodesInEnvironment,
BootstrapConfigForGroup bootstrapConfigForGroup, ListEnvironments listEnvironments,
ListNodesInEnvironment listNodesInEnvironment,
ListCookbookVersionsInEnvironment listCookbookVersionsInEnvironment, Json json, Crypto crypto) {
this.chefContext = checkNotNull(chefContext, "chefContext");
this.api = checkNotNull(api, "api");
this.cleanupStaleNodesAndClients = checkNotNull(cleanupStaleNodesAndClients, "cleanupStaleNodesAndClients");
this.createNodeAndPopulateAutomaticAttributes = checkNotNull(createNodeAndPopulateAutomaticAttributes,
"createNodeAndPopulateAutomaticAttributes");
this.deleteAllNodesInList = checkNotNull(deleteAllNodesInList, "deleteAllNodesInList");
this.listNodes = checkNotNull(listNodes, "listNodes");
this.deleteAllClientsInList = checkNotNull(deleteAllClientsInList, "deleteAllClientsInList");
this.listClients = checkNotNull(listClients, "listClients");
this.listCookbookVersions = checkNotNull(listCookbookVersions, "listCookbookVersions");
this.updateAutomaticAttributesOnNode = checkNotNull(updateAutomaticAttributesOnNode,
"updateAutomaticAttributesOnNode");
this.privateKey = checkNotNull(privateKey, "privateKey");
this.groupToBootScript = checkNotNull(groupToBootScript, "groupToBootScript");
this.databag = checkNotNull(databag, "databag");
this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup");
this.runListForGroup = checkNotNull(runListForGroup, "runListForGroup");
this.listEnvironments = checkNotNull(listEnvironments, "listEnvironments");
this.listNodesInEnvironment = checkNotNull(listNodesInEnvironment, "listNodesInEnvironment");
this.listCookbookVersionsInEnvironment = checkNotNull(listCookbookVersionsInEnvironment, "listCookbookVersionsInEnvironment");
this.json = checkNotNull(json, "json");
this.crypto = checkNotNull(crypto, "crypto");
this.chefContext = chefContext;
this.api = api;
this.cleanupStaleNodesAndClients = cleanupStaleNodesAndClients;
this.createNodeAndPopulateAutomaticAttributes = createNodeAndPopulateAutomaticAttributes;
this.deleteAllNodesInList = deleteAllNodesInList;
this.listNodes = listNodes;
this.deleteAllClientsInList = deleteAllClientsInList;
this.listClients = listClients;
this.listCookbookVersions = listCookbookVersions;
this.updateAutomaticAttributesOnNode = updateAutomaticAttributesOnNode;
this.privateKey = privateKey;
this.groupToBootScript = groupToBootScript;
this.databag = databag;
this.bootstrapConfigForGroup = bootstrapConfigForGroup;
this.listEnvironments = listEnvironments;
this.listNodesInEnvironment = listNodesInEnvironment;
this.listCookbookVersionsInEnvironment = listCookbookVersionsInEnvironment;
this.json = json;
this.crypto = crypto;
}
@Override
@ -141,34 +134,20 @@ public class BaseChefService implements ChefService {
@Override
public byte[] encrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
return ByteStreams2.toByteArrayAndClose(new RSAEncryptingPayload(crypto, Payloads.newPayload(supplier.getInput()), privateKey
.get()).openStream());
return ByteStreams2.toByteArrayAndClose(new RSAEncryptingPayload(crypto,
Payloads.newPayload(supplier.getInput()), privateKey.get()).openStream());
}
@Override
public byte[] decrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
return ByteStreams2.toByteArrayAndClose(new RSADecryptingPayload(crypto, Payloads.newPayload(supplier.getInput()), privateKey
.get()).openStream());
return ByteStreams2.toByteArrayAndClose(new RSADecryptingPayload(crypto,
Payloads.newPayload(supplier.getInput()), privateKey.get()).openStream());
}
@VisibleForTesting
String buildBootstrapConfiguration(BootstrapConfig bootstrapConfig) {
checkNotNull(bootstrapConfig, "bootstrapConfig must not be null");
Map<String, Object> configMap = Maps.newLinkedHashMap();
configMap.put("run_list", bootstrapConfig.getRunList());
if (bootstrapConfig.getEnvironment().isPresent()) {
configMap.put("environment", bootstrapConfig.getEnvironment().get());
private static void putIfPresent(Map<String, Object> configMap, Optional<?> configProperty, String name) {
if (configProperty.isPresent()) {
configMap.put(name, configProperty.get().toString());
}
if (bootstrapConfig.getAttribtues().isPresent()) {
Map<String, Object> attributes = json.fromJson(bootstrapConfig.getAttribtues().get().toString(),
BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
configMap.putAll(attributes);
}
return json.toJson(configMap);
}
@Override
@ -183,6 +162,7 @@ public class BaseChefService implements ChefService {
@Override
public void updateBootstrapConfigForGroup(String group, BootstrapConfig bootstrapConfig) {
checkNotNull(bootstrapConfig, "bootstrapConfig cannot be null");
try {
api.createDatabag(databag);
} catch (IllegalStateException e) {
@ -190,22 +170,31 @@ public class BaseChefService implements ChefService {
}
String jsonConfig = buildBootstrapConfiguration(bootstrapConfig);
DatabagItem runlist = new DatabagItem(group, jsonConfig);
DatabagItem config = new DatabagItem(group, jsonConfig);
if (api.getDatabagItem(databag, group) == null) {
api.createDatabagItem(databag, runlist);
api.createDatabagItem(databag, config);
} else {
api.updateDatabagItem(databag, runlist);
api.updateDatabagItem(databag, config);
}
}
@VisibleForTesting
String buildBootstrapConfiguration(BootstrapConfig config) {
return json.toJson(config);
}
/**
* @deprecated Use {{@link #getBootstrapConfigForGroup(String)}.
*/
@Override
@Deprecated
public List<String> getRunListForGroup(String group) {
return runListForGroup.apply(group);
return getBootstrapConfigForGroup(group).getRunList();
}
@Override
public JsonBall getBootstrapConfigForGroup(String group) {
public BootstrapConfig getBootstrapConfigForGroup(String group) {
return bootstrapConfigForGroup.apply(group);
}
@ -259,8 +248,8 @@ public class BaseChefService implements ChefService {
return listCookbookVersions.execute();
}
@Override public Iterable<? extends CookbookVersion> listCookbookVersions(
ExecutorService executorService) {
@Override
public Iterable<? extends CookbookVersion> listCookbookVersions(ExecutorService executorService) {
return listCookbookVersions.execute(executorService);
}

View File

@ -24,6 +24,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.Context;
import org.jclouds.chef.ChefApi;
import org.jclouds.chef.ChefContext;
import org.jclouds.chef.ChefService;
import org.jclouds.internal.BaseView;
@ -31,7 +32,13 @@ import org.jclouds.location.Provider;
import com.google.common.reflect.TypeToken;
/**
* @deprecated Will be removed in next version. Directly create the
* {@link ChefApi} instead and access the {@link ChefService} from
* it.
*/
@Singleton
@Deprecated
public class ChefContextImpl extends BaseView implements ChefContext {
private final ChefService chefService;

View File

@ -25,50 +25,65 @@ import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.jclouds.chef.ChefApi;
import org.jclouds.chef.domain.BootstrapConfig;
import org.jclouds.chef.domain.Client;
import org.jclouds.chef.domain.DatabagItem;
import org.jclouds.rest.annotations.Api;
import org.jclouds.json.Json;
import org.jclouds.json.config.GsonModule;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
@Test(groups = "unit", testName = "BootstrapConfigForGroupTest")
public class BootstrapConfigForGroupTest {
private Json json;
@BeforeClass
public void setup() {
Injector injector = Guice.createInjector(new GsonModule());
json = injector.getInstance(Json.class);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testWhenNoDatabagItem() throws IOException {
ChefApi chefApi = createMock(ChefApi.class);
Client client = createMock(Client.class);
BootstrapConfigForGroup fn = new BootstrapConfigForGroup("jclouds", chefApi);
BootstrapConfigForGroup fn = new BootstrapConfigForGroup("jclouds", chefApi, json);
expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(null);
replay(client);
replay(chefApi);
replay(client, chefApi);
fn.apply("foo");
verify(client);
verify(chefApi);
verify(client, chefApi);
}
@Test
public void testReturnsItem() throws IOException {
ChefApi chefApi = createMock(ChefApi.class);
Api api = createMock(Api.class);
Client client = createMock(Client.class);
BootstrapConfigForGroup fn = new BootstrapConfigForGroup("jclouds", chefApi);
DatabagItem config = new DatabagItem("foo",
"{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
BootstrapConfigForGroup fn = new BootstrapConfigForGroup("jclouds", chefApi, json);
DatabagItem databag = new DatabagItem("foo",
"{\"environment\":\"development\",\"ssl_ca_file\":\"/etc/certs/chef-server.crt\","
+ "\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"],"
+ "\"attributes\":{\"tomcat6\":{\"ssl_port\":8433}}}");
expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(config);
expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(databag);
replay(client, chefApi);
replay(api);
replay(chefApi);
BootstrapConfig config = fn.apply("foo");
assertEquals(config.getEnvironment(), "development");
assertEquals(config.getSslCAFile(), "/etc/certs/chef-server.crt");
assertEquals(config.getRunList().get(0), "recipe[apache2]");
assertEquals(config.getRunList().get(1), "role[webserver]");
assertEquals(config.getAttributes().toString(), "{\"tomcat6\":{\"ssl_port\":8433}}");
assertEquals(fn.apply("foo"), config);
verify(api);
verify(chefApi);
verify(client, chefApi);
}
}

View File

@ -33,10 +33,11 @@ import org.jclouds.chef.ChefApiMetadata;
import org.jclouds.chef.config.ChefBootstrapModule;
import org.jclouds.chef.config.ChefParserModule;
import org.jclouds.chef.config.InstallChef;
import org.jclouds.chef.domain.DatabagItem;
import org.jclouds.chef.domain.BootstrapConfig;
import org.jclouds.chef.domain.BootstrapConfig.SSLVerifyMode;
import org.jclouds.chef.util.RunListBuilder;
import org.jclouds.crypto.PemsTest;
import org.jclouds.domain.JsonBall;
import org.jclouds.json.Json;
import org.jclouds.json.config.GsonModule;
import org.jclouds.rest.annotations.ApiVersion;
import org.jclouds.scriptbuilder.domain.OsFamily;
@ -61,7 +62,6 @@ import com.google.inject.name.Names;
@Test(groups = "unit", testName = "GroupToBootScriptTest")
public class GroupToBootScriptTest {
private Json json;
private Statement installChefGems;
private Statement installChefOmnibus;
private Optional<String> validatorName;
@ -88,7 +88,6 @@ public class GroupToBootScriptTest {
}
}, new ChefParserModule(), new GsonModule(), new ChefBootstrapModule());
json = injectorGems.getInstance(Json.class);
installChefGems = injectorGems.getInstance(Key.get(Statement.class, InstallChef.class));
installChefOmnibus = injectorOmnibus.getInstance(Key.get(Statement.class, InstallChef.class));
validatorName = Optional.<String> of("chef-validator");
@ -97,25 +96,25 @@ public class GroupToBootScriptTest {
@Test(expectedExceptions = IllegalStateException.class)
public void testMustHaveValidatorName() {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, DatabagItem> of())), installChefGems,
Optional.<String> absent(), validatorCredential);
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")),
CacheLoader.from(Functions.forMap(ImmutableMap.of("foo", BootstrapConfig.builder().build()))),
installChefOmnibus, Optional.<String> absent(), validatorCredential);
fn.apply("foo", null);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMustHaveValidatorCredential() {
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, DatabagItem> of())), installChefGems,
validatorName, Optional.<PrivateKey> absent());
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")),
CacheLoader.from(Functions.forMap(ImmutableMap.of("foo", BootstrapConfig.builder().build()))),
installChefOmnibus, validatorName, Optional.<PrivateKey> absent());
fn.apply("foo", null);
}
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Key 'foo' not present in map")
public void testMustHaveRunScriptsName() {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, DatabagItem> of())), installChefGems,
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")),
CacheLoader.from(Functions.forMap(ImmutableMap.<String, BootstrapConfig> of())), installChefOmnibus,
validatorName, validatorCredential);
fn.apply("foo", null);
}
@ -123,18 +122,18 @@ public class GroupToBootScriptTest {
@Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "null value in entry: foo=null")
public void testMustHaveRunScriptsValue() {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, DatabagItem> of("foo", (DatabagItem) null))),
installChefGems, validatorName, validatorCredential);
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")),
CacheLoader.from(Functions.forMap(ImmutableMap.of("foo", (BootstrapConfig) null))), installChefOmnibus,
validatorName, validatorCredential);
fn.apply("foo", null);
}
public void testOneRecipe() throws IOException {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, JsonBall> of("foo", new JsonBall(
"{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}")))),
installChefGems, validatorName, validatorCredential);
BootstrapConfig config = BootstrapConfig.builder().attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}"))
.runList(new RunListBuilder().addRecipe("apache2").addRole("webserver").build()).build();
GroupToBootScript fn = groupToBootScriptFor(config, validatorCredential, false);
PrivateKey validatorKey = validatorCredential.get();
expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
@ -142,26 +141,19 @@ public class GroupToBootScriptTest {
assertEquals(
fn.apply("foo", null).render(OsFamily.UNIX),
exitInsteadOfReturn(
OsFamily.UNIX,
Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8)
+ Resources.toString(
Resources.getResource("test_install_rubygems." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8)
+ "gem install chef --no-rdoc --no-ri\n"
+ Resources.toString(Resources.getResource("bootstrap.sh"), Charsets.UTF_8)));
exitInsteadOfReturn(OsFamily.UNIX, readContent("test_install_ruby.sh")
+ readContent("test_install_rubygems.sh"))
+ "gem install chef --no-rdoc --no-ri\n" + readContent("bootstrap.sh"));
verify(validatorKey);
}
public void testOneRecipeAndEnvironment() throws IOException {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, JsonBall> of("foo", new JsonBall(
"{\"tomcat6\":{\"ssl_port\":8433},\"environment\":\"env\","
+ "\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}")))), installChefGems,
validatorName, validatorCredential);
BootstrapConfig config = BootstrapConfig.builder().attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}"))
.environment("env").runList(new RunListBuilder().addRecipe("apache2").addRole("webserver").build()).build();
GroupToBootScript fn = groupToBootScriptFor(config, validatorCredential, false);
PrivateKey validatorKey = validatorCredential.get();
expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
@ -169,56 +161,83 @@ public class GroupToBootScriptTest {
assertEquals(
fn.apply("foo", null).render(OsFamily.UNIX),
exitInsteadOfReturn(
OsFamily.UNIX,
Resources.toString(Resources.getResource("test_install_ruby." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8)
+ Resources.toString(
Resources.getResource("test_install_rubygems." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8)
+ "gem install chef --no-rdoc --no-ri\n"
+ Resources.toString(Resources.getResource("bootstrap-env.sh"), Charsets.UTF_8)));
exitInsteadOfReturn(OsFamily.UNIX, readContent("test_install_ruby.sh")
+ readContent("test_install_rubygems.sh") + "gem install chef --no-rdoc --no-ri\n"
+ readContent("bootstrap-env.sh")));
verify(validatorKey);
}
public void testOneRecipeOmnibus() throws IOException {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, JsonBall> of("foo", new JsonBall(
"{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}")))),
installChefOmnibus, validatorName, validatorCredential);
BootstrapConfig config = BootstrapConfig.builder().attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}"))
.runList(new RunListBuilder().addRecipe("apache2").addRole("webserver").build()).build();
GroupToBootScript fn = groupToBootScriptFor(config, validatorCredential, true);
PrivateKey validatorKey = validatorCredential.get();
expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
replay(validatorKey);
assertEquals(
fn.apply("foo", null).render(OsFamily.UNIX),
assertEquals(fn.apply("foo", null).render(OsFamily.UNIX),
"setupPublicCurl || exit 1\ncurl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 "
+ "-X GET https://www.opscode.com/chef/install.sh |(bash)\n"
+ Resources.toString(Resources.getResource("bootstrap.sh"), Charsets.UTF_8));
+ "-X GET https://www.opscode.com/chef/install.sh |(bash)\n" + readContent("bootstrap.sh"));
verify(validatorKey);
}
public void testOneRecipeAndEnvironmentOmnibus() throws IOException {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, JsonBall> of("foo", new JsonBall(
"{\"tomcat6\":{\"ssl_port\":8433},\"environment\":\"env\","
+ "\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}")))), installChefOmnibus,
validatorName, validatorCredential);
BootstrapConfig config = BootstrapConfig.builder().attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}"))
.environment("env").runList(new RunListBuilder().addRecipe("apache2").addRole("webserver").build()).build();
GroupToBootScript fn = groupToBootScriptFor(config, validatorCredential, true);
PrivateKey validatorKey = validatorCredential.get();
expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
replay(validatorKey);
assertEquals(
fn.apply("foo", null).render(OsFamily.UNIX),
assertEquals(fn.apply("foo", null).render(OsFamily.UNIX),
"setupPublicCurl || exit 1\ncurl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 "
+ "-X GET https://www.opscode.com/chef/install.sh |(bash)\n"
+ Resources.toString(Resources.getResource("bootstrap-env.sh"), Charsets.UTF_8));
+ "-X GET https://www.opscode.com/chef/install.sh |(bash)\n" + readContent("bootstrap-env.sh"));
verify(validatorKey);
}
public void testCustomNodeName() throws IOException {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
BootstrapConfig config = BootstrapConfig.builder().attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}"))
.environment("env").runList(new RunListBuilder().addRecipe("apache2").addRole("webserver").build()).build();
GroupToBootScript fn = groupToBootScriptFor(config, validatorCredential, true);
PrivateKey validatorKey = validatorCredential.get();
expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
replay(validatorKey);
assertEquals(fn.apply("foo", "bar").render(OsFamily.UNIX),
"setupPublicCurl || exit 1\ncurl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 "
+ "-X GET https://www.opscode.com/chef/install.sh |(bash)\n" + readContent("bootstrap-node-env.sh"));
verify(validatorKey);
}
public void testCustomSecurityOptions() throws IOException {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
BootstrapConfig config = BootstrapConfig.builder().attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}"))
.runList(new RunListBuilder().addRecipe("apache2").addRole("webserver").build())
.sslCAFile(readContent("chef.crt")).sslCAPath("/etc/chef").sslVerifyMode(SSLVerifyMode.PEER)
.verifyApiCert(true).build();
GroupToBootScript fn = groupToBootScriptFor(config, validatorCredential, true);
PrivateKey validatorKey = validatorCredential.get();
expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
replay(validatorKey);
assertEquals(fn.apply("foo", null).render(OsFamily.UNIX),
"setupPublicCurl || exit 1\ncurl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 "
+ "-X GET https://www.opscode.com/chef/install.sh |(bash)\n" + readContent("bootstrap-ssl.sh"));
verify(validatorKey);
}
@ -227,24 +246,14 @@ public class GroupToBootScriptTest {
return input.replaceAll(ShellToken.RETURN.to(family), ShellToken.EXIT.to(family));
}
public void testCustomNodeName() throws IOException {
Optional<PrivateKey> validatorCredential = Optional.of(createMock(PrivateKey.class));
GroupToBootScript fn = new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")), json,
CacheLoader.from(Functions.forMap(ImmutableMap.<String, JsonBall> of("foo", new JsonBall(
"{\"tomcat6\":{\"ssl_port\":8433},\"environment\":\"env\","
+ "\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}")))), installChefOmnibus,
validatorName, validatorCredential);
private GroupToBootScript groupToBootScriptFor(BootstrapConfig config, Optional<PrivateKey> validatorCredential,
boolean useOmnibus) {
return new GroupToBootScript(Suppliers.ofInstance(URI.create("http://localhost:4000")),
CacheLoader.from(Functions.forMap(ImmutableMap.of("foo", config))), useOmnibus ? installChefOmnibus
: installChefGems, validatorName, validatorCredential);
}
PrivateKey validatorKey = validatorCredential.get();
expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
replay(validatorKey);
assertEquals(
fn.apply("foo", "bar").render(OsFamily.UNIX),
"setupPublicCurl || exit 1\ncurl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 "
+ "-X GET https://www.opscode.com/chef/install.sh |(bash)\n"
+ Resources.toString(Resources.getResource("bootstrap-node-env.sh"), Charsets.UTF_8));
verify(validatorKey);
}
private static String readContent(String resource) throws IOException {
return Resources.toString(Resources.getResource(resource), Charsets.UTF_8);
}
}

View File

@ -1,92 +0,0 @@
/*
* 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.chef.functions;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.jclouds.chef.ChefApi;
import org.jclouds.chef.ChefApiMetadata;
import org.jclouds.chef.config.ChefParserModule;
import org.jclouds.chef.domain.Client;
import org.jclouds.chef.domain.DatabagItem;
import org.jclouds.json.Json;
import org.jclouds.json.config.GsonModule;
import org.jclouds.rest.annotations.Api;
import org.jclouds.rest.annotations.ApiVersion;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@Test(groups = "unit", testName = "RunListForGroupTest")
public class RunListForGroupTest {
private Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefApiMetadata.DEFAULT_API_VERSION);
}
}, new ChefParserModule(), new GsonModule());
private Json json = injector.getInstance(Json.class);
@Test(expectedExceptions = IllegalStateException.class)
public void testWhenNoDatabagItem() throws IOException {
ChefApi chefApi = createMock(ChefApi.class);
Client client = createMock(Client.class);
RunListForGroup fn = new RunListForGroup(new BootstrapConfigForGroup("jclouds", chefApi), json);
expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(null);
replay(client);
replay(chefApi);
fn.apply("foo");
verify(client);
verify(chefApi);
}
@Test
public void testReadRunList() throws IOException {
ChefApi chefApi = createMock(ChefApi.class);
Api api = createMock(Api.class);
RunListForGroup fn = new RunListForGroup(new BootstrapConfigForGroup("jclouds", chefApi), json);
DatabagItem config = new DatabagItem("foo",
"{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(config);
replay(api);
replay(chefApi);
assertEquals(fn.apply("foo"), ImmutableList.of("recipe[apache2]", "role[webserver]"));
verify(api);
verify(chefApi);
}
}

View File

@ -53,11 +53,6 @@ public class BaseChefServiceTest {
chefService = injector.getInstance(BaseChefService.class);
}
@Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "bootstrapConfig must not be null")
public void testBuildBootstrapConfigurationWithNullConfig() {
chefService.buildBootstrapConfiguration(null);
}
public void testBuildBootstrapConfigurationWithEmptyRunlist() {
BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(ImmutableList.<String> of()).build();
String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
@ -76,7 +71,7 @@ public class BaseChefServiceTest {
BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runlist).attributes(new JsonBall("{}"))
.build();
String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
assertEquals(config, "{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
assertEquals(config, "{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"],\"attributes\":{}}");
}
public void testBuildBootstrapConfigurationWithRunlistAndAttributes() {
@ -84,7 +79,8 @@ public class BaseChefServiceTest {
BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runlist)
.attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}")).build();
String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
assertEquals(config, "{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"],\"tomcat6\":{\"ssl_port\":8433}}");
assertEquals(config,
"{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"],\"attributes\":{\"tomcat6\":{\"ssl_port\":8433}}}");
}
public void testBuildBootstrapConfigurationWithRunlistAndAttributesAndEnvironment() {
@ -92,8 +88,8 @@ public class BaseChefServiceTest {
BootstrapConfig bootstrapConfig = BootstrapConfig.builder().runList(runlist)
.attributes(new JsonBall("{\"tomcat6\":{\"ssl_port\":8433}}")).environment("env").build();
String config = chefService.buildBootstrapConfiguration(bootstrapConfig);
assertEquals(config,
"{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"],\"environment\":\"env\",\"tomcat6\":{\"ssl_port\":8433}}");
assertEquals(config, "{\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"],\"environment\":\"env\","
+ "\"attributes\":{\"tomcat6\":{\"ssl_port\":8433}}}");
}
}

View File

@ -1,5 +1,5 @@
mkdir -p /etc/chef
cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
cat > /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
require 'rubygems'
require 'ohai'
o = Ohai::System.new
@ -9,8 +9,9 @@ cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
log_location STDOUT
validation_client_name "chef-validator"
chef_server_url "http://localhost:4000"
environment "env"
END_OF_JCLOUDS_FILE
cat >> /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
cat > /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
-----BEGIN PRIVATE KEY-----
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVB
eWIyWkpKcUdtMEtLUis4bmZRSk5zU2QrRjl0WE5NVjdDZk9jVzZqc3FzOEVaZ2lW
@ -50,7 +51,7 @@ cat >> /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
-----END PRIVATE KEY-----
END_OF_JCLOUDS_FILE
cat >> /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE'
{"tomcat6":{"ssl_port":8433},"environment":"env","run_list":["recipe[apache2]","role[webserver]"]}
cat > /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE'
{"tomcat6":{"ssl_port":8433},"run_list":["recipe[apache2]","role[webserver]"]}
END_OF_JCLOUDS_FILE
chef-client -j /etc/chef/first-boot.json -E "env"
chef-client -j /etc/chef/first-boot.json

View File

@ -1,5 +1,5 @@
mkdir -p /etc/chef
cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
cat > /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
require 'rubygems'
require 'ohai'
o = Ohai::System.new
@ -9,8 +9,9 @@ cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
log_location STDOUT
validation_client_name "chef-validator"
chef_server_url "http://localhost:4000"
environment "env"
END_OF_JCLOUDS_FILE
cat >> /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
cat > /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
-----BEGIN PRIVATE KEY-----
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVB
eWIyWkpKcUdtMEtLUis4bmZRSk5zU2QrRjl0WE5NVjdDZk9jVzZqc3FzOEVaZ2lW
@ -50,7 +51,7 @@ cat >> /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
-----END PRIVATE KEY-----
END_OF_JCLOUDS_FILE
cat >> /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE'
{"tomcat6":{"ssl_port":8433},"environment":"env","run_list":["recipe[apache2]","role[webserver]"]}
cat > /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE'
{"tomcat6":{"ssl_port":8433},"run_list":["recipe[apache2]","role[webserver]"]}
END_OF_JCLOUDS_FILE
chef-client -j /etc/chef/first-boot.json -E "env"
chef-client -j /etc/chef/first-boot.json

View File

@ -0,0 +1,93 @@
mkdir -p /etc/chef
cat > /etc/chef/chef-server.crt <<-'END_OF_JCLOUDS_FILE'
-----BEGIN CERTIFICATE-----
MIIFDTCCA/WgAwIBAgIQBZ8R1sZP2Lbc8x554UUQ2DANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTQxMTEwMDAwMDAwWhcN
MTcxMTE0MTIwMDAwWjBlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
bjEQMA4GA1UEBxMHU2VhdHRsZTEbMBkGA1UEChMSQ2hlZiBTb2Z0d2FyZSwgSW5j
MRIwEAYDVQQDDAkqLmNoZWYuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC3xCIczkV10O5jTDpbd4YlPLC6kfnVoOkno2N/OOlcLQu3ulj/Lj1j4r6e
2XthJLcFgTO+y+1/IKnnpLKDfkx1YngWEBXEBP+MrrpDUKKs053s45/bI9QBPISA
tXgnYxMH9Glo6FWWd13TUq++OKGw1p1wazH64XK4MAf5y/lkmWXIWumNuO35ZqtB
ME3wJISwVHzHB2CQjlDklt+Mb0APEiIFIZflgu9JNBYzLdvUtxiz15FUZQI7SsYL
TfXOD1KBNMWqN8snG2e5gRAzB2D161DFvAZt8OiYUe+3QurNlTYVzeHv1ok6UqgM
ZcLzg8m801rRip0D7FCGvMCU/ktdAgMBAAGjggHPMIIByzAfBgNVHSMEGDAWgBQP
gGEcgjFh1S8o541GOLQs4cbZ4jAdBgNVHQ4EFgQUwldjw4Pb4HV+wxGZ7MSSRh+d
pm4wHQYDVR0RBBYwFIIJKi5jaGVmLmlvggdjaGVmLmlvMA4GA1UdDwEB/wQEAwIF
oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwawYDVR0fBGQwYjAvoC2g
K4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NzY2Etc2hhMi1nMy5jcmwwL6At
oCuGKWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLXNoYTItZzMuY3JsMEIG
A1UdIAQ7MDkwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
LmRpZ2ljZXJ0LmNvbS9DUFMwfAYIKwYBBQUHAQEEcDBuMCQGCCsGAQUFBzABhhho
dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNl
cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQw
DAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAvcTWenNuvvrhX2omm8LQ
zWOuu8jqpoflACwD4lOSZ4TgOe4pQGCjXq8aRBD5k+goqQrPVf9lHnelUHFQac0Q
5WT4YUmisUbF0S4uY5OGQymM52MvUWG4ODL4gaWhFvN+HAXrDPP/9iitsjV0QOnl
CDq7Q4/XYRYW3opu5nLLbfW6v4QvF5yzZagEACGs7Vt32p6l391UcU8f6wiB3uMD
eioCvjpv/+2YOUNlDPCM3uBubjUhHOwO817wBxXkzdk1OSRe4jzcw/uX6wL7birt
fbaSkpilvVX529pSzB2Lvi9xWOoGMM578dpQ0h3PwhmmvKhhCWP+pI05k3oSkYCP
ng==
-----END CERTIFICATE-----
END_OF_JCLOUDS_FILE
cat > /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
require 'rubygems'
require 'ohai'
o = Ohai::System.new
o.all_plugins
node_name "foo-" + o[:ipaddress]
log_level :info
log_location STDOUT
validation_client_name "chef-validator"
chef_server_url "http://localhost:4000"
ssl_ca_file "/etc/chef/chef-server.crt"
ssl_ca_path "/etc/chef"
ssl_verify_mode :verify_peer
verify_api_cert true
END_OF_JCLOUDS_FILE
cat > /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
-----BEGIN PRIVATE KEY-----
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVB
eWIyWkpKcUdtMEtLUis4bmZRSk5zU2QrRjl0WE5NVjdDZk9jVzZqc3FzOEVaZ2lW
ClIwOWhEMUlZT2o0WXFNMHFKT05sZ3lnNHhSV2V3ZFNHN1FUUGoxbEpwVkFpZGE5
c1h5MitrenlhZ1pBMUFtME8KWmNicWI1aG9lSURnY1grZURhNzlzMHUwRG9tamNm
TzlFS2h2SExCeit6TSszUXFQUmtQVjhuWVRiZnMrSGpWegp6T1U2RDFCMFhSMytJ
UFpabDJBbldzMmQwcWhuU3RIY0RVdm5SVlEwUDQ4Mll3TjlWZ2NlT1p0cFB6MERD
S0VKCjVUeDVTVHViOGswL3p0L1ZBTUhRYWZMU3VRTUxkMnM0Wkx1T1pwdE4vL3VB
c1RteGlyZXFkMzd6KzhaVGRCYkoKOExFcEoraUNYdVNmbTVhVWg3aXc2b3h2VG9Z
MkFMNTMraksyVVFJREFRQUJBb0lCQVFEQTg4QjNpL3hXbjB2WApCVnhGYW1DWW9l
Y3VOakd3WFhrU3laZXc2MTZBK0VPQ3U0N2JoNGFUdXJkRmJZTDBZRmFBdGFXdnps
YU4yZUhnCkRiK0hEdVRlZkUyOStXa2NHazZTc2hQbWl6NVQwWE9DQUlDV3c2d1NW
RGtIbUd3UzRqWnZiQUZtN1c4bndHazkKWWh4Z3hGaVJuZ3N3SlpGb3BPTG9GNVdY
czJ0ZDhndUlZTnNsTXBvN3R1NTBpRm5CSHdLTzJac1BBazh0OW5uUwp4bERhdkty
dXltRW1xSENyMytkdGlvNWVhZW5KY3AzZmpvWEJRT0tVazNpcElJMjlYUkI4TnFl
Q1ZWLzdLeHdxCmNrcU9CRWJSd0JjbGNreUliRCtSaUFnS3ZPZWxPUmpFaUU5UjQy
dnVxdnhSQTZrOWtkOW83dXRsWDBBVXRwRW4KM2daYzZMZXBBb0dCQVA5YWVsNVk3
NStzSzJKSlVOT09oTzhhZTQ1Y2RzaWxwMnlJMFgrVUJhU3VRczIrZHlQcAprcEVI
QXhkNHBtbVN2bi84YzlUbEVaaHIrcVliQUJYVlBsRG5jeHBJdXcyQWpiazdzL1M0
WGFTS3NScXBYTDU3CnpqL1FPcUxrUms4K09WVjlxNmxNZVFOcUx0RWoxdTZKUHZp
WDcwUm8rRlF0UnR0Tk9ZYmZkUC9mQW9HQkFNcEEKWGpSNXdvVjVzVWIrUkVnOXZF
dVlvOFJTeU9hcnhxS0ZDSVhWVU5zTE94KzIyK0FLNCtDUXBidWVXTjdqb3RybApZ
RDZ1VDZzdldpM0FBQzdraVkwVUkvZmpWUFJDVWk4dFZvUVVFMFRhVTVWTElUYVlP
QitXL2JCYURFNE05NTYwCjFOdURXTzkwYmFBNWRmVTQ0aXV6dmEwMnJHSlhLOStu
UzNvOG5rL1BBb0dCQUxPTDZkam5EZTRtd0FhRzZKY28KY2Q0eHI4amt5UHpDUlp1
eUJDU0Jid3BoSVVYTGM3aERwclBreTA2NG5jSkQxVURtd0lka1hkL2ZwTWtnMlFt
QQovQ1VrNkxFRmpNaXNxSG9qT2FDTDlnUVpKUGhMTjVRVU4yeDFQSldHanMxdlFo
OFRreDBpVVVDT2E4YlFQWE5SCiszNE9Uc1c2VFVuYTRDU1pBeWNMZmhmZkFvR0JB
SWdnVnNlZkJDdnVRa0YwTmVVaG1EQ1JaZmhuZDh5NTVSSFIKMUhDdnFLSWxwdity
aGNYL3pteUJMdXRlb3BZeVJKUnNPaUUyRlcwMGk4K3JJUFJ1NFozUTVueWJ4N3cz
UHpWOQpvSE41UjViYUU5T3lJNEtwWld6dHBZWWl0WkY2N05jbkF2VlVMSEhPdlZK
UUduS1lmTEhKWW1ySkY3R0Exb2pNCkF1TWRGYmpGQW9HQVB4VWh4d0Z5OGdhcUJh
aEtVRVpuNEY4MUhGUDVpaEdoa1Q0UUw2QUZQTzJlK0poSUdqdVIKMjcrODVoY0Zx
UStISFZ0RnNtODFiL2ErUjdQNFV1Q1JnYzhlQ2p4UU1vSjFYbDRuN1ZialBiSE1u
SU4wUnl2ZApPNFpwV0RXWW5DTzAyMUpUT1VVT0o0Si95MDQxNkJ2a3cwejU5eTdz
Tlg3d0RCQkhIYksvWENjPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
-----END PRIVATE KEY-----
END_OF_JCLOUDS_FILE
cat > /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE'
{"tomcat6":{"ssl_port":8433},"run_list":["recipe[apache2]","role[webserver]"]}
END_OF_JCLOUDS_FILE
chef-client -j /etc/chef/first-boot.json

View File

@ -1,5 +1,5 @@
mkdir -p /etc/chef
cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
cat > /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
require 'rubygems'
require 'ohai'
o = Ohai::System.new
@ -10,7 +10,7 @@ cat >> /etc/chef/client.rb <<-'END_OF_JCLOUDS_FILE'
validation_client_name "chef-validator"
chef_server_url "http://localhost:4000"
END_OF_JCLOUDS_FILE
cat >> /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
cat > /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
-----BEGIN PRIVATE KEY-----
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVB
eWIyWkpKcUdtMEtLUis4bmZRSk5zU2QrRjl0WE5NVjdDZk9jVzZqc3FzOEVaZ2lW
@ -50,7 +50,7 @@ cat >> /etc/chef/validation.pem <<-'END_OF_JCLOUDS_FILE'
-----END PRIVATE KEY-----
END_OF_JCLOUDS_FILE
cat >> /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE'
cat > /etc/chef/first-boot.json <<-'END_OF_JCLOUDS_FILE'
{"tomcat6":{"ssl_port":8433},"run_list":["recipe[apache2]","role[webserver]"]}
END_OF_JCLOUDS_FILE
chef-client -j /etc/chef/first-boot.json

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFDTCCA/WgAwIBAgIQBZ8R1sZP2Lbc8x554UUQ2DANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTQxMTEwMDAwMDAwWhcN
MTcxMTE0MTIwMDAwWjBlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
bjEQMA4GA1UEBxMHU2VhdHRsZTEbMBkGA1UEChMSQ2hlZiBTb2Z0d2FyZSwgSW5j
MRIwEAYDVQQDDAkqLmNoZWYuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC3xCIczkV10O5jTDpbd4YlPLC6kfnVoOkno2N/OOlcLQu3ulj/Lj1j4r6e
2XthJLcFgTO+y+1/IKnnpLKDfkx1YngWEBXEBP+MrrpDUKKs053s45/bI9QBPISA
tXgnYxMH9Glo6FWWd13TUq++OKGw1p1wazH64XK4MAf5y/lkmWXIWumNuO35ZqtB
ME3wJISwVHzHB2CQjlDklt+Mb0APEiIFIZflgu9JNBYzLdvUtxiz15FUZQI7SsYL
TfXOD1KBNMWqN8snG2e5gRAzB2D161DFvAZt8OiYUe+3QurNlTYVzeHv1ok6UqgM
ZcLzg8m801rRip0D7FCGvMCU/ktdAgMBAAGjggHPMIIByzAfBgNVHSMEGDAWgBQP
gGEcgjFh1S8o541GOLQs4cbZ4jAdBgNVHQ4EFgQUwldjw4Pb4HV+wxGZ7MSSRh+d
pm4wHQYDVR0RBBYwFIIJKi5jaGVmLmlvggdjaGVmLmlvMA4GA1UdDwEB/wQEAwIF
oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwawYDVR0fBGQwYjAvoC2g
K4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NzY2Etc2hhMi1nMy5jcmwwL6At
oCuGKWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLXNoYTItZzMuY3JsMEIG
A1UdIAQ7MDkwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
LmRpZ2ljZXJ0LmNvbS9DUFMwfAYIKwYBBQUHAQEEcDBuMCQGCCsGAQUFBzABhhho
dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNl
cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQw
DAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAvcTWenNuvvrhX2omm8LQ
zWOuu8jqpoflACwD4lOSZ4TgOe4pQGCjXq8aRBD5k+goqQrPVf9lHnelUHFQac0Q
5WT4YUmisUbF0S4uY5OGQymM52MvUWG4ODL4gaWhFvN+HAXrDPP/9iitsjV0QOnl
CDq7Q4/XYRYW3opu5nLLbfW6v4QvF5yzZagEACGs7Vt32p6l391UcU8f6wiB3uMD
eioCvjpv/+2YOUNlDPCM3uBubjUhHOwO817wBxXkzdk1OSRe4jzcw/uX6wL7birt
fbaSkpilvVX529pSzB2Lvi9xWOoGMM578dpQ0h3PwhmmvKhhCWP+pI05k3oSkYCP
ng==
-----END CERTIFICATE-----

View File

@ -423,6 +423,7 @@
<exclude>**/src/test/resources/**/*.txt</exclude>
<exclude>**/src/test/resources/**/*.gz</exclude>
<exclude>**/src/test/resources/**/*.xml</exclude>
<exclude>**/src/test/resources/**/*.crt</exclude>
<!-- META-INF/services files -->
<exclude>**/services/*LoggingModule</exclude>

View File

@ -51,74 +51,78 @@ import org.jclouds.rest.annotations.WrapWith;
@RequestFilters(SignedHeaderAuth.class)
@Consumes(MediaType.APPLICATION_JSON)
@Headers(keys = "X-Chef-Version", values = "{" + Constants.PROPERTY_API_VERSION + "}")
public interface EnterpriseChefApi extends ChefApi
{
/**
* Retrieves an existing user.
*
* @param name The name of the user to get.
* @return The details of the user or <code>null</code> if not found.
*/
@Named("user:get")
@GET
@Path("/users/{name}")
@Fallback(NullOnNotFoundOr404.class)
User getUser(@PathParam("name") String name);
public interface EnterpriseChefApi extends ChefApi {
/**
* Retrieves an existing user.
*
* @param name
* The name of the user to get.
* @return The details of the user or <code>null</code> if not found.
*/
@Named("user:get")
@GET
@Path("/users/{name}")
@Fallback(NullOnNotFoundOr404.class)
User getUser(@PathParam("name") String name);
/**
* List all existing groups.
*
* @return The list of groups.
*/
@Named("group:list")
@GET
@Path("/groups")
@ResponseParser(ParseKeySetFromJson.class)
Set<String> listGroups();
/**
* List all existing groups.
*
* @return The list of groups.
*/
@Named("group:list")
@GET
@Path("/groups")
@ResponseParser(ParseKeySetFromJson.class)
Set<String> listGroups();
/**
* Retrieves an existing group.
*
* @param name The name of the group to get.
* @return The details of the group or <code>null</code> if not found.
*/
@Named("group:get")
@GET
@Path("/groups/{name}")
@Fallback(NullOnNotFoundOr404.class)
Group getGroup(@PathParam("name") String name);
/**
* Retrieves an existing group.
*
* @param name
* The name of the group to get.
* @return The details of the group or <code>null</code> if not found.
*/
@Named("group:get")
@GET
@Path("/groups/{name}")
@Fallback(NullOnNotFoundOr404.class)
Group getGroup(@PathParam("name") String name);
/**
* Creates a new group.
*
* @param name The name of the group to create.
*/
@Named("group:create")
@POST
@Path("/groups")
void createGroup(@WrapWith("groupname") String name);
/**
* Creates a new group.
*
* @param name
* The name of the group to create.
*/
@Named("group:create")
@POST
@Path("/groups")
void createGroup(@WrapWith("groupname") String name);
/**
* Updates a group.
* <p>
* This method can be used to add actors (clients, groups) to the group.
*
* @param group The group with the updated information.
*/
@Named("group:update")
@PUT
@Path("/groups/{name}")
void updateGroup(
@PathParam("name") @ParamParser(GroupName.class) @BinderParam(BindGroupToUpdateRequestJsonPayload.class) Group group);
/**
* Updates a group.
* <p>
* This method can be used to add actors (clients, groups) to the group.
*
* @param group
* The group with the updated information.
*/
@Named("group:update")
@PUT
@Path("/groups/{name}")
void updateGroup(
@PathParam("name") @ParamParser(GroupName.class) @BinderParam(BindGroupToUpdateRequestJsonPayload.class) Group group);
/**
* Deletes a group.
*
* @param name The name of the group to delete.
*/
@Named("group:delete")
@DELETE
@Path("/groups/{name}")
void deleteGroup(@PathParam("name") String name);
/**
* Deletes a group.
*
* @param name
* The name of the group to delete.
*/
@Named("group:delete")
@DELETE
@Path("/groups/{name}")
void deleteGroup(@PathParam("name") String name);
}

View File

@ -23,64 +23,55 @@ import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.internal.BaseProviderMetadata;
/**
* Implementation of @ link org.jclouds.types.ProviderMetadata} for Enterprise Chef
* Implementation of @ link org.jclouds.types.ProviderMetadata} for Enterprise
* Chef
*/
public class EnterpriseChefProviderMetadata extends BaseProviderMetadata
{
public class EnterpriseChefProviderMetadata extends BaseProviderMetadata {
public static Builder builder()
{
return new Builder();
}
public static Builder builder() {
return new Builder();
}
@Override
public Builder toBuilder()
{
return builder().fromProviderMetadata(this);
}
@Override
public Builder toBuilder() {
return builder().fromProviderMetadata(this);
}
public EnterpriseChefProviderMetadata()
{
super(builder());
}
public EnterpriseChefProviderMetadata() {
super(builder());
}
public EnterpriseChefProviderMetadata(Builder builder)
{
super(builder);
}
public EnterpriseChefProviderMetadata(final Builder builder) {
super(builder);
}
public static Properties defaultProperties()
{
Properties properties = new Properties();
return properties;
}
public static Properties defaultProperties() {
Properties properties = new Properties();
return properties;
}
public static class Builder extends BaseProviderMetadata.Builder
{
public static class Builder extends BaseProviderMetadata.Builder {
protected Builder()
{
id("enterprisechef") //
.name("OpsCode Enterprise Chef") //
.endpoint("https://api.opscode.com") //
.homepage(URI.create("https://manage.opscode.com")) //
.console(URI.create("https://manage.opscode.com")) //
.apiMetadata(new EnterpriseChefApiMetadata()) //
.defaultProperties(EnterpriseChefProviderMetadata.defaultProperties());
}
protected Builder() {
id("enterprisechef") //
.name("OpsCode Enterprise Chef") //
.endpoint("https://api.opscode.com") //
.homepage(URI.create("https://manage.opscode.com")) //
.console(URI.create("https://manage.opscode.com")) //
.apiMetadata(new EnterpriseChefApiMetadata()) //
.defaultProperties(EnterpriseChefProviderMetadata.defaultProperties());
}
@Override
public EnterpriseChefProviderMetadata build()
{
return new EnterpriseChefProviderMetadata(this);
}
@Override
public EnterpriseChefProviderMetadata build() {
return new EnterpriseChefProviderMetadata(this);
}
@Override
public Builder fromProviderMetadata(ProviderMetadata in)
{
super.fromProviderMetadata(in);
return this;
}
@Override
public Builder fromProviderMetadata(final ProviderMetadata in) {
super.fromProviderMetadata(in);
return this;
}
}
}
}