You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2015/01/05 14:44:19 UTC

[1/2] jclouds git commit: JCLOUDS-792: Configure chef-client security attributes

Repository: jclouds
Updated Branches:
  refs/heads/master 38ae98e40 -> 6b350fb6b


http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefApi.java
----------------------------------------------------------------------
diff --git a/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefApi.java b/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefApi.java
index 8f1a0ed..159cb1b 100644
--- a/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefApi.java
+++ b/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefApi.java
@@ -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);
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefProviderMetadata.java
----------------------------------------------------------------------
diff --git a/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefProviderMetadata.java b/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefProviderMetadata.java
index 336ea7a..d72cc78 100644
--- a/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefProviderMetadata.java
+++ b/providers/enterprisechef/src/main/java/org/jclouds/enterprisechef/EnterpriseChefProviderMetadata.java
@@ -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;
+      }
 
-    }
+   }
 }


[2/2] jclouds git commit: JCLOUDS-792: Configure chef-client security attributes

Posted by na...@apache.org.
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.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/6b350fb6
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/6b350fb6
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/6b350fb6

Branch: refs/heads/master
Commit: 6b350fb6b75abbe93a8077d52b50eaa93f2d1165
Parents: 38ae98e
Author: Ignasi Barrera <na...@apache.org>
Authored: Fri Dec 5 16:51:53 2014 +0100
Committer: Ignasi Barrera <na...@apache.org>
Committed: Mon Jan 5 14:15:54 2015 +0100

----------------------------------------------------------------------
 .../src/main/java/org/jclouds/chef/ChefApi.java |   8 +
 .../main/java/org/jclouds/chef/ChefContext.java |   5 +
 .../main/java/org/jclouds/chef/ChefService.java |  24 +--
 .../chef/config/BaseChefHttpApiModule.java      |  12 +-
 .../jclouds/chef/config/ChefParserModule.java   |   2 +-
 .../org/jclouds/chef/config/CookbookParser.java |   2 +
 .../chef/config/CookbookVersionsParser.java     |   2 +
 .../jclouds/chef/domain/BootstrapConfig.java    | 128 +++++++++++++--
 .../chef/functions/BootstrapConfigForGroup.java |  27 ++-
 .../chef/functions/GroupToBootScript.java       | 137 +++++++++-------
 ...seCookbookDefinitionCheckingChefVersion.java |   2 +
 ...arseCookbookVersionsCheckingChefVersion.java |   2 +
 .../ParseCookbookVersionsV09FromJson.java       |   2 +
 .../jclouds/chef/functions/RunListForGroup.java |  61 -------
 .../jclouds/chef/internal/BaseChefService.java  | 105 ++++++------
 .../jclouds/chef/internal/ChefContextImpl.java  |   7 +
 .../functions/BootstrapConfigForGroupTest.java  |  51 ++++--
 .../chef/functions/GroupToBootScriptTest.java   | 163 ++++++++++---------
 .../chef/functions/RunListForGroupTest.java     |  92 -----------
 .../chef/internal/BaseChefServiceTest.java      |  14 +-
 apis/chef/src/test/resources/bootstrap-env.sh   |  11 +-
 .../src/test/resources/bootstrap-node-env.sh    |  11 +-
 apis/chef/src/test/resources/bootstrap-ssl.sh   |  93 +++++++++++
 apis/chef/src/test/resources/bootstrap.sh       |   6 +-
 apis/chef/src/test/resources/chef.crt           |  30 ++++
 project/pom.xml                                 |   1 +
 .../enterprisechef/EnterpriseChefApi.java       | 132 +++++++--------
 .../EnterpriseChefProviderMetadata.java         |  89 +++++-----
 28 files changed, 667 insertions(+), 552 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java b/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java
index 13f4971..0c4889b 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java
@@ -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
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/ChefContext.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefContext.java b/apis/chef/src/main/java/org/jclouds/chef/ChefContext.java
index 7809634..794116d 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/ChefContext.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/ChefContext.java
@@ -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 {
 
    /**

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/ChefService.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefService.java b/apis/chef/src/main/java/org/jclouds/chef/ChefService.java
index 6f136fa..e15d00c 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/ChefService.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/ChefService.java
@@ -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
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java b/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java
index 2fc4201..c8b2faa 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/BaseChefHttpApiModule.java
@@ -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);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java b/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java
index c214236..c00eda5 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/ChefParserModule.java
@@ -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,

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java
index 34eedde..22048c5 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookParser.java
@@ -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 {
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java
index c946917..5fd2ba5 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/config/CookbookVersionsParser.java
@@ -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 {
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java b/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java
index c10c150..7b10edc 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/BootstrapConfig.java
@@ -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;
-
-   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");
+   @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;
+
+   @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;
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java b/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
index ec75e47..32f7c4b 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
@@ -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);
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java b/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java
index 6010353..fad4fb6 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java
@@ -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())));
+      String chefBootFile = chefConfigDir + "{fs}first-boot.json";
 
-      Statement createValidationPem = appendFile(chefConfigDir + "{fs}validation.pem",
-            Splitter.on(newLinePattern).split(Pems.pem(validatorKey)));
+      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())));
+      }
+      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));
 
-      String chefBootFile = chefConfigDir + "{fs}first-boot.json";
-      Statement createFirstBoot = appendFile(chefBootFile, Collections.singleton(json.toJson(bootstrapConfig)));
+      return new StatementList(statements.build());
+   }
 
-      ImmutableMap.Builder<String, String> options = ImmutableMap.builder();
-      options.put("-j", chefBootFile);
-      if (environment.isPresent()) {
-         options.put("-E", environment.get().toString());
+   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");
       }
-      String strOptions = Joiner.on(' ').withKeyValueSeparator(" ").join(options.build());
-      Statement runChef = exec("chef-client " + strOptions);
+      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());
+   }
 
-      return newStatementList(new ExitInsteadOfReturn(installChef), createChefConfigDir, createClientRb, createValidationPem,
-            createFirstBoot, runChef);
+   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()));
+      }
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
index ffb4201..53c6ea7 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
@@ -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

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java
index f82a900..e98b27a 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java
@@ -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

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.java
index 4421b3e..4bc76d4 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.java
@@ -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;

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.java b/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.java
deleted file mode 100644
index b14ae71..0000000
--- a/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.java
+++ /dev/null
@@ -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);
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java
index 838fa4f..ca79b77 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java
@@ -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());
-      }
-
-      if (bootstrapConfig.getAttribtues().isPresent()) {
-         Map<String, Object> attributes = json.fromJson(bootstrapConfig.getAttribtues().get().toString(),
-               BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
-         configMap.putAll(attributes);
+   private static void putIfPresent(Map<String, Object> configMap, Optional<?> configProperty, String name) {
+      if (configProperty.isPresent()) {
+         configMap.put(name, configProperty.get().toString());
       }
-
-      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);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java b/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java
index 8aef880..e7d1170 100644
--- a/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java
+++ b/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java
@@ -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;
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/java/org/jclouds/chef/functions/BootstrapConfigForGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/java/org/jclouds/chef/functions/BootstrapConfigForGroupTest.java b/apis/chef/src/test/java/org/jclouds/chef/functions/BootstrapConfigForGroupTest.java
index f326482..c704ee6 100644
--- a/apis/chef/src/test/java/org/jclouds/chef/functions/BootstrapConfigForGroupTest.java
+++ b/apis/chef/src/test/java/org/jclouds/chef/functions/BootstrapConfigForGroupTest.java
@@ -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);
-
-      BootstrapConfigForGroup fn = new BootstrapConfigForGroup("jclouds", chefApi);
-      DatabagItem config = new DatabagItem("foo",
-            "{\"tomcat6\":{\"ssl_port\":8433},\"run_list\":[\"recipe[apache2]\",\"role[webserver]\"]}");
+      Client client = createMock(Client.class);
 
-      expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(config);
+      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}}}");
 
-      replay(api);
-      replay(chefApi);
+      expect(chefApi.getDatabagItem("jclouds", "foo")).andReturn(databag);
+      replay(client, chefApi);
 
-      assertEquals(fn.apply("foo"), config);
+      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}}");
 
-      verify(api);
-      verify(chefApi);
+      verify(client, chefApi);
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java b/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java
index 89b6ec0..9cf0690 100644
--- a/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java
+++ b/apis/chef/src/test/java/org/jclouds/chef/functions/GroupToBootScriptTest.java
@@ -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,82 +161,99 @@ 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);
    }
 
-   private static String exitInsteadOfReturn(OsFamily family, String input) {
-      return input.replaceAll(ShellToken.RETURN.to(family), ShellToken.EXIT.to(family));
+   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 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);
+   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();
 
-    PrivateKey validatorKey = validatorCredential.get();
-    expect(validatorKey.getEncoded()).andReturn(PemsTest.PRIVATE_KEY.getBytes());
-    replay(validatorKey);
+      GroupToBootScript fn = groupToBootScriptFor(config, validatorCredential, true);
 
-    assertEquals(
-            fn.apply("foo", "bar").render(OsFamily.UNIX),
+      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"
-                    + Resources.toString(Resources.getResource("bootstrap-node-env.sh"), Charsets.UTF_8));
+                  + "-X GET  https://www.opscode.com/chef/install.sh |(bash)\n" + readContent("bootstrap-ssl.sh"));
 
-    verify(validatorKey);
-  }
+      verify(validatorKey);
+   }
+
+   private static String exitInsteadOfReturn(OsFamily family, String input) {
+      return input.replaceAll(ShellToken.RETURN.to(family), ShellToken.EXIT.to(family));
+   }
+
+   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);
+   }
+
+   private static String readContent(String resource) throws IOException {
+      return Resources.toString(Resources.getResource(resource), Charsets.UTF_8);
+   }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.java b/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.java
deleted file mode 100644
index 28b8833..0000000
--- a/apis/chef/src/test/java/org/jclouds/chef/functions/RunListForGroupTest.java
+++ /dev/null
@@ -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);
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java
index d998534..e6de8e3 100644
--- a/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java
+++ b/apis/chef/src/test/java/org/jclouds/chef/internal/BaseChefServiceTest.java
@@ -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}}}");
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/resources/bootstrap-env.sh
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/resources/bootstrap-env.sh b/apis/chef/src/test/resources/bootstrap-env.sh
index 315e248..0bd241d9 100755
--- a/apis/chef/src/test/resources/bootstrap-env.sh
+++ b/apis/chef/src/test/resources/bootstrap-env.sh
@@ -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

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/resources/bootstrap-node-env.sh
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/resources/bootstrap-node-env.sh b/apis/chef/src/test/resources/bootstrap-node-env.sh
index 84713d6..bf73cc6 100755
--- a/apis/chef/src/test/resources/bootstrap-node-env.sh
+++ b/apis/chef/src/test/resources/bootstrap-node-env.sh
@@ -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

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/resources/bootstrap-ssl.sh
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/resources/bootstrap-ssl.sh b/apis/chef/src/test/resources/bootstrap-ssl.sh
new file mode 100755
index 0000000..0299f8b
--- /dev/null
+++ b/apis/chef/src/test/resources/bootstrap-ssl.sh
@@ -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

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/resources/bootstrap.sh
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/resources/bootstrap.sh b/apis/chef/src/test/resources/bootstrap.sh
index 0eb402e..c5381f4 100755
--- a/apis/chef/src/test/resources/bootstrap.sh
+++ b/apis/chef/src/test/resources/bootstrap.sh
@@ -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

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/apis/chef/src/test/resources/chef.crt
----------------------------------------------------------------------
diff --git a/apis/chef/src/test/resources/chef.crt b/apis/chef/src/test/resources/chef.crt
new file mode 100644
index 0000000..d2cfb31
--- /dev/null
+++ b/apis/chef/src/test/resources/chef.crt
@@ -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-----

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6b350fb6/project/pom.xml
----------------------------------------------------------------------
diff --git a/project/pom.xml b/project/pom.xml
index 0e5eedc..9dfbf74 100644
--- a/project/pom.xml
+++ b/project/pom.xml
@@ -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>