Merge pull request #61 from kulya/master

Ability to work with environments at chef > 0.10.0
This commit is contained in:
Adrian Cole 2013-02-20 08:26:34 -08:00
commit 57d97e63a2
19 changed files with 1228 additions and 350 deletions

View File

@ -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,33 +35,36 @@ import org.jclouds.http.HttpResponseException;
import org.jclouds.io.Payload;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.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()}.
*/
@ -71,422 +72,365 @@ public interface ChefApi {
/**
* 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 @@ public interface ChefApi {
* 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 @@ public interface ChefApi {
* <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 @@ public interface ChefApi {
/**
* 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 @@ public interface ChefApi {
* <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 @@ public interface ChefApi {
/**
* 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 @@ public interface ChefApi {
* <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 @@ public interface ChefApi {
/**
* 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 @@ public interface ChefApi {
* <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 @@ public interface ChefApi {
/**
* 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 File

@ -67,6 +67,7 @@ public class ChefApiMetadata extends BaseRestApiMetadata {
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 File

@ -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,11 +30,14 @@ import org.jclouds.chef.binders.BindGenerateKeyForClientToJsonPayload;
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;
@ -58,11 +45,14 @@ 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.ParamParser;
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 @@ public interface ChefAsyncApi {
ListenableFuture<Node> getNode(@PathParam("nodename") String nodename);
/**
* @see ChefNode#deleteNode
* @see ChefApi#deleteNode
*/
@Named("node:delete")
@DELETE
@ -521,6 +526,26 @@ public interface ChefAsyncApi {
ListenableFuture<? extends SearchResult<? extends DatabagItem>> searchDatabag(
@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)
*/
@ -528,4 +553,100 @@ public interface ChefAsyncApi {
@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 File

@ -24,9 +24,11 @@ import java.util.List;
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;
@ -170,4 +172,13 @@ public interface ChefService {
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 File

@ -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 File

@ -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 File

@ -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 @@ public class Node {
@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 class Node {
@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 class Node {
return chefEnvironment;
}
@SerializedName("chef_type")
private String _chefType = "node";
@Override
public int hashCode() {
final int prime = 31;

View File

@ -94,7 +94,8 @@ public class SignedHeaderAuth implements HttpRequestFilter {
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 File

@ -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 File

@ -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 File

@ -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 File

@ -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.DeleteAllClientsInList;
import org.jclouds.chef.strategy.DeleteAllNodesInList;
import org.jclouds.chef.strategy.ListClients;
import org.jclouds.chef.strategy.ListCookbookVersions;
import org.jclouds.chef.strategy.ListEnvironments;
import org.jclouds.chef.strategy.ListNodes;
import org.jclouds.chef.strategy.UpdateAutomaticAttributesOnNode;
import org.jclouds.domain.JsonBall;
@ -59,26 +55,25 @@ 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.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 @@ public class BaseChefService implements ChefService {
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 @@ public class BaseChefService implements ChefService {
this.databag = checkNotNull(databag, "databag");
this.bootstrapConfigForGroup = checkNotNull(bootstrapConfigForGroup, "bootstrapConfigForGroup");
this.runListForGroup = checkNotNull(runListForGroup, "runListForGroup");
this.listEnvironments = checkNotNull(listEnvironments, "listEnvironments");
}
@Override
@ -267,4 +268,18 @@ public class BaseChefService implements ChefService {
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 File

@ -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 File

@ -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 File

@ -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 class TransientChefAsyncApi implements ChefAsyncApi {
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 File

@ -20,6 +20,7 @@ package org.jclouds.chef.filters;
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 @@ public class SignedHeaderAuthTest {
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 File

@ -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 File

@ -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 File

@ -42,6 +42,7 @@ import org.jclouds.chef.domain.ChecksumStatus;
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 abstract class BaseChefApiLiveTest<C extends Context> extends BaseChefCon
@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 abstract class BaseChefApiLiveTest<C extends Context> extends BaseChefCon
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 abstract class BaseChefApiLiveTest<C extends Context> extends BaseChefCon
chefApi.deleteNode(PREFIX);
chefApi.deleteRole(PREFIX);
chefApi.deleteDatabag(PREFIX);
chefApi.deleteEnvironment(PREFIX);
try {
Closeables.close(validatorContext, true);
} catch (IOException e) {