http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Environment.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Environment.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Environment.java
new file mode 100644
index 0000000..48e6efa
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Environment.java
@@ -0,0 +1,178 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.util.Map;
+
+import org.jclouds.domain.JsonBall;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * An environment.
+ */
+public class Environment {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String name;
+      private ImmutableMap.Builder<String, JsonBall> attributes = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, JsonBall> overrideAttributes = 
ImmutableMap.builder();
+      private String description = "";
+      private ImmutableMap.Builder<String, String> cookbookVersions = 
ImmutableMap.builder();
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder attribute(String key, JsonBall value) {
+         this.attributes.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder attributes(Map<String, JsonBall> attributes) {
+         this.attributes.putAll(checkNotNull(attributes, "attributes"));
+         return this;
+      }
+
+      public Builder overrideAttribute(String key, JsonBall value) {
+         this.overrideAttributes.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder overrideAttributes(Map<String, JsonBall> 
overrideAttributes) {
+         this.overrideAttributes.putAll(checkNotNull(overrideAttributes, 
"overrideAttributes"));
+         return this;
+      }
+
+      public Builder cookbookVersion(String key, String version) {
+         this.cookbookVersions.put(checkNotNull(key, "key"), 
checkNotNull(version, "version"));
+         return this;
+      }
+
+      public Builder cookbookVersions(Map<String, String> cookbookVersions) {
+         this.cookbookVersions.putAll(checkNotNull(cookbookVersions, 
"cookbookVersions"));
+         return this;
+      }
+
+      public Builder description(String description) {
+         this.description = checkNotNull(description, "description");
+         return this;
+      }
+
+      public Environment build() {
+         return new Environment(name, attributes.build(), 
overrideAttributes.build(), description,
+               cookbookVersions.build());
+      }
+   }
+
+   private final String name;
+   @SerializedName("default_attributes")
+   private final Map<String, JsonBall> attributes;
+   @SerializedName("override_attributes")
+   private final Map<String, JsonBall> overrideAttributes;
+   private final String description;
+   @SerializedName("cookbook_versions")
+   private final Map<String, String> cookbookVersions;
+
+   // internal
+   @SerializedName("json_class")
+   private final String _jsonClass = "Chef::Environment";
+   @SerializedName("chef_type")
+   private final String _chefType = "environment";
+
+   @ConstructorProperties({ "name", "default_attributes", 
"override_attributes", "description", "cookbook_versions" })
+   protected Environment(String name, @Nullable Map<String, JsonBall> 
attributes,
+         @Nullable Map<String, JsonBall> overrideAttributes, String 
description,
+         @Nullable Map<String, String> cookbookVersions) {
+      this.name = name;
+      this.attributes = copyOfOrEmpty(attributes);
+      this.overrideAttributes = copyOfOrEmpty(overrideAttributes);
+      this.description = description;
+      this.cookbookVersions = copyOfOrEmpty(cookbookVersions);
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public Map<String, JsonBall> getAttributes() {
+      return attributes;
+   }
+
+   public Map<String, JsonBall> getOverrideAttributes() {
+      return overrideAttributes;
+   }
+
+   public String getDescription() {
+      return description;
+   }
+
+   public Map<String, String> getCookbookVersions() {
+      return cookbookVersions;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o)
+         return true;
+      if (o == null || getClass() != o.getClass())
+         return false;
+
+      Environment that = (Environment) o;
+
+      if (attributes != null ? !attributes.equals(that.attributes) : 
that.attributes != null)
+         return false;
+      if (cookbookVersions != null ? 
!cookbookVersions.equals(that.cookbookVersions) : that.cookbookVersions != null)
+         return false;
+      if (description != null ? !description.equals(that.description) : 
that.description != null)
+         return false;
+      if (!name.equals(that.name))
+         return false;
+      if (overrideAttributes != null ? 
!overrideAttributes.equals(that.overrideAttributes)
+            : that.overrideAttributes != null)
+         return false;
+
+      return true;
+   }
+
+   @Override
+   public int hashCode() {
+      int result = name.hashCode();
+      result = 31 * result + (attributes != null ? attributes.hashCode() : 0);
+      result = 31 * result + (overrideAttributes != null ? 
overrideAttributes.hashCode() : 0);
+      result = 31 * result + (description != null ? description.hashCode() : 
0);
+      result = 31 * result + (cookbookVersions != null ? 
cookbookVersions.hashCode() : 0);
+      return result;
+   }
+
+   @Override
+   public String toString() {
+      return "Environment [" + "name='" + name + '\'' + ", attributes=" + 
attributes + ", overrideAttributes="
+            + overrideAttributes + ", description='" + description + '\'' + ", 
cookbookVersions=" + cookbookVersions
+            + ']';
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Metadata.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Metadata.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Metadata.java
new file mode 100644
index 0000000..33f8dfb
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Metadata.java
@@ -0,0 +1,447 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.util.Map;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * A metadata object.
+ */
+public class Metadata {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String license;
+      private String maintainer;
+      private ImmutableMap.Builder<String, String> suggestions = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, String> dependencies = 
ImmutableMap.builder();
+      private String maintainerEmail;
+      private ImmutableMap.Builder<String, String> conflicting = 
ImmutableMap.builder();
+      private String description;
+      private ImmutableMap.Builder<String, String> providing = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, String> platforms = 
ImmutableMap.builder();
+      private String version;
+      private ImmutableMap.Builder<String, String> recipes = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, String> replacing = 
ImmutableMap.builder();
+      private String name;
+      private ImmutableMap.Builder<String, String> groupings = 
ImmutableMap.builder();
+      private String longDescription;
+      private ImmutableMap.Builder<String, Attribute> attributes = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, String> recommendations = 
ImmutableMap.builder();
+
+      public Builder license(String license) {
+         this.license = checkNotNull(license, "license");
+         return this;
+      }
+
+      public Builder maintainer(String maintainer) {
+         this.maintainer = checkNotNull(maintainer, "maintainer");
+         return this;
+      }
+
+      public Builder suggestion(String key, String value) {
+         this.suggestions.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder suggestions(Map<String, String> suggestions) {
+         this.suggestions.putAll(checkNotNull(suggestions, "suggestions"));
+         return this;
+      }
+
+      public Builder dependency(String key, String value) {
+         this.dependencies.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder dependencies(Map<String, String> dependencies) {
+         this.dependencies.putAll(checkNotNull(dependencies, "dependencies"));
+         return this;
+      }
+
+      public Builder maintainerEmail(String maintainerEmail) {
+         this.maintainerEmail = checkNotNull(maintainerEmail, 
"maintainerEmail");
+         return this;
+      }
+
+      public Builder conflicting(String key, String value) {
+         this.conflicting.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder conflicting(Map<String, String> conflicting) {
+         this.conflicting.putAll(checkNotNull(conflicting, "conflicting"));
+         return this;
+      }
+
+      public Builder description(String description) {
+         this.description = checkNotNull(description, "description");
+         return this;
+      }
+
+      public Builder providing(String key, String value) {
+         this.providing.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder providing(Map<String, String> providing) {
+         this.providing.putAll(checkNotNull(providing, "providing"));
+         return this;
+      }
+
+      public Builder platform(String key, String value) {
+         this.platforms.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder platforms(Map<String, String> platforms) {
+         this.platforms.putAll(checkNotNull(platforms, "platforms"));
+         return this;
+      }
+
+      public Builder version(String version) {
+         this.version = checkNotNull(version, "version");
+         return this;
+      }
+
+      public Builder recipe(String key, String value) {
+         this.recipes.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder recipes(Map<String, String> recipes) {
+         this.recipes.putAll(checkNotNull(recipes, "recipes"));
+         return this;
+      }
+
+      public Builder replacing(String key, String value) {
+         this.replacing.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder replacing(Map<String, String> replacing) {
+         this.replacing.putAll(checkNotNull(replacing, "replacing"));
+         return this;
+      }
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder grouping(String key, String value) {
+         this.groupings.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder grouping(Map<String, String> groupings) {
+         this.groupings.putAll(checkNotNull(groupings, "groupings"));
+         return this;
+      }
+
+      public Builder longDescription(String longDescription) {
+         this.longDescription = checkNotNull(longDescription, 
"longDescription");
+         return this;
+      }
+
+      public Builder attribute(String key, Attribute value) {
+         this.attributes.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder attributes(Map<String, Attribute> attributes) {
+         this.attributes.putAll(checkNotNull(attributes, "attributes"));
+         return this;
+      }
+
+      public Builder recommendation(String key, String value) {
+         this.recommendations.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder recommendations(Map<String, String> recommendations) {
+         this.recommendations.putAll(checkNotNull(recommendations, 
"recommendations"));
+         return this;
+      }
+
+      public Metadata build() {
+         return new Metadata(license, maintainer, suggestions.build(), 
dependencies.build(), maintainerEmail,
+               conflicting.build(), description, providing.build(), 
platforms.build(), version, recipes.build(),
+               replacing.build(), name, groupings.build(), longDescription, 
attributes.build(), recommendations.build());
+      }
+
+   }
+
+   private final String license;
+   private final String maintainer;
+   private final Map<String, String> suggestions;
+   private final Map<String, String> dependencies;
+   @SerializedName("maintainer_email")
+   private final String maintainerEmail;
+   private final Map<String, String> conflicting;
+   private final String description;
+   private final Map<String, String> providing;
+   private final Map<String, String> platforms;
+   private final String version;
+   private final Map<String, String> recipes;
+   private final Map<String, String> replacing;
+   private final String name;
+   private final Map<String, String> groupings;
+   @SerializedName("long_description")
+   private final String longDescription;
+   private final Map<String, Attribute> attributes;
+   private final Map<String, String> recommendations;
+
+   @ConstructorProperties({ "license", "maintainer", "suggestions", 
"dependencies", "maintainer_email", "conflicting",
+         "description", "providing", "platforms", "version", "recipes", 
"replacing", "name", "groupings",
+         "long_description", "attributes", "recommendations" })
+   protected Metadata(String license, String maintainer, @Nullable Map<String, 
String> suggestions,
+         @Nullable Map<String, String> dependencies, String maintainerEmail, 
@Nullable Map<String, String> conflicting,
+         String description, @Nullable Map<String, String> providing, 
@Nullable Map<String, String> platforms,
+         String version, @Nullable Map<String, String> recipes, @Nullable 
Map<String, String> replacing, String name,
+         @Nullable Map<String, String> groupings, String longDescription, 
@Nullable Map<String, Attribute> attributes,
+         @Nullable Map<String, String> recommendations) {
+      this.license = license;
+      this.maintainer = maintainer;
+      this.suggestions = copyOfOrEmpty(suggestions);
+      this.dependencies = copyOfOrEmpty(dependencies);
+      this.maintainerEmail = maintainerEmail;
+      this.conflicting = copyOfOrEmpty(conflicting);
+      this.description = description;
+      this.providing = copyOfOrEmpty(providing);
+      this.platforms = copyOfOrEmpty(platforms);
+      this.version = version;
+      this.recipes = copyOfOrEmpty(recipes);
+      this.replacing = copyOfOrEmpty(replacing);
+      this.name = name;
+      this.groupings = copyOfOrEmpty(groupings);
+      this.longDescription = longDescription;
+      this.attributes = copyOfOrEmpty(attributes);
+      this.recommendations = copyOfOrEmpty(recommendations);
+   }
+
+   public String getLicense() {
+      return license;
+   }
+
+   public String getMaintainer() {
+      return maintainer;
+   }
+
+   public Map<String, String> getSuggestions() {
+      return suggestions;
+   }
+
+   public Map<String, String> getDependencies() {
+      return dependencies;
+   }
+
+   public String getMaintainerEmail() {
+      return maintainerEmail;
+   }
+
+   public Map<String, String> getConflicting() {
+      return conflicting;
+   }
+
+   public String getDescription() {
+      return description;
+   }
+
+   public Map<String, String> getProviding() {
+      return providing;
+   }
+
+   public Map<String, String> getPlatforms() {
+      return platforms;
+   }
+
+   public String getVersion() {
+      return version;
+   }
+
+   public Map<String, String> getRecipes() {
+      return recipes;
+   }
+
+   public Map<String, String> getReplacing() {
+      return replacing;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public Map<String, String> getGroupings() {
+      return groupings;
+   }
+
+   public String getLongDescription() {
+      return longDescription;
+   }
+
+   public Map<String, Attribute> getAttributes() {
+      return attributes;
+   }
+
+   public Map<String, String> getRecommendations() {
+      return recommendations;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((attributes == null) ? 0 : 
attributes.hashCode());
+      result = prime * result + ((conflicting == null) ? 0 : 
conflicting.hashCode());
+      result = prime * result + ((dependencies == null) ? 0 : 
dependencies.hashCode());
+      result = prime * result + ((description == null) ? 0 : 
description.hashCode());
+      result = prime * result + ((groupings == null) ? 0 : 
groupings.hashCode());
+      result = prime * result + ((license == null) ? 0 : license.hashCode());
+      result = prime * result + ((longDescription == null) ? 0 : 
longDescription.hashCode());
+      result = prime * result + ((maintainer == null) ? 0 : 
maintainer.hashCode());
+      result = prime * result + ((maintainerEmail == null) ? 0 : 
maintainerEmail.hashCode());
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((platforms == null) ? 0 : 
platforms.hashCode());
+      result = prime * result + ((providing == null) ? 0 : 
providing.hashCode());
+      result = prime * result + ((recipes == null) ? 0 : recipes.hashCode());
+      result = prime * result + ((recommendations == null) ? 0 : 
recommendations.hashCode());
+      result = prime * result + ((replacing == null) ? 0 : 
replacing.hashCode());
+      result = prime * result + ((suggestions == null) ? 0 : 
suggestions.hashCode());
+      result = prime * result + ((version == null) ? 0 : version.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Metadata other = (Metadata) obj;
+      if (attributes == null) {
+         if (other.attributes != null)
+            return false;
+      } else if (!attributes.equals(other.attributes))
+         return false;
+      if (conflicting == null) {
+         if (other.conflicting != null)
+            return false;
+      } else if (!conflicting.equals(other.conflicting))
+         return false;
+      if (dependencies == null) {
+         if (other.dependencies != null)
+            return false;
+      } else if (!dependencies.equals(other.dependencies))
+         return false;
+      if (description == null) {
+         if (other.description != null)
+            return false;
+      } else if (!description.equals(other.description))
+         return false;
+      if (groupings == null) {
+         if (other.groupings != null)
+            return false;
+      } else if (!groupings.equals(other.groupings))
+         return false;
+      if (license == null) {
+         if (other.license != null)
+            return false;
+      } else if (!license.equals(other.license))
+         return false;
+      if (longDescription == null) {
+         if (other.longDescription != null)
+            return false;
+      } else if (!longDescription.equals(other.longDescription))
+         return false;
+      if (maintainer == null) {
+         if (other.maintainer != null)
+            return false;
+      } else if (!maintainer.equals(other.maintainer))
+         return false;
+      if (maintainerEmail == null) {
+         if (other.maintainerEmail != null)
+            return false;
+      } else if (!maintainerEmail.equals(other.maintainerEmail))
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (platforms == null) {
+         if (other.platforms != null)
+            return false;
+      } else if (!platforms.equals(other.platforms))
+         return false;
+      if (providing == null) {
+         if (other.providing != null)
+            return false;
+      } else if (!providing.equals(other.providing))
+         return false;
+      if (recipes == null) {
+         if (other.recipes != null)
+            return false;
+      } else if (!recipes.equals(other.recipes))
+         return false;
+      if (recommendations == null) {
+         if (other.recommendations != null)
+            return false;
+      } else if (!recommendations.equals(other.recommendations))
+         return false;
+      if (replacing == null) {
+         if (other.replacing != null)
+            return false;
+      } else if (!replacing.equals(other.replacing))
+         return false;
+      if (suggestions == null) {
+         if (other.suggestions != null)
+            return false;
+      } else if (!suggestions.equals(other.suggestions))
+         return false;
+      if (version == null) {
+         if (other.version != null)
+            return false;
+      } else if (!version.equals(other.version))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Metadata [attributes=" + attributes + ", conflicting=" + 
conflicting + ", dependencies=" + dependencies
+            + ", description=" + description + ", groupings=" + groupings + ", 
license=" + license
+            + ", longDescription=" + longDescription + ", maintainer=" + 
maintainer + ", maintainerEmail="
+            + maintainerEmail + ", name=" + name + ", platforms=" + platforms 
+ ", providing=" + providing
+            + ", recipes=" + recipes + ", recommendations=" + recommendations 
+ ", replacing=" + replacing
+            + ", suggestions=" + suggestions + ", version=" + version + "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Node.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Node.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Node.java
new file mode 100644
index 0000000..ec83f4c
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Node.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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.domain.JsonBall;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Node object.
+ */
+public class Node {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String name;
+      private ImmutableMap.Builder<String, JsonBall> normalAttributes = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, JsonBall> overrideAttributes = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, JsonBall> defaultAttributes = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, JsonBall> automaticAttributes = 
ImmutableMap.builder();
+      private ImmutableList.Builder<String> runList = ImmutableList.builder();
+      private String environment;
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder normalAttribute(String key, JsonBall value) {
+         this.normalAttributes.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder normalAttributes(Map<String, JsonBall> normalAttributes) {
+         this.normalAttributes.putAll(checkNotNull(normalAttributes, 
"normalAttributes"));
+         return this;
+      }
+
+      public Builder overrideAttribute(String key, JsonBall value) {
+         this.overrideAttributes.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder overrideAttributes(Map<String, JsonBall> 
overrideAttributes) {
+         this.overrideAttributes.putAll(checkNotNull(overrideAttributes, 
"overrideAttributes"));
+         return this;
+      }
+
+      public Builder defaultAttribute(String key, JsonBall value) {
+         this.defaultAttributes.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder defaultAttributes(Map<String, JsonBall> 
defaultAttributes) {
+         this.defaultAttributes.putAll(checkNotNull(defaultAttributes, 
"defaultAttributes"));
+         return this;
+      }
+
+      public Builder automaticAttribute(String key, JsonBall value) {
+         this.automaticAttributes.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder automaticAttributes(Map<String, JsonBall> 
automaticAttribute) {
+         this.automaticAttributes.putAll(checkNotNull(automaticAttribute, 
"automaticAttribute"));
+         return this;
+      }
+
+      public Builder runListElement(String element) {
+         this.runList.add(checkNotNull(element, "element"));
+         return this;
+      }
+
+      public Builder runList(Iterable<String> runList) {
+         this.runList.addAll(checkNotNull(runList, "runList"));
+         return this;
+      }
+
+      /**
+       * @since Chef 0.10
+       */
+      public Builder environment(String environment) {
+         this.environment = checkNotNull(environment, "environment");
+         return this;
+      }
+
+      public Node build() {
+         return new Node(name, normalAttributes.build(), 
overrideAttributes.build(), defaultAttributes.build(),
+               automaticAttributes.build(), runList.build(), environment);
+      }
+   }
+
+   private final String name;
+   @SerializedName("normal")
+   private final Map<String, JsonBall> normalAttributes;
+   @SerializedName("override")
+   private final Map<String, JsonBall> overrideAttributes;
+   @SerializedName("default")
+   private final Map<String, JsonBall> defaultAttributes;
+   @SerializedName("automatic")
+   private final Map<String, JsonBall> automaticAttributes;
+   @SerializedName("run_list")
+   private final List<String> runList;
+   @SerializedName("chef_environment")
+   private final String environment;
+
+   // internal
+   @SerializedName("json_class")
+   private final String _jsonClass = "Chef::Node";
+   @SerializedName("chef_type")
+   private final String _chefType = "node";
+
+   @ConstructorProperties({ "name", "normal", "override", "default", 
"automatic", "run_list", "chef_environment" })
+   protected Node(String name, @Nullable Map<String, JsonBall> 
normalAttributes,
+         @Nullable Map<String, JsonBall> overrideAttributes, @Nullable 
Map<String, JsonBall> defaultAttributes,
+         @Nullable Map<String, JsonBall> automaticAttributes, List<String> 
runList, @Nullable String environment) {
+      this.name = name;
+      this.environment = environment;
+      this.normalAttributes = copyOfOrEmpty(normalAttributes);
+      this.overrideAttributes = copyOfOrEmpty(overrideAttributes);
+      this.defaultAttributes = copyOfOrEmpty(defaultAttributes);
+      this.automaticAttributes = copyOfOrEmpty(automaticAttributes);
+      this.runList = copyOfOrEmpty(runList);
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public Map<String, JsonBall> getNormalAttributes() {
+      return normalAttributes;
+   }
+
+   public Map<String, JsonBall> getOverrideAttributes() {
+      return overrideAttributes;
+   }
+
+   public Map<String, JsonBall> getDefaultAttributes() {
+      return defaultAttributes;
+   }
+
+   public Map<String, JsonBall> getAutomaticAttributes() {
+      return automaticAttributes;
+   }
+
+   public List<String> getRunList() {
+      return runList;
+   }
+
+   /**
+    * @since Chef 0.10
+    */
+   public String getEnvironment() {
+      return environment;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((_chefType == null) ? 0 : 
_chefType.hashCode());
+      result = prime * result + ((_jsonClass == null) ? 0 : 
_jsonClass.hashCode());
+      result = prime * result + ((automaticAttributes == null) ? 0 : 
automaticAttributes.hashCode());
+      result = prime * result + ((defaultAttributes == null) ? 0 : 
defaultAttributes.hashCode());
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((normalAttributes == null) ? 0 : 
normalAttributes.hashCode());
+      result = prime * result + ((overrideAttributes == null) ? 0 : 
overrideAttributes.hashCode());
+      result = prime * result + ((runList == null) ? 0 : runList.hashCode());
+      result = prime * result + ((environment == null) ? 0 : 
environment.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Node other = (Node) obj;
+      if (_chefType == null) {
+         if (other._chefType != null)
+            return false;
+      } else if (!_chefType.equals(other._chefType))
+         return false;
+      if (_jsonClass == null) {
+         if (other._jsonClass != null)
+            return false;
+      } else if (!_jsonClass.equals(other._jsonClass))
+         return false;
+      if (automaticAttributes == null) {
+         if (other.automaticAttributes != null)
+            return false;
+      } else if (!automaticAttributes.equals(other.automaticAttributes))
+         return false;
+      if (defaultAttributes == null) {
+         if (other.defaultAttributes != null)
+            return false;
+      } else if (!defaultAttributes.equals(other.defaultAttributes))
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (normalAttributes == null) {
+         if (other.normalAttributes != null)
+            return false;
+      } else if (!normalAttributes.equals(other.normalAttributes))
+         return false;
+      if (overrideAttributes == null) {
+         if (other.overrideAttributes != null)
+            return false;
+      } else if (!overrideAttributes.equals(other.overrideAttributes))
+         return false;
+      if (runList == null) {
+         if (other.runList != null)
+            return false;
+      } else if (!runList.equals(other.runList))
+         return false;
+      if (environment == null) {
+         if (other.environment != null)
+            return false;
+      } else if (!environment.equals(other.environment))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Node [name=" + name + ", runList=" + runList + ", 
normalAttributes=" + normalAttributes
+            + ", defaultAttributes=" + defaultAttributes + ", 
overrideAttributes=" + overrideAttributes
+            + ", chefEnvironment=" + environment + ", automaticAttributes=" + 
automaticAttributes + "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Resource.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Resource.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Resource.java
new file mode 100644
index 0000000..94b5471
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Resource.java
@@ -0,0 +1,169 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.net.URI;
+import java.util.Arrays;
+
+import org.jclouds.io.payloads.FilePayload;
+
+import com.google.common.primitives.Bytes;
+
+/**
+ * Resource object.
+ */
+public class Resource {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String name;
+      private URI url;
+      private byte[] checksum;
+      private String path;
+      private String specificity = "default";
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder url(URI url) {
+         this.url = checkNotNull(url, "url");
+         return this;
+      }
+
+      public Builder checksum(byte[] checksum) {
+         this.checksum = checkNotNull(checksum, "checksum");
+         return this;
+      }
+
+      public Builder path(String path) {
+         this.path = checkNotNull(path, "path");
+         return this;
+      }
+
+      public Builder specificity(String specificity) {
+         this.specificity = checkNotNull(specificity, "specificity");
+         return this;
+      }
+
+      public Builder fromPayload(FilePayload payload) {
+         checkNotNull(payload, "payload");
+         this.name(payload.getRawContent().getName());
+         this.checksum(payload.getContentMetadata().getContentMD5());
+         this.path(payload.getRawContent().getPath());
+         return this;
+      }
+
+      public Resource build() {
+         return new Resource(name, url, checksum, path, specificity);
+      }
+   }
+
+   private final String name;
+   private final URI url;
+   private final byte[] checksum;
+   private final String path;
+   private final String specificity;
+
+   @ConstructorProperties({ "name", "url", "checksum", "path", "specificity" })
+   protected Resource(String name, URI url, byte[] checksum, String path, 
String specificity) {
+      this.name = name;
+      this.url = url;
+      this.checksum = checksum;
+      this.path = path;
+      this.specificity = specificity;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public URI getUrl() {
+      return url;
+   }
+
+   public byte[] getChecksum() {
+      return checksum;
+   }
+
+   public String getPath() {
+      return path;
+   }
+
+   public String getSpecificity() {
+      return specificity;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + Arrays.hashCode(checksum);
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((path == null) ? 0 : path.hashCode());
+      result = prime * result + ((specificity == null) ? 0 : 
specificity.hashCode());
+      result = prime * result + ((url == null) ? 0 : url.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Resource other = (Resource) obj;
+      if (!Arrays.equals(checksum, other.checksum))
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (path == null) {
+         if (other.path != null)
+            return false;
+      } else if (!path.equals(other.path))
+         return false;
+      if (specificity == null) {
+         if (other.specificity != null)
+            return false;
+      } else if (!specificity.equals(other.specificity))
+         return false;
+      if (url == null) {
+         if (other.url != null)
+            return false;
+      } else if (!url.equals(other.url))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Resource [checksum=" + Bytes.asList(checksum) + ", name=" + name 
+ ", path=" + path + ", specificity="
+            + specificity + ", url=" + url + "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Role.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Role.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Role.java
new file mode 100644
index 0000000..d51dc1e
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Role.java
@@ -0,0 +1,205 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.domain.JsonBall;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Role object.
+ */
+public class Role {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String name;
+      private String description;
+      private ImmutableMap.Builder<String, JsonBall> overrideAttributes = 
ImmutableMap.builder();
+      private ImmutableMap.Builder<String, JsonBall> defaultAttributes = 
ImmutableMap.builder();
+      private ImmutableList.Builder<String> runList = ImmutableList.builder();
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder description(String description) {
+         this.description = checkNotNull(description, "description");
+         return this;
+      }
+
+      public Builder overrideAttribute(String key, JsonBall value) {
+         this.overrideAttributes.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder overrideAttributes(Map<String, JsonBall> 
overrideAttributes) {
+         this.overrideAttributes.putAll(checkNotNull(overrideAttributes, 
"overrideAttributes"));
+         return this;
+      }
+
+      public Builder defaultAttribute(String key, JsonBall value) {
+         this.defaultAttributes.put(checkNotNull(key, "key"), 
checkNotNull(value, "value"));
+         return this;
+      }
+
+      public Builder defaultAttributes(Map<String, JsonBall> 
defaultAttributes) {
+         this.defaultAttributes.putAll(checkNotNull(defaultAttributes, 
"defaultAttributes"));
+         return this;
+      }
+
+      public Builder runListElement(String element) {
+         this.runList.add(checkNotNull(element, "element"));
+         return this;
+      }
+
+      public Builder runList(Iterable<String> runList) {
+         this.runList.addAll(checkNotNull(runList, "runList"));
+         return this;
+      }
+
+      public Role build() {
+         return new Role(name, description, defaultAttributes.build(), 
runList.build(), overrideAttributes.build());
+      }
+   }
+
+   private final String name;
+   private final String description;
+   @SerializedName("override_attributes")
+   private final Map<String, JsonBall> overrideAttributes;
+   @SerializedName("default_attributes")
+   private final Map<String, JsonBall> defaultAttributes;
+   @SerializedName("run_list")
+   private final List<String> runList;
+
+   // internal
+   @SerializedName("json_class")
+   private final String _jsonClass = "Chef::Role";
+   @SerializedName("chef_type")
+   private final String _chefType = "role";
+
+   @ConstructorProperties({ "name", "description", "default_attributes", 
"run_list", "override_attributes" })
+   protected Role(String name, String description, @Nullable Map<String, 
JsonBall> defaultAttributes,
+         @Nullable List<String> runList, @Nullable Map<String, JsonBall> 
overrideAttributes) {
+      this.name = name;
+      this.description = description;
+      this.defaultAttributes = copyOfOrEmpty(defaultAttributes);
+      this.runList = copyOfOrEmpty(runList);
+      this.overrideAttributes = copyOfOrEmpty(overrideAttributes);
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public String getDescription() {
+      return description;
+   }
+
+   public Map<String, JsonBall> getOverrideAttributes() {
+      return overrideAttributes;
+   }
+
+   public Map<String, JsonBall> getDefaultAttributes() {
+      return defaultAttributes;
+   }
+
+   public List<String> getRunList() {
+      return runList;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((_chefType == null) ? 0 : 
_chefType.hashCode());
+      result = prime * result + ((_jsonClass == null) ? 0 : 
_jsonClass.hashCode());
+      result = prime * result + ((defaultAttributes == null) ? 0 : 
defaultAttributes.hashCode());
+      result = prime * result + ((description == null) ? 0 : 
description.hashCode());
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((overrideAttributes == null) ? 0 : 
overrideAttributes.hashCode());
+      result = prime * result + ((runList == null) ? 0 : runList.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Role other = (Role) obj;
+      if (_chefType == null) {
+         if (other._chefType != null)
+            return false;
+      } else if (!_chefType.equals(other._chefType))
+         return false;
+      if (_jsonClass == null) {
+         if (other._jsonClass != null)
+            return false;
+      } else if (!_jsonClass.equals(other._jsonClass))
+         return false;
+      if (defaultAttributes == null) {
+         if (other.defaultAttributes != null)
+            return false;
+      } else if (!defaultAttributes.equals(other.defaultAttributes))
+         return false;
+      if (description == null) {
+         if (other.description != null)
+            return false;
+      } else if (!description.equals(other.description))
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (overrideAttributes == null) {
+         if (other.overrideAttributes != null)
+            return false;
+      } else if (!overrideAttributes.equals(other.overrideAttributes))
+         return false;
+      if (runList == null) {
+         if (other.runList != null)
+            return false;
+      } else if (!runList.equals(other.runList))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Role [name=" + name + ", description=" + description + ", 
defaultAttributes=" + defaultAttributes
+            + ", overrideAttributes=" + overrideAttributes + ", runList=" + 
runList + "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/Sandbox.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/Sandbox.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/Sandbox.java
new file mode 100644
index 0000000..c58d02d
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/Sandbox.java
@@ -0,0 +1,195 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.util.Date;
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Sandbox object.
+ */
+public class Sandbox {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private String rev;
+      private boolean isCompleted;
+      private Date createTime;
+      private ImmutableSet.Builder<String> checksums = ImmutableSet.builder();
+      private String name;
+      private String guid;
+
+      public Builder rev(String rev) {
+         this.rev = checkNotNull(rev, "rev");
+         return this;
+      }
+
+      public Builder isCompleted(boolean isCompleted) {
+         this.isCompleted = isCompleted;
+         return this;
+      }
+
+      public Builder createTime(Date createTime) {
+         this.createTime = createTime;
+         return this;
+      }
+
+      public Builder checksum(String checksum) {
+         this.checksums.add(checkNotNull(checksum, "checksum"));
+         return this;
+      }
+
+      public Builder checksums(Iterable<String> checksums) {
+         this.checksums.addAll(checkNotNull(checksums, "checksums"));
+         return this;
+      }
+
+      public Builder name(String name) {
+         this.name = checkNotNull(name, "name");
+         return this;
+      }
+
+      public Builder guid(String guid) {
+         this.guid = checkNotNull(guid, "guid");
+         return this;
+      }
+
+      public Sandbox build() {
+         return new Sandbox(rev, isCompleted, createTime, checksums.build(), 
name, guid);
+      }
+   }
+
+   @SerializedName("_rev")
+   private final String rev;
+   @SerializedName("is_completed")
+   private final boolean isCompleted;
+   @SerializedName("create_time")
+   private final Date createTime;
+   private final Set<String> checksums;
+   private final String name;
+   private final String guid;
+
+   // internal
+   @SerializedName("json_class")
+   private final String _jsonClass = "Chef::Sandbox";
+   @SerializedName("chef_type")
+   private final String _chefType = "sandbox";
+
+   @ConstructorProperties({ "_rev", "is_completed", "create_time", 
"checksums", "name", "guid" })
+   protected Sandbox(String rev, boolean isCompleted, Date createTime, 
@Nullable Set<String> checksums, String name,
+         String guid) {
+      this.rev = rev;
+      this.isCompleted = isCompleted;
+      this.createTime = createTime;
+      this.checksums = copyOfOrEmpty(checksums);
+      this.name = name;
+      this.guid = guid;
+   }
+
+   public String getRev() {
+      return rev;
+   }
+
+   public boolean isCompleted() {
+      return isCompleted;
+   }
+
+   public Date getCreateTime() {
+      return createTime;
+   }
+
+   public Set<String> getChecksums() {
+      return checksums;
+   }
+
+   public String getName() {
+      return name;
+   }
+
+   public String getGuid() {
+      return guid;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((checksums == null) ? 0 : 
checksums.hashCode());
+      result = prime * result + ((createTime == null) ? 0 : 
createTime.hashCode());
+      result = prime * result + ((guid == null) ? 0 : guid.hashCode());
+      result = prime * result + (isCompleted ? 1231 : 1237);
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + ((rev == null) ? 0 : rev.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      Sandbox other = (Sandbox) obj;
+      if (checksums == null) {
+         if (other.checksums != null)
+            return false;
+      } else if (!checksums.equals(other.checksums))
+         return false;
+      if (createTime == null) {
+         if (other.createTime != null)
+            return false;
+      } else if (!createTime.equals(other.createTime))
+         return false;
+      if (guid == null) {
+         if (other.guid != null)
+            return false;
+      } else if (!guid.equals(other.guid))
+         return false;
+      if (isCompleted != other.isCompleted)
+         return false;
+      if (name == null) {
+         if (other.name != null)
+            return false;
+      } else if (!name.equals(other.name))
+         return false;
+      if (rev == null) {
+         if (other.rev != null)
+            return false;
+      } else if (!rev.equals(other.rev))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "Sandbox [checksums=" + checksums + ", createTime=" + createTime 
+ ", guid=" + guid + ", isCompleted="
+            + isCompleted + ", name=" + name + ", rev=" + rev + "]";
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/SearchResult.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/SearchResult.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/SearchResult.java
new file mode 100644
index 0000000..7e406c1
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/SearchResult.java
@@ -0,0 +1,46 @@
+/*
+ * 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.domain;
+
+import java.util.LinkedHashSet;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * A result of a search.
+ */
+public class SearchResult<T> extends LinkedHashSet<T> {
+   private static final long serialVersionUID = 4000610660948065287L;
+   private long start;
+
+   SearchResult() {
+   }
+
+   public SearchResult(long start, Iterable<T> results) {
+      this.start = start;
+      Iterables.addAll(this, results);
+   }
+
+   /**
+    * 
+    * @return the result position this started from from
+    */
+   long getStart() {
+      return start;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/domain/UploadSandbox.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/domain/UploadSandbox.java 
b/apis/chef/src/main/java/org/jclouds/chef/domain/UploadSandbox.java
new file mode 100644
index 0000000..9ca4c0f
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/domain/UploadSandbox.java
@@ -0,0 +1,136 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.util.CollectionUtils.copyOfOrEmpty;
+
+import java.beans.ConstructorProperties;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * An upload sandbox.
+ */
+public class UploadSandbox {
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      private URI uri;
+      private ImmutableMap.Builder<List<Byte>, ChecksumStatus> checksums = 
ImmutableMap.builder();
+      private String sandboxId;
+
+      public Builder uri(URI uri) {
+         this.uri = checkNotNull(uri, "uri");
+         return this;
+      }
+
+      public Builder checksum(List<Byte> key, ChecksumStatus value) {
+         this.checksums.put(checkNotNull(key, "key"), checkNotNull(value, 
"value"));
+         return this;
+      }
+
+      public Builder checksums(Map<List<Byte>, ChecksumStatus> checksums) {
+         this.checksums.putAll(checkNotNull(checksums, "checksums"));
+         return this;
+      }
+
+      public Builder sandboxId(String sandboxId) {
+         this.sandboxId = checkNotNull(sandboxId, "sandboxId");
+         return this;
+      }
+
+      public UploadSandbox build() {
+         return new UploadSandbox(uri, checksums.build(), sandboxId);
+      }
+   }
+
+   private final URI uri;
+   private final Map<List<Byte>, ChecksumStatus> checksums;
+   @SerializedName("sandbox_id")
+   private final String sandboxId;
+
+   @ConstructorProperties({ "uri", "checksums", "sandbox_id" })
+   protected UploadSandbox(URI uri, @Nullable Map<List<Byte>, ChecksumStatus> 
checksums, String sandboxId) {
+      this.uri = uri;
+      this.checksums = copyOfOrEmpty(checksums);
+      this.sandboxId = sandboxId;
+   }
+
+   public URI getUri() {
+      return uri;
+   }
+
+   public Map<List<Byte>, ChecksumStatus> getChecksums() {
+      return checksums;
+   }
+
+   public String getSandboxId() {
+      return sandboxId;
+   }
+
+   @Override
+   public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((checksums == null) ? 0 : 
checksums.hashCode());
+      result = prime * result + ((sandboxId == null) ? 0 : 
sandboxId.hashCode());
+      result = prime * result + ((uri == null) ? 0 : uri.hashCode());
+      return result;
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      UploadSandbox other = (UploadSandbox) obj;
+      if (checksums == null) {
+         if (other.checksums != null)
+            return false;
+      } else if (!checksums.equals(other.checksums))
+         return false;
+      if (sandboxId == null) {
+         if (other.sandboxId != null)
+            return false;
+      } else if (!sandboxId.equals(other.sandboxId))
+         return false;
+      if (uri == null) {
+         if (other.uri != null)
+            return false;
+      } else if (!uri.equals(other.uri))
+         return false;
+      return true;
+   }
+
+   @Override
+   public String toString() {
+      return "UploadSandbox [checksums=" + checksums + ", id=" + sandboxId + 
", uri=" + uri + "]";
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java 
b/apis/chef/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
new file mode 100644
index 0000000..cabe579
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
@@ -0,0 +1,200 @@
+/*
+ * 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.filters;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.hash.Hashing.sha1;
+import static com.google.common.io.BaseEncoding.base64;
+import static com.google.common.io.ByteStreams.toByteArray;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.util.NoSuchElementException;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpRequestFilter;
+import org.jclouds.http.HttpUtils;
+import org.jclouds.http.internal.SignatureWire;
+import org.jclouds.io.ByteStreams2;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.io.payloads.MultipartForm;
+import org.jclouds.io.payloads.Part;
+import org.jclouds.io.payloads.RSAEncryptingPayload;
+import org.jclouds.logging.Logger;
+import org.jclouds.util.Strings2;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+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.
+ * 
+ * @see <a href= "http://github.com/opscode/mixlib-authentication"; />
+ */
+@Singleton
+public class SignedHeaderAuth implements HttpRequestFilter {
+   public static final String SIGNING_DESCRIPTION = "version=1.0";
+
+   private final SignatureWire signatureWire;
+   private final Supplier<Credentials> creds;
+   private final Supplier<PrivateKey> supplyKey;
+   private final Provider<String> timeStampProvider;
+   private final String emptyStringHash;
+   private final HttpUtils utils;
+   private final Crypto crypto;
+
+   @Resource
+   @Named(Constants.LOGGER_SIGNATURE)
+   Logger signatureLog = Logger.NULL;
+
+   @Inject
+   public SignedHeaderAuth(SignatureWire signatureWire, 
@org.jclouds.location.Provider Supplier<Credentials> creds,
+         Supplier<PrivateKey> supplyKey, @TimeStamp Provider<String> 
timeStampProvider, HttpUtils utils, Crypto crypto) {
+      this.signatureWire = checkNotNull(signatureWire, "signatureWire");
+      this.creds = checkNotNull(creds, "creds");
+      this.supplyKey = checkNotNull(supplyKey, "supplyKey");
+      this.timeStampProvider = checkNotNull(timeStampProvider, 
"timeStampProvider");
+      this.emptyStringHash = hashBody(Payloads.newStringPayload(""));
+      this.utils = checkNotNull(utils, "utils");
+      this.crypto = checkNotNull(crypto, "crypto");
+   }
+
+   public HttpRequest filter(HttpRequest input) throws HttpException {
+      HttpRequest request = 
input.toBuilder().endpoint(input.getEndpoint().toString().replace("%3F", 
"?")).build();
+      String contentHash = hashBody(request.getPayload());
+      Multimap<String, String> headers = ArrayListMultimap.create();
+      headers.put("X-Ops-Content-Hash", contentHash);
+      String timestamp = timeStampProvider.get();
+      String toSign = createStringToSign(request.getMethod(), 
hashPath(request.getEndpoint().getPath()), contentHash,
+            timestamp);
+      headers.put("X-Ops-Userid", creds.get().identity);
+      headers.put("X-Ops-Sign", SIGNING_DESCRIPTION);
+      request = calculateAndReplaceAuthorizationHeaders(request, toSign);
+      headers.put("X-Ops-Timestamp", timestamp);
+      utils.logRequest(signatureLog, request, "<<");
+
+      return request.toBuilder().replaceHeaders(headers).build();
+   }
+
+   @VisibleForTesting
+   HttpRequest calculateAndReplaceAuthorizationHeaders(HttpRequest request, 
String toSign) throws HttpException {
+      String signature = sign(toSign);
+      if (signatureWire.enabled())
+         signatureWire.input(Strings2.toInputStream(signature));
+      String[] signatureLines = 
Iterables.toArray(Splitter.fixedLength(60).split(signature), String.class);
+
+      Multimap<String, String> headers = ArrayListMultimap.create();
+      for (int i = 0; i < signatureLines.length; i++) {
+         headers.put("X-Ops-Authorization-" + (i + 1), signatureLines[i]);
+      }
+      return request.toBuilder().replaceHeaders(headers).build();
+   }
+
+   public String createStringToSign(String request, String hashedPath, String 
contentHash, String timestamp) {
+
+      return new 
StringBuilder().append("Method:").append(request).append("\n").append("Hashed 
Path:")
+            
.append(hashedPath).append("\n").append("X-Ops-Content-Hash:").append(contentHash).append("\n")
+            
.append("X-Ops-Timestamp:").append(timestamp).append("\n").append("X-Ops-UserId:")
+            .append(creds.get().identity).toString();
+
+   }
+
+   @VisibleForTesting
+   String hashPath(String path) {
+      try {
+         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);
+      }
+   }
+
+   /**
+    * Build the canonicalized path, which collapses multiple slashes (/) and
+    * removes a trailing slash unless the path is only "/"
+    */
+   @VisibleForTesting
+   String canonicalPath(String path) {
+      path = path.replaceAll("\\/+", "/");
+      return path.endsWith("/") && path.length() > 1 ? path.substring(0, 
path.length() - 1) : path;
+   }
+
+   @VisibleForTesting
+   String hashBody(Payload payload) {
+      if (payload == null)
+         return emptyStringHash;
+      payload = useTheFilePartIfForm(payload);
+      checkArgument(payload != null, "payload was null");
+      checkArgument(payload.isRepeatable(), "payload must be repeatable: " + 
payload);
+      try {
+         return base64().encode(ByteStreams2.hashAndClose(payload.getInput(), 
sha1()).asBytes());
+      } catch (Exception e) {
+         Throwables.propagateIfPossible(e);
+         throw new HttpException("error creating sigature for payload: " + 
payload, e);
+      }
+   }
+
+   private Payload useTheFilePartIfForm(Payload payload) {
+      if (payload instanceof MultipartForm) {
+         Iterable<? extends Part> parts = 
MultipartForm.class.cast(payload).getRawContent();
+         try {
+            payload = Iterables.find(parts, new Predicate<Part>() {
+
+               @Override
+               public boolean apply(Part input) {
+                  return "file".equals(input.getName());
+               }
+
+            });
+         } catch (NoSuchElementException e) {
+
+         }
+      }
+      return payload;
+   }
+
+   public String sign(String toSign) {
+      try {
+         byte[] encrypted = toByteArray(new RSAEncryptingPayload(crypto, 
Payloads.newStringPayload(toSign), supplyKey.get()));
+         return base64().encode(encrypted);
+      } catch (IOException e) {
+         throw new HttpException("error signing request", e);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
 
b/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
new file mode 100644
index 0000000..ec75e47
--- /dev/null
+++ 
b/apis/chef/src/main/java/org/jclouds/chef/functions/BootstrapConfigForGroup.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.jclouds.chef.config.ChefProperties.CHEF_BOOTSTRAP_DATABAG;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.domain.JsonBall;
+
+import com.google.common.base.Function;
+import com.google.inject.TypeLiteral;
+
+/**
+ * 
+ * Retrieves the bootstrap configuration for a specific group
+ */
+@Singleton
+public class BootstrapConfigForGroup implements Function<String, DatabagItem> {
+   public static final Type BOOTSTRAP_CONFIG_TYPE = new 
TypeLiteral<Map<String, JsonBall>>() {
+   }.getType();
+   private final ChefApi api;
+   private final String databag;
+
+   @Inject
+   public BootstrapConfigForGroup(@Named(CHEF_BOOTSTRAP_DATABAG) String 
databag, ChefApi api) {
+      this.databag = checkNotNull(databag, "databag");
+      this.api = checkNotNull(api, "api");
+   }
+
+   @Override
+   public DatabagItem apply(String from) {
+      DatabagItem bootstrapConfig = api.getDatabagItem(databag, from);
+      checkState(bootstrapConfig != null, "databag item %s/%s not found", 
databag, from);
+      return bootstrapConfig;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ClientForGroup.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/functions/ClientForGroup.java 
b/apis/chef/src/main/java/org/jclouds/chef/functions/ClientForGroup.java
new file mode 100644
index 0000000..d1a9250
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ClientForGroup.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Sets.newHashSet;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.Client;
+
+import com.google.common.base.Function;
+
+/**
+ * 
+ * Generates a client relevant for a particular group
+ */
+@Singleton
+public class ClientForGroup implements Function<String, Client> {
+   private final ChefApi chefApi;
+
+   @Inject
+   public ClientForGroup(ChefApi chefApi) {
+      this.chefApi = checkNotNull(chefApi, "chefApi");
+   }
+
+   @Override
+   public Client apply(String from) {
+      String clientName = findNextClientName(chefApi.listClients(), from + 
"-client-%02d");
+      Client client = chefApi.createClient(clientName);
+      // response from create only includes the key
+      return Client.builder() //
+            .clientname(clientName) //
+            .name(clientName) //
+            .isValidator(false) //
+            .privateKey(client.getPrivateKey()) //
+            .build();
+   }
+
+   private static String findNextClientName(Set<String> clients, String 
pattern) {
+      String clientName;
+      Set<String> names = newHashSet(clients);
+      int index = 0;
+      while (true) {
+         clientName = String.format(pattern, index++);
+         if (!names.contains(clientName))
+            break;
+      }
+      return clientName;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java 
b/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java
new file mode 100644
index 0000000..516d9f9
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/GroupToBootScript.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import static org.jclouds.scriptbuilder.domain.Statements.appendFile;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+import static org.jclouds.scriptbuilder.domain.Statements.newStatementList;
+
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.security.PrivateKey;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.config.InstallChef;
+import org.jclouds.chef.config.Validator;
+import org.jclouds.crypto.Pems;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.json.Json;
+import org.jclouds.location.Provider;
+import org.jclouds.scriptbuilder.ExitInsteadOfReturn;
+import org.jclouds.scriptbuilder.domain.Statement;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheLoader;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.TypeLiteral;
+
+/**
+ * 
+ * Generates a bootstrap script relevant for a particular group
+ */
+@Singleton
+public class GroupToBootScript implements Function<String, Statement> {
+   private static final Pattern newLinePattern = 
Pattern.compile("(\\r\\n)|(\\n)");
+   
+   @VisibleForTesting
+   static final Type RUN_LIST_TYPE = new TypeLiteral<Map<String, 
List<String>>>() {
+   }.getType();
+   private final Supplier<URI> endpoint;
+   private final Json json;
+   private final CacheLoader<String, ? extends JsonBall> 
bootstrapConfigForGroup;
+   private final Statement installChef;
+   private final Optional<String> validatorName;
+   private final Optional<PrivateKey> validatorCredential;
+
+   @Inject
+   public GroupToBootScript(@Provider Supplier<URI> endpoint, Json json,
+         CacheLoader<String, ? extends JsonBall> bootstrapConfigForGroup,
+         @InstallChef Statement installChef, @Validator Optional<String> 
validatorName,
+         @Validator Optional<PrivateKey> validatorCredential) {
+      this.endpoint = checkNotNull(endpoint, "endpoint");
+      this.json = checkNotNull(json, "json");
+      this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, 
"bootstrapConfigForGroup");
+      this.installChef = checkNotNull(installChef, "installChef");
+      this.validatorName = checkNotNull(validatorName, "validatorName");
+      this.validatorCredential = checkNotNull(validatorCredential, 
validatorCredential);
+   }
+
+   @Override
+   public Statement apply(String group) {
+      checkNotNull(group, "group");
+      String validatorClientName = validatorName.get();
+      PrivateKey validatorKey = validatorCredential.get();
+
+      JsonBall bootstrapConfig = null;
+      try {
+         bootstrapConfig = bootstrapConfigForGroup.load(group);
+      } catch (Exception e) {
+         throw propagate(e);
+      }
+
+      Map<String, JsonBall> config = json.fromJson(bootstrapConfig.toString(),
+            BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
+      Optional<JsonBall> environment = 
Optional.fromNullable(config.get("environment"));
+
+      String chefConfigDir = "{root}etc{fs}chef";
+      Statement createChefConfigDir = exec("{md} " + chefConfigDir);
+      Statement createClientRb = appendFile(chefConfigDir + "{fs}client.rb", 
ImmutableList.of("require 'rubygems'",
+            "require 'ohai'", "o = Ohai::System.new", "o.all_plugins",
+            String.format("node_name \"%s-\" + o[:ipaddress]", group), 
"log_level :info", "log_location STDOUT",
+            String.format("validation_client_name \"%s\"", 
validatorClientName),
+            String.format("chef_server_url \"%s\"", endpoint.get())));
+
+      Statement createValidationPem = appendFile(chefConfigDir + 
"{fs}validation.pem",
+            Splitter.on(newLinePattern).split(Pems.pem(validatorKey)));
+
+      String chefBootFile = chefConfigDir + "{fs}first-boot.json";
+      Statement createFirstBoot = appendFile(chefBootFile, 
Collections.singleton(json.toJson(bootstrapConfig)));
+
+      ImmutableMap.Builder<String, String> options = ImmutableMap.builder();
+      options.put("-j", chefBootFile);
+      if (environment.isPresent()) {
+         options.put("-E", environment.get().toString());
+      }
+      String strOptions = Joiner.on(' ').withKeyValueSeparator(" 
").join(options.build());
+      Statement runChef = exec("chef-client " + strOptions);
+
+      return newStatementList(new ExitInsteadOfReturn(installChef), 
createChefConfigDir, createClientRb, createValidationPem,
+            createFirstBoot, runChef);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
 
b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
new file mode 100644
index 0000000..ffb4201
--- /dev/null
+++ 
b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionCheckingChefVersion.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.functions;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.config.CookbookParser;
+import org.jclouds.http.HttpResponse;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+
+/**
+ * Parses a cookbook definition from a Json response, taking care of using the
+ * appropriate parser.
+ */
+@Singleton
+public class ParseCookbookDefinitionCheckingChefVersion implements 
Function<HttpResponse, Set<String>> {
+
+   @VisibleForTesting
+   final Function<HttpResponse, Set<String>> parser;
+
+   @Inject
+   ParseCookbookDefinitionCheckingChefVersion(@CookbookParser 
Function<HttpResponse, Set<String>> parser) {
+      this.parser = parser;
+   }
+
+   @Override
+   public Set<String> apply(HttpResponse response) {
+      return parser.apply(response);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJson.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJson.java
 
b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJson.java
new file mode 100644
index 0000000..3e172e4
--- /dev/null
+++ 
b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJson.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.functions;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.CookbookDefinition;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+
+import com.google.common.base.Function;
+
+/**
+ * Parses a cookbook definition from a Json response, assuming a Chef Server >=
+ * 0.10.8.
+ */
+@Singleton
+public class ParseCookbookDefinitionFromJson implements Function<HttpResponse, 
Set<String>> {
+
+   /** Parser for responses from chef server >= 0.10.8 */
+   private final ParseJson<Map<String, CookbookDefinition>> parser;
+
+   @Inject
+   ParseCookbookDefinitionFromJson(ParseJson<Map<String, CookbookDefinition>> 
parser) {
+      this.parser = parser;
+   }
+
+   @Override
+   public Set<String> apply(HttpResponse response) {
+      return parser.apply(response).keySet();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10.java
----------------------------------------------------------------------
diff --git 
a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10.java
 
b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10.java
new file mode 100644
index 0000000..692d969
--- /dev/null
+++ 
b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.functions;
+
+import com.google.common.base.Function;
+import org.jclouds.chef.domain.CookbookDefinition;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.Map;
+
+/**
+ * Parses the cookbook versions in a Chef Server >= 0.10.8.
+ */
+@Singleton
+public class ParseCookbookDefinitionFromJsonv10 implements 
Function<HttpResponse, CookbookDefinition> {
+
+   /** Parser for responses from chef server >= 0.10.8 */
+   private final ParseJson<Map<String, CookbookDefinition>> parser;
+
+   @Inject
+   ParseCookbookDefinitionFromJsonv10(ParseJson<Map<String, 
CookbookDefinition>> parser) {
+      this.parser = parser;
+   }
+
+   @Override
+   public CookbookDefinition apply(HttpResponse response) {
+      Map<String, CookbookDefinition> result = parser.apply(response);
+      String cookbookName = result.keySet().iterator().next();
+      CookbookDefinition def = result.values().iterator().next();
+      return CookbookDefinition.builder() //
+             .from(def) //
+             .name(cookbookName) //
+             .build();
+   }
+}

Reply via email to