Skip to content
Browse files

Added support for chef environment api.

Change formating to jcloud standarts. Added SinceApiVersion annotation to new methods. Removed environmentExists method from api.

Added forgeted serializable name for attributes field of environment
  • Loading branch information...
1 parent 2935480 commit dca619bd8924dcc6e548724ab5adaae6f2981caa @kulya kulya committed Feb 4, 2013
Showing with 1,231 additions and 350 deletions.
  1. +3 −0 .gitignore
  2. +311 −258 core/src/main/java/org/jclouds/chef/ChefApi.java
  3. +1 −0 core/src/main/java/org/jclouds/chef/ChefApiMetadata.java
  4. +140 −19 core/src/main/java/org/jclouds/chef/ChefAsyncApi.java
  5. +11 −0 core/src/main/java/org/jclouds/chef/ChefService.java
  6. +33 −0 core/src/main/java/org/jclouds/chef/binders/EnvironmentName.java
  7. +123 −0 core/src/main/java/org/jclouds/chef/domain/Environment.java
  8. +10 −10 core/src/main/java/org/jclouds/chef/domain/Node.java
  9. +2 −1 core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
  10. +50 −0 core/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10.java
  11. +54 −0 core/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10.java
  12. +40 −0 core/src/main/java/org/jclouds/chef/functions/ParseSearchEnvironmentsFromJson.java
  13. +48 −33 core/src/main/java/org/jclouds/chef/internal/BaseChefService.java
  14. +34 −0 core/src/main/java/org/jclouds/chef/strategy/ListEnvironments.java
  15. +80 −0 core/src/main/java/org/jclouds/chef/strategy/internal/ListEnvironmentsImpl.java
  16. +74 −29 core/src/main/java/org/jclouds/chef/test/TransientChefAsyncApi.java
  17. +11 −0 core/src/test/java/org/jclouds/chef/filters/SignedHeaderAuthTest.java
  18. +72 −0 core/src/test/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10Test.java
  19. +84 −0 core/src/test/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10Test.java
  20. +50 −0 core/src/test/java/org/jclouds/chef/internal/BaseChefApiLiveTest.java
View
3 .gitignore
@@ -3,3 +3,6 @@
.settings
target
test-output
+
+.idea/**
+*.iml
View
569 core/src/main/java/org/jclouds/chef/ChefApi.java
@@ -18,13 +18,11 @@
*/
package org.jclouds.chef;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.List;
-import java.util.Set;
import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.domain.CookbookDefinition;
import org.jclouds.chef.domain.CookbookVersion;
import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.domain.Environment;
import org.jclouds.chef.domain.Node;
import org.jclouds.chef.domain.Resource;
import org.jclouds.chef.domain.Role;
@@ -37,456 +35,402 @@
import org.jclouds.io.Payload;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.SinceApiVersion;
import org.jclouds.rest.binders.BindToJsonPayload;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
/**
* Provides synchronous access to Chef.
* <p/>
- *
+ *
+ * @author Adrian Cole
* @see ChefAsyncApi
* @see <a href="TODO: insert URL of Chef documentation" />
- * @author Adrian Cole
*/
public interface ChefApi {
/**
- *
* Creates a new sandbox. It accepts a list of checksums as input and returns
* the URLs against which to PUT files that need to be uploaded.
- *
- * @param md5s
- * raw md5s; uses {@code Bytes.asList()} and
- * {@code Bytes.toByteArray()} as necessary
+ *
+ * @param md5s raw md5s; uses {@code Bytes.asList()} and
+ * {@code Bytes.toByteArray()} as necessary
* @return The URLs against which to PUT files that need to be uploaded.
*/
UploadSandbox getUploadSandboxForChecksums(Set<List<Byte>> md5s);
/**
- *
* Uploads the given content to the sandbox at the given URI.
- * <p>
+ * <p/>
* The URI must be obtained, after uploading a sandbox, from the
* {@link UploadSandbox#getUri()}.
*/
void uploadContent(URI location, Payload content);
/**
* Confirms if the sandbox is completed or not.
- * <p>
+ * <p/>
* This method should be used after uploading contents to the sandbox.
- *
+ *
* @return The sandbox
*/
Sandbox commitSandbox(String id, boolean isCompleted);
/**
- *
* @return a list of all the cookbook names
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have permission to see the
- * cookbook list.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have permission to see the
+ * cookbook list.
*/
Set<String> listCookbooks();
/**
* Creates or updates (uploads) a cookbook //TODO document
- *
+ *
* @param cookbookName
- * @throws HttpResponseException
- * "409 Conflict" if the cookbook already exists
+ * @throws HttpResponseException "409 Conflict" if the cookbook already exists
*/
CookbookVersion updateCookbook(String cookbookName, String version, CookbookVersion cookbook);
/**
* deletes an existing cookbook.
- *
+ *
* @return last state of the api you deleted or null, if not found
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have Delete rights on the
- * cookbook.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have Delete rights on the
+ * cookbook.
*/
CookbookVersion deleteCookbook(String cookbookName, String version);
/**
- *
* @return the versions of a cookbook or null, if not found
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if the caller is not authorized to view the
- * cookbook.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to view the
+ * cookbook.
*/
Set<String> getVersionsOfCookbook(String cookbookName);
/**
* Returns a description of the cookbook, with links to all of its component
* parts, and the metadata.
- *
+ *
* @return the cookbook or null, if not found
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if the caller is not authorized to view the
- * cookbook.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to view the
+ * cookbook.
*/
CookbookVersion getCookbook(String cookbookName, String version);
/**
* creates a new client
- *
+ *
* @return the private key of the client. You can then use this client name
* and private key to access the Opscode API.
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if the caller is not authorized to create a
- * client.
- * @throws HttpResponseException
- * "409 Conflict" if the client already exists
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to create a
+ * client.
+ * @throws HttpResponseException "409 Conflict" if the client already exists
*/
Client createClient(String name);
/**
* creates a new administrator client
- *
+ *
* @return the private key of the client. You can then use this client name
* and private key to access the Opscode API.
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if the caller is not authorized to create a
- * client.
- * @throws HttpResponseException
- * "409 Conflict" if the client already exists
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to create a
+ * client.
+ * @throws HttpResponseException "409 Conflict" if the client already exists
*/
Client createClient(String name, CreateClientOptions options);
/**
* generate a new key-pair for this client, and return the new private key in
* the response body.
- *
+ *
* @return the new private key
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if the caller is not authorized to modify the
- * client.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to modify the
+ * client.
*/
Client generateKeyForClient(String name);
/**
* @return list of client names.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have rights to list clients.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have rights to list clients.
*/
Set<String> listClients();
/**
- *
* @return true if the specified client name exists.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have rights to view the client.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have rights to view the client.
*/
boolean clientExists(String name);
/**
* deletes an existing client.
- *
+ *
* @return last state of the client you deleted or null, if not found
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have Delete rights on the client.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have Delete rights on the client.
*/
Client deleteClient(String name);
/**
* gets an existing client.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the client.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the client.
*/
Client getClient(String name);
/**
* creates a new node
- *
+ *
* @return //TODO
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if the caller is not authorized to create a
- * node.
- * @throws HttpResponseException
- * "409 Conflict" if the node already exists
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to create a
+ * node.
+ * @throws HttpResponseException "409 Conflict" if the node already exists
*/
void createNode(Node node);
/**
* Creates or updates (uploads) a node //TODO document
- *
- * @param nodeName
- * @throws HttpResponseException
- * "409 Conflict" if the node already exists
+ *
+ * @param node updated node
+ * @throws HttpResponseException "409 Conflict" if the node already exists
*/
Node updateNode(Node node);
/**
* @return list of node names.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have rights to list nodes.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have rights to list nodes.
*/
Set<String> listNodes();
/**
- *
* @return true if the specified node name exists.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have rights to view the node.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have rights to view the node.
*/
boolean nodeExists(String name);
/**
* deletes an existing node.
- *
+ *
* @return last state of the node you deleted or null, if not found
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have Delete rights on the node.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have Delete rights on the node.
*/
Node deleteNode(String name);
/**
* gets an existing node.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the node.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the node.
*/
Node getNode(String name);
/**
* creates a new role
- *
+ *
* @return //TODO
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if the caller is not a recognized user.
- * <p/>
- * "403 Forbidden" if the caller is not authorized to create a
- * role.
- * @throws HttpResponseException
- * "409 Conflict" if the role already exists
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to create a
+ * role.
+ * @throws HttpResponseException "409 Conflict" if the role already exists
*/
void createRole(Role role);
/**
* Creates or updates (uploads) a role //TODO document
- *
+ *
* @param roleName
- * @throws HttpResponseException
- * "409 Conflict" if the role already exists
+ * @throws HttpResponseException "409 Conflict" if the role already exists
*/
Role updateRole(Role role);
/**
* @return list of role names.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have rights to list roles.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have rights to list roles.
*/
Set<String> listRoles();
/**
- *
* @return true if the specified role name exists.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have rights to view the role.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have rights to view the role.
*/
boolean roleExists(String name);
/**
* deletes an existing role.
- *
+ *
* @return last state of the role you deleted or null, if not found
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have Delete rights on the role.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have Delete rights on the role.
*/
Role deleteRole(String name);
/**
* gets an existing role.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the role.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the role.
*/
Role getRole(String name);
/**
* lists databags available to the api
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
Set<String> listDatabags();
/**
* creates a databag.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
void createDatabag(String databagName);
/**
* true is a databag exists
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
boolean databagExists(String databagName);
/**
* Delete a data bag, including its items
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
void deleteDatabag(String databagName);
/**
* Show the items in a data bag.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
Set<String> listDatabagItems(String databagName);
/**
* Create a data bag item in the data bag
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
- * <p/>
- * @throws IllegalStateException
- * if the item already exists
- *
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
+ * <p/>
+ * @throws IllegalStateException if the item already exists
*/
DatabagItem createDatabagItem(String databagName, @BinderParam(BindToJsonPayload.class) DatabagItem node);
/**
* Update (or create if not exists) a data bag item
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
DatabagItem updateDatabagItem(String databagName, DatabagItem item);
/**
* determines if a databag item exists
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
boolean databagItemExists(String databagName, String databagItemId);
/**
* gets an existing databag item.
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
DatabagItem getDatabagItem(String databagName, String databagItemId);
/**
* Delete a data bag item
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
DatabagItem deleteDatabagItem(String databagName, String databagItemId);
@@ -499,12 +443,11 @@
* least 10 seconds at any given time - so if you need to write data and
* immediately query it, you likely need to produce an artificial delay (or
* simply retry until the data is available.)
- *
- * @throws AuthorizationException
- * <p/>
- * "401 Unauthorized" if you are not a recognized user.
- * <p/>
- * "403 Forbidden" if you do not have view rights on the databag.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the databag.
*/
Set<String> listSearchIndexes();
@@ -513,7 +456,7 @@
* <p/>
* Note that without any request parameters this will return all of the data
* within the index.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
@@ -522,7 +465,7 @@
/**
* search all roles that match the given options.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
@@ -534,7 +477,7 @@
* <p/>
* Note that without any request parameters this will return all of the data
* within the index.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
@@ -543,7 +486,7 @@
/**
* search all clients that match the given options.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
@@ -555,7 +498,7 @@
* <p/>
* Note that without any request parameters this will return all of the data
* within the index.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
@@ -564,7 +507,7 @@
/**
* search all nodes that match the given options.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
@@ -576,7 +519,7 @@
* <p/>
* Note that without any request parameters this will return all of the data
* within the index.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
@@ -585,19 +528,129 @@
/**
* search all items in a databag that match the given options.
- *
+ *
* @return The response contains the total number of rows that matched your
* request, the position this result set returns (useful for paging)
* and the rows themselves.
*/
SearchResult<? extends DatabagItem> searchDatabag(String databagName, SearchOptions options);
/**
+ * search all items in a environment that match the given options.
+ *
+ * @return The response contains the total number of rows that matched your
+ * request, the position this result set returns (useful for paging)
+ * and the rows themselves.
+ */
+ @SinceApiVersion("0.10.0")
+ SearchResult<? extends Environment> searchEnvironments();
+
+ /**
+ * search all environments that match the given options.
+ *
+ * @return The response contains the total number of rows that matched your
+ * request, the position this result set returns (useful for paging)
+ * and the rows themselves.
+ */
+ @SinceApiVersion("0.10.0")
+ SearchResult<? extends Environment> searchEnvironments(SearchOptions options);
+
+
+ /**
* Get the contents of the given resource.
- *
- * @param resource
- * The resource to get.
+ *
+ * @param resource The resource to get.
* @return An input stream for the content of the requested resource.
*/
InputStream getResourceContents(Resource resource);
+
+ /**
+ * @return list of environments names.
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have rights to list environments.
+ */
+ @SinceApiVersion("0.10.0")
+ Set<String> listEnvironments();
+
+ /**
+ * creates a new environment
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if the caller is not a recognized user.
+ * <p/>
+ * "403 Forbidden" if the caller is not authorized to create a
+ * client.
+ * @throws HttpResponseException "409 Conflict" if the client already exists
+ */
+ @SinceApiVersion("0.10.0")
+ void createEnvironment(Environment environment);
+
+ /**
+ * Creates or updates (uploads) a environment//TODO document
+ *
+ * @param environment
+ * @throws HttpResponseException "409 Conflict" if the node already exists
+ */
+ @SinceApiVersion("0.10.0")
+ Environment updateEnvironment(Environment environment);
+
+ /**
+ * deletes an existing environment.
+ *
+ * @return last state of the environment you deleted or null, if not found
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have Delete rights on the node.
+ */
+ @SinceApiVersion("0.10.0")
+ Environment deleteEnvironment(String name);
+
+ /**
+ * gets an existing environment.
+ *
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the node.
+ */
+ @SinceApiVersion("0.10.0")
+ Environment getEnvironment(String name);
+
+ /**
+ * gets an environment cookbook list, show only latest cookbook version
+ *
+ * @return List of environment cookbooks
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the node.
+ */
+ @SinceApiVersion("0.10.0")
+ Set<CookbookDefinition> listEnvironmentCookbooks(String environmentName);
+
+ /**
+ * gets an environment cookbook list
+ *
+ * @param environmentname environment name that you looking for
+ * @param numversions how many versions you want to see:
+ * 3 returns 3 latest versions, in descending order (high to low);
+ * all returns all available versions in this environment, in descending order (high to low);
+ * 0 is a valid input that returns an empty array for the versions of each cookbooks.up
+ * @return List of environment cookbooks
+ * @throws AuthorizationException <p/>
+ * "401 Unauthorized" if you are not a recognized user.
+ * <p/>
+ * "403 Forbidden" if you do not have view rights on the node.
+ */
+ @SinceApiVersion("0.10.0")
+ Set<CookbookDefinition> listEnvironmentCookbooks(String environmentname, String numversions);
+
+ @SinceApiVersion("0.10.0")
+ CookbookDefinition getEnvironmentCookbook(String environmentname, String cookbookname);
+
+ @SinceApiVersion("0.10.0")
+ CookbookDefinition getEnvironmentCookbook(String environmentname, String cookbookname, String numversions);
}
View
1 core/src/main/java/org/jclouds/chef/ChefApiMetadata.java
@@ -67,6 +67,7 @@ public static Properties defaultProperties() {
properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.updateNode", MINUTES.toMillis(10) + "");
properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.createRole", MINUTES.toMillis(2) + "");
properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.updateRole", MINUTES.toMillis(10) + "");
+ properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "ChefApi.createEnvironment", MINUTES.toMillis(2) + "");
properties.setProperty(PROPERTY_SESSION_INTERVAL, "1");
properties.setProperty(CHEF_BOOTSTRAP_DATABAG, "bootstrap");
return properties;
View
159 core/src/main/java/org/jclouds/chef/ChefAsyncApi.java
@@ -18,23 +18,7 @@
*/
package org.jclouds.chef;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Named;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.HEAD;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-
+import com.google.common.util.concurrent.ListenableFuture;
import org.jclouds.Constants;
import org.jclouds.Fallbacks.EmptySetOnNotFoundOr404;
import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
@@ -46,23 +30,29 @@
import org.jclouds.chef.binders.BindIsCompletedToJsonPayload;
import org.jclouds.chef.binders.BindNameToJsonPayload;
import org.jclouds.chef.binders.DatabagItemId;
+import org.jclouds.chef.binders.EnvironmentName;
import org.jclouds.chef.binders.NodeName;
import org.jclouds.chef.binders.RoleName;
import org.jclouds.chef.domain.Client;
+import org.jclouds.chef.domain.CookbookDefinition;
import org.jclouds.chef.domain.CookbookVersion;
import org.jclouds.chef.domain.DatabagItem;
+import org.jclouds.chef.domain.Environment;
import org.jclouds.chef.domain.Node;
import org.jclouds.chef.domain.Resource;
import org.jclouds.chef.domain.Role;
import org.jclouds.chef.domain.Sandbox;
import org.jclouds.chef.domain.SearchResult;
import org.jclouds.chef.domain.UploadSandbox;
import org.jclouds.chef.filters.SignedHeaderAuth;
+import org.jclouds.chef.functions.ParseCookbookDefinitionFromJsonv10;
+import org.jclouds.chef.functions.ParseCookbookDefinitionListFromJsonv10;
import org.jclouds.chef.functions.ParseCookbookDefinitionCheckingChefVersion;
import org.jclouds.chef.functions.ParseCookbookVersionsCheckingChefVersion;
import org.jclouds.chef.functions.ParseKeySetFromJson;
import org.jclouds.chef.functions.ParseSearchClientsFromJson;
import org.jclouds.chef.functions.ParseSearchDatabagFromJson;
+import org.jclouds.chef.functions.ParseSearchEnvironmentsFromJson;
import org.jclouds.chef.functions.ParseSearchNodesFromJson;
import org.jclouds.chef.functions.ParseSearchRolesFromJson;
import org.jclouds.chef.functions.UriForResource;
@@ -78,9 +68,24 @@
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.SinceApiVersion;
import org.jclouds.rest.binders.BindToJsonPayload;
-import com.google.common.util.concurrent.ListenableFuture;
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
/**
* Provides asynchronous access to Chef via their REST API.
@@ -270,7 +275,7 @@
ListenableFuture<Node> getNode(@PathParam("nodename") String nodename);
/**
- * @see ChefNode#deleteNode
+ * @see ChefApi#deleteNode
*/
@Named("node:delete")
@DELETE
@@ -522,10 +527,126 @@
@PathParam("databagName") String databagName, SearchOptions options);
/**
+ * @see ChefApi#searchEnvironments() ()
+ */
+ @Named("search:environments")
+ @GET
+ @SinceApiVersion("0.10.0")
+ @Path("/search/environment")
+ @ResponseParser(ParseSearchEnvironmentsFromJson.class)
+ ListenableFuture<? extends SearchResult<? extends Environment>> searchEnvironments();
+
+ /**
+ * @see ChefApi#searchEnvironments(SearchOptions)
+ */
+ @Named("search:environments")
+ @GET
+ @SinceApiVersion("0.10.0")
+ @Path("/search/environment")
+ @ResponseParser(ParseSearchEnvironmentsFromJson.class)
+ ListenableFuture<? extends SearchResult<? extends Environment>> searchEnvironments(SearchOptions options);
+
+ /**
* @see ChefApi#getResourceContents(Resource)
*/
@Named("content:get")
@GET
@Fallback(NullOnNotFoundOr404.class)
ListenableFuture<InputStream> getResourceContents(@EndpointParam(parser = UriForResource.class) Resource resource);
+
+ /**
+ * @see org.jclouds.chef.ChefApi#listEnvironments()
+ */
+ @Named("environment:list")
+ @GET
+ @SinceApiVersion("0.10.0")
+ @Path("/environments")
+ @ResponseParser(ParseKeySetFromJson.class)
+ @Fallback(EmptySetOnNotFoundOr404.class)
+ ListenableFuture<Set<String>> listEnvironments();
+
+ /**
+ * @see ChefApi#createEnvironment(Environment)
+ */
+ @Named("environment:create")
+ @POST
+ @SinceApiVersion("0.10.0")
+ @Path("/environments")
+ ListenableFuture<Void> createEnvironment(@BinderParam(BindToJsonPayload.class) Environment environment);
+
+ /**
+ * @see ChefApi#updateEnvironment(Environment)
+ */
+ @Named("environment:update")
+ @PUT
+ @SinceApiVersion("0.10.0")
+ @Path("/environments/{environmentname}")
+ ListenableFuture<Environment> updateEnvironment(@PathParam("environmentname") @ParamParser(EnvironmentName.class)
+ @BinderParam(BindToJsonPayload.class) Environment environment);
+
+ /**
+ * @see ChefApi#getEnvironment(String)
+ */
+ @Named("environment:get")
+ @GET
+ @SinceApiVersion("0.10.0")
+ @Path("/environments/{environmentname}")
+ @Fallback(NullOnNotFoundOr404.class)
+ ListenableFuture<Environment> getEnvironment(@PathParam("environmentname") String environmentname);
+
+ /**
+ * @see ChefApi#deleteEnvironment(String)
+ */
+ @Named("environment:delete")
+ @DELETE
+ @SinceApiVersion("0.10.0")
+ @Path("/environments/{environmentname}")
+ @Fallback(NullOnNotFoundOr404.class)
+ ListenableFuture<Environment> deleteEnvironment(@PathParam("environmentname") String environmentname);
+
+ /**
+ * @see ChefApi#listEnvironmentCookbooks(String)
+ */
+ @Named("environment:cookbooklist")
+ @GET
+ @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
+ @SinceApiVersion("0.10.0")
+ @Path("/environments/{environmentname}/cookbooks")
+ @Fallback(NullOnNotFoundOr404.class)
+ ListenableFuture<Set<CookbookDefinition>> listEnvironmentCookbooks(@PathParam("environmentname") String environmentname);
+
+ /**
+ * @see ChefApi#listEnvironmentCookbooks(String)
+ */
+ @Named("environment:cookbooklist")
+ @GET
+ @ResponseParser(ParseCookbookDefinitionListFromJsonv10.class)
+ @SinceApiVersion("0.10.0")
+ @Path("/environments/{environmentname}/cookbooks?num_versions={numversions}")
+ @Fallback(NullOnNotFoundOr404.class)
+ ListenableFuture<Set<CookbookDefinition>> listEnvironmentCookbooks(@PathParam("environmentname") String environmentname,
+ @PathParam("numversions") String numversions);
+
+ /**
+ * @see ChefApi#getEnvironmentCookbook(String, String)
+ */
+ @Named("environment:cookbook")
+ @GET
+ @ResponseParser(ParseCookbookDefinitionFromJsonv10.class)
+ @SinceApiVersion("0.10.0")
+ @Path("/environments/{environmentname}/cookbooks/{cookbookname}")
+ ListenableFuture<CookbookDefinition> getEnvironmentCookbook(@PathParam("environmentname") String environmentname,
+ @PathParam("cookbookname") String cookbookname);
+
+ /**
+ * @see ChefApi#getEnvironmentCookbook(String, String, String)
+ */
+ @Named("environment:cookbook")
+ @GET
+ @ResponseParser(ParseCookbookDefinitionFromJsonv10.class)
+ @SinceApiVersion("0.10.0")
+ @Path("/environments/{environmentname}/cookbooks/{cookbookname}?num_versions={numversions}")
+ ListenableFuture<CookbookDefinition> getEnvironmentCookbook(@PathParam("environmentname") String environmentname,
+ @PathParam("cookbookname") String cookbookname,
+ @PathParam("numversions") String numversions);
}
View
11 core/src/main/java/org/jclouds/chef/ChefService.java
@@ -24,9 +24,11 @@
import org.jclouds.chef.domain.Client;
import org.jclouds.chef.domain.CookbookVersion;
+import org.jclouds.chef.domain.Environment;
import org.jclouds.chef.domain.Node;
import org.jclouds.chef.internal.BaseChefService;
import org.jclouds.domain.JsonBall;
+import org.jclouds.rest.annotations.SinceApiVersion;
import org.jclouds.scriptbuilder.domain.Statement;
import com.google.common.base.Predicate;
@@ -154,4 +156,13 @@
Iterable<? extends CookbookVersion> listCookbookVersionsNamed(Iterable<String> cookbookNames);
void updateAutomaticAttributesOnNode(String nodeName);
+
+ @SinceApiVersion("0.10.0")
+ Iterable<? extends Environment> listEnvironments();
+
+ @SinceApiVersion("0.10.0")
+ Iterable<? extends Environment> listEnvironmentsMatching(Predicate<String> environmentNameSelector);
+
+ @SinceApiVersion("0.10.0")
+ Iterable<? extends Environment> listEnvironmentsNamed(Iterable<String> names);
}
View
33 core/src/main/java/org/jclouds/chef/binders/EnvironmentName.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jclouds.chef.binders;
+
+import com.google.common.base.Function;
+import org.jclouds.chef.domain.Environment;
+
+import javax.inject.Singleton;
+
+@Singleton
+public class EnvironmentName implements Function<Object, String> {
+
+ @Override
+ public String apply(Object input) {
+ return ((Environment) input).getName();
+ }
+}
View
123 core/src/main/java/org/jclouds/chef/domain/Environment.java
@@ -0,0 +1,123 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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 com.google.common.collect.Maps;
+import com.google.gson.annotations.SerializedName;
+import org.jclouds.domain.JsonBall;
+
+import java.util.Map;
+
+public class Environment {
+
+ private String name;
+ @SerializedName("default_attributes")
+ private Map<String, JsonBall> attributes = Maps.newLinkedHashMap();
+ @SerializedName("override_attributes")
+ private Map<String, JsonBall> overrideAttributes = Maps.newLinkedHashMap();
+ private String description = "";
+ @SerializedName("cookbook_versions")
+ private Map<String, String> cookbookVersions = Maps.newLinkedHashMap();
+ // internal
+ @SerializedName("json_class")
+ private String _jsonClass = "Chef::Environment";
+ @SerializedName("chef_type")
+ private String _chefType = "environment";
+
+ public Environment(String name, Map<String, JsonBall> attributes, Map<String, JsonBall> overrideAttributes,
+ String description, Map<String, String> cookbookVersions) {
+ this.name = name;
+ this.attributes.putAll(attributes);
+ this.overrideAttributes.putAll(overrideAttributes);
+ this.description = description;
+ this.cookbookVersions.putAll(cookbookVersions);
+ }
+
+ public Environment(String name, String description) {
+ this.name = name;
+ this.description = description;
+ }
+
+ public Environment(String name) {
+ this(name, null);
+ }
+
+ // hidden but needs to be here for json deserialization to work
+ Environment() {
+ }
+
+ 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 "[" +
+ "name='" + name + '\'' +
+ ", attributes=" + attributes +
+ ", overrideAttributes=" + overrideAttributes +
+ ", description='" + description + '\'' +
+ ", cookbookVersions=" + cookbookVersions +
+ ']';
+ }
+}
View
20 core/src/main/java/org/jclouds/chef/domain/Node.java
@@ -18,16 +18,15 @@
*/
package org.jclouds.chef.domain;
-import java.util.List;
-import java.util.Map;
-
-import org.jclouds.domain.JsonBall;
-import org.jclouds.javax.annotation.Nullable;
-
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.annotations.SerializedName;
+import org.jclouds.domain.JsonBall;
+import org.jclouds.javax.annotation.Nullable;
+
+import java.util.List;
+import java.util.Map;
/**
* Sandbox object.
@@ -56,6 +55,10 @@
@SerializedName("json_class")
private String _jsonClass = "Chef::Node";
+
+ @SerializedName("chef_type")
+ private String _chefType = "node";
+
public Node(String name, Map<String, JsonBall> normal, Map<String, JsonBall> override,
Map<String, JsonBall> defaultA, Map<String, JsonBall> automatic, Iterable<String> runList) {
this(name, normal, override, defaultA, automatic, runList, null);
@@ -79,7 +82,7 @@ public Node(String name, Map<String, JsonBall> normal, Map<String, JsonBall> ove
@Override
public String toString() {
return "Node [name=" + name + ", runList=" + runList + ", normal=" + normal + ", default=" + defaultA
- + ", override=" + override + ", automatic=" + automatic + "]";
+ + ", override=" + override + ", chefEnvironment=" + chefEnvironment + ", automatic=" + automatic + "]";
}
public Node(String name, Iterable<String> runList) {
@@ -131,9 +134,6 @@ public String getChefEnvironment() {
return chefEnvironment;
}
- @SerializedName("chef_type")
- private String _chefType = "node";
-
@Override
public int hashCode() {
final int prime = 31;
View
3 core/src/main/java/org/jclouds/chef/filters/SignedHeaderAuth.java
@@ -94,7 +94,8 @@ public SignedHeaderAuth(SignatureWire signatureWire, @org.jclouds.location.Provi
this.utils = utils;
}
- public HttpRequest filter(HttpRequest request) throws HttpException {
+ 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);
View
50 core/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.
+ *
+ * @author Alexandr Kulik
+ */
+@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) {
+ return parser.apply(response).values().iterator().next();
+ }
+}
View
54 core/src/main/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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 com.google.common.collect.Sets;
+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;
+
+/**
+ * Parses the cookbook versions in a Chef Server >= 0.10.8.
+ *
+ * @author Alexandr Kulik
+ */
+@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) {
+ return Sets.newLinkedHashSet(parser.apply(response).values());
+ }
+}
View
40 core/src/main/java/org/jclouds/chef/functions/ParseSearchEnvironmentsFromJson.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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;
+
+/**
+ * @author Alex Kulik
+ */
+@Singleton
+public class ParseSearchEnvironmentsFromJson extends ParseSearchResultFromJson<Environment> {
+
+ // TODO add generic json parser detector
+
+ @Inject
+ ParseSearchEnvironmentsFromJson(ParseJson<Response<Environment>> json) {
+ super(json);
+ }
+
+}
View
81 core/src/main/java/org/jclouds/chef/internal/BaseChefService.java
@@ -18,26 +18,21 @@
*/
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 javax.annotation.Resource;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.InputSupplier;
import org.jclouds.chef.ChefContext;
import org.jclouds.chef.ChefService;
import org.jclouds.chef.config.ChefProperties;
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;
@@ -48,6 +43,7 @@
import org.jclouds.chef.strategy.DeleteAllNodesInList;
import org.jclouds.chef.strategy.ListClients;
import org.jclouds.chef.strategy.ListCookbookVersions;
+import org.jclouds.chef.strategy.ListEnvironments;
import org.jclouds.chef.strategy.ListNodes;
import org.jclouds.chef.strategy.UpdateAutomaticAttributesOnNode;
import org.jclouds.domain.JsonBall;
@@ -59,26 +55,25 @@
import org.jclouds.logging.Logger;
import org.jclouds.scriptbuilder.domain.Statement;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.io.ByteStreams;
-import com.google.common.io.InputSupplier;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.PrivateKey;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.chef.config.ChefProperties.CHEF_BOOTSTRAP_DATABAG;
/**
- *
* @author Adrian Cole
*/
@Singleton
public class BaseChefService implements ChefService {
- @Resource
- @Named(ChefProperties.CHEF_LOGGER)
- protected Logger logger = Logger.NULL;
-
private final ChefContext chefContext;
private final CleanupStaleNodesAndClients cleanupStaleNodesAndClients;
private final CreateNodeAndPopulateAutomaticAttributes createNodeAndPopulateAutomaticAttributes;
@@ -93,15 +88,20 @@
private final BootstrapConfigForGroup bootstrapConfigForGroup;
private final RunListForGroup runListForGroup;
private final ListCookbookVersions listCookbookVersions;
+ private final ListEnvironments listEnvironments;
+ @Resource
+ @Named(ChefProperties.CHEF_LOGGER)
+ protected Logger logger = Logger.NULL;
@Inject
protected BaseChefService(ChefContext chefContext, 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) {
+ 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) {
this.chefContext = checkNotNull(chefContext, "chefContext");
this.cleanupStaleNodesAndClients = checkNotNull(cleanupStaleNodesAndClients, "cleanupStaleNodesAndClients");
this.createNodeAndPopulateAutomaticAttributes = checkNotNull(createNodeAndPopulateAutomaticAttributes,
@@ -118,6 +118,7 @@ protected BaseChefService(ChefContext chefContext, CleanupStaleNodesAndClients c
this.databag = checkNotNull(databag, "databag");
this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup");
this.runListForGroup = checkNotNull(runListForGroup, "runListForGroup");
+ this.listEnvironments = checkNotNull(listEnvironments, "listEnvironments");
}
@Override
@@ -262,4 +263,18 @@ String buildBootstrapConfiguration(Iterable<String> runList, Optional<JsonBall>
return json.toJson(bootstrapConfig);
}
+ @Override
+ public Iterable<? extends Environment> listEnvironments() {
+ return listEnvironments.execute();
+ }
+
+ @Override
+ public Iterable<? extends Environment> listEnvironmentsMatching(Predicate<String> environmentNameSelector) {
+ return listEnvironments.execute(environmentNameSelector);
+ }
+
+ @Override
+ public Iterable<? extends Environment> listEnvironmentsNamed(Iterable<String> names) {
+ return listEnvironments.execute(names);
+ }
}
View
34 core/src/main/java/org/jclouds/chef/strategy/ListEnvironments.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.common.base.Predicate;
+import com.google.inject.ImplementedBy;
+import org.jclouds.chef.domain.Environment;
+import org.jclouds.chef.strategy.internal.ListEnvironmentsImpl;
+
+@ImplementedBy(ListEnvironmentsImpl.class)
+public interface ListEnvironments {
+
+ Iterable<? extends Environment> execute();
+
+ Iterable<? extends Environment> execute(Predicate<String> environmentNameSelector);
+
+ Iterable<? extends Environment> execute(Iterable<String> toGet);
+}
View
80 core/src/main/java/org/jclouds/chef/strategy/internal/ListEnvironmentsImpl.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.base.Predicate;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.inject.Inject;
+import org.jclouds.Constants;
+import org.jclouds.chef.ChefApi;
+import org.jclouds.chef.ChefAsyncApi;
+import org.jclouds.chef.config.ChefProperties;
+import org.jclouds.chef.domain.Environment;
+import org.jclouds.chef.strategy.ListEnvironments;
+import org.jclouds.logging.Logger;
+
+import javax.annotation.Resource;
+import javax.inject.Named;
+
+import static com.google.common.collect.Iterables.filter;
+import static org.jclouds.concurrent.FutureIterables.transformParallel;
+
+
+public class ListEnvironmentsImpl implements ListEnvironments {
+
+ protected final ChefApi chefApi;
+ protected final ChefAsyncApi chefAsyncApi;
+ protected final ListeningExecutorService userExecutor;
+ @Resource
+ @Named(ChefProperties.CHEF_LOGGER)
+ protected Logger logger = Logger.NULL;
+ @Inject(optional = true)
+ @Named(Constants.PROPERTY_REQUEST_TIMEOUT)
+ protected Long maxTime;
+
+ @Inject
+ ListEnvironmentsImpl(@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, ChefApi getAllNode,
+ ChefAsyncApi ablobstore) {
+ this.userExecutor = userExecutor;
+ this.chefAsyncApi = ablobstore;
+ this.chefApi = getAllNode;
+ }
+
+ @Override
+ public Iterable<? extends Environment> execute() {
+ return execute(chefApi.listEnvironments());
+ }
+
+ @Override
+ public Iterable<? extends Environment> execute(Predicate<String> environmentNameSelector) {
+ return execute(filter(chefApi.listEnvironments(), environmentNameSelector));
+ }
+
+ @Override
+ public Iterable<? extends Environment> execute(Iterable<String> toGet) {
+ return transformParallel(toGet, new Function<String, ListenableFuture<? extends Environment>>() {
+ @Override
+ public ListenableFuture<? extends Environment> apply(String from) {
+ return chefAsyncApi.getEnvironment(from);
+ }
+ }, userExecutor, maxTime, logger, "getting environments");
+ }
+}
View
103 core/src/main/java/org/jclouds/chef/test/TransientChefAsyncApi.java
@@ -18,46 +18,37 @@
*/
package org.jclouds.chef.test;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Throwables.propagate;
-import static com.google.common.collect.Iterables.transform;
-import static com.google.common.collect.Sets.newLinkedHashSet;
-import static com.google.common.util.concurrent.Futures.immediateFuture;
-import static com.google.common.util.concurrent.Futures.transform;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
import org.jclouds.Constants;
import org.jclouds.blobstore.LocalAsyncBlobStore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
import org.jclouds.chef.ChefAsyncApi;
-import org.jclouds.chef.domain.Client;
-import org.jclouds.chef.domain.CookbookVersion;
-import org.jclouds.chef.domain.DatabagItem;
-import org.jclouds.chef.domain.Node;
-import org.jclouds.chef.domain.Resource;
-import org.jclouds.chef.domain.Role;
-import org.jclouds.chef.domain.Sandbox;
-import org.jclouds.chef.domain.SearchResult;
-import org.jclouds.chef.domain.UploadSandbox;
+import org.jclouds.chef.domain.*;
import org.jclouds.chef.options.CreateClientOptions;
import org.jclouds.chef.options.SearchOptions;
import org.jclouds.io.Payload;
import org.jclouds.util.Strings2;
-import com.google.common.base.Function;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.ws.rs.PathParam;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Sets.newLinkedHashSet;
+import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static com.google.common.util.concurrent.Futures.transform;
/**
* In-memory chef simulator.
@@ -352,4 +343,58 @@ public Void apply(Boolean input) {
throw new UnsupportedOperationException();
}
+ @Override
+ public ListenableFuture<Set<String>> listEnvironments() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Void> createEnvironment(Environment environment) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Environment> deleteEnvironment(String environmentname) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Environment> getEnvironment(String environmentname) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Environment> updateEnvironment(Environment environment) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Set<CookbookDefinition>> listEnvironmentCookbooks(String environmentname) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<Set<CookbookDefinition>> listEnvironmentCookbooks(String environmentname, String numversions) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<CookbookDefinition> getEnvironmentCookbook(String environmentname, String cookbookname) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<CookbookDefinition> getEnvironmentCookbook(String environmentname, String cookbookname, String numversions) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<? extends SearchResult<? extends Environment>> searchEnvironments() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListenableFuture<? extends SearchResult<? extends Environment>> searchEnvironments(SearchOptions options) {
+ throw new UnsupportedOperationException();
+ }
}
View
11 core/src/test/java/org/jclouds/chef/filters/SignedHeaderAuthTest.java
@@ -20,6 +20,7 @@
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertEqualsNoOrder;
+import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.security.PrivateKey;
@@ -173,6 +174,16 @@ void shouldNotChokeWhenSigningARequestForAResourceWithALongName() {
signing_obj.filter(request);
}
+ @Test
+ void shouldReplacePercentage3FWithQuestionMarkAtUrl() {
+ StringBuilder path = new StringBuilder("nodes/");
+ path.append("test/cookbooks/myCookBook%3Fnum_versions=5");
+ HttpRequest request = HttpRequest.builder().method(HttpMethod.GET)
+ .endpoint("http://localhost/" + path.toString()).payload(BODY).build();
+ request = signing_obj.filter(request);
+ assertTrue(request.getRequestLine().contains("?num_versions=5"));
+ }
+
private SignedHeaderAuth signing_obj;
/**
View
72 core/src/test/java/org/jclouds/chef/functions/ParseCookbookDefinitionFromJsonv10Test.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.jclouds.chef.ChefAsyncApi;
+import org.jclouds.chef.config.ChefParserModule;
+import org.jclouds.chef.domain.CookbookDefinition;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static org.testng.Assert.assertEquals;
+
+@Test(groups = {"unit"}, singleThreaded = true)
+public class ParseCookbookDefinitionFromJsonv10Test {
+
+ private ParseCookbookDefinitionFromJsonv10 handler;
+
+ @BeforeTest
+ protected void setUpInjector() throws IOException {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefAsyncApi.VERSION);
+ }
+ }, new ChefParserModule(), new GsonModule());
+
+ handler = injector.getInstance(ParseCookbookDefinitionFromJsonv10.class);
+ }
+
+ public void testCookbokDefinitionParsing() throws URISyntaxException {
+ assertEquals(handler.apply(HttpResponse
+ .builder()
+ .statusCode(200)
+ .message("ok")
+ .payload(
+ "{" + "\"apache2\" => {" + "\"url\" => \"http://localhost:4000/cookbooks/apache2\","
+ + "\"versions\" => [" + "{\"url\" => \"http://localhost:4000/cookbooks/apache2/5.1.0\","
+ + "\"version\" => \"5.1.0\"},"
+ + "{\"url\" => \"http://localhost:4000/cookbooks/apache2/4.2.0\","
+ + "\"version\" => \"4.2.0\"}" + "]" + "}" + "}").build()),
+ new CookbookDefinition(new URI("http://localhost:4000/cookbooks/apache2"),
+ ImmutableSet.of(new CookbookDefinition.Version(new URI("http://localhost:4000/cookbooks/apache2/5.1.0"), "5.1.0"),
+ new CookbookDefinition.Version(new URI("http://localhost:4000/cookbooks/apache2/4.2.0"), "4.2.0"))));
+ }
+}
View
84 .../src/test/java/org/jclouds/chef/functions/ParseCookbookDefinitionListFromJsonv10Test.java
@@ -0,0 +1,84 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.jclouds.chef.ChefAsyncApi;
+import org.jclouds.chef.config.ChefParserModule;
+import org.jclouds.chef.domain.CookbookDefinition;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.json.config.GsonModule;
+import org.jclouds.rest.annotations.ApiVersion;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static org.testng.Assert.assertEquals;
+
+@Test(groups = {"unit"}, singleThreaded = true)
+public class ParseCookbookDefinitionListFromJsonv10Test {
+
+ private ParseCookbookDefinitionListFromJsonv10 handler;
+
+ @BeforeTest
+ protected void setUpInjector() throws IOException {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(String.class).annotatedWith(ApiVersion.class).toInstance(ChefAsyncApi.VERSION);
+ }
+ }, new ChefParserModule(), new GsonModule());
+
+ handler = injector.getInstance(ParseCookbookDefinitionListFromJsonv10.class);
+ }
+
+ public void testCookbokDefinitionListParsing() throws URISyntaxException {
+ assertEquals(handler.apply(HttpResponse
+ .builder()
+ .statusCode(200)
+ .message("ok")
+ .payload(
+ "{" + "\"apache2\" => {" + "\"url\" => \"http://localhost:4000/cookbooks/apache2\","
+ + "\"versions\" => [" + "{\"url\" => \"http://localhost:4000/cookbooks/apache2/5.1.0\","
+ + "\"version\" => \"5.1.0\"},"
+ + "{\"url\" => \"http://localhost:4000/cookbooks/apache2/4.2.0\","
+ + "\"version\" => \"4.2.0\"}" + "]" + "},"
+ + "\"nginx\" => {"
+ + "\"url\" => \"http://localhost:4000/cookbooks/nginx\","
+ + "\"versions\" => ["
+ + "{\"url\" => \"http://localhost:4000/cookbooks/nginx/1.0.0\","
+ + "\"version\" => \"1.0.0\"},"
+ + "{\"url\" => \"http://localhost:4000/cookbooks/nginx/0.3.0\","
+ + "\"version\" => \"0.3.0\"}"
+ + "]}" +
+ "}").build()),
+ ImmutableSet.of(new CookbookDefinition(new URI("http://localhost:4000/cookbooks/apache2"),
+ ImmutableSet.of(new CookbookDefinition.Version(new URI("http://localhost:4000/cookbooks/apache2/5.1.0"), "5.1.0"),
+ new CookbookDefinition.Version(new URI("http://localhost:4000/cookbooks/apache2/4.2.0"), "4.2.0"))),
+ new CookbookDefinition(new URI("http://localhost:4000/cookbooks/nginx"),
+ ImmutableSet.of(new CookbookDefinition.Version(new URI("http://localhost:4000/cookbooks/nginx/1.0.0"), "1.0.0"),
+ new CookbookDefinition.Version(new URI("http://localhost:4000/cookbooks/nginx/0.3.0"), "0.3.0")))));
+ }
+}
View
50 core/src/test/java/org/jclouds/chef/internal/BaseChefApiLiveTest.java
@@ -42,6 +42,7 @@
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.domain.Resource;
import org.jclouds.chef.domain.Role;
@@ -202,18 +203,21 @@ public void testValidatorCannotDeleteClient() throws Exception {
@Test
public void testValidatorCreateClient() throws Exception {
+ chefApi.deleteClient(VALIDATOR_PREFIX);
String credential = Pems.pem(validatorClient.createClient(VALIDATOR_PREFIX).getPrivateKey());
assertClientCreated(VALIDATOR_PREFIX, credential);
}
@Test
public void testCreateClient() throws Exception {
+ chefApi.deleteClient(PREFIX);
String credential = Pems.pem(chefApi.createClient(PREFIX).getPrivateKey());
assertClientCreated(PREFIX, credential);
}
@Test
public void testCreateAdminClient() throws Exception {
+ chefApi.deleteClient(ADMIN_PREFIX);
String credential = Pems.pem(chefApi.createClient(ADMIN_PREFIX, CreateClientOptions.Builder.admin())
.getPrivateKey());
assertClientCreated(ADMIN_PREFIX, credential);
@@ -464,6 +468,51 @@ public void testSearchDatabagNotFound() throws Exception {
assertNotNull(results);
}
+ @Test
+ public void testCreateEnvironment() {
+ chefApi.deleteEnvironment(PREFIX);
+ chefApi.createEnvironment(new Environment(PREFIX, PREFIX));
+ Environment env = chefApi.getEnvironment(PREFIX);
+ assertNotNull(env);
+ assertEquals(env.getName(), PREFIX);
+ assertEquals(env.getDescription(), PREFIX);
+ }
+
+ @Test(dependsOnMethods = "testCreateEnvironment")
+ public void testListEnvironment() {
+ Set<String> envList = chefApi.listEnvironments();
+ assertNotNull(envList);
+ assertTrue(envList.contains(PREFIX));
+ }
+
+ @Test(dependsOnMethods = "testCreateEnvironment")
+ public void testSearchEnvironments() throws Exception {
+ SearchResult<? extends Environment> results = chefApi.searchEnvironments();
+ assertNotNull(results);
+ }
+
+ @Test(dependsOnMethods = {"testListSearchIndexes", "testCreateEnvironment"})
+ public void testSearchEnvironmentsWithOptions() throws Exception {
+ Predicate<SearchOptions> waitForIndex = retry(new Predicate<SearchOptions>() {
+ @Override
+ public boolean apply(SearchOptions input) {
+ SearchResult<? extends Environment> results = chefApi.searchEnvironments(input);
+ assertNotNull(results);
+ if (results.size() > 0) {
+ assertEquals(results.size(), 1);
+ assertEquals(results.iterator().next().getName(), PREFIX);
+ return true;
+ } else {
+ // The index may still not be populated
+ return false;
+ }
+ }
+ }, maxWaitForIndexInMs, 5000L, MILLISECONDS);
+
+ SearchOptions options = SearchOptions.Builder.query("name:" + PREFIX);
+ assertTrue(waitForIndex.apply(options));
+ }
+
@AfterClass(groups = { "live", "integration" })
@Override
public void tearDownContext() {
@@ -473,6 +522,7 @@ public void tearDownContext() {
chefApi.deleteNode(PREFIX);