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:02:58 UTC

[07/10] Move jclouds-chef to the main jclouds repo

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10.java
new file mode 100644
index 0000000..5da0797
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10.java
@@ -0,0 +1,63 @@
+/*
+ * 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;
+import java.util.Set;
+
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+
+/**
+ * Parses the cookbook versions in a Chef Server >= 0.10.8.
+ */
+@Singleton
+public class ParseCookbookDefinitionListFromJsonv10 implements Function<HttpResponse, Set<CookbookDefinition>> {
+
+   /**
+    * Parser for responses from chef server >= 0.10.8
+    */
+   private final ParseJson<Map<String, CookbookDefinition>> parser;
+
+   @Inject
+   ParseCookbookDefinitionListFromJsonv10(ParseJson<Map<String, CookbookDefinition>> parser) {
+      this.parser = parser;
+   }
+
+   @Override
+   public Set<CookbookDefinition> apply(HttpResponse response) {
+      Set<Map.Entry<String, CookbookDefinition>> result = parser.apply(response).entrySet();
+      return newLinkedHashSet(transform(result, new Function<Map.Entry<String, CookbookDefinition>, CookbookDefinition>() {
+         @Override
+         public CookbookDefinition apply(Map.Entry<String, CookbookDefinition> input) {
+            String cookbookName = input.getKey();
+            CookbookDefinition def = input.getValue();
+            return CookbookDefinition.builder() //
+                   .from(def) //              
+                   .name(cookbookName) //
+                   .build();
+         }
+      }));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.java
new file mode 100644
index 0000000..f82a900
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsCheckingChefVersion.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.CookbookVersionsParser;
+import org.jclouds.http.HttpResponse;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+
+/**
+ * Parses a cookbook versions from a Json response, taking care of using the
+ * appropriate parser.
+ */
+@Singleton
+public class ParseCookbookVersionsCheckingChefVersion implements Function<HttpResponse, Set<String>> {
+
+   @VisibleForTesting
+   final Function<HttpResponse, Set<String>> parser;
+
+   @Inject
+   ParseCookbookVersionsCheckingChefVersion(@CookbookVersionsParser 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/ParseCookbookVersionsV09FromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.java
new file mode 100644
index 0000000..4421b3e
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV09FromJson.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.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+
+import com.google.common.base.Function;
+import static com.google.common.collect.Iterables.getFirst;
+
+/**
+ * Parses the cookbook versions in a Chef Server <= 0.9.8.
+ */
+@Singleton
+public class ParseCookbookVersionsV09FromJson implements Function<HttpResponse, Set<String>> {
+
+   private final ParseJson<Map<String, Set<String>>> json;
+
+   @Inject
+   ParseCookbookVersionsV09FromJson(ParseJson<Map<String, Set<String>>> json) {
+      this.json = json;
+   }
+
+   @Override
+   public Set<String> apply(HttpResponse response) {
+      return getFirst(json.apply(response).values(), null);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV10FromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV10FromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV10FromJson.java
new file mode 100644
index 0000000..1a25ac0
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseCookbookVersionsV10FromJson.java
@@ -0,0 +1,59 @@
+/*
+ * 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.chef.domain.CookbookDefinition.Version;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+
+import com.google.common.base.Function;
+import static com.google.common.collect.Iterables.getFirst;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+
+/**
+ * Parses the cookbook versions in a Chef Server >= 0.10.8.
+ */
+@Singleton
+public class ParseCookbookVersionsV10FromJson implements Function<HttpResponse, Set<String>> {
+
+   /** Parser for responses from chef server >= 0.10.8 */
+   private final ParseJson<Map<String, CookbookDefinition>> parser;
+
+   @Inject
+   ParseCookbookVersionsV10FromJson(ParseJson<Map<String, CookbookDefinition>> parser) {
+      this.parser = parser;
+   }
+
+   @Override
+   public Set<String> apply(HttpResponse response) {
+      CookbookDefinition def = getFirst(parser.apply(response).values(), null);
+      return newLinkedHashSet(transform(def.getVersions(), new Function<Version, String>() {
+         @Override
+         public String apply(Version input) {
+            return input.getVersion();
+         }
+      }));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseErrorFromJsonOrReturnBody.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseErrorFromJsonOrReturnBody.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseErrorFromJsonOrReturnBody.java
new file mode 100644
index 0000000..6440409
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseErrorFromJsonOrReturnBody.java
@@ -0,0 +1,55 @@
+/*
+ * 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.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ReturnStringIf2xx;
+
+import com.google.common.base.Function;
+
+@Singleton
+public class ParseErrorFromJsonOrReturnBody implements Function<HttpResponse, String> {
+   Pattern pattern = Pattern.compile(".*\\[\"([^\"]+)\"\\].*");
+   private final ReturnStringIf2xx returnStringIf200;
+
+   @Inject
+   ParseErrorFromJsonOrReturnBody(ReturnStringIf2xx returnStringIf200) {
+      this.returnStringIf200 = returnStringIf200;
+   }
+
+   @Override
+   public String apply(HttpResponse response) {
+      String content = returnStringIf200.apply(response);
+      if (content == null)
+         return null;
+      return parse(content);
+   }
+
+   public String parse(String in) {
+      Matcher matcher = pattern.matcher(in);
+      if (matcher.find()) {
+         return matcher.group(1);
+      }
+      return in;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseKeySetFromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseKeySetFromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseKeySetFromJson.java
new file mode 100644
index 0000000..963c19b
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseKeySetFromJson.java
@@ -0,0 +1,45 @@
+/*
+ * 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.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+
+import com.google.common.base.Function;
+
+@Singleton
+public class ParseKeySetFromJson implements Function<HttpResponse, Set<String>> {
+
+   private final ParseJson<Map<String, String>> json;
+
+   @Inject
+   ParseKeySetFromJson(ParseJson<Map<String, String>> json) {
+      this.json = json;
+   }
+
+   @Override
+   public Set<String> apply(HttpResponse response) {
+      return json.apply(response).keySet();
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchClientsFromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchClientsFromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchClientsFromJson.java
new file mode 100644
index 0000000..18ecbfa
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchClientsFromJson.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.functions;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.Client;
+import org.jclouds.http.functions.ParseJson;
+
+@Singleton
+public class ParseSearchClientsFromJson extends ParseSearchResultFromJson<Client> {
+
+   // TODO add generic json parser detector
+
+   @Inject
+   ParseSearchClientsFromJson(ParseJson<Response<Client>> json) {
+      super(json);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchDatabagFromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchDatabagFromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchDatabagFromJson.java
new file mode 100644
index 0000000..c2c58ef
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchDatabagFromJson.java
@@ -0,0 +1,77 @@
+/*
+ * 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.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.domain.SearchResult;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+
+import com.google.common.base.Function;
+import com.google.gson.annotations.SerializedName;
+import static com.google.common.collect.Iterables.transform;
+
+/**
+ * Parses the search result into a {@link DatabagItem} object.
+ * <p>
+ * When searching databags, the items are contained inside the
+ * <code>raw_data</code> list.
+ */
+@Singleton
+public class ParseSearchDatabagFromJson implements Function<HttpResponse, SearchResult<DatabagItem>> {
+
+   private final ParseJson<Response> responseParser;
+
+   private final Json json;
+
+   static class Row {
+      @SerializedName("raw_data")
+      JsonBall rawData;
+   }
+
+   static class Response {
+      long start;
+      List<Row> rows;
+   }
+
+   @Inject
+   ParseSearchDatabagFromJson(ParseJson<Response> responseParser, Json json) {
+      this.responseParser = responseParser;
+      this.json = json;
+   }
+
+   @Override
+   public SearchResult<DatabagItem> apply(HttpResponse response) {
+      Response returnVal = responseParser.apply(response);
+      Iterable<DatabagItem> items = transform(returnVal.rows, new Function<Row, DatabagItem>() {
+         @Override
+         public DatabagItem apply(Row input) {
+            return json.fromJson(input.rawData.toString(), DatabagItem.class);
+         }
+      });
+
+      return new SearchResult<DatabagItem>(returnVal.start, items);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchEnvironmentsFromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchEnvironmentsFromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchEnvironmentsFromJson.java
new file mode 100644
index 0000000..852e0f3
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchEnvironmentsFromJson.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.functions;
+
+import org.jclouds.chef.domain.Environment;
+import org.jclouds.http.functions.ParseJson;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
+public class ParseSearchEnvironmentsFromJson extends ParseSearchResultFromJson<Environment> {
+
+   // TODO add generic json parser detector
+
+   @Inject
+   ParseSearchEnvironmentsFromJson(ParseJson<Response<Environment>> json) {
+      super(json);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchNodesFromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchNodesFromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchNodesFromJson.java
new file mode 100644
index 0000000..6d34575
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchNodesFromJson.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.functions;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.Node;
+import org.jclouds.http.functions.ParseJson;
+
+@Singleton
+public class ParseSearchNodesFromJson extends ParseSearchResultFromJson<Node> {
+
+   // TODO add generic json parser detector
+
+   @Inject
+   ParseSearchNodesFromJson(ParseJson<Response<Node>> json) {
+      super(json);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchResultFromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchResultFromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchResultFromJson.java
new file mode 100644
index 0000000..6c9bd84
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchResultFromJson.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.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.SearchResult;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+
+import com.google.common.base.Function;
+
+@Singleton
+public class ParseSearchResultFromJson<T> implements Function<HttpResponse, SearchResult<T>> {
+
+   private final ParseJson<Response<T>> json;
+
+   static class Response<T> {
+      long start;
+      List<T> rows;
+   }
+
+   @Inject
+   ParseSearchResultFromJson(ParseJson<Response<T>> json) {
+      this.json = json;
+   }
+
+   @Override
+   public SearchResult<T> apply(HttpResponse response) {
+      Response<T> returnVal = json.apply(response);
+      return new SearchResult<T>(returnVal.start, returnVal.rows);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchRolesFromJson.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchRolesFromJson.java b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchRolesFromJson.java
new file mode 100644
index 0000000..42ba797
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/ParseSearchRolesFromJson.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.functions;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.Role;
+import org.jclouds.http.functions.ParseJson;
+
+@Singleton
+public class ParseSearchRolesFromJson extends ParseSearchResultFromJson<Role> {
+
+   // TODO add generic json parser detector
+
+   @Inject
+   ParseSearchRolesFromJson(ParseJson<Response<Role>> json) {
+      super(json);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.java b/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.java
new file mode 100644
index 0000000..b14ae71
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/RunListForGroup.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 java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.json.Json;
+
+import com.google.common.base.Function;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Retrieves the run-list for a specific group
+ */
+@Singleton
+public class RunListForGroup implements Function<String, List<String>> {
+   public static final Type RUN_LIST_TYPE = new TypeLiteral<List<String>>() {
+   }.getType();
+   private final BootstrapConfigForGroup bootstrapConfigForGroup;
+
+   private final Json json;
+
+   @Inject
+   public RunListForGroup(BootstrapConfigForGroup bootstrapConfigForGroup, Json json) {
+      this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup");
+      this.json = checkNotNull(json, "json");
+   }
+
+   @Override
+   public List<String> apply(String from) {
+      DatabagItem bootstrapConfig = bootstrapConfigForGroup.apply(from);
+      Map<String, JsonBall> config = json.fromJson(bootstrapConfig.toString(),
+            BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
+      JsonBall runlist = config.get("run_list");
+      return json.fromJson(runlist.toString(), RUN_LIST_TYPE);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/functions/UriForResource.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/functions/UriForResource.java b/apis/chef/src/main/java/org/jclouds/chef/functions/UriForResource.java
new file mode 100644
index 0000000..d5d0810
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/functions/UriForResource.java
@@ -0,0 +1,42 @@
+/*
+ * 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.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+
+import javax.inject.Singleton;
+
+import org.jclouds.chef.domain.Resource;
+
+import com.google.common.base.Function;
+
+/**
+ * Extracts the uri field of the given {@link Resource}.
+ */
+@Singleton
+public class UriForResource implements Function<Object, URI> {
+
+   @Override
+   public URI apply(Object input) {
+      checkArgument(checkNotNull(input, "input") instanceof Resource,
+            "This function can only be applied to Resource objects");
+      return ((Resource) input).getUrl();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandler.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandler.java b/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandler.java
new file mode 100644
index 0000000..3b4da4a
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefApiErrorRetryHandler.java
@@ -0,0 +1,67 @@
+/*
+ * 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.handlers;
+
+import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import org.jclouds.Constants;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpRetryHandler;
+import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
+import org.jclouds.logging.Logger;
+
+import com.google.inject.Inject;
+
+/**
+ * Allow for eventual consistency on sandbox requests.
+ */
+public class ChefApiErrorRetryHandler implements HttpRetryHandler {
+
+   @Inject(optional = true)
+   @Named(Constants.PROPERTY_MAX_RETRIES)
+   private int retryCountLimit = 5;
+
+   @Resource
+   protected Logger logger = Logger.NULL;
+
+   private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
+
+   @Inject
+   ChefApiErrorRetryHandler(BackoffLimitedRetryHandler backoffLimitedRetryHandler) {
+      this.backoffLimitedRetryHandler = backoffLimitedRetryHandler;
+   }
+
+   public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
+      if (command.getFailureCount() > retryCountLimit)
+         return false;
+      if (response.getStatusCode() == 400 && command.getCurrentRequest().getMethod().equals("PUT")
+            && command.getCurrentRequest().getEndpoint().getPath().indexOf("sandboxes") != -1) {
+         if (response.getPayload() != null) {
+            String error = new String(closeClientButKeepContentStream(response));
+            if (error != null && error.indexOf("was not uploaded") != -1) {
+               return backoffLimitedRetryHandler.shouldRetryRequest(command, response);
+            }
+         }
+      }
+      return false;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefErrorHandler.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefErrorHandler.java b/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefErrorHandler.java
new file mode 100644
index 0000000..3376219
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/handlers/ChefErrorHandler.java
@@ -0,0 +1,81 @@
+/*
+ * 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.handlers;
+
+import static com.google.common.base.Throwables.propagate;
+
+import java.io.IOException;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.functions.ParseErrorFromJsonOrReturnBody;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.AuthorizationException;
+import org.jclouds.rest.ResourceNotFoundException;
+
+import com.google.common.io.Closeables;
+
+/**
+ * This will parse and set an appropriate exception on the command object.
+ */
+@Singleton
+public class ChefErrorHandler implements HttpErrorHandler {
+   @Resource
+   protected Logger logger = Logger.NULL;
+   private final ParseErrorFromJsonOrReturnBody errorParser;
+
+   @Inject
+   ChefErrorHandler(ParseErrorFromJsonOrReturnBody errorParser) {
+      this.errorParser = errorParser;
+   }
+
+   public void handleError(HttpCommand command, HttpResponse response) {
+      String message = errorParser.apply(response);
+      Exception exception = new HttpResponseException(command, response, message);
+      try {
+         message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
+               response.getStatusLine());
+         switch (response.getStatusCode()) {
+            case 401:
+            case 403:
+               exception = new AuthorizationException(message, exception);
+               break;
+            case 404:
+               if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
+                  exception = new ResourceNotFoundException(message, exception);
+               }
+               break;
+         }
+      } finally {
+         if (response.getPayload() != null) {
+            try {
+               Closeables.close(response.getPayload().getInput(), true);
+            } catch (IOException e) {
+               throw propagate(e);
+            }
+         }
+         command.setException(exception);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java
new file mode 100644
index 0000000..d390555
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/internal/BaseChefService.java
@@ -0,0 +1,299 @@
+/*
+ * 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.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.config.ChefProperties.CHEF_BOOTSTRAP_DATABAG;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.PrivateKey;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.ChefContext;
+import org.jclouds.chef.ChefService;
+import org.jclouds.chef.config.ChefProperties;
+import org.jclouds.chef.domain.BootstrapConfig;
+import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.domain.Environment;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.functions.BootstrapConfigForGroup;
+import org.jclouds.chef.functions.GroupToBootScript;
+import org.jclouds.chef.functions.RunListForGroup;
+import org.jclouds.chef.strategy.CleanupStaleNodesAndClients;
+import org.jclouds.chef.strategy.CreateNodeAndPopulateAutomaticAttributes;
+import org.jclouds.chef.strategy.DeleteAllClientsInList;
+import org.jclouds.chef.strategy.DeleteAllNodesInList;
+import org.jclouds.chef.strategy.ListClients;
+import org.jclouds.chef.strategy.ListCookbookVersions;
+import org.jclouds.chef.strategy.ListCookbookVersionsInEnvironment;
+import org.jclouds.chef.strategy.ListEnvironments;
+import org.jclouds.chef.strategy.ListNodes;
+import org.jclouds.chef.strategy.ListNodesInEnvironment;
+import org.jclouds.chef.strategy.UpdateAutomaticAttributesOnNode;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.io.Payloads;
+import org.jclouds.io.payloads.RSADecryptingPayload;
+import org.jclouds.io.payloads.RSAEncryptingPayload;
+import org.jclouds.json.Json;
+import org.jclouds.logging.Logger;
+import org.jclouds.scriptbuilder.domain.Statement;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.InputSupplier;
+
+@Singleton
+public class BaseChefService implements ChefService {
+
+   private final ChefContext chefContext;
+   private final ChefApi api;
+   private final CleanupStaleNodesAndClients cleanupStaleNodesAndClients;
+   private final CreateNodeAndPopulateAutomaticAttributes createNodeAndPopulateAutomaticAttributes;
+   private final DeleteAllNodesInList deleteAllNodesInList;
+   private final ListNodes listNodes;
+   private final DeleteAllClientsInList deleteAllClientsInList;
+   private final ListClients listClients;
+   private final UpdateAutomaticAttributesOnNode updateAutomaticAttributesOnNode;
+   private final Supplier<PrivateKey> privateKey;
+   private final GroupToBootScript groupToBootScript;
+   private final String databag;
+   private final BootstrapConfigForGroup bootstrapConfigForGroup;
+   private final RunListForGroup runListForGroup;
+   private final ListCookbookVersions listCookbookVersions;
+   private final ListCookbookVersionsInEnvironment listCookbookVersionsInEnvironment;
+   private final ListEnvironments listEnvironments;
+   private final ListNodesInEnvironment listNodesInEnvironment;
+   private final Json json;
+   private final Crypto crypto;
+
+   @Resource
+   @Named(ChefProperties.CHEF_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   @Inject
+   protected BaseChefService(ChefContext chefContext, ChefApi api,
+         CleanupStaleNodesAndClients cleanupStaleNodesAndClients,
+         CreateNodeAndPopulateAutomaticAttributes createNodeAndPopulateAutomaticAttributes,
+         DeleteAllNodesInList deleteAllNodesInList, ListNodes listNodes, DeleteAllClientsInList deleteAllClientsInList,
+         ListClients listClients, ListCookbookVersions listCookbookVersions,
+         UpdateAutomaticAttributesOnNode updateAutomaticAttributesOnNode, Supplier<PrivateKey> privateKey,
+         @Named(CHEF_BOOTSTRAP_DATABAG) String databag, GroupToBootScript groupToBootScript,
+         BootstrapConfigForGroup bootstrapConfigForGroup, RunListForGroup runListForGroup,
+         ListEnvironments listEnvironments, ListNodesInEnvironment listNodesInEnvironment,
+         ListCookbookVersionsInEnvironment listCookbookVersionsInEnvironment, Json json, Crypto crypto) {
+      this.chefContext = checkNotNull(chefContext, "chefContext");
+      this.api = checkNotNull(api, "api");
+      this.cleanupStaleNodesAndClients = checkNotNull(cleanupStaleNodesAndClients, "cleanupStaleNodesAndClients");
+      this.createNodeAndPopulateAutomaticAttributes = checkNotNull(createNodeAndPopulateAutomaticAttributes,
+            "createNodeAndPopulateAutomaticAttributes");
+      this.deleteAllNodesInList = checkNotNull(deleteAllNodesInList, "deleteAllNodesInList");
+      this.listNodes = checkNotNull(listNodes, "listNodes");
+      this.deleteAllClientsInList = checkNotNull(deleteAllClientsInList, "deleteAllClientsInList");
+      this.listClients = checkNotNull(listClients, "listClients");
+      this.listCookbookVersions = checkNotNull(listCookbookVersions, "listCookbookVersions");
+      this.updateAutomaticAttributesOnNode = checkNotNull(updateAutomaticAttributesOnNode,
+            "updateAutomaticAttributesOnNode");
+      this.privateKey = checkNotNull(privateKey, "privateKey");
+      this.groupToBootScript = checkNotNull(groupToBootScript, "groupToBootScript");
+      this.databag = checkNotNull(databag, "databag");
+      this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup");
+      this.runListForGroup = checkNotNull(runListForGroup, "runListForGroup");
+      this.listEnvironments = checkNotNull(listEnvironments, "listEnvironments");
+      this.listNodesInEnvironment = checkNotNull(listNodesInEnvironment, "listNodesInEnvironment");
+      this.listCookbookVersionsInEnvironment = checkNotNull(listCookbookVersionsInEnvironment,"listCookbookVersionsInEnvironment");
+      this.json = checkNotNull(json, "json");
+      this.crypto = checkNotNull(crypto, "crypto");
+   }
+
+   @Override
+   public ChefContext getContext() {
+      return chefContext;
+   }
+
+   @Override
+   public byte[] encrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
+      return ByteStreams.toByteArray(new RSAEncryptingPayload(crypto, Payloads.newPayload(supplier.getInput()), privateKey
+                  .get()));
+   }
+
+   @Override
+   public byte[] decrypt(InputSupplier<? extends InputStream> supplier) throws IOException {
+      return ByteStreams.toByteArray(new RSADecryptingPayload(crypto, Payloads.newPayload(supplier.getInput()), privateKey
+                  .get()));
+   }
+
+   @VisibleForTesting
+   String buildBootstrapConfiguration(BootstrapConfig bootstrapConfig) {
+      checkNotNull(bootstrapConfig, "bootstrapConfig must not be null");
+
+      Map<String, Object> configMap = Maps.newHashMap();
+      configMap.put("run_list", bootstrapConfig.getRunList());
+
+      if (bootstrapConfig.getEnvironment().isPresent()) {
+         configMap.put("environment", bootstrapConfig.getEnvironment().get());
+      }
+
+      if (bootstrapConfig.getAttribtues().isPresent()) {
+         Map<String, Object> attributes = json.fromJson(bootstrapConfig.getAttribtues().get().toString(),
+               BootstrapConfigForGroup.BOOTSTRAP_CONFIG_TYPE);
+         configMap.putAll(attributes);
+      }
+
+      return json.toJson(configMap);
+   }
+
+   @Override
+   public Statement createBootstrapScriptForGroup(String group) {
+      return groupToBootScript.apply(group);
+   }
+
+   @Override
+   public void updateBootstrapConfigForGroup(String group, BootstrapConfig bootstrapConfig) {
+      try {
+         api.createDatabag(databag);
+      } catch (IllegalStateException e) {
+
+      }
+
+      String jsonConfig = buildBootstrapConfiguration(bootstrapConfig);
+      DatabagItem runlist = new DatabagItem(group, jsonConfig);
+
+      if (api.getDatabagItem(databag, group) == null) {
+         api.createDatabagItem(databag, runlist);
+      } else {
+         api.updateDatabagItem(databag, runlist);
+      }
+   }
+
+   @Override
+   public List<String> getRunListForGroup(String group) {
+      return runListForGroup.apply(group);
+   }
+
+   @Override
+   public JsonBall getBootstrapConfigForGroup(String group) {
+      return bootstrapConfigForGroup.apply(group);
+   }
+
+   @Override
+   public void cleanupStaleNodesAndClients(String prefix, int secondsStale) {
+      cleanupStaleNodesAndClients.execute(prefix, secondsStale);
+   }
+
+   @Override
+   public Node createNodeAndPopulateAutomaticAttributes(String nodeName, Iterable<String> runList) {
+      return createNodeAndPopulateAutomaticAttributes.execute(nodeName, runList);
+   }
+
+   @Override
+   public void updateAutomaticAttributesOnNode(String nodeName) {
+      updateAutomaticAttributesOnNode.execute(nodeName);
+   }
+
+   @Override
+   public void deleteAllNodesInList(Iterable<String> names) {
+      deleteAllNodesInList.execute(names);
+   }
+
+   @Override
+   public void deleteAllClientsInList(Iterable<String> names) {
+      deleteAllClientsInList.execute(names);
+   }
+
+   @Override
+   public Iterable<? extends Node> listNodes() {
+      return listNodes.execute();
+   }
+
+   @Override
+   public Iterable<? extends Node> listNodes(ExecutorService executorService) {
+      return listNodes.execute(executorService);
+   }
+
+   @Override
+   public Iterable<? extends Client> listClients() {
+      return listClients.execute();
+   }
+
+   @Override
+   public Iterable<? extends Client> listClients(ExecutorService executorService) {
+      return listClients.execute(executorService);
+   }
+
+   @Override
+   public Iterable<? extends CookbookVersion> listCookbookVersions() {
+      return listCookbookVersions.execute();
+   }
+
+   @Override public Iterable<? extends CookbookVersion> listCookbookVersions(
+         ExecutorService executorService) {
+      return listCookbookVersions.execute(executorService);
+   }
+
+   @Override
+   public Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName) {
+      return listCookbookVersionsInEnvironment.execute(environmentName);
+   }
+
+   @Override
+   public Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName,
+         ExecutorService executorService) {
+      return listCookbookVersionsInEnvironment.execute(executorService, environmentName);
+   }
+
+   @Override
+   public Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName,
+         String numVersions) {
+      return listCookbookVersionsInEnvironment.execute(environmentName, numVersions);
+   }
+
+   @Override
+   public Iterable<? extends CookbookVersion> listCookbookVersionsInEnvironment(String environmentName,
+         String numVersions, ExecutorService executorService) {
+      return listCookbookVersionsInEnvironment.execute(executorService, environmentName, numVersions);
+   }
+
+   @Override
+   public Iterable<? extends Environment> listEnvironments() {
+      return listEnvironments.execute();
+   }
+
+   @Override
+   public Iterable<? extends Node> listNodesInEnvironment(String environmentName) {
+      return listNodesInEnvironment.execute(environmentName);
+   }
+
+   @Override
+   public Iterable<? extends Node> listNodesInEnvironment(String environmentName, ExecutorService executorService) {
+      return listNodesInEnvironment.execute(executorService, environmentName);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java b/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java
new file mode 100644
index 0000000..8aef880
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/internal/ChefContextImpl.java
@@ -0,0 +1,55 @@
+/*
+ * 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.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.Context;
+import org.jclouds.chef.ChefContext;
+import org.jclouds.chef.ChefService;
+import org.jclouds.internal.BaseView;
+import org.jclouds.location.Provider;
+
+import com.google.common.reflect.TypeToken;
+
+@Singleton
+public class ChefContextImpl extends BaseView implements ChefContext {
+   private final ChefService chefService;
+
+   @Inject
+   protected ChefContextImpl(@Provider Context backend, @Provider TypeToken<? extends Context> backendType,
+         ChefService chefService) {
+      super(backend, backendType);
+      this.chefService = checkNotNull(chefService, "chefService");
+   }
+
+   @Override
+   public ChefService getChefService() {
+      return chefService;
+   }
+
+   @Override
+   public void close() throws IOException {
+      delegate().close();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/options/CreateClientOptions.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/options/CreateClientOptions.java b/apis/chef/src/main/java/org/jclouds/chef/options/CreateClientOptions.java
new file mode 100644
index 0000000..776d7bb
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/options/CreateClientOptions.java
@@ -0,0 +1,64 @@
+/*
+ * 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.options;
+
+/**
+ * Options for the create client method.
+ */
+public class CreateClientOptions implements Cloneable {
+   /** Administrator flag. This flag will be ignored in Opscode Hosted Chef. */
+   private boolean admin;
+
+   public CreateClientOptions() {
+   }
+
+   CreateClientOptions(final boolean admin) {
+      super();
+      this.admin = admin;
+   }
+
+   public boolean isAdmin() {
+      return admin;
+   }
+
+   public CreateClientOptions admin() {
+      this.admin = true;
+      return this;
+   }
+
+   @Override
+   protected Object clone() throws CloneNotSupportedException {
+      return new CreateClientOptions(admin);
+   }
+
+   @Override
+   public String toString() {
+      return "[admin=" + admin + "]";
+   }
+
+   public static class Builder {
+      /**
+       * @see CreateClientOptions#admin()
+       */
+      public static CreateClientOptions admin() {
+         CreateClientOptions options = new CreateClientOptions();
+         return options.admin();
+      }
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java b/apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java
new file mode 100644
index 0000000..cbc1d54
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/options/SearchOptions.java
@@ -0,0 +1,95 @@
+/*
+ * 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.options;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options for the search api.
+ */
+public class SearchOptions extends BaseHttpRequestOptions {
+
+   /**
+    * A valid search string.
+    */
+   public SearchOptions query(String query) {
+      this.queryParameters.put("q", checkNotNull(query, "query"));
+      return this;
+   }
+
+   /**
+    * A sort string, such as 'name DESC'.
+    */
+   public SearchOptions sort(String sort) {
+      this.queryParameters.put("sort", checkNotNull(sort, "sort"));
+      return this;
+   }
+
+   /**
+    * The number of rows to return.
+    */
+   public SearchOptions rows(int rows) {
+      this.queryParameters.put("rows", String.valueOf(rows));
+      return this;
+   }
+
+   /**
+    * The result number to start from.
+    */
+   public SearchOptions start(int start) {
+      this.queryParameters.put("start", String.valueOf(start));
+      return this;
+   }
+
+   public static class Builder {
+
+      /**
+       * @see SearchOptions#query(String)
+       */
+      public static SearchOptions query(String query) {
+         SearchOptions options = new SearchOptions();
+         return options.query(query);
+      }
+
+      /**
+       * @see SearchOptions#sort(String)
+       */
+      public static SearchOptions start(String start) {
+         SearchOptions options = new SearchOptions();
+         return options.sort(start);
+      }
+
+      /**
+       * @see SearchOptions#rows(int)
+       */
+      public static SearchOptions rows(int rows) {
+         SearchOptions options = new SearchOptions();
+         return options.rows(rows);
+      }
+
+      /**
+       * @see SearchOptions#start(int)
+       */
+      public static SearchOptions start(int start) {
+         SearchOptions options = new SearchOptions();
+         return options.start(start);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/predicates/CookbookVersionPredicates.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/predicates/CookbookVersionPredicates.java b/apis/chef/src/main/java/org/jclouds/chef/predicates/CookbookVersionPredicates.java
new file mode 100644
index 0000000..7edbcae
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/predicates/CookbookVersionPredicates.java
@@ -0,0 +1,93 @@
+/*
+ * 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.predicates;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.any;
+import static com.google.common.collect.Iterables.get;
+
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.domain.Resource;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Container for cookbook filters (predicates).
+ * 
+ * This class has static methods that create customized predicates to use with
+ * {@link org.jclouds.chef.ChefService}.
+ */
+public class CookbookVersionPredicates {
+   /**
+    * @see #containsRecipes
+    */
+   public static Predicate<CookbookVersion> containsRecipe(String recipe) {
+      return containsRecipes(checkNotNull(recipe, "recipe must be defined"));
+   }
+
+   /**
+    * Note that the default recipe of a cookbook is its name. Otherwise, you
+    * prefix the recipe with the name of the cookbook. ex. {@code apache2} will
+    * be the default recipe where {@code apache2::mod_proxy} is a specific one
+    * in the cookbook.
+    * 
+    * @param recipes
+    *           names of the recipes.
+    * @return true if the cookbook version contains a recipe in the list.
+    */
+   public static Predicate<CookbookVersion> containsRecipes(String... recipes) {
+      checkNotNull(recipes, "recipes must be defined");
+      final Multimap<String, String> search = LinkedListMultimap.create();
+      for (String recipe : recipes) {
+         if (recipe.indexOf("::") != -1) {
+            Iterable<String> nameRecipe = Splitter.on("::").split(recipe);
+            search.put(get(nameRecipe, 0), get(nameRecipe, 1) + ".rb");
+         } else {
+            search.put(recipe, "default.rb");
+         }
+      }
+      return new Predicate<CookbookVersion>() {
+         @Override
+         public boolean apply(final CookbookVersion cookbookVersion) {
+            return search.containsKey(cookbookVersion.getCookbookName())
+                  && any(search.get(cookbookVersion.getCookbookName()), new Predicate<String>() {
+
+                     @Override
+                     public boolean apply(final String recipeName) {
+                        return any(cookbookVersion.getRecipes(), new Predicate<Resource>() {
+
+                           @Override
+                           public boolean apply(Resource resource) {
+                              return resource.getName().equals(recipeName);
+                           }
+
+                        });
+                     }
+
+                  });
+         }
+
+         @Override
+         public String toString() {
+            return "containsRecipes(" + search + ")";
+         }
+      };
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/CleanupStaleNodesAndClients.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/CleanupStaleNodesAndClients.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/CleanupStaleNodesAndClients.java
new file mode 100644
index 0000000..8519450
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/CleanupStaleNodesAndClients.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.strategy;
+
+import org.jclouds.chef.strategy.internal.CleanupStaleNodesAndClientsImpl;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * 
+ * Cleans up nodes and clients who have been hanging around too long.
+ */
+@ImplementedBy(CleanupStaleNodesAndClientsImpl.class)
+public interface CleanupStaleNodesAndClients {
+
+   void execute(String prefix, int secondsStale);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/CreateNodeAndPopulateAutomaticAttributes.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/CreateNodeAndPopulateAutomaticAttributes.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/CreateNodeAndPopulateAutomaticAttributes.java
new file mode 100644
index 0000000..249be48
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/CreateNodeAndPopulateAutomaticAttributes.java
@@ -0,0 +1,33 @@
+/*
+ * 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.strategy;
+
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.strategy.internal.CreateNodeAndPopulateAutomaticAttributesImpl;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * 
+ * Creates a new node with automatic attributes.
+ */
+@ImplementedBy(CreateNodeAndPopulateAutomaticAttributesImpl.class)
+public interface CreateNodeAndPopulateAutomaticAttributes {
+   Node execute(Node node);
+
+   Node execute(String nodeName, Iterable<String> runList);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllClientsInList.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllClientsInList.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllClientsInList.java
new file mode 100644
index 0000000..7dd7b89
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllClientsInList.java
@@ -0,0 +1,34 @@
+/*
+ * 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.strategy;
+
+import org.jclouds.chef.strategy.internal.DeleteAllClientsInListImpl;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.inject.ImplementedBy;
+
+/**
+ * Deletes all clients in a given list.
+ */
+@ImplementedBy(DeleteAllClientsInListImpl.class)
+public interface DeleteAllClientsInList {
+
+   void execute(Iterable<String> names);
+
+   void execute(ListeningExecutorService executor, Iterable<String> names);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllNodesInList.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllNodesInList.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllNodesInList.java
new file mode 100644
index 0000000..8867e49
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/DeleteAllNodesInList.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.strategy;
+
+import org.jclouds.chef.strategy.internal.DeleteAllNodesInListImpl;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.inject.ImplementedBy;
+
+@ImplementedBy(DeleteAllNodesInListImpl.class)
+public interface DeleteAllNodesInList {
+
+   void execute(Iterable<String> names);
+
+   void execute(ListeningExecutorService executor, Iterable<String> names);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/ListClients.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/ListClients.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListClients.java
new file mode 100644
index 0000000..aa40c2a
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListClients.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.strategy;
+
+import com.google.inject.ImplementedBy;
+import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.strategy.internal.ListClientsImpl;
+
+import java.util.concurrent.ExecutorService;
+
+@ImplementedBy(ListClientsImpl.class)
+public interface ListClients {
+
+   Iterable<? extends Client> execute();
+
+   Iterable<? extends Client> execute(ExecutorService executor);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersions.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersions.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersions.java
new file mode 100644
index 0000000..45663a3
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersions.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.strategy;
+
+import com.google.inject.ImplementedBy;
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.strategy.internal.ListCookbookVersionsImpl;
+
+import java.util.concurrent.ExecutorService;
+
+@ImplementedBy(ListCookbookVersionsImpl.class)
+public interface ListCookbookVersions {
+
+   Iterable<? extends CookbookVersion> execute();
+
+   Iterable<? extends CookbookVersion> execute(ExecutorService executor);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersionsInEnvironment.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersionsInEnvironment.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersionsInEnvironment.java
new file mode 100644
index 0000000..188d29f
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListCookbookVersionsInEnvironment.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.strategy;
+
+import com.google.inject.ImplementedBy;
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.strategy.internal.ListCookbookVersionsInEnvironmentImpl;
+
+import java.util.concurrent.ExecutorService;
+
+@ImplementedBy(ListCookbookVersionsInEnvironmentImpl.class)
+public interface ListCookbookVersionsInEnvironment {
+
+   Iterable<? extends CookbookVersion> execute(String environmentName);
+
+   Iterable<? extends CookbookVersion> execute(String environmentName, String numVersions);
+
+   Iterable<? extends CookbookVersion> execute(ExecutorService executor, String environmentName);
+
+   Iterable<? extends CookbookVersion> execute(ExecutorService executor, String environmentName, String numVersions);
+
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/ListEnvironments.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/ListEnvironments.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListEnvironments.java
new file mode 100644
index 0000000..553e8d4
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListEnvironments.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.strategy;
+
+import com.google.inject.ImplementedBy;
+import org.jclouds.chef.domain.Environment;
+import org.jclouds.chef.strategy.internal.ListEnvironmentsImpl;
+
+import java.util.concurrent.ExecutorService;
+
+@ImplementedBy(ListEnvironmentsImpl.class)
+public interface ListEnvironments {
+
+   Iterable<? extends Environment> execute();
+
+   Iterable<? extends Environment> execute(ExecutorService executor);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodes.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodes.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodes.java
new file mode 100644
index 0000000..660eed0
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodes.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.strategy;
+
+import com.google.inject.ImplementedBy;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.strategy.internal.ListNodesImpl;
+
+import java.util.concurrent.ExecutorService;
+
+@ImplementedBy(ListNodesImpl.class)
+public interface ListNodes {
+
+   Iterable<? extends Node> execute();
+
+   Iterable<? extends Node> execute(ExecutorService executor);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodesInEnvironment.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodesInEnvironment.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodesInEnvironment.java
new file mode 100644
index 0000000..efeffe6
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/ListNodesInEnvironment.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.strategy;
+
+import com.google.inject.ImplementedBy;
+import org.jclouds.chef.domain.Node;
+import org.jclouds.chef.strategy.internal.ListNodesInEnvironmentImpl;
+
+import java.util.concurrent.ExecutorService;
+
+@ImplementedBy(ListNodesInEnvironmentImpl.class)
+public interface ListNodesInEnvironment {
+
+   Iterable<? extends Node> execute(String environmentName);
+
+   Iterable<? extends Node> execute(ExecutorService executor, String environmentName);
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/UpdateAutomaticAttributesOnNode.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/UpdateAutomaticAttributesOnNode.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/UpdateAutomaticAttributesOnNode.java
new file mode 100644
index 0000000..0dcb81e
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/UpdateAutomaticAttributesOnNode.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.strategy;
+
+import org.jclouds.chef.strategy.internal.UpdateAutomaticAttributesOnNodeImpl;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * 
+ * Updates node with new automatic attributes.
+ */
+@ImplementedBy(UpdateAutomaticAttributesOnNodeImpl.class)
+public interface UpdateAutomaticAttributesOnNode {
+
+   void execute(String nodeName);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/867c7a40/apis/chef/src/main/java/org/jclouds/chef/strategy/internal/BaseListCookbookVersionsImpl.java
----------------------------------------------------------------------
diff --git a/apis/chef/src/main/java/org/jclouds/chef/strategy/internal/BaseListCookbookVersionsImpl.java b/apis/chef/src/main/java/org/jclouds/chef/strategy/internal/BaseListCookbookVersionsImpl.java
new file mode 100644
index 0000000..94cf79a
--- /dev/null
+++ b/apis/chef/src/main/java/org/jclouds/chef/strategy/internal/BaseListCookbookVersionsImpl.java
@@ -0,0 +1,97 @@
+/*
+ * 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.strategy.internal;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.logging.Logger;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.util.concurrent.Futures.allAsList;
+import static com.google.common.util.concurrent.Futures.getUnchecked;
+
+public abstract class BaseListCookbookVersionsImpl {
+
+   protected final ChefApi api;
+
+   protected Logger logger = Logger.NULL;
+
+   BaseListCookbookVersionsImpl(ChefApi api) {
+      this.api = checkNotNull(api, "api");
+   }
+
+   protected Iterable<? extends CookbookVersion> execute(Iterable<String> toGet) {
+      return concat(transform(toGet, new Function<String, Iterable<? extends CookbookVersion>>() {
+
+         @Override
+         public Iterable<? extends CookbookVersion> apply(final String cookbook) {
+            // TODO getting each version could also go parallel
+            Set<String> cookbookVersions = api.listVersionsOfCookbook(cookbook);
+            Iterable<? extends CookbookVersion> cookbooksVersions = transform(cookbookVersions,
+                  new Function<String, CookbookVersion>() {
+                     @Override
+                     public CookbookVersion apply(final String version) {
+                        return api.getCookbook(cookbook, version);
+                     }
+                  }
+            );
+
+            logger.trace(String.format("getting versions of cookbook: %s", cookbook));
+            return cookbooksVersions;
+         }
+      }));
+
+   }
+
+   protected Iterable<? extends CookbookVersion> executeConcurrently(final ListeningExecutorService executor,
+         Iterable<String> cookbookNames) {
+      return concat(transform(cookbookNames, new Function<String, Iterable<? extends CookbookVersion>>() {
+
+         @Override
+         public Iterable<? extends CookbookVersion> apply(final String cookbook) {
+            // TODO getting each version could also go parallel
+            Set<String> cookbookVersions = api.listVersionsOfCookbook(cookbook);
+            ListenableFuture<List<CookbookVersion>> futures = allAsList(transform(cookbookVersions,
+                  new Function<String, ListenableFuture<CookbookVersion>>() {
+                     @Override
+                     public ListenableFuture<CookbookVersion> apply(final String version) {
+                        return executor.submit(new Callable<CookbookVersion>() {
+                           @Override
+                           public CookbookVersion call() throws Exception {
+                              return api.getCookbook(cookbook, version);
+                           }
+                        });
+                     }
+                  }
+            ));
+
+            logger.trace(String.format("getting versions of cookbook: %s", cookbook));
+            return getUnchecked(futures);
+         }
+      }));
+   }
+
+}