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 2013/09/13 17:56:08 UTC

[2/2] git commit: Cleaned up ChefApi and ChefService interfaces

Cleaned up ChefApi and ChefService interfaces

Cleaned up and renamed some methods to have a more consistent naming,
and improved the javadocs.


Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/commit/96a5d917
Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/tree/96a5d917
Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/diff/96a5d917

Branch: refs/heads/master
Commit: 96a5d917fc7c04623896fbea7d60f4790e262ad0
Parents: 538b3c2
Author: Ignasi Barrera <na...@apache.org>
Authored: Thu Sep 12 21:40:01 2013 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Sep 13 17:55:02 2013 +0200

----------------------------------------------------------------------
 .../src/main/java/org/jclouds/chef/ChefApi.java | 1078 ++++++++----------
 .../java/org/jclouds/chef/ChefApiMetadata.java  |    7 +-
 .../main/java/org/jclouds/chef/ChefService.java |  194 ++--
 .../jclouds/chef/filters/SignedHeaderAuth.java  |    4 +-
 .../jclouds/chef/internal/BaseChefService.java  |  153 +--
 .../internal/ListCookbookVersionsImpl.java      |    2 +-
 .../internal/ListEnvironmentNodesImpl.java      |    4 +-
 .../org/jclouds/chef/test/TransientChefApi.java |   20 +-
 .../org/jclouds/chef/ChefApiExpectTest.java     |   20 +-
 .../test/java/org/jclouds/chef/ChefApiTest.java |  100 +-
 .../BindHexEncodedMD5sToJsonPayloadTest.java    |    4 +-
 .../chef/functions/GroupToBootScriptTest.java   |    4 +-
 .../chef/functions/ParseClientFromJsonTest.java |    4 +-
 .../ParseCookbookDefinitionFromJsonTest.java    |    4 +-
 .../ParseCookbookDefinitionFromJsonv10Test.java |    4 +-
 ...seCookbookDefinitionListFromJsonv10Test.java |    4 +-
 .../ParseCookbookVersionFromJsonTest.java       |    4 +-
 .../ParseCookbookVersionsV09FromJsonTest.java   |    4 +-
 .../ParseCookbookVersionsV10FromJsonTest.java   |    4 +-
 .../functions/ParseDataBagItemFromJsonTest.java |    4 +-
 .../chef/functions/ParseKeySetFromJsonTest.java |    4 +-
 .../chef/functions/ParseNodeFromJsonTest.java   |    4 +-
 .../functions/ParseSandboxFromJsonTest.java     |    4 +-
 .../ParseSearchDataBagItemFromJsonTest.java     |    4 +-
 .../ParseUploadSandboxFromJsonTest.java         |    4 +-
 .../chef/functions/RunListForGroupTest.java     |    3 +-
 .../chef/internal/BaseChefApiLiveTest.java      |   18 +-
 .../chef/internal/BaseStubbedOhaiLiveTest.java  |    2 +-
 .../java/org/jclouds/ohai/config/JMXTest.java   |    4 +-
 .../org/jclouds/ohai/config/OhaiModuleTest.java |    6 +-
 .../ohai/functions/NestSlashKeysTest.java       |    4 +-
 .../EnterpriseChefApiMetadata.java              |    3 +-
 .../EnterpriseChefApiExpectTest.java            |   22 +-
 .../EnterpriseChefProviderMetadataTest.java     |    2 -
 ...BindGroupToUpdateRequestJsonPayloadTest.java |    4 +-
 .../enterprisechef/binders/GroupNameTest.java   |    1 -
 36 files changed, 784 insertions(+), 927 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/ChefApi.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/ChefApi.java b/core/src/main/java/org/jclouds/chef/ChefApi.java
index fb5366f..877deb6 100644
--- a/core/src/main/java/org/jclouds/chef/ChefApi.java
+++ b/core/src/main/java/org/jclouds/chef/ChefApi.java
@@ -69,9 +69,7 @@ import org.jclouds.chef.functions.ParseSearchRolesFromJson;
 import org.jclouds.chef.functions.UriForResource;
 import org.jclouds.chef.options.CreateClientOptions;
 import org.jclouds.chef.options.SearchOptions;
-import org.jclouds.http.HttpResponseException;
 import org.jclouds.io.Payload;
-import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.EndpointParam;
 import org.jclouds.rest.annotations.Fallback;
@@ -96,141 +94,39 @@ 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 {
-   /**
-    * The target Chef Server version.
-    */
-   public static final String VERSION = "0.10.8";
-
-   /**
-    * Creates a new sandbox. It accepts a list of checksums as input and returns
-    * the URLs against which to PUT files that need to be uploaded.
-    * 
-    * @param md5s
-    *           raw md5s; uses {@code Bytes.asList()} and
-    *           {@code Bytes.toByteArray()} as necessary
-    * @return The URLs against which to PUT files that need to be uploaded.
-    */
-   @Named("sandbox:upload")
-   @POST
-   @Path("/sandboxes")
-   UploadSandbox getUploadSandboxForChecksums(@BinderParam(BindChecksumsToJsonPayload.class) Set<List<Byte>> md5s);
-
-   /**
-    * Uploads the given content to the sandbox at the given URI.
-    * <p/>
-    * The URI must be obtained, after uploading a sandbox, from the
-    * {@link UploadSandbox#getUri()}.
-    */
-   @Named("content:upload")
-   @PUT
-   @Produces("application/x-binary")
-   void uploadContent(@EndpointParam URI location, Payload content);
-
-   /**
-    * Confirms if the sandbox is completed or not.
-    * <p/>
-    * This method should be used after uploading contents to the sandbox.
-    * 
-    * @return The sandbox
-    */
-   @Named("sandbox:commit")
-   @PUT
-   @Path("/sandboxes/{id}")
-   Sandbox commitSandbox(@PathParam("id") String id, @WrapWith("is_completed") boolean isCompleted);
-
-   /**
-    * @return a list of all the cookbook names
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have permission to see the
-    *            cookbook list.
-    */
-   @Named("cookbook:list")
-   @GET
-   @Path("/cookbooks")
-   @ResponseParser(ParseCookbookDefinitionCheckingChefVersion.class)
-   @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listCookbooks();
 
-   /**
-    * Creates or updates (uploads) a cookbook
-    * 
-    * @param cookbookName
-    * @throws HttpResponseException
-    *            "409 Conflict" if the cookbook already exists
-    */
-   @Named("cookbook:update")
-   @PUT
-   @Path("/cookbooks/{cookbookname}/{version}")
-   CookbookVersion updateCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version,
-         @BinderParam(BindToJsonPayload.class) CookbookVersion cookbook);
+   // Clients
 
    /**
-    * deletes an existing cookbook.
+    * List the names of the existing clients.
     * 
-    * @return last state of the api you deleted or null, if not found
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have Delete rights on the
-    *            cookbook.
-    */
-   @Named("cookbook:delete")
-   @DELETE
-   @Path("/cookbooks/{cookbookname}/{version}")
-   @Fallback(NullOnNotFoundOr404.class)
-   CookbookVersion deleteCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version);
-
-   /**
-    * @return the versions of a cookbook or null, if not found
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to view the
-    *            cookbook.
+    * @return The names of the existing clients.
     */
-   @Named("cookbook:versions")
+   @Named("client:list")
    @GET
-   @Path("/cookbooks/{cookbookname}")
-   @ResponseParser(ParseCookbookVersionsCheckingChefVersion.class)
+   @Path("/clients")
+   @ResponseParser(ParseKeySetFromJson.class)
    @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> getVersionsOfCookbook(@PathParam("cookbookname") String cookbookName);
+   Set<String> listClients();
 
    /**
-    * Returns a description of the cookbook, with links to all of its component
-    * parts, and the metadata.
+    * Get the details of existing client.
     * 
-    * @return the cookbook or null, if not found
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to view the
-    *            cookbook.
+    * @param clientname The name of the client to get.
+    * @return The details of the given client.
     */
-   @Named("cookbook:get")
+   @Named("client:get")
    @GET
-   @Path("/cookbooks/{cookbookname}/{version}")
+   @Path("/clients/{clientname}")
    @Fallback(NullOnNotFoundOr404.class)
-   CookbookVersion getCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version);
+   Client getClient(@PathParam("clientname") String clientname);
 
    /**
-    * creates a new client
+    * Creates a new client.
     * 
-    * @return the private key of the client. You can then use this client name
-    *         and private key to access the Opscode API.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to create a
-    *            client.
-    * @throws HttpResponseException
-    *            "409 Conflict" if the client already exists
+    * @param clientname The name of the new client
+    * @return The client with the generated private key. This key should be
+    *         stored so client can be properly authenticated .
     */
    @Named("client:create")
    @POST
@@ -239,18 +135,12 @@ public interface ChefApi extends Closeable {
    Client createClient(@PayloadParam("name") String clientname);
 
    /**
-    * creates a new administrator client
+    * Creates a new client with custom options.
     * 
-    * @return the private key of the client. You can then use this client name
-    *         and private key to access the Opscode API.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to create a
-    *            client.
-    * @throws HttpResponseException
-    *            "409 Conflict" if the client already exists
+    * @param clientname The name of the new client
+    * @param options The options to customize the client creation.
+    * @return The client with the generated private key. This key should be
+    *         stored so client can be properly authenticated .
     */
    @Named("client:create")
    @POST
@@ -259,16 +149,11 @@ public interface ChefApi extends Closeable {
    Client createClient(@PayloadParam("name") String clientname, CreateClientOptions options);
 
    /**
-    * generate a new key-pair for this client, and return the new private key in
+    * Generate a new key-pair for this client, and return the new private key in
     * the response body.
     * 
-    * @return the new private key
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to modify the
-    *            client.
+    * @param clientname The name of the client.
+    * @return The details of the client with the new private key.
     */
    @Named("client:generatekey")
    @PUT
@@ -277,29 +162,10 @@ public interface ChefApi extends Closeable {
          @PathParam("clientname") @BinderParam(BindGenerateKeyForClientToJsonPayload.class) String clientname);
 
    /**
-    * @return list of client names.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have rights to list clients.
-    */
-   @Named("client:list")
-   @GET
-   @Path("/clients")
-   @ResponseParser(ParseKeySetFromJson.class)
-   @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listClients();
-
-   /**
-    * deletes an existing client.
+    * Deletes the given client.
     * 
-    * @return last state of the client you deleted or null, if not found
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have Delete rights on the client.
+    * @param clientname The name of the client to delete.
+    * @return The deleted client.
     */
    @Named("client:delete")
    @DELETE
@@ -307,180 +173,162 @@ public interface ChefApi extends Closeable {
    @Fallback(NullOnNotFoundOr404.class)
    Client deleteClient(@PathParam("clientname") String clientname);
 
+   // Cookbooks
+
    /**
-    * gets an existing client.
+    * List the names of the existing cookbooks.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the client.
+    * @return The names of the exsisting cookbooks.
     */
-   @Named("client:get")
+   @Named("cookbook:list")
    @GET
-   @Path("/clients/{clientname}")
-   @Fallback(NullOnNotFoundOr404.class)
-   Client getClient(@PathParam("clientname") String clientname);
+   @Path("/cookbooks")
+   @ResponseParser(ParseCookbookDefinitionCheckingChefVersion.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listCookbooks();
 
    /**
-    * creates a new node
+    * List the cookbooks that are available in the given environment.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to create a
-    *            node.
-    * @throws HttpResponseException
-    *            "409 Conflict" if the node already exists
+    * @param environmentname The name of the environment to get the cookbooks
+    *        from.
+    * @return The definitions of the cookbooks (URL and versions) available in
+    *         the given environment.
     */
-   @Named("node:create")
-   @POST
-   @Path("/nodes")
-   void createNode(@BinderParam(BindToJsonPayload.class) Node node);
+   @SinceApiVersion("0.10.0")
+   @Named("cookbook:list")
+   @GET
+   @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
+   @Path("/environments/{environmentname}/cookbooks")
+   @Fallback(NullOnNotFoundOr404.class)
+   Set<CookbookDefinition> listCookbooksInEnvironment(@PathParam("environmentname") String environmentname);
 
    /**
-    * Creates or updates (uploads) a node
+    * List the cookbooks that are available in the given environment, limiting
+    * the number of versions returned for each cookbook.
     * 
-    * @param node
-    *           updated node
-    * @throws HttpResponseException
-    *            "409 Conflict" if the node already exists
+    * @param environmentname The name of the environment.
+    * @param numversions The number of cookbook versions to include in the
+    *        response, where n is the number of cookbook versions.
+    * @return The definitions of the cookbooks (URL and versions) available in
+    *         the given environment.
     */
-   @Named("node:update")
-   @PUT
-   @Path("/nodes/{nodename}")
-   Node updateNode(@PathParam("nodename") @ParamParser(NodeName.class) @BinderParam(BindToJsonPayload.class) Node node);
+   @SinceApiVersion("0.10.0")
+   @Named("cookbook:list")
+   @GET
+   @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
+   @Path("/environments/{environmentname}/cookbooks?num_versions={numversions}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Set<CookbookDefinition> listCookbooksInEnvironment(@PathParam("environmentname") String environmentname,
+         @PathParam("numversions") String numversions);
 
    /**
-    * @return list of node names.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have rights to list nodes.
+    * List the available versions of the given cookbook.
+    * 
+    * @param cookbookName The name of the cookbook.
+    * @return The available versions of the given cookbook.
     */
-   @Named("node:list")
+   @Named("cookbook:versions")
    @GET
-   @Path("/nodes")
-   @ResponseParser(ParseKeySetFromJson.class)
+   @Path("/cookbooks/{cookbookname}")
+   @ResponseParser(ParseCookbookVersionsCheckingChefVersion.class)
    @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listNodes();
+   Set<String> listVersionsOfCookbook(@PathParam("cookbookname") String cookbookName);
 
    /**
-    * deletes an existing node.
+    * Get the details of the given cookbook, with the links to each resource
+    * such as recipe files, attributes, etc.
     * 
-    * @return last state of the node you deleted or null, if not found
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have Delete rights on the node.
+    * @param cookbookName The name of the cookbook.
+    * @param version The version of the cookbook to get.
+    * @return The details of the given cookbook.
     */
-   @Named("node:delete")
-   @DELETE
-   @Path("/nodes/{nodename}")
+   @Named("cookbook:get")
+   @GET
+   @Path("/cookbooks/{cookbookname}/{version}")
    @Fallback(NullOnNotFoundOr404.class)
-   Node deleteNode(@PathParam("nodename") String nodename);
+   CookbookVersion getCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version);
 
    /**
-    * gets an existing node.
+    * Get the definition of the cookbook in the given environment.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the node.
+    * @param environmentname The name of the environment.
+    * @param cookbookname The name of the cookbook.
+    * @return The definition of the cookbook (URL and versions) of the cookbook
+    *         in the given environment.
     */
-   @Named("node:get")
+   @SinceApiVersion("0.10.0")
+   @Named("environment:cookbook")
    @GET
-   @Path("/nodes/{nodename}")
-   @Fallback(NullOnNotFoundOr404.class)
-   Node getNode(@PathParam("nodename") String nodename);
+   @ResponseParser(ParseCookbookDefinitionFromJsonv10.class)
+   @Path("/environments/{environmentname}/cookbooks/{cookbookname}")
+   CookbookDefinition getCookbookInEnvironment(@PathParam("environmentname") String environmentname,
+         @PathParam("cookbookname") String cookbookname);
 
    /**
-    * creates a new role
+    * Get the definition of the cookbook in the given environment.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to create a
-    *            role.
-    * @throws HttpResponseException
-    *            "409 Conflict" if the role already exists
+    * @param environmentname The name of the environment.
+    * @param cookbookname The name of the cookbook.
+    * @param numversions The number of cookbook versions to include in the
+    *        response, where n is the number of cookbook versions.
+    * @return The definition of the cookbook (URL and versions) of the cookbook
+    *         in the given environment.
     */
-   @Named("role:create")
-   @POST
-   @Path("/roles")
-   void createRole(@BinderParam(BindToJsonPayload.class) Role role);
+   @SinceApiVersion("0.10.0")
+   @Named("environment:cookbook")
+   @GET
+   @ResponseParser(ParseCookbookDefinitionFromJsonv10.class)
+   @Path("/environments/{environmentname}/cookbooks/{cookbookname}?num_versions={numversions}")
+   CookbookDefinition getCookbookInEnvironment(@PathParam("environmentname") String environmentname,
+         @PathParam("cookbookname") String cookbookname, @PathParam("numversions") String numversions);
 
    /**
-    * Creates or updates (uploads) a role
+    * List the names of the recipes in the given environment.
     * 
-    * @param roleName
-    * @throws HttpResponseException
-    *            "409 Conflict" if the role already exists
-    */
-   @Named("role:update")
-   @PUT
-   @Path("/roles/{rolename}")
-   Role updateRole(@PathParam("rolename") @ParamParser(RoleName.class) @BinderParam(BindToJsonPayload.class) Role role);
-
-   /**
-    * @return list of role names.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have rights to list roles.
+    * @param environmentname The name of the environment.
+    * @return The names of the recipes in the given environment.
     */
-   @Named("role:list")
+   @SinceApiVersion("0.10.0")
+   @Named("environment:recipelist")
    @GET
-   @Path("/roles")
-   @ResponseParser(ParseKeySetFromJson.class)
+   @Path("/environments/{environmentname}/recipes")
    @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listRoles();
+   Set<String> listRecipesInEnvironment(@PathParam("environmentname") String environmentname);
 
    /**
-    * deletes an existing role.
+    * Creates or updates the given cookbook.
     * 
-    * @return last state of the role you deleted or null, if not found
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have Delete rights on the role.
+    * @param cookbookName The name of the cookbook to create or update.
+    * @param version The version of the cookbook to create or update.
+    * @param cookbook The contents of the cookbook to create or update.
+    * @return The details of the created or updated cookbook.
     */
-   @Named("role:delete")
-   @DELETE
-   @Path("/roles/{rolename}")
-   @Fallback(NullOnNotFoundOr404.class)
-   Role deleteRole(@PathParam("rolename") String rolename);
+   @Named("cookbook:update")
+   @PUT
+   @Path("/cookbooks/{cookbookname}/{version}")
+   CookbookVersion updateCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version,
+         @BinderParam(BindToJsonPayload.class) CookbookVersion cookbook);
 
    /**
-    * gets an existing role.
+    * Delete the given cookbook.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the role.
+    * @param cookbookName The name of the cookbook to delete.
+    * @param version The version of the cookbook to delete.
+    * @return The details of the deleted cookbook.
     */
-   @Named("role:get")
-   @GET
-   @Path("/roles/{rolename}")
+   @Named("cookbook:delete")
+   @DELETE
+   @Path("/cookbooks/{cookbookname}/{version}")
    @Fallback(NullOnNotFoundOr404.class)
-   Role getRole(@PathParam("rolename") String rolename);
+   CookbookVersion deleteCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version);
+
+   // Data bags
 
    /**
-    * lists databags available to the api
+    * List the names of the existing data bags.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
+    * @return The names of the existing data bags.
     */
    @Named("databag:list")
    @GET
@@ -490,13 +338,9 @@ public interface ChefApi extends Closeable {
    Set<String> listDatabags();
 
    /**
-    * creates a databag.
+    * Creates a new data bag.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
+    * @param databagName The name for the new data bag.
     */
    @Named("databag:create")
    @POST
@@ -504,13 +348,9 @@ public interface ChefApi extends Closeable {
    void createDatabag(@WrapWith("name") String databagName);
 
    /**
-    * Delete a data bag, including its items
+    * Deletes a data bag, including its items.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
+    * @param databagName The name of the data bag to delete.
     */
    @Named("databag:delete")
    @DELETE
@@ -519,13 +359,10 @@ public interface ChefApi extends Closeable {
    void deleteDatabag(@PathParam("name") String databagName);
 
    /**
-    * Show the items in a data bag.
+    * List the names of the items in a data bag.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
+    * @param databagName The name of the data bag.
+    * @return The names of the items in the given data bag.
     */
    @Named("databag:listitems")
    @GET
@@ -535,16 +372,25 @@ public interface ChefApi extends Closeable {
    Set<String> listDatabagItems(@PathParam("name") String databagName);
 
    /**
-    * Create a data bag item in the data bag
+    * Get an item in a data bag.
+    * 
+    * @param databagName The name of the data bag.
+    * @param databagItemId The identifier of the item to get.
+    * @return The details of the item in the given data bag.
+    */
+   @Named("databag:getitem")
+   @GET
+   @Path("/data/{databagName}/{databagItemId}")
+   @Fallback(NullOnNotFoundOr404.class)
+   DatabagItem getDatabagItem(@PathParam("databagName") String databagName,
+         @PathParam("databagItemId") String databagItemId);
+
+   /**
+    * Adds an item in a data bag.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
-    *            <p/>
-    * @throws IllegalStateException
-    *            if the item already exists
+    * @param databagName The name of the data bag.
+    * @param The item to add to the data bag.
+    * @param The item just added to the data bag.
     */
    @Named("databag:createitem")
    @POST
@@ -553,13 +399,11 @@ public interface ChefApi extends Closeable {
          @BinderParam(BindToJsonPayload.class) DatabagItem databagItem);
 
    /**
-    * Update (or create if not exists) a data bag item
+    * Update an item in a data bag.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
+    * @param databagName The name of the data bag.
+    * @param item The new contents for the item in the data bag.
+    * @return The details for the updated item in the data bag.
     */
    @Named("databag:updateitem")
    @PUT
@@ -569,29 +413,11 @@ public interface ChefApi extends Closeable {
          @PathParam("databagItemId") @ParamParser(DatabagItemId.class) @BinderParam(BindToJsonPayload.class) DatabagItem item);
 
    /**
-    * gets an existing databag item.
-    * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
-    */
-   @Named("databag:getitem")
-   @GET
-   @Path("/data/{databagName}/{databagItemId}")
-   @Fallback(NullOnNotFoundOr404.class)
-   DatabagItem getDatabagItem(@PathParam("databagName") String databagName,
-         @PathParam("databagItemId") String databagItemId);
-
-   /**
-    * Delete a data bag item
+    * Delete an item from a data bag.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
+    * @param databagName The name of the data bag.
+    * @param databagItemId The identifier of the item to delete.
+    * @return The item deleted from the data bag.
     */
    @Named("databag:deleteitem")
    @DELETE
@@ -600,178 +426,239 @@ public interface ChefApi extends Closeable {
    DatabagItem deleteDatabagItem(@PathParam("databagName") String databagName,
          @PathParam("databagItemId") String databagItemId);
 
+   // Environments
+
    /**
-    * Show indexes you can search on
-    * <p/>
-    * By default, the "role", "node" and "api" indexes will always be available.
-    * <p/>
-    * Note that the search indexes may lag behind the most current data by at
-    * least 10 seconds at any given time - so if you need to write data and
-    * immediately query it, you likely need to produce an artificial delay (or
-    * simply retry until the data is available.)
+    * List the names of the existing environments.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the databag.
+    * @return The names of the existing environments.
     */
-   @Named("search:indexes")
+   @SinceApiVersion("0.10.0")
+   @Named("environment:list")
    @GET
-   @Path("/search")
+   @Path("/environments")
    @ResponseParser(ParseKeySetFromJson.class)
    @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listSearchIndexes();
+   Set<String> listEnvironments();
 
    /**
-    * search all roles.
-    * <p/>
-    * Note that without any request parameters this will return all of the data
-    * within the index.
+    * Get the details of an existing environment.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param environmentname The name of the environment to get.
+    * @return The details of the given environment.
     */
-   @Named("search:roles")
+   @SinceApiVersion("0.10.0")
+   @Named("environment:get")
    @GET
-   @Path("/search/role")
-   @ResponseParser(ParseSearchRolesFromJson.class)
-   SearchResult<? extends Role> searchRoles();
+   @Path("/environments/{environmentname}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Environment getEnvironment(@PathParam("environmentname") String environmentname);
 
    /**
-    * search all roles that match the given options.
+    * Create a new environment.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param environment The environment to create.
     */
-   @Named("search:roles")
-   @GET
-   @Path("/search/role")
-   @ResponseParser(ParseSearchRolesFromJson.class)
-   SearchResult<? extends Role> searchRoles(SearchOptions options);
+   @SinceApiVersion("0.10.0")
+   @Named("environment:create")
+   @POST
+   @Path("/environments")
+   void createEnvironment(@BinderParam(BindToJsonPayload.class) Environment environment);
 
    /**
-    * search all clients.
-    * <p/>
-    * Note that without any request parameters this will return all of the data
-    * within the index.
+    * Updated the given environment.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param environment The new details for the environment.
+    * @return The details of the updated environment.
     */
-   @Named("search:clients")
-   @GET
-   @Path("/search/client")
-   @ResponseParser(ParseSearchClientsFromJson.class)
-   SearchResult<? extends Client> searchClients();
+   @SinceApiVersion("0.10.0")
+   @Named("environment:update")
+   @PUT
+   @Path("/environments/{environmentname}")
+   Environment updateEnvironment(
+         @PathParam("environmentname") @ParamParser(EnvironmentName.class) @BinderParam(BindToJsonPayload.class) Environment environment);
 
    /**
-    * search all clients that match the given options.
+    * Delete the given environment.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param environmentname The name of the environment to delete.
+    * @return The details of the deleted environment.
     */
-   @Named("search:clients")
-   @GET
-   @Path("/search/client")
-   @ResponseParser(ParseSearchClientsFromJson.class)
-   SearchResult<? extends Client> searchClients(SearchOptions options);
-
-   /**
-    * search all nodes.
-    * <p/>
-    * Note that without any request parameters this will return all of the data
-    * within the index.
+   @SinceApiVersion("0.10.0")
+   @Named("environment:delete")
+   @DELETE
+   @Path("/environments/{environmentname}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Environment deleteEnvironment(@PathParam("environmentname") String environmentname);
+
+   // Nodes
+
+   /**
+    * List the names of the existing nodes.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @return The names of the existing nodes.
     */
-   @Named("search:nodes")
+   @Named("node:list")
    @GET
-   @Path("/search/node")
-   @ResponseParser(ParseSearchNodesFromJson.class)
-   SearchResult<? extends Node> searchNodes();
+   @Path("/nodes")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listNodes();
 
    /**
-    * search all nodes that match the given options.
+    * List the names of the nodes in the given environment.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param environmentname The name of the environment.
+    * @return The names of the existing nodes in the given environment.
     */
-   @Named("search:nodes")
+   @SinceApiVersion("0.10.0")
+   @Named("environment:nodelist")
    @GET
-   @Path("/search/node")
-   @ResponseParser(ParseSearchNodesFromJson.class)
-   SearchResult<? extends Node> searchNodes(SearchOptions options);
+   @Path("/environments/{environmentname}/nodes")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listNodesInEnvironment(@PathParam("environmentname") String environmentname);
 
    /**
-    * search all items in a databag.
-    * <p/>
-    * Note that without any request parameters this will return all of the data
-    * within the index.
+    * Get the details of the given node.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param nodename The name of the node to get.
+    * @return The details of the given node.
     */
-   @Named("search:databag")
+   @Named("node:get")
    @GET
-   @Path("/search/{databagName}")
-   @ResponseParser(ParseSearchDatabagFromJson.class)
-   SearchResult<? extends DatabagItem> searchDatabag(@PathParam("databagName") String databagName);
+   @Path("/nodes/{nodename}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Node getNode(@PathParam("nodename") String nodename);
 
    /**
-    * search all items in a databag that match the given options.
+    * Create a new node.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param node The details of the node to create.
     */
-   @Named("search:databag")
-   @GET
-   @Path("/search/{databagName}")
-   @ResponseParser(ParseSearchDatabagFromJson.class)
-   SearchResult<? extends DatabagItem> searchDatabag(@PathParam("databagName") String databagName, SearchOptions options);
+   @Named("node:create")
+   @POST
+   @Path("/nodes")
+   void createNode(@BinderParam(BindToJsonPayload.class) Node node);
 
    /**
-    * search all items in a environment that match the given options.
+    * Update an existing node.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param node The new details for the node.
+    * @return The details of the updated node.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("search:environments")
+   @Named("node:update")
+   @PUT
+   @Path("/nodes/{nodename}")
+   Node updateNode(@PathParam("nodename") @ParamParser(NodeName.class) @BinderParam(BindToJsonPayload.class) Node node);
+
+   /**
+    * Delete the given node.
+    * 
+    * @param nodename The name of the node to delete.
+    * @return The details of the deleted node.
+    */
+   @Named("node:delete")
+   @DELETE
+   @Path("/nodes/{nodename}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Node deleteNode(@PathParam("nodename") String nodename);
+
+   // Roles
+
+   /**
+    * List the names of the existing roles.
+    * 
+    * @return The names of the existing roles.
+    */
+   @Named("role:list")
    @GET
-   @Path("/search/environment")
-   @ResponseParser(ParseSearchEnvironmentsFromJson.class)
-   SearchResult<? extends Environment> searchEnvironments();
+   @Path("/roles")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listRoles();
 
    /**
-    * search all environments that match the given options.
+    * Get the details of the given role.
     * 
-    * @return The response contains the total number of rows that matched your
-    *         request, the position this result set returns (useful for paging)
-    *         and the rows themselves.
+    * @param rolename The name of the role to get.
+    * @return The details of the given role.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("search:environments")
+   @Named("role:get")
    @GET
-   @Path("/search/environment")
-   @ResponseParser(ParseSearchEnvironmentsFromJson.class)
-   SearchResult<? extends Environment> searchEnvironments(SearchOptions options);
+   @Path("/roles/{rolename}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Role getRole(@PathParam("rolename") String rolename);
+
+   /**
+    * Create a new role.
+    * 
+    * @param role The details for the new role.
+    */
+   @Named("role:create")
+   @POST
+   @Path("/roles")
+   void createRole(@BinderParam(BindToJsonPayload.class) Role role);
+
+   /**
+    * Update the given role.
+    * 
+    * @param role The new details for the role.
+    * @return The details of the updated role.
+    */
+   @Named("role:update")
+   @PUT
+   @Path("/roles/{rolename}")
+   Role updateRole(@PathParam("rolename") @ParamParser(RoleName.class) @BinderParam(BindToJsonPayload.class) Role role);
+
+   /**
+    * Delete the given role.
+    * 
+    * @param rolename The name of the role to delete.
+    * @return The details of the deleted role.
+    */
+   @Named("role:delete")
+   @DELETE
+   @Path("/roles/{rolename}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Role deleteRole(@PathParam("rolename") String rolename);
+
+   // Sandboxes
+
+   /**
+    * Creates a new sandbox.
+    * <p>
+    * It accepts a list of checksums as input and returns the URLs against which
+    * to PUT files that need to be uploaded.
+    * 
+    * @param md5s The raw md5 sums. Uses {@code Bytes.asList()} and
+    *        {@code Bytes.toByteArray()} as necessary
+    * @return The upload sandbox with the URLs against which to PUT files that
+    *         need to be uploaded.
+    */
+   @Named("sandbox:upload")
+   @POST
+   @Path("/sandboxes")
+   UploadSandbox createUploadSandboxForChecksums(@BinderParam(BindChecksumsToJsonPayload.class) Set<List<Byte>> md5s);
+
+   /**
+    * Uploads the given content to the sandbox at the given URI.
+    * <p>
+    * The URI must be obtained, after uploading a sandbox, from the
+    * {@link UploadSandbox#getUri()}.
+    * 
+    * @param location The URI where the upload must be performed.
+    * @param content The contents to upload.
+    */
+   @Named("content:upload")
+   @PUT
+   @Produces("application/x-binary")
+   void uploadContent(@EndpointParam URI location, Payload content);
 
    /**
     * Get the contents of the given resource.
     * 
-    * @param resource
-    *           The resource to get.
+    * @param resource The resource to get.
     * @return An input stream for the content of the requested resource.
     */
    @Named("content:get")
@@ -781,175 +668,186 @@ public interface ChefApi extends Closeable {
    InputStream getResourceContents(@EndpointParam(parser = UriForResource.class) Resource resource);
 
    /**
-    * @return list of environments names.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have rights to list environments.
+    * Confirms if the sandbox is completed or not.
+    * <p>
+    * This method should be used after uploading contents to the sandbox.
+    * 
+    * @param id The id of the sandbox to commit.
+    * @param isCompleted Flag to set if the sandbox is completed or not.
+    * @return The details of the sandbox.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("environment:list")
+   @Named("sandbox:commit")
+   @PUT
+   @Path("/sandboxes/{id}")
+   Sandbox commitSandbox(@PathParam("id") String id, @WrapWith("is_completed") boolean isCompleted);
+
+   // Search
+
+   /**
+    * List the names of the available search indexes.
+    * <p>
+    * By default, the "role", "node" and "api" indexes will always be available.
+    * <p>
+    * Note that the search indexes may lag behind the most current data by at
+    * least 10 seconds at any given time - so if you need to write data and
+    * immediately query it, you likely need to produce an artificial delay (or
+    * simply retry until the data is available).
+    * 
+    * @return The names of the available search indexes.
+    */
+   @Named("search:indexes")
    @GET
-   @Path("/environments")
+   @Path("/search")
    @ResponseParser(ParseKeySetFromJson.class)
    @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listEnvironments();
+   Set<String> listSearchIndexes();
 
    /**
-    * creates a new environment
+    * Search all clients.
+    * <p>
+    * Note that without any request parameters this will return all of the data
+    * within the index.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if the caller is not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if the caller is not authorized to create a
-    *            client.
-    * @throws HttpResponseException
-    *            "409 Conflict" if the client already exists
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("environment:create")
-   @POST
-   @Path("/environments")
-   void createEnvironment(@BinderParam(BindToJsonPayload.class) Environment environment);
+   @Named("search:clients")
+   @GET
+   @Path("/search/client")
+   @ResponseParser(ParseSearchClientsFromJson.class)
+   SearchResult<? extends Client> searchClients();
 
    /**
-    * Creates or updates (uploads) a environment
+    * Search all clients that match the given options.
     * 
-    * @param environment
-    * @throws HttpResponseException
-    *            "409 Conflict" if the node already exists
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("environment:update")
-   @PUT
-   @Path("/environments/{environmentname}")
-   Environment updateEnvironment(
-         @PathParam("environmentname") @ParamParser(EnvironmentName.class) @BinderParam(BindToJsonPayload.class) Environment environment);
+   @Named("search:clients")
+   @GET
+   @Path("/search/client")
+   @ResponseParser(ParseSearchClientsFromJson.class)
+   SearchResult<? extends Client> searchClients(SearchOptions options);
 
    /**
-    * deletes an existing environment.
+    * Search all items in a data bag.
+    * <p>
+    * Note that without any request parameters this will return all of the data
+    * within the index.
     * 
-    * @return last state of the environment you deleted or null, if not found
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have Delete rights on the node.
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("environment:delete")
-   @DELETE
-   @Path("/environments/{environmentname}")
-   @Fallback(NullOnNotFoundOr404.class)
-   Environment deleteEnvironment(@PathParam("environmentname") String environmentname);
+   @Named("search:databag")
+   @GET
+   @Path("/search/{databagName}")
+   @ResponseParser(ParseSearchDatabagFromJson.class)
+   SearchResult<? extends DatabagItem> searchDatabagItems(@PathParam("databagName") String databagName);
 
    /**
-    * gets an existing environment.
+    * Search all items in a data bag that match the given options.
     * 
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the node.
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("environment:get")
+   @Named("search:databag")
    @GET
-   @Path("/environments/{environmentname}")
-   @Fallback(NullOnNotFoundOr404.class)
-   Environment getEnvironment(@PathParam("environmentname") String environmentname);
+   @Path("/search/{databagName}")
+   @ResponseParser(ParseSearchDatabagFromJson.class)
+   SearchResult<? extends DatabagItem> searchDatabagItems(@PathParam("databagName") String databagName,
+         SearchOptions options);
 
    /**
-    * gets an environment cookbook list, show only latest cookbook version
+    * Search all environments.
+    * <p>
+    * Note that without any request parameters this will return all of the data
+    * within the index.
     * 
-    * @return List of environment cookbooks
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the node.
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
    @SinceApiVersion("0.10.0")
-   @Named("environment:cookbooklist")
+   @Named("search:environments")
    @GET
-   @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
-   @Path("/environments/{environmentname}/cookbooks")
-   @Fallback(NullOnNotFoundOr404.class)
-   Set<CookbookDefinition> listEnvironmentCookbooks(@PathParam("environmentname") String environmentname);
-
-   /**
-    * gets an environment cookbook list
-    * 
-    * @param environmentname
-    *           environment name that you looking for
-    * @param numversions
-    *           how many versions you want to see: 3 returns 3 latest versions,
-    *           in descending order (high to low); all returns all available
-    *           versions in this environment, in descending order (high to low);
-    *           0 is a valid input that returns an empty array for the versions
-    *           of each cookbooks.up
-    * @return List of environment cookbooks
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have view rights on the node.
+   @Path("/search/environment")
+   @ResponseParser(ParseSearchEnvironmentsFromJson.class)
+   SearchResult<? extends Environment> searchEnvironments();
+
+   /**
+    * Search all environments that match the given options.
+    * 
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
    @SinceApiVersion("0.10.0")
-   @Named("environment:cookbooklist")
+   @Named("search:environments")
    @GET
-   @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
-   @Path("/environments/{environmentname}/cookbooks?num_versions={numversions}")
-   @Fallback(NullOnNotFoundOr404.class)
-   Set<CookbookDefinition> listEnvironmentCookbooks(@PathParam("environmentname") String environmentname,
-         @PathParam("numversions") String numversions);
+   @Path("/search/environment")
+   @ResponseParser(ParseSearchEnvironmentsFromJson.class)
+   SearchResult<? extends Environment> searchEnvironments(SearchOptions options);
 
-   @SinceApiVersion("0.10.0")
-   @Named("environment:cookbook")
+   /**
+    * Search all nodes.
+    * <p>
+    * Note that without any request parameters this will return all of the data
+    * within the index.
+    * 
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
+    */
+   @Named("search:nodes")
    @GET
-   @ResponseParser(ParseCookbookDefinitionFromJsonv10.class)
-   @Path("/environments/{environmentname}/cookbooks/{cookbookname}")
-   CookbookDefinition getEnvironmentCookbook(@PathParam("environmentname") String environmentname,
-         @PathParam("cookbookname") String cookbookname);
+   @Path("/search/node")
+   @ResponseParser(ParseSearchNodesFromJson.class)
+   SearchResult<? extends Node> searchNodes();
 
-   @SinceApiVersion("0.10.0")
-   @Named("environment:cookbook")
+   /**
+    * Search all nodes that match the given options.
+    * 
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
+    */
+   @Named("search:nodes")
    @GET
-   @ResponseParser(ParseCookbookDefinitionFromJsonv10.class)
-   @Path("/environments/{environmentname}/cookbooks/{cookbookname}?num_versions={numversions}")
-   CookbookDefinition getEnvironmentCookbook(@PathParam("environmentname") String environmentname,
-         @PathParam("cookbookname") String cookbookname, @PathParam("numversions") String numversions);
-  
+   @Path("/search/node")
+   @ResponseParser(ParseSearchNodesFromJson.class)
+   SearchResult<? extends Node> searchNodes(SearchOptions options);
+
    /**
-    * @return List of environment recipes.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have rights to list environment recipes.
+    * Search all roles.
+    * <p>
+    * Note that without any request parameters this will return all of the data
+    * within the index.
+    * 
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("environment:recipelist")
+   @Named("search:roles")
    @GET
-   @Path("/environments/{environmentname}/recipes")
-   @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listEnvironmentRecipes(@PathParam("environmentname") String environmentname);
+   @Path("/search/role")
+   @ResponseParser(ParseSearchRolesFromJson.class)
+   SearchResult<? extends Role> searchRoles();
 
    /**
-    * @return List of environment nodes.
-    * @throws AuthorizationException
-    *            <p/>
-    *            "401 Unauthorized" if you are not a recognized user.
-    *            <p/>
-    *            "403 Forbidden" if you do not have rights to list environment nodes.
+    * Search all roles that match the given options.
+    * 
+    * @return The response contains the total number of rows that matched the
+    *         request, the position this result set returns (useful for paging)
+    *         and the rows themselves.
     */
-   @SinceApiVersion("0.10.0")
-   @Named("environment:nodelist")
+   @Named("search:roles")
    @GET
-   @Path("/environments/{environmentname}/nodes")
-   @ResponseParser(ParseKeySetFromJson.class)   
-   @Fallback(EmptySetOnNotFoundOr404.class)
-   Set<String> listEnvironmentNodes(@PathParam("environmentname") String environmentname);  
+   @Path("/search/role")
+   @ResponseParser(ParseSearchRolesFromJson.class)
+   SearchResult<? extends Role> searchRoles(SearchOptions options);
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/ChefApiMetadata.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/ChefApiMetadata.java b/core/src/main/java/org/jclouds/chef/ChefApiMetadata.java
index 302e01b..d767fdd 100644
--- a/core/src/main/java/org/jclouds/chef/ChefApiMetadata.java
+++ b/core/src/main/java/org/jclouds/chef/ChefApiMetadata.java
@@ -44,6 +44,11 @@ import com.google.inject.Module;
  * @author Ignasi Barrera
  */
 public class ChefApiMetadata extends BaseHttpApiMetadata<ChefApi> {
+   
+   /**
+    * The default Chef Server API version to use.
+    */
+   public static final String DEFAULT_VERSION = "0.10.8";
 
    @Override
    public Builder toBuilder() {
@@ -83,7 +88,7 @@ public class ChefApiMetadata extends BaseHttpApiMetadata<ChefApi> {
                .name("OpsCode Chef Api")
                .identityName("User")
                .credentialName("Certificate")
-               .version(ChefApi.VERSION)
+               .version(DEFAULT_VERSION)
                .documentation(URI.create("http://wiki.opscode.com/display/chef/Server+API"))
                .defaultEndpoint("http://localhost:4000")
                .defaultProperties(ChefApiMetadata.defaultProperties())

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/ChefService.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/ChefService.java b/core/src/main/java/org/jclouds/chef/ChefService.java
index 293fb5b..f1c431b 100644
--- a/core/src/main/java/org/jclouds/chef/ChefService.java
+++ b/core/src/main/java/org/jclouds/chef/ChefService.java
@@ -20,116 +20,84 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
+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.chef.util.ChefUtils;
 import org.jclouds.domain.JsonBall;
+import org.jclouds.ohai.config.OhaiModule;
 import org.jclouds.rest.annotations.SinceApiVersion;
 import org.jclouds.scriptbuilder.domain.Statement;
 
-import com.google.common.base.Predicate;
 import com.google.common.io.InputSupplier;
 import com.google.inject.ImplementedBy;
 
 /**
- * Provides high level chef operations
+ * Provides high level Chef operations.
  * 
  * @author Adrian Cole
  */
 @ImplementedBy(BaseChefService.class)
 public interface ChefService {
+
    /**
-    * @return a reference to the context that created this.
+    * Get the context that created this service.
+    * 
+    * @return The context that created the service.
     */
    ChefContext getContext();
 
-   byte[] encrypt(InputSupplier<? extends InputStream> supplier) throws IOException;
-
-   byte[] decrypt(InputSupplier<? extends InputStream> supplier) throws IOException;
-
-   void cleanupStaleNodesAndClients(String prefix, int secondsStale);
+   // Crypto
 
    /**
+    * Encrypt the given input stream.
     * 
-    * @param nodeName
-    * @param runList
-    * @return node sent to the server containing the automatic attributes
+    * @param supplier The input stream to encrypt.
+    * @return The encrypted bytes for the given input stream.
+    * @throws IOException If there is an error reading from the input stream.
     */
-   Node createNodeAndPopulateAutomaticAttributes(String nodeName, Iterable<String> runList);
+   byte[] encrypt(InputSupplier<? extends InputStream> supplier) throws IOException;
 
    /**
-    * Creates all steps necessary to bootstrap and run the chef api.
+    * Decrypt the given input stream.
     * 
-    * @param group
-    *           corresponds to a configured
-    *           {@link org.jclouds.chef.config.ChefProperties#CHEF_BOOTSTRAP_DATABAG
-    *           databag} where run_list and other information are stored
-    * @return boot script
-    * @see #updateRunListForTag
+    * @param supplier The input stream to decrypt.
+    * @return The decrypted bytes for the given input stream.
+    * @throws IOException If there is an error reading from the input stream.
     */
-   Statement createBootstrapScriptForGroup(String group);
+   byte[] decrypt(InputSupplier<? extends InputStream> supplier) throws IOException;
 
-   /**
-    * Configures how the nodes of a certain group will be bootstrapped
-    * 
-    * @param group
-    *           The group where the given bootstrap configuration will be
-    *           applied.
-    * @param bootstrapConfig
-    *           The configuration to be applied to the nodes in the group.
-    */
-   void updateBootstrapConfigForGroup(String group, BootstrapConfig bootstrapConfig);
+   // Bootstrap
 
    /**
-    * assigns a run list to all nodes bootstrapped with a certain group
+    * Creates all steps necessary to bootstrap the node.
     * 
-    * @param runList
-    *           list of recipes or roles to assign. syntax is
-    *           {@code recipe[name]} and {@code role[name]}
-    * 
-    * @param group
-    *           corresponds to a configured
-    *           {@link org.jclouds.chef.config.ChefProperties#CHEF_BOOTSTRAP_DATABAG
-    *           databag} where run_list and other information are stored
-    * @deprecated Use {link
-    *             {@link #updateBootstrapConfigForGroup(String, BootstrapConfig)}
+    * @param group corresponds to a configured
+    *        {@link ChefProperties#CHEF_BOOTSTRAP_DATABAG} data bag where
+    *        run_list and other information are stored.
+    * @return The script used to bootstrap the node.
     */
-   @Deprecated
-   void updateBootstrapConfigForGroup(Iterable<String> runList, String group);
+   Statement createBootstrapScriptForGroup(String group);
 
    /**
-    * assigns a run list to all nodes bootstrapped with a certain group, and
-    * configures the chef run to use the given json attributes.
-    * 
-    * @param runList
-    *           list of recipes or roles to assign. syntax is
-    *           {@code recipe[name]} and {@code role[name]}
-    * 
-    * @param jsonAttributes
-    *           A json string with the attributes to be populated. Since each
-    *           cookbook may define its own attribute structure, a simple Map or
-    *           Properties object may not be convenient.
+    * Configures how the nodes of a certain group will be bootstrapped
     * 
-    * @param group
-    *           corresponds to a configured
-    *           {@link org.jclouds.chef.config.ChefProperties#CHEF_BOOTSTRAP_DATABAG
-    *           databag} where run_list and other information are stored
-    * @deprecated Use {link
-    *             {@link #updateBootstrapConfigForGroup(String, BootstrapConfig)}
+    * @param group The group where the given bootstrap configuration will be
+    *        applied.
+    * @param bootstrapConfig The configuration to be applied to the nodes in the
+    *        group.
     */
-   @Deprecated
-   void updateBootstrapConfigForGroup(Iterable<String> runList, JsonBall jsonAttributes, String group);
+   void updateBootstrapConfigForGroup(String group, BootstrapConfig bootstrapConfig);
 
    /**
-    * @param group
-    *           corresponds to a configured
-    *           {@link org.jclouds.chef.config.ChefProperties#CHEF_BOOTSTRAP_DATABAG
-    *           databag} where run_list and other information are stored
+    * Get the run list for the given group.
+    * 
+    * @param The group to get the configured run list for.
     * @return run list for all nodes bootstrapped with a certain group
-    * @see #updateRunListForTag
     */
    List<String> getRunListForGroup(String group);
 
@@ -139,45 +107,91 @@ public interface ChefService {
     * The bootstrap configuration is a Json object containing the run list and
     * the configured attributes.
     * 
-    * @param group
-    *           The name of the group.
+    * @param group The name of the group.
     * @return The bootstrap configuration for the given group.
     */
    public JsonBall getBootstrapConfigForGroup(String group);
 
-   void deleteAllNodesInList(Iterable<String> names);
+   // Nodes
 
-   Iterable<? extends Node> listNodes();
+   /**
+    * Creates a new node and populates the automatic attributes.
+    * 
+    * @param nodeName The name of the node to create.
+    * @param runList The run list for the created node.
+    * @return The created node with the automatic attributes populated.
+    * @see OhaiModule
+    * @see ChefUtils#ohaiAutomaticAttributeBinder(com.google.inject.Binder)
+    */
+   Node createNodeAndPopulateAutomaticAttributes(String nodeName, Iterable<String> runList);
 
-   Iterable<? extends Node> listNodesMatching(Predicate<String> nodeNameSelector);
+   /**
+    * Update and populate the automatic attributes of the given node.
+    * 
+    * @param nodeName The node to update.
+    */
+   void updateAutomaticAttributesOnNode(String nodeName);
 
-   Iterable<? extends Node> listNodesNamed(Iterable<String> names);
+   /**
+    * Remove the nodes and clients that have been inactive for a given amount of
+    * time.
+    * 
+    * @param prefix The prefix for the nodes and clients to delete.
+    * @param secondsStale The seconds of inactivity to consider a node and
+    *        client obsolete.
+    */
+   void cleanupStaleNodesAndClients(String prefix, int secondsStale);
 
+   /**
+    * Delete the given nodes.
+    * 
+    * @param names The names of the nodes to delete.
+    */
+   void deleteAllNodesInList(Iterable<String> names);
+
+   /**
+    * Delete the given clients.
+    * 
+    * @param names The names of the client to delete.
+    */
    void deleteAllClientsInList(Iterable<String> names);
 
-   Iterable<? extends Client> listClientsDetails();
+   /**
+    * List the details of all existing nodes.
+    * 
+    * @return The details of all existing nodes.
+    */
+   Iterable<? extends Node> listNodes();
 
-   Iterable<? extends Client> listClientsDetailsMatching(Predicate<String> clientNameSelector);
+   /**
+    * List the details of all existing nodes in the given environment.
+    * 
+    * @param environmentName The name fo the environment.
+    * @return The details of all existing nodes in the given environment.
+    */
+   @SinceApiVersion("0.10.0")
+   Iterable<? extends Node> listNodesInEnvironment(String environmentName);
 
-   Iterable<? extends Client> listClientsNamed(Iterable<String> names);
+   /**
+    * List the details of all existing clients.
+    * 
+    * @return The details of all existing clients.
+    */
+   Iterable<? extends Client> listClients();
 
+   /**
+    * List the details of all existing cookbooks.
+    * 
+    * @return The details of all existing cookbooks.
+    */
    Iterable<? extends CookbookVersion> listCookbookVersions();
 
-   Iterable<? extends CookbookVersion> listCookbookVersionsMatching(Predicate<String> cookbookNameSelector);
-
-   Iterable<? extends CookbookVersion> listCookbookVersionsNamed(Iterable<String> cookbookNames);
-
-   void updateAutomaticAttributesOnNode(String nodeName);
-
+   /**
+    * List the details of all existing environments.
+    * 
+    * @return The details of all existing environments.
+    */
    @SinceApiVersion("0.10.0")
    Iterable<? extends Environment> listEnvironments();
 
-   @SinceApiVersion("0.10.0")
-   Iterable<? extends Environment> listEnvironmentsMatching(Predicate<String> environmentNameSelector);
-
-   @SinceApiVersion("0.10.0")
-   Iterable<? extends Environment> listEnvironmentsNamed(Iterable<String> names);
-
-   @SinceApiVersion("0.10.0")
-   Iterable<? extends Node> listEnvironmentNodes(String environmentName);
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java b/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
index 34d9005..50b58df 100644
--- a/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
+++ b/core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
@@ -20,7 +20,6 @@ import static com.google.common.base.Charsets.UTF_8;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.hash.Hashing.sha1;
 import static com.google.common.io.BaseEncoding.base64;
-import static com.google.common.io.ByteStreams.asByteSource;
 import static com.google.common.io.ByteStreams.toByteArray;
 
 import java.io.IOException;
@@ -58,6 +57,7 @@ import com.google.common.base.Throwables;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
+import com.google.common.io.ByteSource;
 
 /**
  * Ported from mixlib-authentication in order to sign Chef requests.
@@ -135,7 +135,7 @@ public class SignedHeaderAuth implements HttpRequestFilter {
    @VisibleForTesting
    String hashPath(String path) {
       try {
-         return base64().encode(asByteSource(canonicalPath(path).getBytes(UTF_8)).hash(sha1()).asBytes());
+         return base64().encode(ByteSource.wrap(canonicalPath(path).getBytes(UTF_8)).hash(sha1()).asBytes());
       } catch (Exception e) {
          Throwables.propagateIfPossible(e);
          throw new HttpException("error creating sigature for path: " + path, e);

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/internal/BaseChefService.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/internal/BaseChefService.java b/core/src/main/java/org/jclouds/chef/internal/BaseChefService.java
index cf3f879..9d33570 100644
--- a/core/src/main/java/org/jclouds/chef/internal/BaseChefService.java
+++ b/core/src/main/java/org/jclouds/chef/internal/BaseChefService.java
@@ -49,21 +49,19 @@ import org.jclouds.chef.strategy.DeleteAllClientsInList;
 import org.jclouds.chef.strategy.DeleteAllNodesInList;
 import org.jclouds.chef.strategy.ListClients;
 import org.jclouds.chef.strategy.ListCookbookVersions;
-import org.jclouds.chef.strategy.ListEnvironments;
 import org.jclouds.chef.strategy.ListEnvironmentNodes;
+import org.jclouds.chef.strategy.ListEnvironments;
 import org.jclouds.chef.strategy.ListNodes;
 import org.jclouds.chef.strategy.UpdateAutomaticAttributesOnNode;
 import org.jclouds.domain.JsonBall;
 import org.jclouds.io.Payloads;
 import org.jclouds.io.payloads.RSADecryptingPayload;
 import org.jclouds.io.payloads.RSAEncryptingPayload;
-import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.Json;
 import org.jclouds.logging.Logger;
 import org.jclouds.scriptbuilder.domain.Statement;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Predicate;
 import com.google.common.base.Supplier;
 import com.google.common.collect.Maps;
 import com.google.common.io.ByteStreams;
@@ -130,78 +128,40 @@ public class BaseChefService implements ChefService {
    }
 
    @Override
-   public void cleanupStaleNodesAndClients(String prefix, int secondsStale) {
-      cleanupStaleNodesAndClients.execute(prefix, secondsStale);
-   }
-
-   @Override
-   public Node createNodeAndPopulateAutomaticAttributes(String nodeName, Iterable<String> runList) {
-      return createNodeAndPopulateAutomaticAttributes.execute(nodeName, runList);
-   }
-
-   @Override
-   public void deleteAllNodesInList(Iterable<String> names) {
-      deleteAllNodesInList.execute(names);
-   }
-
-   @Override
-   public Iterable<? extends Node> listNodes() {
-      return listNodes.execute();
-   }
-
-   @Override
-   public Iterable<? extends Node> listNodesMatching(Predicate<String> nodeNameSelector) {
-      return listNodes.execute(nodeNameSelector);
-   }
-
-   @Override
-   public Iterable<? extends Node> listNodesNamed(Iterable<String> names) {
-      return listNodes.execute(names);
-   }
-
-   @Override
-   public void deleteAllClientsInList(Iterable<String> names) {
-      deleteAllClientsInList.execute(names);
-   }
-
-   @Override
-   public Iterable<? extends Client> listClientsDetails() {
-      return listClients.execute();
+   public ChefContext getContext() {
+      return chefContext;
    }
 
    @Override
-   public Iterable<? extends Client> listClientsDetailsMatching(Predicate<String> clientNameSelector) {
-      return listClients.execute(clientNameSelector);
+   public byte[] encrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
+      return ByteStreams.toByteArray(new RSAEncryptingPayload(Payloads.newPayload(supplier.getInput()), privateKey
+            .get()));
    }
 
    @Override
-   public Iterable<? extends Client> listClientsNamed(Iterable<String> names) {
-      return listClients.execute(names);
+   public byte[] decrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
+      return ByteStreams.toByteArray(new RSADecryptingPayload(Payloads.newPayload(supplier.getInput()), privateKey
+            .get()));
    }
 
-   @Override
-   public Iterable<? extends CookbookVersion> listCookbookVersions() {
-      return listCookbookVersions.execute();
-   }
+   @VisibleForTesting
+   String buildBootstrapConfiguration(BootstrapConfig bootstrapConfig) {
+      checkNotNull(bootstrapConfig, "bootstrapConfig must not be null");
 
-   @Override
-   public Iterable<? extends CookbookVersion> listCookbookVersionsMatching(Predicate<String> cookbookNameSelector) {
-      return listCookbookVersions.execute(cookbookNameSelector);
-   }
+      Map<String, Object> configMap = Maps.newHashMap();
+      configMap.put("run_list", bootstrapConfig.getRunList());
 
-   @Override
-   public Iterable<? extends CookbookVersion> listCookbookVersionsNamed(Iterable<String> names) {
-      return listCookbookVersions.execute(names);
-   }
+      if (bootstrapConfig.getEnvironment().isPresent()) {
+         configMap.put("environment", bootstrapConfig.getEnvironment().get());
+      }
 
-   @Override
-   public void updateAutomaticAttributesOnNode(String nodeName) {
-      updateAutomaticAttributesOnNode.execute(nodeName);
-   }
+      if (bootstrapConfig.getAttribtues().isPresent()) {
+         Map<String, Object> attributes = json.fromJson(bootstrapConfig.getAttribtues().get().toString(),
+               BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
+         configMap.putAll(attributes);
+      }
 
-   @Override
-   public ChefContext getContext() {
-      return chefContext;
+      return json.toJson(configMap);
    }
 
    @Override
@@ -228,19 +188,6 @@ public class BaseChefService implements ChefService {
    }
 
    @Override
-   @Deprecated
-   public void updateBootstrapConfigForGroup(Iterable<String> runList, String group) {
-      updateBootstrapConfigForGroup(runList, null, group);
-   }
-
-   @Override
-   @Deprecated
-   public void updateBootstrapConfigForGroup(Iterable<String> runList, @Nullable JsonBall jsonAttributes, String group) {
-      updateBootstrapConfigForGroup(group, BootstrapConfig.builder().runList(runList).attributes(jsonAttributes)
-            .build());
-   }
-
-   @Override
    public List<String> getRunListForGroup(String group) {
       return runListForGroup.apply(group);
    }
@@ -251,54 +198,52 @@ public class BaseChefService implements ChefService {
    }
 
    @Override
-   public byte[] decrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
-      return ByteStreams.toByteArray(new RSADecryptingPayload(Payloads.newPayload(supplier.getInput()), privateKey
-            .get()));
+   public void cleanupStaleNodesAndClients(String prefix, int secondsStale) {
+      cleanupStaleNodesAndClients.execute(prefix, secondsStale);
    }
 
    @Override
-   public byte[] encrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
-      return ByteStreams.toByteArray(new RSAEncryptingPayload(Payloads.newPayload(supplier.getInput()), privateKey
-            .get()));
+   public Node createNodeAndPopulateAutomaticAttributes(String nodeName, Iterable<String> runList) {
+      return createNodeAndPopulateAutomaticAttributes.execute(nodeName, runList);
    }
 
-   @VisibleForTesting
-   String buildBootstrapConfiguration(BootstrapConfig bootstrapConfig) {
-      checkNotNull(bootstrapConfig, "bootstrapConfig must not be null");
-
-      Map<String, Object> configMap = Maps.newHashMap();
-      configMap.put("run_list", bootstrapConfig.getRunList());
+   @Override
+   public void updateAutomaticAttributesOnNode(String nodeName) {
+      updateAutomaticAttributesOnNode.execute(nodeName);
+   }
 
-      if (bootstrapConfig.getEnvironment().isPresent()) {
-         configMap.put("environment", bootstrapConfig.getEnvironment().get());
-      }
+   @Override
+   public void deleteAllNodesInList(Iterable<String> names) {
+      deleteAllNodesInList.execute(names);
+   }
 
-      if (bootstrapConfig.getAttribtues().isPresent()) {
-         Map<String, Object> attributes = json.fromJson(bootstrapConfig.getAttribtues().get().toString(),
-               BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
-         configMap.putAll(attributes);
-      }
+   @Override
+   public void deleteAllClientsInList(Iterable<String> names) {
+      deleteAllClientsInList.execute(names);
+   }
 
-      return json.toJson(configMap);
+   @Override
+   public Iterable<? extends Node> listNodes() {
+      return listNodes.execute();
    }
 
    @Override
-   public Iterable<? extends Environment> listEnvironments() {
-      return listEnvironments.execute();
+   public Iterable<? extends Client> listClients() {
+      return listClients.execute();
    }
 
    @Override
-   public Iterable<? extends Environment> listEnvironmentsMatching(Predicate<String> environmentNameSelector) {
-      return listEnvironments.execute(environmentNameSelector);
+   public Iterable<? extends CookbookVersion> listCookbookVersions() {
+      return listCookbookVersions.execute();
    }
 
    @Override
-   public Iterable<? extends Environment> listEnvironmentsNamed(Iterable<String> names) {
-      return listEnvironments.execute(names);
+   public Iterable<? extends Environment> listEnvironments() {
+      return listEnvironments.execute();
    }
 
    @Override
-   public Iterable<? extends Node> listEnvironmentNodes(String environmentName) {
+   public Iterable<? extends Node> listNodesInEnvironment(String environmentName) {
       return listEnvironmentNodes.execute(environmentName);
    }
 

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsImpl.java b/core/src/main/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsImpl.java
index aab51b3..3bf77a6 100644
--- a/core/src/main/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsImpl.java
+++ b/core/src/main/java/org/jclouds/chef/strategy/internal/ListCookbookVersionsImpl.java
@@ -98,7 +98,7 @@ public class ListCookbookVersionsImpl implements ListCookbookVersions {
          @Override
          public Iterable<? extends CookbookVersion> apply(final String cookbook) {
             // TODO getting each version could also go parallel
-            Set<String> cookbookVersions = api.getVersionsOfCookbook(cookbook);
+            Set<String> cookbookVersions = api.listVersionsOfCookbook(cookbook);
             ListenableFuture<List<CookbookVersion>> futures = allAsList(transform(cookbookVersions,
                   new Function<String, ListenableFuture<CookbookVersion>>() {
                      @Override

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/strategy/internal/ListEnvironmentNodesImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/strategy/internal/ListEnvironmentNodesImpl.java b/core/src/main/java/org/jclouds/chef/strategy/internal/ListEnvironmentNodesImpl.java
index a70db5a..c017828 100644
--- a/core/src/main/java/org/jclouds/chef/strategy/internal/ListEnvironmentNodesImpl.java
+++ b/core/src/main/java/org/jclouds/chef/strategy/internal/ListEnvironmentNodesImpl.java
@@ -80,12 +80,12 @@ public class ListEnvironmentNodesImpl implements ListEnvironmentNodes {
 
    @Override
    public Iterable<? extends Node> execute(ListeningExecutorService executor, String environmentName) {
-      return execute(executor, environmentName, api.listEnvironmentNodes(environmentName));
+      return execute(executor, environmentName, api.listNodesInEnvironment(environmentName));
    }
 
    @Override
    public Iterable<? extends Node> execute(ListeningExecutorService executor, String environmentName, Predicate<String> nodeNameSelector) {
-      return execute(executor, environmentName, filter(api.listEnvironmentNodes(environmentName), nodeNameSelector));
+      return execute(executor, environmentName, filter(api.listNodesInEnvironment(environmentName), nodeNameSelector));
    }
 
    @Override

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/main/java/org/jclouds/chef/test/TransientChefApi.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/chef/test/TransientChefApi.java b/core/src/main/java/org/jclouds/chef/test/TransientChefApi.java
index ad09275..2245826 100644
--- a/core/src/main/java/org/jclouds/chef/test/TransientChefApi.java
+++ b/core/src/main/java/org/jclouds/chef/test/TransientChefApi.java
@@ -203,12 +203,12 @@ public class TransientChefApi implements ChefApi {
    }
 
    @Override
-   public UploadSandbox getUploadSandboxForChecksums(Set<List<Byte>> md5s) {
+   public UploadSandbox createUploadSandboxForChecksums(Set<List<Byte>> md5s) {
       throw new UnsupportedOperationException();
    }
 
    @Override
-   public Set<String> getVersionsOfCookbook(String cookbookName) {
+   public Set<String> listVersionsOfCookbook(String cookbookName) {
       throw new UnsupportedOperationException();
    }
 
@@ -258,12 +258,12 @@ public class TransientChefApi implements ChefApi {
    }
 
    @Override
-   public SearchResult<? extends DatabagItem> searchDatabag(String databagName) {
+   public SearchResult<? extends DatabagItem> searchDatabagItems(String databagName) {
       throw new UnsupportedOperationException();
    }
 
    @Override
-   public SearchResult<? extends DatabagItem> searchDatabag(String databagName, SearchOptions options) {
+   public SearchResult<? extends DatabagItem> searchDatabagItems(String databagName, SearchOptions options) {
       throw new UnsupportedOperationException();
    }
 
@@ -343,22 +343,22 @@ public class TransientChefApi implements ChefApi {
    }
 
    @Override
-   public Set<CookbookDefinition> listEnvironmentCookbooks(String environmentname) {
+   public Set<CookbookDefinition> listCookbooksInEnvironment(String environmentname) {
       throw new UnsupportedOperationException();
    }
 
    @Override
-   public Set<CookbookDefinition> listEnvironmentCookbooks(String environmentname, String numversions) {
+   public Set<CookbookDefinition> listCookbooksInEnvironment(String environmentname, String numversions) {
       throw new UnsupportedOperationException();
    }
 
    @Override
-   public CookbookDefinition getEnvironmentCookbook(String environmentname, String cookbookname) {
+   public CookbookDefinition getCookbookInEnvironment(String environmentname, String cookbookname) {
       throw new UnsupportedOperationException();
    }
 
    @Override
-   public CookbookDefinition getEnvironmentCookbook(String environmentname, String cookbookname, String numversions) {
+   public CookbookDefinition getCookbookInEnvironment(String environmentname, String cookbookname, String numversions) {
       throw new UnsupportedOperationException();
    }
 
@@ -373,12 +373,12 @@ public class TransientChefApi implements ChefApi {
    }
 
    @Override
-   public Set<String> listEnvironmentRecipes(String environmentname) {
+   public Set<String> listRecipesInEnvironment(String environmentname) {
       throw new UnsupportedOperationException();
    }
 
    @Override
-   public Set<String> listEnvironmentNodes(String environmentname) {
+   public Set<String> listNodesInEnvironment(String environmentname) {
       throw new UnsupportedOperationException();
    }
   

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-chef/blob/96a5d917/core/src/test/java/org/jclouds/chef/ChefApiExpectTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/chef/ChefApiExpectTest.java b/core/src/test/java/org/jclouds/chef/ChefApiExpectTest.java
index 39e6e5e..8cabec4 100644
--- a/core/src/test/java/org/jclouds/chef/ChefApiExpectTest.java
+++ b/core/src/test/java/org/jclouds/chef/ChefApiExpectTest.java
@@ -23,10 +23,8 @@ import java.util.Set;
 
 import javax.ws.rs.core.MediaType;
 
-import org.jclouds.chef.BaseChefApiExpectTest;
-import org.jclouds.chef.ChefApi;
-import org.jclouds.date.TimeStamp;
 import org.jclouds.chef.config.ChefHttpApiModule;
+import org.jclouds.date.TimeStamp;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpResponse;
 import org.jclouds.rest.ConfiguresRestClient;
@@ -51,12 +49,12 @@ public class ChefApiExpectTest extends BaseChefApiExpectTest<ChefApi> {
             signed(HttpRequest.builder() //
                   .method("GET") //
                   .endpoint("http://localhost:4000/environments/dev/recipes") //
-                  .addHeader("X-Chef-Version", ChefApi.VERSION) //
+                  .addHeader("X-Chef-Version", ChefApiMetadata.DEFAULT_VERSION) //
                   .addHeader("Accept", MediaType.APPLICATION_JSON).build()), //
             HttpResponse.builder().statusCode(200)
                   .payload(payloadFromResourceWithContentType("/environment_recipes.json", MediaType.APPLICATION_JSON)) //
                   .build());
-      Set<String> recipes = api.listEnvironmentRecipes("dev");
+      Set<String> recipes = api.listRecipesInEnvironment("dev");
       assertEquals(recipes.size(), 3);
       assertTrue(recipes.contains("apache2"));
    }
@@ -66,12 +64,12 @@ public class ChefApiExpectTest extends BaseChefApiExpectTest<ChefApi> {
             signed(HttpRequest.builder() //
                   .method("GET") //
                   .endpoint("http://localhost:4000/environments/dev/recipes") //
-                  .addHeader("X-Chef-Version", ChefApi.VERSION) //
+                  .addHeader("X-Chef-Version", ChefApiMetadata.DEFAULT_VERSION) //
                   .addHeader("Accept", MediaType.APPLICATION_JSON).build()), //
             HttpResponse.builder().statusCode(404)
                   .build());
 
-      assertTrue(api.listEnvironmentRecipes("dev").isEmpty());
+      assertTrue(api.listRecipesInEnvironment("dev").isEmpty());
    }
 
    public void testListEnvironmentNodesReturns2xx() {
@@ -79,12 +77,12 @@ public class ChefApiExpectTest extends BaseChefApiExpectTest<ChefApi> {
             signed(HttpRequest.builder() //
                   .method("GET") //
                   .endpoint("http://localhost:4000/environments/dev/nodes") //
-                  .addHeader("X-Chef-Version", ChefApi.VERSION) //
+                  .addHeader("X-Chef-Version", ChefApiMetadata.DEFAULT_VERSION) //
                   .addHeader("Accept", MediaType.APPLICATION_JSON).build()), //
             HttpResponse.builder().statusCode(200)
                   .payload(payloadFromResourceWithContentType("/environment_nodes.json", MediaType.APPLICATION_JSON)) //
                   .build());
-      Set<String> nodes = api.listEnvironmentNodes("dev");
+      Set<String> nodes = api.listNodesInEnvironment("dev");
       assertEquals(nodes.size(), 3);
       assertTrue(nodes.contains("blah"));
    }
@@ -94,12 +92,12 @@ public class ChefApiExpectTest extends BaseChefApiExpectTest<ChefApi> {
             signed(HttpRequest.builder() //
                   .method("GET") //
                   .endpoint("http://localhost:4000/environments/dev/nodes") //
-                  .addHeader("X-Chef-Version", ChefApi.VERSION) //
+                  .addHeader("X-Chef-Version", ChefApiMetadata.DEFAULT_VERSION) //
                   .addHeader("Accept", MediaType.APPLICATION_JSON).build()), //
             HttpResponse.builder().statusCode(404)
                   .build());
 
-      assertTrue(api.listEnvironmentNodes("dev").isEmpty());
+      assertTrue(api.listNodesInEnvironment("dev").isEmpty());
    }
 
    @Override