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 2014/07/28 22:03:01 UTC

[10/10] git commit: Move jclouds-chef to the main jclouds repo

Move jclouds-chef to the main jclouds repo


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

Branch: refs/heads/master
Commit: 867c7a407c1db8f56ff3211d70481755cf71440f
Parents: eba727f
Author: Ignasi Barrera <na...@apache.org>
Authored: Mon Jul 28 17:34:30 2014 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Mon Jul 28 21:08:54 2014 +0200

----------------------------------------------------------------------
 apis/chef/pom.xml                               | 131 +++
 apis/chef/src/main/clojure/org/jclouds/chef.clj | 261 ++++++
 .../src/main/java/org/jclouds/chef/ChefApi.java | 853 +++++++++++++++++++
 .../java/org/jclouds/chef/ChefApiMetadata.java  | 110 +++
 .../main/java/org/jclouds/chef/ChefContext.java |  37 +
 .../main/java/org/jclouds/chef/ChefService.java | 263 ++++++
 .../binders/BindChecksumsToJsonPayload.java     |  54 ++
 .../BindCreateClientOptionsToJsonPayload.java   |  71 ++
 .../BindGenerateKeyForClientToJsonPayload.java  |  35 +
 .../org/jclouds/chef/binders/DatabagItemId.java |  32 +
 .../jclouds/chef/binders/EnvironmentName.java   |  31 +
 .../java/org/jclouds/chef/binders/NodeName.java |  32 +
 .../java/org/jclouds/chef/binders/RoleName.java |  32 +
 .../chef/config/BaseChefHttpApiModule.java      | 208 +++++
 .../chef/config/ChefBootstrapModule.java        | 121 +++
 .../jclouds/chef/config/ChefHttpApiModule.java  |  28 +
 .../jclouds/chef/config/ChefParserModule.java   | 321 +++++++
 .../org/jclouds/chef/config/ChefProperties.java | 110 +++
 .../org/jclouds/chef/config/CookbookParser.java |  41 +
 .../chef/config/CookbookVersionsParser.java     |  41 +
 .../org/jclouds/chef/config/InstallChef.java    |  37 +
 .../java/org/jclouds/chef/config/Validator.java |  40 +
 .../java/org/jclouds/chef/domain/Attribute.java | 235 +++++
 .../jclouds/chef/domain/BootstrapConfig.java    |  95 +++
 .../org/jclouds/chef/domain/ChecksumStatus.java | 102 +++
 .../java/org/jclouds/chef/domain/Client.java    | 182 ++++
 .../jclouds/chef/domain/CookbookDefinition.java | 217 +++++
 .../jclouds/chef/domain/CookbookVersion.java    | 369 ++++++++
 .../org/jclouds/chef/domain/DatabagItem.java    |  63 ++
 .../org/jclouds/chef/domain/Environment.java    | 178 ++++
 .../java/org/jclouds/chef/domain/Metadata.java  | 447 ++++++++++
 .../main/java/org/jclouds/chef/domain/Node.java | 263 ++++++
 .../java/org/jclouds/chef/domain/Resource.java  | 169 ++++
 .../main/java/org/jclouds/chef/domain/Role.java | 205 +++++
 .../java/org/jclouds/chef/domain/Sandbox.java   | 195 +++++
 .../org/jclouds/chef/domain/SearchResult.java   |  46 +
 .../org/jclouds/chef/domain/UploadSandbox.java  | 136 +++
 .../jclouds/chef/filters/SignedHeaderAuth.java  | 200 +++++
 .../chef/functions/BootstrapConfigForGroup.java |  61 ++
 .../jclouds/chef/functions/ClientForGroup.java  |  69 ++
 .../chef/functions/GroupToBootScript.java       | 130 +++
 ...seCookbookDefinitionCheckingChefVersion.java |  49 ++
 .../ParseCookbookDefinitionFromJson.java        |  50 ++
 .../ParseCookbookDefinitionFromJsonv10.java     |  52 ++
 .../ParseCookbookDefinitionListFromJsonv10.java |  63 ++
 ...arseCookbookVersionsCheckingChefVersion.java |  49 ++
 .../ParseCookbookVersionsV09FromJson.java       |  49 ++
 .../ParseCookbookVersionsV10FromJson.java       |  59 ++
 .../ParseErrorFromJsonOrReturnBody.java         |  55 ++
 .../chef/functions/ParseKeySetFromJson.java     |  45 +
 .../functions/ParseSearchClientsFromJson.java   |  35 +
 .../functions/ParseSearchDatabagFromJson.java   |  77 ++
 .../ParseSearchEnvironmentsFromJson.java        |  35 +
 .../functions/ParseSearchNodesFromJson.java     |  35 +
 .../functions/ParseSearchResultFromJson.java    |  50 ++
 .../functions/ParseSearchRolesFromJson.java     |  35 +
 .../jclouds/chef/functions/RunListForGroup.java |  61 ++
 .../jclouds/chef/functions/UriForResource.java  |  42 +
 .../chef/handlers/ChefApiErrorRetryHandler.java |  67 ++
 .../jclouds/chef/handlers/ChefErrorHandler.java |  81 ++
 .../jclouds/chef/internal/BaseChefService.java  | 299 +++++++
 .../jclouds/chef/internal/ChefContextImpl.java  |  55 ++
 .../chef/options/CreateClientOptions.java       |  64 ++
 .../org/jclouds/chef/options/SearchOptions.java |  95 +++
 .../predicates/CookbookVersionPredicates.java   |  93 ++
 .../strategy/CleanupStaleNodesAndClients.java   |  32 +
 ...reateNodeAndPopulateAutomaticAttributes.java |  33 +
 .../chef/strategy/DeleteAllClientsInList.java   |  34 +
 .../chef/strategy/DeleteAllNodesInList.java     |  31 +
 .../org/jclouds/chef/strategy/ListClients.java  |  32 +
 .../chef/strategy/ListCookbookVersions.java     |  32 +
 .../ListCookbookVersionsInEnvironment.java      |  37 +
 .../jclouds/chef/strategy/ListEnvironments.java |  31 +
 .../org/jclouds/chef/strategy/ListNodes.java    |  32 +
 .../chef/strategy/ListNodesInEnvironment.java   |  32 +
 .../UpdateAutomaticAttributesOnNode.java        |  31 +
 .../internal/BaseListCookbookVersionsImpl.java  |  97 +++
 .../strategy/internal/BaseListNodesImpl.java    |  77 ++
 .../CleanupStaleNodesAndClientsImpl.java        | 102 +++
 ...eNodeAndPopulateAutomaticAttributesImpl.java |  83 ++
 .../internal/DeleteAllClientsInListImpl.java    |  85 ++
 .../internal/DeleteAllNodesInListImpl.java      |  81 ++
 .../chef/strategy/internal/ListClientsImpl.java | 109 +++
 .../internal/ListCookbookVersionsImpl.java      |  60 ++
 .../ListCookbookVersionsInEnvironmentImpl.java  | 117 +++
 .../strategy/internal/ListEnvironmentsImpl.java |  96 +++
 .../chef/strategy/internal/ListNodesImpl.java   |  62 ++
 .../internal/ListNodesInEnvironmentImpl.java    |  62 ++
 .../UpdateAutomaticAttributesOnNodeImpl.java    |  75 ++
 .../chef/suppliers/ChefVersionSupplier.java     |  73 ++
 .../org/jclouds/chef/test/TransientChefApi.java | 387 +++++++++
 .../chef/test/TransientChefApiMetadata.java     |  75 ++
 .../test/config/TransientChefApiModule.java     | 116 +++
 .../java/org/jclouds/chef/util/ChefUtils.java   |  71 ++
 .../org/jclouds/chef/util/CollectionUtils.java  |  69 ++
 .../org/jclouds/chef/util/RunListBuilder.java   |  83 ++
 .../main/java/org/jclouds/ohai/Automatic.java   |  33 +
 .../org/jclouds/ohai/AutomaticSupplier.java     |  48 ++
 .../org/jclouds/ohai/config/ConfiguresOhai.java |  28 +
 .../org/jclouds/ohai/config/JMXOhaiModule.java  |  48 ++
 .../org/jclouds/ohai/config/OhaiModule.java     | 183 ++++
 .../ohai/functions/ByteArrayToMacAddress.java   |  51 ++
 .../ohai/functions/MapSetToMultimap.java        |  42 +
 .../jclouds/ohai/functions/NestSlashKeys.java   | 156 ++++
 .../ohai/suppliers/UptimeSecondsSupplier.java   |  44 +
 .../services/org.jclouds.apis.ApiMetadata       |   2 +
 .../src/test/clojure/org/jclouds/chef_test.clj  |  70 ++
 .../org/jclouds/chef/BaseChefApiExpectTest.java |  44 +
 .../org/jclouds/chef/ChefApiExpectTest.java     | 279 ++++++
 .../java/org/jclouds/chef/ChefApiLiveTest.java  |  32 +
 .../org/jclouds/chef/ChefApiMetadataTest.java   |  33 +
 .../test/java/org/jclouds/chef/ChefApiTest.java | 741 ++++++++++++++++
 .../BindHexEncodedMD5sToJsonPayloadTest.java    |  70 ++
 .../chef/config/ChefParserModuleTest.java       |  93 ++
 .../chef/filters/SignedHeaderAuthTest.java      | 213 +++++
 .../functions/BootstrapConfigForGroupTest.java  |  74 ++
 .../chef/functions/ClientForGroupTest.java      | 106 +++
 .../chef/functions/GroupToBootScriptTest.java   | 230 +++++
 .../chef/functions/ParseClientFromJsonTest.java |  98 +++
 ...okbookDefinitionCheckingChefVersionTest.java |  62 ++
 .../ParseCookbookDefinitionFromJsonTest.java    |  72 ++
 .../ParseCookbookDefinitionFromJsonv10Test.java |  74 ++
 ...seCookbookDefinitionListFromJsonv10Test.java |  92 ++
 .../ParseCookbookVersionFromJsonTest.java       | 129 +++
 ...CookbookVersionsCheckingChefVersionTest.java |  62 ++
 .../ParseCookbookVersionsV09FromJsonTest.java   |  61 ++
 .../ParseCookbookVersionsV10FromJsonTest.java   |  68 ++
 .../functions/ParseDataBagItemFromJsonTest.java |  65 ++
 .../ParseErrorFromJsonOrReturnBodyTest.java     |  42 +
 .../chef/functions/ParseKeySetFromJsonTest.java |  66 ++
 .../chef/functions/ParseNodeFromJsonTest.java   |  73 ++
 .../functions/ParseSandboxFromJsonTest.java     |  71 ++
 .../ParseSearchDataBagItemFromJsonTest.java     |  62 ++
 .../ParseUploadSandboxFromJsonTest.java         |  87 ++
 .../chef/functions/RunListForGroupTest.java     |  92 ++
 .../chef/functions/UriForResourceTest.java      |  54 ++
 .../handlers/ChefApiErrorRetryHandlerTest.java  | 115 +++
 .../chef/internal/BaseChefApiLiveTest.java      | 542 ++++++++++++
 .../jclouds/chef/internal/BaseChefLiveTest.java |  96 +++
 .../chef/internal/BaseChefServiceTest.java      |  99 +++
 .../chef/internal/BaseStubbedOhaiLiveTest.java  |  60 ++
 ...CleanupStaleNodesAndClientsImplLiveTest.java |  61 ++
 ...PopulateAutomaticAttributesImplLiveTest.java |  62 ++
 ...eAndPopulateAutomaticAttributesImplTest.java |  61 ++
 ...DeleteAllApisAndNodesInListImplLiveTest.java |  64 ++
 ...okbookVersionsInEnvironmentImplLiveTest.java | 217 +++++
 .../internal/ListNodesImplLiveTest.java         |  82 ++
 .../ListNodesInEnvironmentImplLiveTest.java     |  86 ++
 ...teAutomaticAttributesOnNodeImplLiveTest.java |  61 ++
 ...UpdateAutomaticAttributesOnNodeImplTest.java |  63 ++
 .../chef/suppliers/ChefVersionSupplierTest.java |  47 +
 .../test/TransientChefApiIntegrationTest.java   |  69 ++
 .../chef/test/TransientChefApiMetadataTest.java |  33 +
 .../org/jclouds/chef/util/ChefUtilsTest.java    |  57 ++
 .../jclouds/chef/util/RunListBuilderTest.java   |  71 ++
 .../java/org/jclouds/ohai/config/JMXTest.java   |  82 ++
 .../org/jclouds/ohai/config/OhaiModuleTest.java | 147 ++++
 .../functions/ByteArrayToMacAddressTest.java    |  33 +
 .../ohai/functions/NestSlashKeysTest.java       | 117 +++
 .../resources/apache-chef-demo-cookbook.json    |  46 +
 apis/chef/src/test/resources/bootstrap-env.sh   |  56 ++
 apis/chef/src/test/resources/bootstrap.sh       |  56 ++
 apis/chef/src/test/resources/brew-cookbook.json |  48 ++
 apis/chef/src/test/resources/client.json        |   8 +
 apis/chef/src/test/resources/clients_list.json  |   5 +
 apis/chef/src/test/resources/data_list.json     |   4 +
 apis/chef/src/test/resources/env_cookbooks.json |  20 +
 .../src/test/resources/environment_recipes.json |   6 +
 .../chef/src/test/resources/mysql-cookbook.json | 268 ++++++
 apis/chef/src/test/resources/node.json          |  10 +
 apis/chef/src/test/resources/nodes_list.json    |   5 +
 apis/chef/src/test/resources/privkey.txt        |  27 +
 apis/chef/src/test/resources/pubkey.txt         |   9 +
 apis/chef/src/test/resources/roles_list.json    |   4 +
 apis/chef/src/test/resources/sandbox.json       |  12 +
 apis/chef/src/test/resources/search_role.json   |  34 +
 .../src/test/resources/search_role_empty.json   |   5 +
 .../src/test/resources/tomcat-cookbook.json     | 121 +++
 apis/chef/src/test/resources/upload-site.json   |  13 +
 apis/pom.xml                                    |   1 +
 project/pom.xml                                 |   2 +
 providers/enterprise-chef/pom.xml               | 110 +++
 .../enterprisechef/EnterpriseChefApi.java       | 124 +++
 .../EnterpriseChefApiMetadata.java              |  82 ++
 .../EnterpriseChefProviderMetadata.java         |  86 ++
 .../BindGroupToUpdateRequestJsonPayload.java    |  79 ++
 .../enterprisechef/binders/GroupName.java       |  38 +
 .../config/EnterpriseChefHttpApiModule.java     |  36 +
 .../jclouds/enterprisechef/domain/Group.java    | 211 +++++
 .../org/jclouds/enterprisechef/domain/User.java | 220 +++++
 .../org.jclouds.providers.ProviderMetadata      |   1 +
 .../EnterpriseChefApiExpectTest.java            | 209 +++++
 .../EnterpriseChefApiLiveTest.java              | 122 +++
 .../EnterpriseChefProviderMetadataTest.java     |  31 +
 ...BindGroupToUpdateRequestJsonPayloadTest.java |  82 ++
 .../enterprisechef/binders/GroupNameTest.java   |  40 +
 .../src/test/resources/group-update.json        |   1 +
 .../src/test/resources/group.json               |  14 +
 .../src/test/resources/groups.json              |   7 +
 .../src/test/resources/user.json                |   9 +
 providers/pom.xml                               |   1 +
 201 files changed, 18721 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/pom.xml
----------------------------------------------------------------------
diff --git a/apis/chef/pom.xml b/apis/chef/pom.xml
new file mode 100644
index 0000000..6d0d889
--- /dev/null
+++ b/apis/chef/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.jclouds</groupId>
+    <artifactId>jclouds-project</artifactId>
+    <version>1.8.0-SNAPSHOT</version>
+    <relativePath>../../project/pom.xml</relativePath>
+  </parent>
+  <groupId>org.apache.jclouds.api</groupId>
+  <artifactId>chef</artifactId>
+  <packaging>bundle</packaging>
+  <name>jclouds Chef api</name>
+  <description>jclouds components to access Chef</description>
+
+  <properties>
+    <test.chef.endpoint>http://localhost:4000</test.chef.endpoint>
+    <test.chef.api-version />
+    <test.chef.build-version />
+    <test.chef.identity>chef-webui</test.chef.identity>
+    <test.chef.credential>${user.home}/.chef/webui.pem</test.chef.credential>
+    <jclouds.osgi.import>org.jclouds*;version=${project.version},*</jclouds.osgi.import>
+    <jclouds.osgi.export>
+      org.jclouds.chef*;version=${project.version};-noimport:=true,
+      org.jclouds.ohai*;version=${project.version};-noimport:=true,
+    </jclouds.osgi.export>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-core</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-scriptbuilder</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-scriptbuilder</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <!--  for ohai -->
+    <dependency>
+        <groupId>com.google.inject.extensions</groupId>
+        <artifactId>guice-multibindings</artifactId>
+        <version>3.0</version>
+    </dependency>
+    <!--  for transient chef provider -->
+    <dependency>
+      <groupId>org.apache.jclouds</groupId>
+      <artifactId>jclouds-blobstore</artifactId>
+      <version>${project.version}</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.driver</groupId>
+      <artifactId>jclouds-slf4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>live</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>integration</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>test</goal>
+                </goals>
+                <configuration>
+                  <systemPropertyVariables>
+                    <test.chef.endpoint>${test.chef.endpoint}</test.chef.endpoint>
+                    <test.chef.api-version>${test.chef.api-version}</test.chef.api-version>
+                    <test.chef.build-version>${test.chef.build-version}</test.chef.build-version>
+                    <test.chef.identity>${test.chef.identity}</test.chef.identity>
+                    <test.chef.credential>${test.chef.credential}</test.chef.credential>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/clojure/org/jclouds/chef.clj
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/clojure/org/jclouds/chef.clj b/apis/chef/src/main/clojure/org/jclouds/chef.clj
new file mode 100644
index 0000000..ad0f200
--- /dev/null
+++ b/apis/chef/src/main/clojure/org/jclouds/chef.clj
@@ -0,0 +1,261 @@
+;
+; 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.
+;
+
+(ns 
+  #^{:author "Adrian Cole"
+     :doc "A clojure binding to the jclouds chef interface.
+
+Here's a quick example of how to manipulate a databag on the Opscode Platform, 
+which is basically Chef Server as a Service.
+
+(use 'org.jclouds.chef)
+
+(def client \"YOUR_CLIENT\")
+;; load the rsa key from ~/.chef/CLIENT_NAME.pem
+(def credential (load-pem client))
+
+;; create a connection to the opscode platform
+(def chef (chef-service \"chef\" client credential :chef.endpoint \"https://api.opscode.com/organizations/YOUR_ORG\"))
+
+(with-chef-service [chef]
+  (create-databag \"cluster-config\")
+  (update-databag-item \"cluster-config\" {:id \"master\" :name \"myhost.com\"})) 
+
+;; note that you can create your chef connection like this to do in-memory testing
+(def chef (chef-service \"transientchef\" \"\" \"\"))
+
+See http://code.google.com/p/jclouds for details."}
+  org.jclouds.chef
+  (:use  [org.jclouds.core])
+  (:require (org.danlarkin [json :as json]))
+  (:import 
+        java.util.Properties
+        [org.jclouds ContextBuilder]
+        [org.jclouds.chef ChefClient
+          ChefService ChefContext]
+        [org.jclouds.chef.domain DatabagItem]))
+(try
+ (use '[clojure.contrib.reflect :only [get-field]])
+ (catch Exception e
+   (use '[clojure.contrib.java-utils
+          :only [wall-hack-field]
+          :rename {wall-hack-field get-field}])))
+
+(defn load-pem
+  "get the pem associated with the supplied identity"
+  ([#^String identity]
+     (slurp (str (. System getProperty "user.home") "/.chef/" identity ".pem"))))
+
+;; TODO find a way to pass the chef provider by default
+
+(defn chef-service
+  "Create a logged in context to a chef server.
+
+provider \"chef\" is a remote connection, and you can pass the option
+   :chef.endpoint \"https://url\" to override the endpoint
+
+provider \"transientchef\" is for in-memory when you are looking to do 
+unit testing"
+  ([#^String provider #^String identity #^String credential & options]
+    (let [module-keys (set (keys module-lookup))
+          ext-modules (filter #(module-keys %) options)
+          opts (apply hash-map (filter #(not (module-keys %)) options))]
+      (.. (ContextBuilder/newBuilder provider)
+          (credentials provider-identity provider-credential)
+          (modules (apply modules (concat ext-modules (opts :extensions))))
+          (overrides (reduce #(do (.put %1 (name (first %2)) (second %2)) %1)
+            (Properties.) (dissoc opts :extensions)))
+          (build ChefContext)
+          (getChefService)))))
+
+(defn chef-context
+  "Returns a chef context from a chef service."
+  [#^ChefService chef]
+  (.getContext chef))
+
+(defn chef-service?
+  [object]
+  (instance? ChefService object))
+
+(defn chef-context?
+  [object]
+  (instance? ChefContext object))
+
+(defn as-chef-service
+  "Tries hard to produce a chef service from its input arguments"
+  [& args]
+  (cond
+   (chef-service? (first args)) (first args)
+   (chef-context? (first args)) (.getChefService (first args))
+   :else (apply chef-service args)))
+
+(defn as-chef-api
+  "Tries hard to produce a chef client from its input arguments"
+  [& args]
+  (cond
+   (chef-service? (first args)) (.getApi (.getContext (first args)))
+   (chef-context? (first args)) (.getApi (first args))
+   :else (.getApi (.getContext (apply chef-service args)))))
+
+(def *chef*)
+
+(defmacro with-chef-service
+  "Specify the default chef service"
+  [[& chef-or-args] & body]
+  `(binding [*chef* (as-chef-service ~@chef-or-args)]
+     ~@body))
+
+(defn nodes
+  "Retrieve the names of the existing nodes in your chef server."
+  ([] (nodes *chef*))
+  ([#^ChefService chef]
+    (seq (.listNodes (as-chef-api chef)))))
+
+(defn nodes-with-details
+  "Retrieve the existing nodes in your chef server including all details."
+  ([] (nodes *chef*))
+  ([#^ChefService chef]
+    (seq (.listNodes chef))))
+
+(defn clients
+  "Retrieve the names of the existing clients in your chef server."
+  ([] (clients *chef*))
+  ([#^ChefService chef]
+    (seq (.listClients (as-chef-api chef)))))
+
+(defn clients-with-details
+  "Retrieve the existing clients in your chef server including all details."
+  ([] (clients *chef*))
+  ([#^ChefService chef]
+    (seq (.listClients chef))))
+
+(defn cookbooks
+  "Retrieve the names of the existing cookbooks in your chef server."
+  ([] (cookbooks *chef*))
+  ([#^ChefService chef]
+    (seq (.listCookbooks (as-chef-api chef)))))
+
+(defn cookbook-versions
+  "Retrieve the versions of an existing cookbook in your chef server."
+  ([name] (cookbook-versions *chef*))
+  ([#^ChefService name chef]
+    (seq (.getVersionsOfCookbook (as-chef-api chef) name))))
+
+(defn cookbook-versions-with-details
+  "Retrieve the existing cookbook versions in your chef server including all details."
+  ([] (cookbook-versions *chef*))
+  ([#^ChefService chef]
+    (seq (.listCookbookVersions chef))))
+
+(defn update-run-list
+  "Updates the run-list associated with a tag"
+  ([run-list tag] (update-run-list run-list tag *chef*))
+  ([run-list tag #^ChefService chef]
+    (.updateRunListForTag chef run-list tag)))
+
+(defn run-list
+  "Retrieves the run-list associated with a tag"
+  ([tag] (run-list tag *chef*))
+  ([tag #^ChefService chef]
+    (seq (.getRunListForTag chef tag))))
+
+(defn create-bootstrap
+  "creates a client and bootstrap script associated with a tag"
+  ([tag] (create-bootstrap tag *chef*))
+  ([tag #^ChefService chef]
+    (.createClientAndBootstrapScriptForTag chef tag)))
+
+(defn databags
+  "Retrieve the names of the existing data bags in your chef server."
+  ([] (databags *chef*))
+  ([#^ChefService chef]
+    (seq (.listDatabags (as-chef-api chef)))))
+
+(defn databag-exists?
+  "Predicate to check presence of a databag"
+  ([databag-name]
+     (databag-exists? databag-name *chef*))
+  ([databag-name #^ChefService chef]
+     (.databagExists (as-chef-api chef) databag-name)))
+
+(defn delete-databag
+  "Delete a data bag, including its items"
+  ([databag]
+    (delete-databag databag *chef*))
+  ([databag chef]
+    (.deleteDatabag (as-chef-api chef) databag)))
+
+(defn create-databag
+  "create a data bag"
+  ([databag]
+    (create-databag databag *chef*))
+  ([databag chef]
+    (.createDatabag (as-chef-api chef) databag)))
+
+(defn databag-items
+  "Retrieve the names of the existing items in a data bag in your chef server."
+  ([databag]
+    (databag-items databag *chef*))
+  ([databag chef]
+    (seq (.listDatabagItems (as-chef-api chef) databag))))
+
+(defn databag-item-exists?
+  "Predicate to check presence of a databag item"
+  ([databag-name item-id]
+     (databag-item-exists? databag-name item-id *chef*))
+  ([databag-name item-id #^ChefService chef]
+     (.databagExists (as-chef-api chef) databag-name item-id)))
+
+(defn databag-item
+  "Get an item from the data bag"
+  ([databag item-id]
+    (databag-item databag item-id *chef*))
+  ([databag item-id chef]
+    (json/decode-from-str (str (.getDatabagItem (as-chef-api chef) databag item-id)))))
+
+(defn delete-databag-item
+  "delete an item from the data bag"
+  ([databag item-id]
+    (delete-databag-item databag item-id *chef*))
+  ([databag item-id chef]
+    (.deleteDatabagItem (as-chef-api chef) databag item-id)))
+
+(defn create-databag-item
+  "put a new item in the data bag.  Note the Map you pass must have an :id key:
+
+ex.
+  (create-databag-item \"cluster-config\" {:id \"master\" :name \"myhost.com\"}))"
+  ([databag value]
+    (create-databag-item databag value *chef*))
+  ([databag value chef]
+    (let [value-str (json/encode-to-str value)]
+      (let [value-json (json/decode-from-str value-str)]
+        (.createDatabagItem  (as-chef-api chef) databag 
+          (DatabagItem. (get value-json :id) value-str))))))
+
+(defn update-databag-item
+  "updates an existing item in the data bag.  Note the Map you pass must have an :id key:
+
+ex.
+  (update-databag-item \"cluster-config\" {:id \"master\" :name \"myhost.com\"}))"
+  ([databag value]
+    (update-databag-item databag value *chef*))
+  ([databag value chef]
+    (let [value-str (json/encode-to-str value)]
+      (let [value-json (json/decode-from-str value-str)]
+        (.updateDatabagItem  (as-chef-api chef) databag 
+          (DatabagItem. (get value-json :id) value-str))))))

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/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
new file mode 100644
index 0000000..13f4971
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/ChefApi.java
@@ -0,0 +1,853 @@
+/*
+ * 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;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Constants;
+import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
+import org.jclouds.chef.binders.BindChecksumsToJsonPayload;
+import org.jclouds.chef.binders.BindCreateClientOptionsToJsonPayload;
+import org.jclouds.chef.binders.BindGenerateKeyForClientToJsonPayload;
+import org.jclouds.chef.binders.DatabagItemId;
+import org.jclouds.chef.binders.EnvironmentName;
+import org.jclouds.chef.binders.NodeName;
+import org.jclouds.chef.binders.RoleName;
+import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.domain.CookbookDefinition;
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.domain.Environment;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.domain.Resource;
+import org.jclouds.chef.domain.Role;
+import org.jclouds.chef.domain.Sandbox;
+import org.jclouds.chef.domain.SearchResult;
+import org.jclouds.chef.domain.UploadSandbox;
+import org.jclouds.chef.filters.SignedHeaderAuth;
+import org.jclouds.chef.functions.ParseCookbookDefinitionCheckingChefVersion;
+import org.jclouds.chef.functions.ParseCookbookDefinitionFromJsonv10;
+import org.jclouds.chef.functions.ParseCookbookDefinitionListFromJsonv10;
+import org.jclouds.chef.functions.ParseCookbookVersionsCheckingChefVersion;
+import org.jclouds.chef.functions.ParseKeySetFromJson;
+import org.jclouds.chef.functions.ParseSearchClientsFromJson;
+import org.jclouds.chef.functions.ParseSearchDatabagFromJson;
+import org.jclouds.chef.functions.ParseSearchEnvironmentsFromJson;
+import org.jclouds.chef.functions.ParseSearchNodesFromJson;
+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.io.Payload;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.EndpointParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.annotations.MapBinder;
+import org.jclouds.rest.annotations.ParamParser;
+import org.jclouds.rest.annotations.PayloadParam;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.annotations.SinceApiVersion;
+import org.jclouds.rest.annotations.SkipEncoding;
+import org.jclouds.rest.annotations.WrapWith;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+/**
+ * Provides synchronous access to Chef.
+ */
+@RequestFilters(SignedHeaderAuth.class)
+@Headers(keys = "X-Chef-Version", values = "{" + Constants.PROPERTY_API_VERSION + "}")
+@Consumes(MediaType.APPLICATION_JSON)
+public interface ChefApi extends Closeable {
+
+   // Clients
+
+   /**
+    * Lists the names of the existing clients.
+    * 
+    * @return The names of the existing clients.
+    */
+   @Named("client:list")
+   @GET
+   @Path("/clients")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listClients();
+
+   /**
+    * Gets the details of existing client.
+    * 
+    * @param clientname The name of the client to get.
+    * @return The details of the given client.
+    */
+   @Named("client:get")
+   @GET
+   @Path("/clients/{clientname}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Client getClient(@PathParam("clientname") String clientName);
+
+   /**
+    * Creates a new client.
+    * 
+    * @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
+   @Path("/clients")
+   @MapBinder(BindToJsonPayload.class)
+   Client createClient(@PayloadParam("name") String clientName);
+
+   /**
+    * Creates a new client with custom options.
+    * 
+    * @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
+   @Path("/clients")
+   @MapBinder(BindCreateClientOptionsToJsonPayload.class)
+   Client createClient(@PayloadParam("name") String clientName, CreateClientOptions options);
+
+   /**
+    * Generates a new key-pair for this client, and return the new private key in
+    * the response body.
+    * 
+    * @param clientname The name of the client.
+    * @return The details of the client with the new private key.
+    */
+   @Named("client:generatekey")
+   @PUT
+   @Path("/clients/{clientname}")
+   Client generateKeyForClient(
+         @PathParam("clientname") @BinderParam(BindGenerateKeyForClientToJsonPayload.class) String clientName);
+
+   /**
+    * Deletes the given client.
+    * 
+    * @param clientname The name of the client to delete.
+    * @return The deleted client.
+    */
+   @Named("client:delete")
+   @DELETE
+   @Path("/clients/{clientname}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Client deleteClient(@PathParam("clientname") String clientName);
+
+   // Cookbooks
+
+   /**
+    * Lists the names of the existing cookbooks.
+    * 
+    * @return The names of the exsisting cookbooks.
+    */
+   @Named("cookbook:list")
+   @GET
+   @Path("/cookbooks")
+   @ResponseParser(ParseCookbookDefinitionCheckingChefVersion.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listCookbooks();
+
+   /**
+    * Lists the cookbooks that are available in the given environment.
+    * 
+    * @param environmentname The name of the environment to get the cookbooks
+    *        from.
+    * @return The definitions of the cookbooks (name, URL and versions) available in
+    *         the given environment.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("cookbook:list")
+   @GET
+   @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
+   @Path("/environments/{environmentname}/cookbooks")
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<CookbookDefinition> listCookbooksInEnvironment(@PathParam("environmentname") String environmentName);
+
+   /**
+    * Lists the cookbooks that are available in the given environment, limiting
+    * the number of versions returned for each cookbook.
+    * 
+    * @param environmentname The name of the environment to get the cookbooks
+    *        from.
+    * @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 (name, URL and versions) available in
+    *         the given environment.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("cookbook:list")
+   @GET
+   @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
+   @Path("/environments/{environmentname}/cookbooks?num_versions={numversions}")
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<CookbookDefinition> listCookbooksInEnvironment(@PathParam("environmentname") String environmentName,
+         @PathParam("numversions") String numVersions);
+
+   /**
+    * Lists the available versions of the given cookbook.
+    * 
+    * @param cookbookName The name of the cookbook.
+    * @return The available versions of the given cookbook.
+    */
+   @Named("cookbook:versions")
+   @GET
+   @Path("/cookbooks/{cookbookname}")
+   @ResponseParser(ParseCookbookVersionsCheckingChefVersion.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listVersionsOfCookbook(@PathParam("cookbookname") String cookbookName);
+
+   /**
+    * Gets the details of the given cookbook, with the links to each resource
+    * such as recipe files, attributes, etc.
+    * 
+    * @param cookbookName The name of the cookbook.
+    * @param version The version of the cookbook to get.
+    * @return The details of the given cookbook.
+    */
+   @Named("cookbook:get")
+   @GET
+   @Path("/cookbooks/{cookbookname}/{version}")
+   @Fallback(NullOnNotFoundOr404.class)
+   CookbookVersion getCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version);
+
+   /**
+    * Gets the definition of the cookbook in the given environment.
+    * 
+    * @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.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:cookbook")
+   @GET
+   @ResponseParser(ParseCookbookDefinitionFromJsonv10.class)
+   @Path("/environments/{environmentname}/cookbooks/{cookbookname}")
+   CookbookDefinition getCookbookInEnvironment(@PathParam("environmentname") String environmentName,
+         @PathParam("cookbookname") String cookbookName);
+
+   /**
+    * Gets the definition of the cookbook in the given environment.
+    * 
+    * @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.
+    */
+   @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);
+
+   /**
+    * Lists the names of the recipes in the given environment.
+    * 
+    * @param environmentname The name of the environment.
+    * @return The names of the recipes in the given environment.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:recipelist")
+   @GET
+   @Path("/environments/{environmentname}/recipes")
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listRecipesInEnvironment(@PathParam("environmentname") String environmentName);
+
+   /**
+    * Creates or updates the given cookbook.
+    * 
+    * @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("cookbook:update")
+   @PUT
+   @Path("/cookbooks/{cookbookname}/{version}")
+   CookbookVersion updateCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version,
+         @BinderParam(BindToJsonPayload.class) CookbookVersion cookbook);
+
+   /**
+    * Deletes the given cookbook.
+    * 
+    * @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("cookbook:delete")
+   @DELETE
+   @Path("/cookbooks/{cookbookname}/{version}")
+   @Fallback(NullOnNotFoundOr404.class)
+   CookbookVersion deleteCookbook(@PathParam("cookbookname") String cookbookName, @PathParam("version") String version);
+
+   // Data bags
+
+   /**
+    * Lists the names of the existing data bags.
+    * 
+    * @return The names of the existing data bags.
+    */
+   @Named("databag:list")
+   @GET
+   @Path("/data")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listDatabags();
+
+   /**
+    * Creates a new data bag.
+    * 
+    * @param databagName The name for the new data bag.
+    */
+   @Named("databag:create")
+   @POST
+   @Path("/data")
+   void createDatabag(@WrapWith("name") String databagName);
+
+   /**
+    * Deletes a data bag, including its items.
+    * 
+    * @param databagName The name of the data bag to delete.
+    */
+   @Named("databag:delete")
+   @DELETE
+   @Path("/data/{name}")
+   @Fallback(VoidOnNotFoundOr404.class)
+   void deleteDatabag(@PathParam("name") String databagName);
+
+   /**
+    * Lists the names of the items in a data bag.
+    * 
+    * @param databagName The name of the data bag.
+    * @return The names of the items in the given data bag.
+    */
+   @Named("databag:listitems")
+   @GET
+   @Path("/data/{name}")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listDatabagItems(@PathParam("name") String databagName);
+
+   /**
+    * Gets 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.
+    * 
+    * @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
+   @Path("/data/{databagName}")
+   DatabagItem createDatabagItem(@PathParam("databagName") String databagName,
+         @BinderParam(BindToJsonPayload.class) DatabagItem databagItem);
+
+   /**
+    * Updates an item in a data bag.
+    * 
+    * @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
+   @Path("/data/{databagName}/{databagItemId}")
+   DatabagItem updateDatabagItem(
+         @PathParam("databagName") String databagName,
+         @PathParam("databagItemId") @ParamParser(DatabagItemId.class) @BinderParam(BindToJsonPayload.class) DatabagItem item);
+
+   /**
+    * Deletes an item from a data bag.
+    * 
+    * @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
+   @Path("/data/{databagName}/{databagItemId}")
+   @Fallback(NullOnNotFoundOr404.class)
+   @SelectJson("raw_data")
+   DatabagItem deleteDatabagItem(@PathParam("databagName") String databagName,
+         @PathParam("databagItemId") String databagItemId);
+
+   // Environments
+
+   /**
+    * Lists the names of the existing environments.
+    * 
+    * @return The names of the existing environments.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:list")
+   @GET
+   @Path("/environments")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listEnvironments();
+
+   /**
+    * Gets the details of an existing environment.
+    * 
+    * @param environmentname The name of the environment to get.
+    * @return The details of the given environment.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:get")
+   @GET
+   @Path("/environments/{environmentname}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Environment getEnvironment(@PathParam("environmentname") String environmentName);
+
+   /**
+    * Creates a new environment.
+    * 
+    * @param environment The environment to create.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:create")
+   @POST
+   @Path("/environments")
+   void createEnvironment(@BinderParam(BindToJsonPayload.class) Environment environment);
+
+   /**
+    * Updates the given environment.
+    * 
+    * @param environment The new details for the environment.
+    * @return The details of the updated environment.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:update")
+   @PUT
+   @Path("/environments/{environmentname}")
+   Environment updateEnvironment(
+         @PathParam("environmentname") @ParamParser(EnvironmentName.class) @BinderParam(BindToJsonPayload.class) Environment environment);
+
+   /**
+    * Deletes the given environment.
+    * 
+    * @param environmentname The name of the environment to delete.
+    * @return The details of the deleted environment.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:delete")
+   @DELETE
+   @Path("/environments/{environmentname}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Environment deleteEnvironment(@PathParam("environmentname") String environmentName);
+
+   // Nodes
+
+   /**
+    * Lists the names of the existing nodes.
+    * 
+    * @return The names of the existing nodes.
+    */
+   @Named("node:list")
+   @GET
+   @Path("/nodes")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listNodes();
+
+   /**
+    * Lists the names of the nodes in the given environment.
+    * 
+    * @param environmentname The name of the environment.
+    * @return The names of the existing nodes in the given environment.
+    */
+   @SinceApiVersion("0.10.0")
+   @Named("environment:nodelist")
+   @GET
+   @Path("/environments/{environmentname}/nodes")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listNodesInEnvironment(@PathParam("environmentname") String environmentName);
+
+   /**
+    * Gets the details of the given node.
+    * 
+    * @param nodename The name of the node to get.
+    * @return The details of the given node.
+    */
+   @Named("node:get")
+   @GET
+   @Path("/nodes/{nodename}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Node getNode(@PathParam("nodename") String nodeName);
+
+   /**
+    * Creates a new node.
+    * 
+    * @param node The details of the node to create.
+    */
+   @Named("node:create")
+   @POST
+   @Path("/nodes")
+   void createNode(@BinderParam(BindToJsonPayload.class) Node node);
+
+   /**
+    * Updates an existing node.
+    * 
+    * @param node The new details for the node.
+    * @return The details of the updated node.
+    */
+   @Named("node:update")
+   @PUT
+   @Path("/nodes/{nodename}")
+   Node updateNode(@PathParam("nodename") @ParamParser(NodeName.class) @BinderParam(BindToJsonPayload.class) Node node);
+
+   /**
+    * Deletes 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
+
+   /**
+    * Lists the names of the existing roles.
+    * 
+    * @return The names of the existing roles.
+    */
+   @Named("role:list")
+   @GET
+   @Path("/roles")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listRoles();
+
+   /**
+    * Gets the details of the given role.
+    * 
+    * @param rolename The name of the role to get.
+    * @return The details of the given role.
+    */
+   @Named("role:get")
+   @GET
+   @Path("/roles/{rolename}")
+   @Fallback(NullOnNotFoundOr404.class)
+   Role getRole(@PathParam("rolename") String roleName);
+
+   /**
+    * Creates a new role.
+    * 
+    * @param role The details for the new role.
+    */
+   @Named("role:create")
+   @POST
+   @Path("/roles")
+   void createRole(@BinderParam(BindToJsonPayload.class) Role role);
+
+   /**
+    * Updates 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);
+
+   /**
+    * Deletes 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);
+
+   /**
+    * Gets the contents of the given resource.
+    * 
+    * @param resource The resource to get.
+    * @return An input stream for the content of the requested resource.
+    */
+   @Named("content:get")
+   @GET
+   @Fallback(NullOnNotFoundOr404.class)
+   @SkipEncoding({ '+', ' ', '/', '=', ':', ';' })
+   InputStream getResourceContents(@EndpointParam(parser = UriForResource.class) Resource resource);
+
+   /**
+    * 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.
+    */
+   @Named("sandbox:commit")
+   @PUT
+   @Path("/sandboxes/{id}")
+   Sandbox commitSandbox(@PathParam("id") String id, @WrapWith("is_completed") boolean isCompleted);
+
+   // Search
+
+   /**
+    * Lists 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("/search")
+   @ResponseParser(ParseKeySetFromJson.class)
+   @Fallback(EmptySetOnNotFoundOr404.class)
+   Set<String> listSearchIndexes();
+
+   /**
+    * Searches all clients.
+    * <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:clients")
+   @GET
+   @Path("/search/client")
+   @ResponseParser(ParseSearchClientsFromJson.class)
+   SearchResult<? extends Client> searchClients();
+
+   /**
+    * Searches all clients 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:clients")
+   @GET
+   @Path("/search/client")
+   @ResponseParser(ParseSearchClientsFromJson.class)
+   SearchResult<? extends Client> searchClients(SearchOptions options);
+
+   /**
+    * Searches all items in a data bag.
+    * <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:databag")
+   @GET
+   @Path("/search/{databagName}")
+   @ResponseParser(ParseSearchDatabagFromJson.class)
+   SearchResult<? extends DatabagItem> searchDatabagItems(@PathParam("databagName") String databagName);
+
+   /**
+    * Searches all items in a data bag 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:databag")
+   @GET
+   @Path("/search/{databagName}")
+   @ResponseParser(ParseSearchDatabagFromJson.class)
+   SearchResult<? extends DatabagItem> searchDatabagItems(@PathParam("databagName") String databagName,
+         SearchOptions options);
+
+   /**
+    * Searches all environments.
+    * <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("search:environments")
+   @GET
+   @Path("/search/environment")
+   @ResponseParser(ParseSearchEnvironmentsFromJson.class)
+   SearchResult<? extends Environment> searchEnvironments();
+
+   /**
+    * Searches 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("search:environments")
+   @GET
+   @Path("/search/environment")
+   @ResponseParser(ParseSearchEnvironmentsFromJson.class)
+   SearchResult<? extends Environment> searchEnvironments(SearchOptions options);
+
+   /**
+    * Searches 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
+   @Path("/search/node")
+   @ResponseParser(ParseSearchNodesFromJson.class)
+   SearchResult<? extends Node> searchNodes();
+
+   /**
+    * Searches 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
+   @Path("/search/node")
+   @ResponseParser(ParseSearchNodesFromJson.class)
+   SearchResult<? extends Node> searchNodes(SearchOptions options);
+
+   /**
+    * Searches 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.
+    */
+   @Named("search:roles")
+   @GET
+   @Path("/search/role")
+   @ResponseParser(ParseSearchRolesFromJson.class)
+   SearchResult<? extends Role> searchRoles();
+
+   /**
+    * Searches 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.
+    */
+   @Named("search:roles")
+   @GET
+   @Path("/search/role")
+   @ResponseParser(ParseSearchRolesFromJson.class)
+   SearchResult<? extends Role> searchRoles(SearchOptions options);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java b/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java
new file mode 100644
index 0000000..ede03e9
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/ChefApiMetadata.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
+import static org.jclouds.chef.config.ChefProperties.CHEF_BOOTSTRAP_DATABAG;
+import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEMS;
+import static org.jclouds.chef.config.ChefProperties.CHEF_UPDATE_GEM_SYSTEM;
+import static org.jclouds.chef.config.ChefProperties.CHEF_USE_OMNIBUS;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.chef.config.ChefBootstrapModule;
+import org.jclouds.chef.config.ChefHttpApiModule;
+import org.jclouds.chef.config.ChefParserModule;
+import org.jclouds.ohai.config.JMXOhaiModule;
+import org.jclouds.rest.internal.BaseHttpApiMetadata;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Module;
+
+/**
+ * Implementation of {@link ApiMetadata} for OpsCode's Chef api.
+ */
+public class ChefApiMetadata extends BaseHttpApiMetadata<ChefApi> {
+
+   /**
+    * The default Chef Server API version to use.
+    */
+   public static final String DEFAULT_API_VERSION = "0.10.8";
+
+   @Override
+   public Builder toBuilder() {
+      return new Builder().fromApiMetadata(this);
+   }
+
+   public ChefApiMetadata() {
+      this(new Builder());
+   }
+
+   protected ChefApiMetadata(Builder builder) {
+      super(builder);
+   }
+
+   public static Properties defaultProperties() {
+      Properties properties = BaseHttpApiMetadata.defaultProperties();
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "default", SECONDS.toMillis(30) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.updateCookbook", MINUTES.toMillis(10) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.createClient", MINUTES.toMillis(2) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.generateKeyForClient", MINUTES.toMillis(2) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.createNode", MINUTES.toMillis(2) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.updateNode", MINUTES.toMillis(10) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.createRole", MINUTES.toMillis(2) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.updateRole", MINUTES.toMillis(10) + "");
+      properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.createEnvironment", MINUTES.toMillis(2) + "");
+      properties.setProperty(PROPERTY_SESSION_INTERVAL, "1");
+      properties.setProperty(CHEF_BOOTSTRAP_DATABAG, "bootstrap");
+      properties.setProperty(CHEF_UPDATE_GEM_SYSTEM, "false");
+      properties.setProperty(CHEF_UPDATE_GEMS, "false");
+      properties.setProperty(CHEF_USE_OMNIBUS, "true");
+      return properties;
+   }
+
+   public static class Builder extends BaseHttpApiMetadata.Builder<ChefApi, Builder> {
+
+      protected Builder() {
+         id("chef")
+               .name("OpsCode Chef Api")
+               .identityName("User")
+               .credentialName("Certificate")
+               .version(DEFAULT_API_VERSION)
+               .documentation(URI.create("http://wiki.opscode.com/display/chef/Server+API"))
+               .defaultEndpoint("http://localhost:4000")
+               .defaultProperties(ChefApiMetadata.defaultProperties())
+               .view(ChefContext.class)
+               .defaultModules(
+                     ImmutableSet.<Class<? extends Module>> of(ChefHttpApiModule.class, ChefParserModule.class,
+                           ChefBootstrapModule.class, JMXOhaiModule.class));
+      }
+
+      @Override
+      public ChefApiMetadata build() {
+         return new ChefApiMetadata(this);
+      }
+
+      @Override
+      protected Builder self() {
+         return this;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/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
new file mode 100644
index 0000000..7809634
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/ChefContext.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import java.io.Closeable;
+
+import org.jclouds.View;
+import org.jclouds.chef.internal.ChefContextImpl;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Provides an entry point to Chef features.
+ */
+@ImplementedBy(ChefContextImpl.class)
+public interface ChefContext extends View, Closeable {
+
+   /**
+    * Provides access to high level Chef features.
+    */
+   ChefService getChefService();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/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
new file mode 100644
index 0000000..040107a
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/ChefService.java
@@ -0,0 +1,263 @@
+/*
+ * 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;
+
+import com.google.common.io.InputSupplier;
+import com.google.inject.ImplementedBy;
+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.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;
+
+/**
+ * Provides high level Chef operations.
+ */
+@ImplementedBy(BaseChefService.class)
+public interface ChefService {
+
+   /**
+    * Gets the context that created this service.
+    *
+    * @return The context that created the service.
+    */
+   ChefContext getContext();
+
+   // Crypto
+
+   /**
+    * Encrypts the given input stream.
+    *
+    * @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.
+    */
+   byte[] encrypt(InputSupplier<? extends InputStream> supplier) throws IOException;
+
+   /**
+    * Decrypts the given input stream.
+    *
+    * @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.
+    */
+   byte[] decrypt(InputSupplier<? extends InputStream> supplier) throws IOException;
+
+   // Bootstrap
+
+   /**
+    * Creates all steps necessary to bootstrap the node.
+    *
+    * @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.
+    */
+   Statement createBootstrapScriptForGroup(String group);
+
+   /**
+    * 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);
+
+   /**
+    * Gets 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
+    */
+   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);
+
+   // Nodes / Clients
+
+   /**
+    * 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);
+
+   /**
+    * Updates and populate the automatic attributes of the given node.
+    *
+    * @param nodeName The node to update.
+    */
+   void updateAutomaticAttributesOnNode(String nodeName);
+
+   /**
+    * Removes 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);
+
+   /**
+    * Deletes the given nodes.
+    *
+    * @param names The names of the nodes to delete.
+    */
+   void deleteAllNodesInList(Iterable<String> names);
+
+   /**
+    * Deletes the given clients.
+    *
+    * @param names The names of the client to delete.
+    */
+   void deleteAllClientsInList(Iterable<String> names);
+
+   /**
+    * Lists the details of all existing nodes.
+    *
+    * @return The details of all existing nodes.
+    */
+   Iterable<? extends Node> listNodes();
+
+   /**
+    * Lists the details of all existing nodes, executing concurrently using the executorService.
+    *
+    * @return The details of all existing nodes.
+    */
+   Iterable<? extends Node> listNodes(ExecutorService executorService);
+
+   /**
+    * Lists 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);
+
+   /**
+    * Lists the details of all existing nodes in the given environment, using the ExecutorService to paralleling the execution.
+    *
+    * @param executorService The thread pool used in this operation
+    * @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, ExecutorService executorService);
+
+   /**
+    * Lists the details of all existing clients.
+    *
+    * @return The details of all existing clients.
+    */
+   Iterable<? extends Client> listClients();
+
+   /**
+    * Lists the details of all existing clients, but executing concurrently using the threads available in the ExecutorService.
+    *
+    * @return The details of all existing clients.
+    */
+   Iterable<? extends Client> listClients(ExecutorService executorService);
+
+   /**
+    * Lists the details of all existing cookbooks.
+    *
+    * @return The details of all existing cookbooks.
+    */
+   Iterable<? extends CookbookVersion> listCookbookVersions();
+
+   /**
+    * Lists the details of all existing cookbooks. This method is executed concurrently, using the threads available in the ExecutorService.
+    *
+    * @return The details of all existing cookbooks.
+    */
+   Iterable<? extends CookbookVersion> listCookbookVersions(ExecutorService executorService);
+
+   /**
+    * Lists the details of all existing cookbooks in an environment.
+    *
+    * @param environmentName The environment name.
+    * @return The details of all existing cookbooks in an environment.
+    */
+   Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName);
+
+   /**
+    * Lists the details of all existing cookbooks in an environment.
+
+    * @param executorService The thread pool to do the concurrent execution.
+    * @param environmentName The environment name.
+    * @return The details of all existing cookbooks in an environment.
+    */
+   Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName, ExecutorService executorService);
+
+   /**
+    * Lists the details of all existing cookbooks in an environment
+    * limiting number of versions.
+    *
+    * @param environmentName The environment name.
+    * @param numVersions     The number of cookbook versions to include.
+    *                        Use 'all' to return all cookbook versions.
+    * @return The details of all existing cookbooks in environment.
+    */
+   Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName, String numVersions);
+
+   /**
+    * Lists the details of all existing cookbooks in an environment
+    * limiting number of versions.
+    *
+    * @param executorService The executorService used to do this operation concurrently.
+    * @param environmentName The environment name.
+    * @param numVersions     The number of cookbook versions to include.
+    *                        Use 'all' to return all cookbook versions.
+    * @return The details of all existing cookbooks in environment.
+    */
+   Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName, String numVersions, ExecutorService executorService);
+
+   /**
+    * Lists the details of all existing environments.
+    *
+    * @return The details of all existing environments.
+    */
+   @SinceApiVersion("0.10.0")
+   Iterable<? extends Environment> listEnvironments();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/binders/BindChecksumsToJsonPayload.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/binders/BindChecksumsToJsonPayload.java b/apis/chef/src/main/java/org/jclouds/chef/binders/BindChecksumsToJsonPayload.java
new file mode 100644
index 0000000..9a744a3
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/binders/BindChecksumsToJsonPayload.java
@@ -0,0 +1,54 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.io.BaseEncoding.base16;
+import static com.google.common.primitives.Bytes.toArray;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Singleton;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.binders.BindToStringPayload;
+
+@Singleton
+public class BindChecksumsToJsonPayload extends BindToStringPayload {
+
+   @SuppressWarnings("unchecked")
+   public HttpRequest bindToRequest(HttpRequest request, Object input) {
+      checkArgument(checkNotNull(input, "input") instanceof Set, "this binder is only valid for Set!");
+
+      Set<List<Byte>> md5s = (Set<List<Byte>>) input;
+
+      StringBuilder builder = new StringBuilder();
+      builder.append("{\"checksums\":{");
+
+      for (List<Byte> md5 : md5s)
+         builder.append(String.format("\"%s\":null,", base16().lowerCase().encode(toArray(md5))));
+      builder.deleteCharAt(builder.length() - 1);
+      builder.append("}}");
+      super.bindToRequest(request, builder.toString());
+      request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_JSON);
+      return request;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/binders/BindCreateClientOptionsToJsonPayload.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/binders/BindCreateClientOptionsToJsonPayload.java b/apis/chef/src/main/java/org/jclouds/chef/binders/BindCreateClientOptionsToJsonPayload.java
new file mode 100644
index 0000000..b719c66
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/binders/BindCreateClientOptionsToJsonPayload.java
@@ -0,0 +1,71 @@
+/*
+ * 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.binders;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.jclouds.chef.options.CreateClientOptions;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.rest.binders.BindToJsonPayload;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+/**
+ * Bind the parameters of a {@link CreateClientOptions} to the payload.
+ */
+public class BindCreateClientOptionsToJsonPayload extends BindToJsonPayload {
+   @Inject
+   public BindCreateClientOptionsToJsonPayload(Json jsonBinder) {
+      super(jsonBinder);
+   }
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+      checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest,
+            "this binder is only valid for GeneratedHttpRequests");
+      GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request;
+      checkState(gRequest.getInvocation().getArgs() != null, "args should be initialized at this point");
+
+      String name = checkNotNull(postParams.remove("name"), "name").toString();
+      CreateClientOptions options = (CreateClientOptions) Iterables.find(gRequest.getInvocation().getArgs(),
+            Predicates.instanceOf(CreateClientOptions.class));
+
+      return bindToRequest(request, new CreateClientParams(name, options));
+   }
+
+   @SuppressWarnings("unused")
+   private static class CreateClientParams {
+      private String name;
+
+      private boolean admin;
+
+      public CreateClientParams(String name, CreateClientOptions options) {
+         this.name = name;
+         this.admin = options.isAdmin();
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/binders/BindGenerateKeyForClientToJsonPayload.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/binders/BindGenerateKeyForClientToJsonPayload.java b/apis/chef/src/main/java/org/jclouds/chef/binders/BindGenerateKeyForClientToJsonPayload.java
new file mode 100644
index 0000000..4bd5821
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/binders/BindGenerateKeyForClientToJsonPayload.java
@@ -0,0 +1,35 @@
+/*
+ * 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.binders;
+
+import javax.inject.Singleton;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.binders.BindToStringPayload;
+
+@Singleton
+public class BindGenerateKeyForClientToJsonPayload extends BindToStringPayload {
+
+   @Override
+   public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
+      super.bindToRequest(request, String.format("{\"name\":\"%s\", \"private_key\": true}", payload));
+      request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_JSON);
+      return request;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/binders/DatabagItemId.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/binders/DatabagItemId.java b/apis/chef/src/main/java/org/jclouds/chef/binders/DatabagItemId.java
new file mode 100644
index 0000000..5dd5a62
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/binders/DatabagItemId.java
@@ -0,0 +1,32 @@
+/*
+ * 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.binders;
+
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.DatabagItem;
+
+import com.google.common.base.Function;
+
+@Singleton
+public class DatabagItemId implements Function<Object, String> {
+
+   public String apply(Object from) {
+      return ((DatabagItem) from).getId();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/binders/EnvironmentName.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/binders/EnvironmentName.java b/apis/chef/src/main/java/org/jclouds/chef/binders/EnvironmentName.java
new file mode 100644
index 0000000..1650521
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/binders/EnvironmentName.java
@@ -0,0 +1,31 @@
+/*
+ * 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.binders;
+
+import com.google.common.base.Function;
+import org.jclouds.chef.domain.Environment;
+
+import javax.inject.Singleton;
+
+@Singleton
+public class EnvironmentName implements Function<Object, String> {
+
+   @Override
+   public String apply(Object input) {
+      return ((Environment) input).getName();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/binders/NodeName.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/binders/NodeName.java b/apis/chef/src/main/java/org/jclouds/chef/binders/NodeName.java
new file mode 100644
index 0000000..5277895
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/binders/NodeName.java
@@ -0,0 +1,32 @@
+/*
+ * 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.binders;
+
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.Node;
+
+import com.google.common.base.Function;
+
+@Singleton
+public class NodeName implements Function<Object, String> {
+
+   public String apply(Object from) {
+      return ((Node) from).getName();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/binders/RoleName.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/binders/RoleName.java b/apis/chef/src/main/java/org/jclouds/chef/binders/RoleName.java
new file mode 100644
index 0000000..aa78a06
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/binders/RoleName.java
@@ -0,0 +1,32 @@
+/*
+ * 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.binders;
+
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.Role;
+
+import com.google.common.base.Function;
+
+@Singleton
+public class RoleName implements Function<Object, String> {
+
+   public String apply(Object from) {
+      return ((Role) from).getName();
+   }
+
+}