Issue 191, Issue 273 refactored opscode api calls into a separate module

This commit is contained in:
Adrian Cole 2010-06-06 18:19:16 -07:00
parent 551a3639b5
commit 18e9294b89
30 changed files with 1950 additions and 620 deletions

View File

@ -0,0 +1,92 @@
#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 ${package}.handlers;
import java.io.IOException;
import javax.annotation.Resource;
import javax.inject.Singleton;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.logging.Logger;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.Utils;
import com.google.common.base.Throwables;
import com.google.common.io.Closeables;
/**
* This will parse and set an appropriate exception on the command object.
*
* @author Adrian Cole
*
*/
@Singleton
public class ${providerName}ErrorHandler implements HttpErrorHandler {
@Resource
protected Logger logger = Logger.NULL;
public void handleError(HttpCommand command, HttpResponse response) {
// it is important to always read fully and close streams
String message = parseMessage(response);
Exception exception = message != null ? new HttpResponseException(command, response, message)
: new HttpResponseException(command, response);
try {
message = message != null ? message : String.format("%s -> %s", command.getRequest()
.getRequestLine(), response.getStatusLine());
switch (response.getStatusCode()) {
case 401:
case 403:
exception = new AuthorizationException(message, exception);
break;
case 404:
if (!command.getRequest().getMethod().equals("DELETE")) {
exception = new ResourceNotFoundException(message, exception);
}
break;
}
} finally {
Closeables.closeQuietly(response.getContent());
command.setException(exception);
}
}
public String parseMessage(HttpResponse response) {
if (response.getContent() == null)
return null;
try {
return Utils.toStringAndClose(response.getContent());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
response.getContent().close();
} catch (IOException e) {
Throwables.propagate(e);
}
}
}
}

View File

@ -60,9 +60,14 @@
</repositories>
<properties>
<jclouds.test.user>user</jclouds.test.user>
<jclouds.test.key>key</jclouds.test.key>
<jclouds.chef.identity>chef-validator</jclouds.chef.identity>
<jclouds.chef.rsa-key>/etc/chef/validation.pem</jclouds.chef.rsa-key>
<jclouds.chef.endpoint>http://localhost:4000</jclouds.chef.endpoint>
<jclouds.test.user>${jclouds.chef.identity}</jclouds.test.user>
<jclouds.test.key>${jclouds.chef.rsa-key}</jclouds.test.key>
<jclouds.test.endpoint>${jclouds.chef.endpoint}</jclouds.test.endpoint>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>

View File

@ -37,24 +37,15 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.chef.binders.BindClientnameToJsonPayload;
import org.jclouds.chef.binders.BindGenerateKeyForClientToJsonPayload;
import org.jclouds.chef.domain.Organization;
import org.jclouds.chef.domain.User;
import org.jclouds.chef.filters.SignedHeaderAuth;
import org.jclouds.chef.functions.OrganizationName;
import org.jclouds.chef.functions.ParseKeyFromJson;
import org.jclouds.chef.functions.ParseKeySetFromJson;
import org.jclouds.chef.functions.ParseOrganizationFromJson;
import org.jclouds.chef.functions.ParseUserFromJson;
import org.jclouds.chef.functions.Username;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.binders.BindToJsonPayload;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
@ -72,116 +63,52 @@ import com.google.common.util.concurrent.ListenableFuture;
@Consumes(MediaType.APPLICATION_JSON)
public interface ChefAsyncClient {
/**
* @see ChefClient#createClientInOrganization
* @see ChefCookbooks#listCookbooks
*/
@GET
@Path("cookbooks")
ListenableFuture<String> listCookbooks();
/**
* @see ChefClient#createClientanization
*/
@POST
@Path("/organizations/{orgname}/clients")
@Path("clients")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> createClientInOrg(@PathParam("orgname") String orgname,
ListenableFuture<String> createClient(
@BinderParam(BindClientnameToJsonPayload.class) String clientname);
/**
* @see ChefClient#generateKeyForClientInOrg
* @see ChefClient#generateKeyForClient
*/
@PUT
@Path("/organizations/{orgname}/clients/{clientname}")
@Path("clients/{clientname}")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> generateKeyForClientInOrg(
@PathParam("orgname") String orgname,
ListenableFuture<String> generateKeyForClient(
@PathParam("clientname") @BinderParam(BindGenerateKeyForClientToJsonPayload.class) String clientname);
/**
* @see ChefClient#clientExistsInOrg
* @see ChefClient#clientExists
*/
@HEAD
@Path("/organizations/{orgname}/clients/{clientname}")
@Path("clients/{clientname}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> clientExistsInOrg(@PathParam("orgname") String orgname,
@PathParam("clientname") String clientname);
ListenableFuture<Boolean> clientExists(@PathParam("clientname") String clientname);
/**
* @see ChefClient#deleteClientInOrg
* @see ChefClient#deleteClient
*/
@DELETE
@Path("/organizations/{orgname}/clients/{clientname}")
@Path("clients/{clientname}")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
ListenableFuture<Void> deleteClientInOrg(@PathParam("orgname") String orgname,
@PathParam("clientname") String clientname);
ListenableFuture<Void> deleteClient(@PathParam("clientname") String clientname);
/**
* @see ChefClient#createClientInOrganization
* @see ChefClient#listClients
*/
@GET
@Path("/organizations/{orgname}/clients")
@Path("clients")
@ResponseParser(ParseKeySetFromJson.class)
ListenableFuture<Set<String>> listClientsInOrg(@PathParam("orgname") String orgname);
/**
* @see ChefClient#createUser
*/
@POST
@Path("/users")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> createUser(@BinderParam(BindToJsonPayload.class) User user);
/**
* @see ChefClient#updateUser
*/
@PUT
@Path("/users/{username}")
@ResponseParser(ParseUserFromJson.class)
ListenableFuture<User> updateUser(
@PathParam("username") @ParamParser(Username.class) @BinderParam(BindToJsonPayload.class) User user);
/**
* @see ChefClient#getUser
*/
@GET
@Path("/users/{username}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@ResponseParser(ParseUserFromJson.class)
ListenableFuture<User> getUser(@PathParam("username") String username);
/**
* @see ChefClient#deleteUser
*/
@DELETE
@Path("/users/{username}")
@ResponseParser(ParseUserFromJson.class)
ListenableFuture<User> deleteUser(@PathParam("username") String username);
/**
* @see ChefClient#createOrg
*/
@POST
@Path("/organizations")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> createOrg(@BinderParam(BindToJsonPayload.class) Organization org);
/**
* @see ChefClient#updateOrg
*/
@PUT
@Path("/organizations/{orgname}")
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> updateOrg(
@PathParam("orgname") @ParamParser(OrganizationName.class) @BinderParam(BindToJsonPayload.class) Organization org);
/**
* @see ChefClient#getOrg
*/
@GET
@Path("/organizations/{orgname}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> getOrg(@PathParam("orgname") String orgname);
/**
* @see ChefClient#deleteOrg
*/
@DELETE
@Path("/organizations/{orgname}")
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> deleteOrg(@PathParam("orgname") String orgname);
ListenableFuture<Set<String>> listClients();
}

View File

@ -44,12 +44,9 @@ package org.jclouds.chef;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.chef.domain.Organization;
import org.jclouds.chef.domain.User;
import org.jclouds.concurrent.Timeout;
import org.jclouds.http.HttpResponseException;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
/**
* Provides synchronous access to Chef.
@ -61,6 +58,7 @@ import org.jclouds.rest.ResourceNotFoundException;
*/
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface ChefClient {
String listCookbooks();
/**
* creates a new client
@ -75,110 +73,16 @@ public interface ChefClient {
* @throws HttpResponseException
* "409 Conflict" if the client already exists
*/
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
String createClientInOrg(String orgname, String name);
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
String createClient(String name);
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
String generateKeyForClientInOrg(String orgname, String name);
String generateKeyForClient(String name);
Set<String> listClientsInOrg(String orgname);
Set<String> listClients();
boolean clientExistsInOrg(String orgname, String name);
boolean clientExists(String name);
void deleteClientInOrg(String orgname, String name);
void deleteClient(String name);
/**
* creates a new user
*
* @return the private key of the user. You can then use this user 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 user.
*/
String createUser(User user);
/**
* updates an existing user. Note: you must have update rights on the user.
*
* @throws AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized user.
* <p/>
* 403 Forbidden if you do not have Update rights on the user.
* @throws ResourceNotFoundException
* if the user does not exist.
*/
User updateUser(User user);
/**
* retrieves an existing user. Note: you must have update rights on the user.
*
* @return null, if the user is not found
*/
User getUser(String username);
/**
* deletes an existing user. Note: you must have delete rights on the user.
*
* @return the last state of the User object in question. * @throws AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized user.
* <p/>
* 403 Forbidden if you do not have Delete rights on the user.
* @throws ResourceNotFoundException
* <p/>
* 404 Not Found if the user does not exist.
*/
User deleteUser(String username);
/**
* creates a new organization
*
* @return the private key of the organization. You can then use this organization name and
* private key to access the Opscode API.
* @throws AuthorizationException
* <p/>
* "401 Unauthorized" if the caller is not a recognized organization.
* <p/>
* "403 Forbidden" if the caller is not authorized to create a organization.
*/
String createOrg(Organization organization);
/**
* updates an existing organization. Note: you must have update rights on the organization.
*
* @throws AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized organization.
* <p/>
* 403 Forbidden if you do not have Update rights on the organization.
* @throws ResourceNotFoundException
* if the organization does not exist.
*/
Organization updateOrg(Organization organization);
/**
* retrieves an existing organization. Note: you must have update rights on the organization.
*
* @return null, if the organization is not found
*/
Organization getOrg(String organizationname);
/**
* deletes an existing organization. Note: you must have delete rights on the organization.
*
* @return the last state of the Organization object in question. * @throws
* AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized organization.
* <p/>
* 403 Forbidden if you do not have Delete rights on the organization.
* @throws ResourceNotFoundException
* <p/>
* 404 Not Found if the organization does not exist.
*/
Organization deleteOrg(String organizationname);
}

View File

@ -28,16 +28,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import java.util.Properties;
import org.jclouds.rest.RestContextBuilder;
import org.jclouds.chef.ChefAsyncClient;
import org.jclouds.chef.ChefClient;
import org.jclouds.chef.config.ChefContextModule;
import org.jclouds.chef.config.ChefRestClientModule;
import org.jclouds.chef.reference.ChefConstants;
import org.jclouds.rest.RestContextBuilder;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
/**
*
@ -46,9 +42,7 @@ import com.google.inject.TypeLiteral;
public class ChefContextBuilder extends RestContextBuilder<ChefAsyncClient, ChefClient> {
public ChefContextBuilder(String providerName, Properties props) {
super(providerName, new TypeLiteral<ChefAsyncClient>() {
}, new TypeLiteral<ChefClient>() {
}, props);
super(providerName, ChefClient.class, ChefAsyncClient.class, props);
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_IDENTITY));
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_RSA_KEY));
}

View File

@ -33,8 +33,8 @@ import org.jclouds.rest.RestContext;
import com.google.inject.Module;
/**
* Creates {@link RestContext} for {@link ChefClient} instances based on the most commonly
* requested arguments.
* Creates {@link RestContext} for {@link ChefClient} instances based on the most commonly requested
* arguments.
* <p/>
* Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
* <p/>
@ -49,19 +49,20 @@ import com.google.inject.Module;
*/
public class ChefContextFactory {
public static RestContext<ChefAsyncClient, ChefClient> createContext(String identity, String rsaKey,
Module... modules) {
public static RestContext<ChefAsyncClient, ChefClient> createContext(String identity,
String rsaKey, Module... modules) {
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(identity, rsaKey).build())
.withModules(modules).buildContext();
}
public static RestContext<ChefAsyncClient, ChefClient> createContext(URI endpoint, String identity, String rsaKey,
Module... modules) {
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(identity, rsaKey).withEndpoint(endpoint).build())
.withModules(modules).buildContext();
public static RestContext<ChefAsyncClient, ChefClient> createContext(URI endpoint,
String identity, String rsaKey, Module... modules) {
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(identity, rsaKey)
.withEndpoint(endpoint).build()).withModules(modules).buildContext();
}
public static RestContext<ChefAsyncClient, ChefClient> createContext(Properties properties, Module... modules) {
public static RestContext<ChefAsyncClient, ChefClient> createContext(Properties properties,
Module... modules) {
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(properties).build())
.withModules(modules).buildContext();
}

View File

@ -25,26 +25,22 @@ package org.jclouds.chef;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_ENDPOINT;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_RSA_KEY;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_TIMESTAMP_INTERVAL;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_IDENTITY;
import java.net.URI;
import java.util.Properties;
import org.jclouds.PropertiesBuilder;
import org.jclouds.chef.internal.BaseChefPropertiesBuilder;
/**
* Builds properties used in Chef Clients
*
* @author Adrian Cole
*/
public class ChefPropertiesBuilder extends PropertiesBuilder {
public class ChefPropertiesBuilder extends BaseChefPropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_CHEF_ENDPOINT, "https://api.opscode.com");
properties.setProperty(PROPERTY_CHEF_TIMESTAMP_INTERVAL, "1");
properties.setProperty(PROPERTY_CHEF_ENDPOINT, "http://localhost:4000");
return properties;
}
@ -53,14 +49,11 @@ public class ChefPropertiesBuilder extends PropertiesBuilder {
}
public ChefPropertiesBuilder(String identity, String rsaKey) {
super();
withCredentials(identity, rsaKey);
super(URI.create("http://localhost:4000"), identity, rsaKey);
}
public ChefPropertiesBuilder withCredentials(String identity, String rsaKey) {
properties.setProperty(PROPERTY_CHEF_IDENTITY, checkNotNull(identity, "identity"));
properties.setProperty(PROPERTY_CHEF_RSA_KEY, checkNotNull(rsaKey, "password"));
return this;
public ChefPropertiesBuilder(URI endpoint, String identity, String rsaKey) {
super(endpoint, identity, rsaKey);
}
public ChefPropertiesBuilder withEndpoint(URI endpoint) {

View File

@ -0,0 +1,129 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.
* ====================================================================
*/
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.chef.config;
import java.io.IOException;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.jclouds.chef.handlers.ChefErrorHandler;
import org.jclouds.chef.reference.ChefConstants;
import org.jclouds.concurrent.ExpirableSupplier;
import org.jclouds.date.DateService;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.config.RestClientModule;
import com.google.common.base.Supplier;
import com.google.inject.Provides;
/**
* Configures the Chef connection.
*
* @author Adrian Cole
*/
@RequiresHttp
@ConfiguresRestClient
public class BaseChefRestClientModule<A, S> extends RestClientModule<A, S> {
protected BaseChefRestClientModule(Class<S> syncClientType, Class<A> asyncClientType) {
super(syncClientType, asyncClientType);
}
protected BaseChefRestClientModule(Class<S> syncClientType, Class<A> asyncClientType,
Map<Class<?>, Class<?>> delegates) {
super(syncClientType, asyncClientType, delegates);
}
@Provides
@TimeStamp
protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
return cache.get();
}
/**
* borrowing concurrency code to ensure that caching takes place properly
*/
@Provides
@TimeStamp
Supplier<String> provideTimeStampCache(
@Named(ChefConstants.PROPERTY_CHEF_TIMESTAMP_INTERVAL) long seconds,
final DateService dateService) {
return new ExpirableSupplier<String>(new Supplier<String>() {
public String get() {
return dateService.iso8601SecondsDateFormat();
}
}, seconds, TimeUnit.SECONDS);
}
@Provides
@Singleton
public PrivateKey provideKey(@Named(ChefConstants.PROPERTY_CHEF_RSA_KEY) String key)
throws IOException {
// TODO do this without adding a provider
Security.addProvider(new BouncyCastleProvider());
KeyPair pair = KeyPair.class.cast(new PEMReader(new StringReader(key)).readObject());
return pair.getPrivate();
}
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(ChefErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(ChefErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ChefErrorHandler.class);
}
}

View File

@ -41,38 +41,18 @@
*/
package org.jclouds.chef.config;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javax.inject.Singleton;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.jclouds.chef.Chef;
import org.jclouds.chef.ChefAsyncClient;
import org.jclouds.chef.ChefClient;
import org.jclouds.chef.handlers.ChefErrorHandler;
import org.jclouds.chef.reference.ChefConstants;
import org.jclouds.concurrent.ExpirableSupplier;
import org.jclouds.concurrent.internal.SyncProxy;
import org.jclouds.date.DateService;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestClientFactory;
import com.google.common.base.Supplier;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
/**
@ -82,56 +62,10 @@ import com.google.inject.Provides;
*/
@RequiresHttp
@ConfiguresRestClient
public class ChefRestClientModule extends AbstractModule {
public class ChefRestClientModule extends BaseChefRestClientModule<ChefAsyncClient, ChefClient> {
@Provides
@TimeStamp
protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
return cache.get();
}
/**
* borrowing concurrency code to ensure that caching takes place properly
*/
@Provides
@TimeStamp
Supplier<String> provideTimeStampCache(
@Named(ChefConstants.PROPERTY_CHEF_TIMESTAMP_INTERVAL) long seconds,
final DateService dateService) {
return new ExpirableSupplier<String>(new Supplier<String>() {
public String get() {
return dateService.iso8601SecondsDateFormat();
}
}, seconds, TimeUnit.SECONDS);
}
@Override
protected void configure() {
bindErrorHandlers();
bindRetryHandlers();
}
@Provides
@Singleton
public PrivateKey provideKey(@Named(ChefConstants.PROPERTY_CHEF_RSA_KEY) String key)
throws IOException {
// TODO do this without adding a provider
Security.addProvider(new BouncyCastleProvider());
KeyPair pair = KeyPair.class.cast(new PEMReader(new StringReader(key)).readObject());
return pair.getPrivate();
}
@Provides
@Singleton
protected ChefAsyncClient provideClient(RestClientFactory factory) {
return factory.create(ChefAsyncClient.class);
}
@Provides
@Singleton
public ChefClient provideClient(ChefAsyncClient provider) throws IllegalArgumentException,
SecurityException, NoSuchMethodException {
return SyncProxy.create(ChefClient.class, provider);
public ChefRestClientModule() {
super(ChefClient.class, ChefAsyncClient.class);
}
@Provides
@ -141,17 +75,4 @@ public class ChefRestClientModule extends AbstractModule {
return URI.create(endpoint);
}
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(
ChefErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(
ChefErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(
ChefErrorHandler.class);
}
protected void bindRetryHandlers() {
// TODO
}
}

View File

@ -0,0 +1,64 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.chef.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_IDENTITY;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_RSA_KEY;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_TIMESTAMP_INTERVAL;
import java.net.URI;
import java.util.Properties;
import org.jclouds.PropertiesBuilder;
/**
* Builds properties used in Chef Clients
*
* @author Adrian Cole
*/
public abstract class BaseChefPropertiesBuilder extends PropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_CHEF_TIMESTAMP_INTERVAL, "1");
return properties;
}
public BaseChefPropertiesBuilder(Properties properties) {
super(properties);
}
public BaseChefPropertiesBuilder(URI endpoint, String identity, String rsaKey) {
super();
withCredentials(identity, rsaKey);
withEndpoint(endpoint);
}
public BaseChefPropertiesBuilder withCredentials(String identity, String rsaKey) {
properties.setProperty(PROPERTY_CHEF_IDENTITY, checkNotNull(identity, "identity"));
properties.setProperty(PROPERTY_CHEF_RSA_KEY, checkNotNull(rsaKey, "password"));
return this;
}
}

View File

@ -27,24 +27,18 @@ import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
import org.jclouds.chef.config.ChefRestClientModule;
import org.jclouds.chef.domain.Organization;
import org.jclouds.chef.domain.User;
import org.jclouds.chef.filters.SignedHeaderAuth;
import org.jclouds.chef.filters.SignedHeaderAuthTest;
import org.jclouds.chef.functions.ParseKeyFromJson;
import org.jclouds.chef.functions.ParseKeySetFromJson;
import org.jclouds.chef.functions.ParseOrganizationFromJson;
import org.jclouds.chef.functions.ParseUserFromJson;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.functions.CloseContentAndReturn;
import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.logging.config.NullLoggingModule;
import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.rest.internal.RestAnnotationProcessor;
@ -52,7 +46,6 @@ import org.jclouds.util.Jsr330;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
@ -63,13 +56,10 @@ import com.google.inject.TypeLiteral;
*/
@Test(groups = "unit", testName = "chef.ChefAsyncClientTest")
public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
public void testClientExistsInOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("clientExistsInOrg", String.class,
String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "org",
"client");
assertRequestLineEquals(httpRequest,
"HEAD https://api.opscode.com/organizations/org/clients/client HTTP/1.1");
public void testClientExists() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("clientExists", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "client");
assertRequestLineEquals(httpRequest, "HEAD http://localhost:4000/clients/client HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
@ -81,13 +71,10 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testDeleteClientInOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("deleteClientInOrg", String.class,
String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "org",
"client");
assertRequestLineEquals(httpRequest,
"DELETE https://api.opscode.com/organizations/org/clients/client HTTP/1.1");
public void testDeleteClient() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("deleteClient", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "client");
assertRequestLineEquals(httpRequest, "DELETE http://localhost:4000/clients/client HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
@ -99,14 +86,11 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testGenerateKeyForClientInOrg() throws SecurityException, NoSuchMethodException,
public void testGenerateKeyForClient() throws SecurityException, NoSuchMethodException,
IOException {
Method method = ChefAsyncClient.class.getMethod("generateKeyForClientInOrg", String.class,
String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "org",
"client");
assertRequestLineEquals(httpRequest,
"PUT https://api.opscode.com/organizations/org/clients/client HTTP/1.1");
Method method = ChefAsyncClient.class.getMethod("generateKeyForClient", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "client");
assertRequestLineEquals(httpRequest, "PUT http://localhost:4000/clients/client HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 44\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"clientname\":\"client\", \"private_key\": true}");
@ -119,14 +103,11 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testCreateClientInOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("createClientInOrg", String.class,
String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "org",
"client");
public void testCreateClient() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("createClient", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "client");
assertRequestLineEquals(httpRequest,
"POST https://api.opscode.com/organizations/org/clients HTTP/1.1");
assertRequestLineEquals(httpRequest, "POST http://localhost:4000/clients HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 23\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"clientname\":\"client\"}");
@ -139,12 +120,11 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testListClientsInOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("listClientsInOrg", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "org");
public void testListClients() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("listClients");
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method);
assertRequestLineEquals(httpRequest,
"GET https://api.opscode.com/organizations/org/clients HTTP/1.1");
assertRequestLineEquals(httpRequest, "GET http://localhost:4000/clients HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
@ -156,183 +136,6 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testCreateUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("createUser", User.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, new User(
"myuser"));
assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/users HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 21\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"username\":\"myuser\"}");
// now make sure request filters apply by replaying
Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/users HTTP/1.1");
assertHeadersEqual(
httpRequest,
new StringBuilder("Accept: application/json")
.append("\n")
.append("Content-Length: 21")
.append("\n")
.append("Content-Type: application/json")
.append("\n")
.append(
"X-Ops-Authorization-1: kfrkDpfgNU26k70R1vl1bEWk0Q0f9Fs/3kxOX7gHd7iNoJq03u7RrcrAOSgL")
.append("\n")
.append(
"X-Ops-Authorization-2: ETj5JNeCk18BmFkHMAbCA9hXVo1T4rlHCpbuzAzFlFxUGAT4wj8UoO7V886X")
.append("\n")
.append(
"X-Ops-Authorization-3: Kf8DvihP6ElthCNuu1xuhN0B4GEmWC9+ut7UMLe0L2T34VzkbCtuInGbf42/")
.append("\n")
.append(
"X-Ops-Authorization-4: G7iu94/xFOT1gN9cex4pNyTnRCHzob4JVU1usxt/2g5grN2SyYwRS5+4MNLN")
.append("\n")
.append(
"X-Ops-Authorization-5: WY/iLUPb/9dwtiIQsnUOXqDrs28zNswZulQW4AzYRd7MczJVKU4y4+4XRcB4")
.append("\n")
.append(
"X-Ops-Authorization-6: 2+BFLT5o6P6G0D+eCu3zSuaqEJRucPJPaDGWdKIMag==")
.append("\n").append("X-Ops-Content-Hash: yLHOxvgIEtNw5UrZDxslOeMw1gw=")
.append("\n").append("X-Ops-Sign: version=1.0").append("\n").append(
"X-Ops-Timestamp: timestamp").append("\n").append(
"X-Ops-Userid: user").append("\n").toString());
assertPayloadEquals(httpRequest, "{\"username\":\"myuser\"}");
assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testUpdateUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("updateUser", User.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, new User(
"myuser"));
assertRequestLineEquals(httpRequest, "PUT https://api.opscode.com/users/myuser HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 21\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"username\":\"myuser\"}");
assertResponseParserClassEquals(method, httpRequest, ParseUserFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testGetUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("getUser", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "myuser");
assertRequestLineEquals(httpRequest, "GET https://api.opscode.com/users/myuser HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseUserFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testDeleteUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("deleteUser", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "myuser");
assertRequestLineEquals(httpRequest, "DELETE https://api.opscode.com/users/myuser HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseUserFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testCreateOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("createOrg", Organization.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
new Organization("myorganization"));
assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/organizations HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 25\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"name\":\"myorganization\"}");
assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testUpdateOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("updateOrg", Organization.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
new Organization("myorganization"));
assertRequestLineEquals(httpRequest,
"PUT https://api.opscode.com/organizations/myorganization HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 25\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"name\":\"myorganization\"}");
assertResponseParserClassEquals(method, httpRequest, ParseOrganizationFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testGetOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("getOrg", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
"myorganization");
assertRequestLineEquals(httpRequest,
"GET https://api.opscode.com/organizations/myorganization HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseOrganizationFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testDeleteOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("deleteOrg", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
"myorganization");
assertRequestLineEquals(httpRequest,
"DELETE https://api.opscode.com/organizations/myorganization HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseOrganizationFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
@Override
protected void checkFilters(GeneratedHttpRequest<ChefAsyncClient> httpRequest) {
assertEquals(httpRequest.getFilters().size(), 1);
@ -350,9 +153,10 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
return new ChefRestClientModule() {
@Override
protected void configure() {
Jsr330.bindProperties(binder(), new ChefPropertiesBuilder(new Properties())
.withCredentials("user", SignedHeaderAuthTest.PRIVATE_KEY).build());
Jsr330.bindProperties(binder(), new ChefPropertiesBuilder("chef-validator",
SignedHeaderAuthTest.PRIVATE_KEY).build());
install(new NullLoggingModule());
super.configure();
}
@Override

View File

@ -28,11 +28,10 @@ import static org.testng.Assert.assertNotNull;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Set;
import org.jclouds.chef.domain.User;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.RestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@ -52,102 +51,65 @@ public class ChefClientLiveTest {
private RestContext<ChefAsyncClient, ChefClient> validatorConnection;
private RestContext<ChefAsyncClient, ChefClient> clientConnection;
private String orgname;
private String clientKey;
private String endpoint;
private String validator;
public static final String PREFIX = System.getProperty("user.name") + "-jcloudstest";
@BeforeClass(groups = { "live" })
public void setupClient() throws IOException {
orgname = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
endpoint = checkNotNull(System.getProperty("jclouds.test.endpoint"), "jclouds.test.endpoint");
validator = System.getProperty("jclouds.test.user");
if (validator == null || validator.equals(""))
validator = "chef-validator";
String keyfile = System.getProperty("jclouds.test.key");
if (keyfile == null || keyfile.equals(""))
keyfile = System.getProperty("user.home") + "/chef/validation.pem";
validatorConnection = createConnection(orgname + "-validator", Files.toString(new File(
keyfile), Charsets.UTF_8));
keyfile = "/etc/chef/validation.pem";
validatorConnection = createConnection(validator, Files.toString(new File(keyfile),
Charsets.UTF_8));
}
private RestContext<ChefAsyncClient, ChefClient> createConnection(String identity, String key)
throws IOException {
return ChefContextFactory.createContext(identity, key, new Log4JLoggingModule());
return ChefContextFactory.createContext(URI.create(endpoint), identity, key,
new Log4JLoggingModule());
}
@Test
public void testListClientsInOrg() throws Exception {
Set<String> clients = validatorConnection.getApi().listClientsInOrg(orgname);
public void testListClients() throws Exception {
Set<String> clients = validatorConnection.getApi().listClients();
assertNotNull(clients);
assert clients.contains(orgname + "-validator");
assert clients.contains(validator) : "validator: " + validator + " not in: " + clients;
}
@Test(dependsOnMethods = "testListClientsInOrg")
public void testCreateClientInOrg() throws Exception {
validatorConnection.getApi().deleteClientInOrg(orgname, PREFIX);
clientKey = validatorConnection.getApi().createClientInOrg(orgname, PREFIX);
@Test(dependsOnMethods = "testListClients")
public void testCreateClient() throws Exception {
validatorConnection.getApi().deleteClient(PREFIX);
clientKey = validatorConnection.getApi().createClient(PREFIX);
assertNotNull(clientKey);
System.out.println(clientKey);
clientConnection = createConnection(PREFIX, clientKey);
clientConnection.getApi().clientExistsInOrg(orgname, PREFIX);
clientConnection.getApi().clientExists(PREFIX);
}
@Test(dependsOnMethods = "testCreateClientInOrg")
public void testGenerateKeyForClientInOrg() throws Exception {
clientKey = validatorConnection.getApi().generateKeyForClientInOrg(orgname, PREFIX);
@Test(dependsOnMethods = "testCreateClient")
public void testGenerateKeyForClient() throws Exception {
clientKey = validatorConnection.getApi().generateKeyForClient(PREFIX);
assertNotNull(clientKey);
clientConnection.close();
clientConnection = createConnection(PREFIX, clientKey);
clientConnection.getApi().clientExistsInOrg(orgname, PREFIX);
clientConnection.getApi().clientExists(PREFIX);
}
@Test(dependsOnMethods = "testCreateClientInOrg")
public void testClientExistsInOrg() throws Exception {
assertNotNull(validatorConnection.getApi().clientExistsInOrg(orgname, PREFIX));
@Test(dependsOnMethods = "testCreateClient")
public void testClientExists() throws Exception {
assertNotNull(validatorConnection.getApi().clientExists(PREFIX));
}
@Test(expectedExceptions = AuthorizationException.class)
public void testGetOrgFailsForValidationKey() throws Exception {
validatorConnection.getApi().getOrg(orgname);
}
@Test(dependsOnMethods = "testGenerateKeyForClientInOrg", expectedExceptions = AuthorizationException.class)
public void testGetOrgFailsForClient() throws Exception {
clientConnection.getApi().getOrg(orgname);
}
@Test(enabled = false)
public void testGetUser() throws Exception {
User user = validatorConnection.getApi().getUser(orgname);
assertNotNull(user);
}
@Test(enabled = false)
public void testCreateUser() throws Exception {
// TODO
}
@Test(enabled = false)
public void testUpdateUser() throws Exception {
// TODO
}
@Test(enabled = false)
public void testDeleteUser() throws Exception {
// TODO
}
@Test(enabled = false)
public void testCreateOrg() throws Exception {
// TODO
}
@Test(enabled = false)
public void testUpdateOrg() throws Exception {
// TODO
}
@Test(enabled = false)
public void testDeleteOrg() throws Exception {
// TODO
@Test(dependsOnMethods = "testGenerateKeyForClient")
public void testListCookbooks() throws Exception {
System.err.println(clientConnection.getApi().listCookbooks());
}
@AfterClass(groups = { "live" })

View File

@ -25,6 +25,7 @@ package org.jclouds.chef.filters;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
import static org.easymock.classextension.EasyMock.createMock;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
@ -37,18 +38,17 @@ import java.security.Security;
import javax.inject.Provider;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.jboss.resteasy.specimpl.UriBuilderImpl;
import org.jclouds.chef.ChefPropertiesBuilder;
import org.jclouds.chef.config.ChefRestClientModule;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.config.ParserModule;
import org.jclouds.http.TransformingHttpCommandExecutorService;
import org.jclouds.http.internal.SignatureWire;
import org.jclouds.rest.config.RestModule;
import org.jclouds.util.Jsr330;
import org.jclouds.util.Utils;
import org.testng.annotations.BeforeClass;
@ -200,9 +200,9 @@ public class SignedHeaderAuthTest {
private SignedHeaderAuth signing_obj;
private EncryptionService encryptionService;
@Test
@Test(enabled = false)
void canParseKeyFromCreateClient() throws IOException {
String key = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA50Iwgq8OIfm5vY9Gwfb6UBt17D7V4djyFSLJ1AbCU/o8Zlrr\nW73JqaK5dC3IO6Dcu+/qPYGtBUWvhFAXrsFOooz0mTod/LtBN1YVurJ60goJrR6w\nKhUYC9H45OW/qcdIM7kdDwiyMZfbHqW6fo0xPqjvgxtZoI+v7pgThacOG6pw7PO6\nGgnJa3MGK3xEbzlI6+EBJWG3EiwexguwOpTD4a4TDIAqKrlVDPeUpU7rFbsBPRS8\nkypR3lU58+WRz/zi9fiH/Sy2X+S3yZg14HiutJjxc8zJsazF3eDxyLGPQmhv3Mso\nA0wbjGusbe6hPdDkzh/B2KO9u96QCdlGu/rc6QIDAQABAoIBAA/7OgD9+fsNF/Hq\nodgrqja4/xg5a2x1Ip2lTs9RPEKza1Mje1pWrkYD0c8ejtTYFAkE1mozuPJBU5TQ\nOCLChx2iohCovIPHqQUa9Nt3FBfJy8tj8Ian+IZwl0OyQOGJvQqeA00Tq8TTmrfu\negne1gVfhVXJIROAeocBiW/WEJqGti0OE5zQQMld3cJ5viTdEsaWYCu2HaEoblKB\nH6KfRGM2N3L3KjKFGtEg+cX1UdaMlzmp+O5/yvjBykZy6cuUOIsgz2e5nQV4hYEq\ntJ/+6E0QVTXfnVZi4IxKlkVMhyonqOxAOKGG+dWeWh3DqPJFzjmp3kcbRN9E3u+2\nqKU5gpECgYEA+a/i5z2jFCJ8rMpoCPPxm2eiIYZVs3LE33WU5FNNieBRC+KqO06h\nMB3rQ3k8KJDNJYWD5UwIrgjCD5mgkmcSDI6SbOn6PA1Mtw6qZlbeg17V9L9ryXxt\nSfC5AC+qVWd6unrLh0LgkvLS8rgG4GjLZY0HDDMrJWodcc+uWVk3Mo0CgYEA7RsG\nC9gOcHWi6WJ2uEDvLj4IkSkB4IFipEVcKl7VVDSnUgxaBZm3o0DLYDYhIOw7XcQL\n6vpxbRZdlApGyu1ahfMbk3+quFNMQuGxZcv9EhHz7ASnXK6mlrWkJzCGjLz6/MdI\nU0VGbtkBtOY/GaLXdTkQksWowVNoedISfQV9as0CgYEA0Tj1JVecw05yskeZDYd8\nOQCJ9xWd0pSlK6pXbUvweUwiHZd9ldy5bJxle1CnfEZ54KsUbptb2pk0I+ZTitob\nYbJGOEWHjbKHSg1b9A1uvx5EoqWUKG2/FmpEW0eVr6LaUFB9I4aCsCARa5mRCZJG\nfX3DHhHyYZOdwLSKIAyGGDECgYALEwkMQpIiFIyAZBXxcy74tPMHfKfWyZRG4ep1\nHCrQnQj3nxYRTuWx3VPicYTImeAH+CEqX3ouwy2pvXUjA0UIHpu6HutlYpacRRhZ\nDdcLIgWHj4wVmx6yyVcacXzHVAhRCCnLod+xS7d1sI9f7igsFHc+s7a3GOM3VWWB\nq2D5PQKBgQDY9eSb5pc5EYbPy0a/wKFLMNCVLXlDT8jRSC2UnhmcHbhi1IdUbn1j\nR+SuUgrAVNAKzJRY9wmF6Zt0pJ2YLFX7L8HaGyfzkib8kli2sXFonlQ4d0dTdcJo\nVGR1jTxfZQicdPcDPOLPpQz/rP31ZqdHtfaegTOxHebX7W2E5QvPZg==\n-----END RSA PRIVATE KEY-----\n";
String key = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA50Iwgq8OIfm5vY9Gwfb6UBt17D7V4djyFSLJ1AbCU/o8Zlrr\nW73JqaK5dC3IO6Dcu+/qPYGtBUWvhFAXrsFOooz0mTod/LtBN1YVurJ60goJrR6w\nKhUYC9H45OW/qcdIM7kdDwiyMZfbHqW6fo0xPqjvgxtZoI+v7pgThacOG6pw7PO6\nGgnJa3MGK3xEbzlI6+EBJWG3EiwexguwOpTD4a4TDIAqKrlVDPeUpU7rFbsBPRS8\nkypR3lU58+WRz/zi9fiH/Sy2X+ChefyZg14HiutJjxc8zJsazF3eDxyLGPQmhv3Mso\nA0wbjGusbe6hPdDkzh/B2KO9u96QCdlGu/rc6QIDAQABAoIBAA/7OgD9+fsNF/Hq\nodgrqja4/xg5a2x1Ip2lTs9RPEKza1Mje1pWrkYD0c8ejtTYFAkE1mozuPJBU5TQ\nOCLChx2iohCovIPHqQUa9Nt3FBfJy8tj8Ian+IZwl0OyQOGJvQqeA00Tq8TTmrfu\negne1gVfhVXJIROAeocBiW/WEJqGti0OE5zQQMld3cJ5viTdEsaWYCu2HaEoblKB\nH6KfRGM2N3L3KjKFGtEg+cX1UdaMlzmp+O5/yvjBykZy6cuUOIsgz2e5nQV4hYEq\ntJ/+6E0QVTXfnVZi4IxKlkVMhyonqOxAOKGG+dWeWh3DqPJFzjmp3kcbRN9E3u+2\nqKU5gpECgYEA+a/i5z2jFCJ8rMpoCPPxm2eiIYZVs3LE33WU5FNNieBRC+KqO06h\nMB3rQ3k8KJDNJYWD5UwIrgjCD5mgkmcSDI6SbOn6PA1Mtw6qZlbeg17V9L9ryXxt\nSfC5AC+qVWd6unrLh0LgkvLS8rgG4GjLZY0HDDMrJWodcc+uWVk3Mo0CgYEA7RsG\nC9gOcHWi6WJ2uEDvLj4IkSkB4IFipEVcKl7VVDSnUgxaBZm3o0DLYDYhIOw7XcQL\n6vpxbRZdlApGyu1ahfMbk3+quFNMQuGxZcv9EhHz7ASnXK6mlrWkJzCGjLz6/MdI\nU0VGbtkBtOY/GaLXdTkQksWowVNoedISfQV9as0CgYEA0Tj1JVecw05yskeZDYd8\nOQCJ9xWd0pSlK6pXbUvweUwiHZd9ldy5bJxle1CnfEZ54KsUbptb2pk0I+ZTitob\nYbJGOEWHjbKHSg1b9A1uvx5EoqWUKG2/FmpEW0eVr6LaUFB9I4aCsCARa5mRCZJG\nfX3DHhHyYZOdwLSKIAyGGDECgYALEwkMQpIiFIyAZBXxcy74tPMHfKfWyZRG4ep1\nHCrQnQj3nxYRTuWx3VPicYTImeAH+CEqX3ouwy2pvXUjA0UIHpu6HutlYpacRRhZ\nDdcLIgWHj4wVmx6yyVcacXzHVAhRCCnLod+xS7d1sI9f7igsFHc+s7a3GOM3VWWB\nq2D5PQKBgQDY9eSb5pc5EYbPy0a/wKFLMNCVLXlDT8jRSC2UnhmcHbhi1IdUbn1j\nR+SuUgrAVNAKzJRY9wmF6Zt0pJ2YLFX7L8HaGyfzkib8kli2sXFonlQ4d0dTdcJo\nVGR1jTxfZQicdPcDPOLPpQz/rP31ZqdHtfaegTOxHebX7W2E5QvPZg==\n-----END RSA PRIVATE KEY-----\n";
KeyPair.class.cast(new PEMReader(new StringReader(key)).readObject());
}
@ -214,14 +214,15 @@ public class SignedHeaderAuthTest {
*/
@BeforeClass
protected void createFilter() throws IOException {
Injector injector = Guice.createInjector(new ChefRestClientModule(),
Injector injector = Guice.createInjector(new RestModule(), new ChefRestClientModule(),
new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor()),
new ParserModule(), new AbstractModule() {
new AbstractModule() {
protected void configure() {
Jsr330.bindProperties(binder(), checkNotNull(
new ChefPropertiesBuilder("foo", "bar")).build());
bind(UriBuilder.class).to(UriBuilderImpl.class);
new ChefPropertiesBuilder(URI.create("http://localhost"), "foo",
"bar")).build());
bind(TransformingHttpCommandExecutorService.class).toInstance(
createMock(TransformingHttpCommandExecutorService.class));
}
});
encryptionService = injector.getInstance(EncryptionService.class);

View File

@ -1,8 +0,0 @@
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="jclouds-chef">
<test verbose="2" name="org.jclouds.chef.functions.ParseKeyFromJsonTest" annotations="JDK">
<classes>
<class name="org.jclouds.chef.functions.ParseKeyFromJsonTest"/>
</classes>
</test>
</suite>

View File

@ -0,0 +1,43 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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
*
* 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.ibmdev.binders;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.binders.BindToStringPayload;
import com.google.common.collect.ImmutableSet;
/**
*
* @author Adrian Cole
*
*/
public class BindImageVisibilityToJsonPayload extends BindToStringPayload {
@Override
public void bindToRequest(HttpRequest request, Object payload) {
request.getHeaders().replaceValues(HttpHeaders.CONTENT_TYPE,
ImmutableSet.of(MediaType.APPLICATION_JSON));
super.bindToRequest(request, String.format("{\"visibility\":\"%s\"}", payload));
}
}

View File

@ -0,0 +1,92 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.ibmdev.handlers;
import java.io.IOException;
import javax.annotation.Resource;
import javax.inject.Singleton;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.logging.Logger;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.Utils;
import com.google.common.base.Throwables;
import com.google.common.io.Closeables;
/**
* This will parse and set an appropriate exception on the command object.
*
* @author Adrian Cole
*
*/
@Singleton
public class IBMDeveloperCloudErrorHandler implements HttpErrorHandler {
@Resource
protected Logger logger = Logger.NULL;
public void handleError(HttpCommand command, HttpResponse response) {
// it is important to always read fully and close streams
String message = parseMessage(response);
Exception exception = message != null ? new HttpResponseException(command, response, message)
: new HttpResponseException(command, response);
try {
message = message != null ? message : String.format("%s -> %s", command.getRequest()
.getRequestLine(), response.getStatusLine());
switch (response.getStatusCode()) {
case 401:
case 403:
exception = new AuthorizationException(message, exception);
break;
case 412:
exception = new IllegalStateException(message, exception);
break;
case 404:
if (!command.getRequest().getMethod().equals("DELETE")) {
exception = new ResourceNotFoundException(message, exception);
}
break;
}
} finally {
Closeables.closeQuietly(response.getContent());
command.setException(exception);
}
}
public String parseMessage(HttpResponse response) {
if (response.getContent() == null)
return null;
try {
return Utils.toStringAndClose(response.getContent());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
response.getContent().close();
} catch (IOException e) {
Throwables.propagate(e);
}
}
}
}

82
opscodeplatform/pom.xml Normal file
View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
$HeadURL$
$Revision$
$Date$
Copyright (C) 2009 Cloud Conscious, LLC <info@cloudconscious.com>
====================================================================
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.html
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-project</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project/pom.xml</relativePath>
</parent>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-opscodeplatform</artifactId>
<name>jclouds Opscode Platform core</name>
<description>jclouds components to access Opscode Platform</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/opscodeplatform</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/opscodeplatform</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk/opscodeplatform</url>
</scm>
<!-- bootstrapping: need to fetch the project POM -->
<repositories>
<repository>
<id>jclouds-googlecode-deploy</id>
<url>http://jclouds.googlecode.com/svn/repo</url>
</repository>
<repository>
<id>jclouds-rimu-snapshots-nexus</id>
<url>http://jclouds.rimuhosting.com:8081/nexus/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<properties>
<jclouds.opscodeplatform.org>MYORG</jclouds.opscodeplatform.org>
<jclouds.opscodeplatform.rsa-key>/etc/chef/validation.pem</jclouds.opscodeplatform.rsa-key>
<jclouds.test.user>${jclouds.opscodeplatform.org}</jclouds.test.user>
<jclouds.test.key>${jclouds.opscodeplatform.rsa-key}</jclouds.test.key>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-chef</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-chef</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,62 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.
* ====================================================================
*/
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
/**
* Related to a Opscode Platform resource.
*
* @author Adrian Cole
*
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Qualifier
public @interface OpscodePlatform {
}

View File

@ -0,0 +1,144 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import org.jclouds.chef.ChefAsyncClient;
import org.jclouds.chef.ChefClient;
import org.jclouds.chef.domain.Organization;
import org.jclouds.chef.domain.User;
import org.jclouds.chef.filters.SignedHeaderAuth;
import org.jclouds.chef.functions.OrganizationName;
import org.jclouds.chef.functions.ParseKeyFromJson;
import org.jclouds.chef.functions.ParseOrganizationFromJson;
import org.jclouds.chef.functions.ParseUserFromJson;
import org.jclouds.chef.functions.Username;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.binders.BindToJsonPayload;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides asynchronous access to the Opscode Platform via their REST API.
* <p/>
*
* @see OpscodePlatformClient
* @see <a href="TODO: insert URL of provider documentation" />
* @author Adrian Cole
*/
@Endpoint(OpscodePlatform.class)
@RequestFilters(SignedHeaderAuth.class)
@Consumes(MediaType.APPLICATION_JSON)
public interface OpscodePlatformAsyncClient {
/**
* @see ChefCookbooks#listCookbooksInOrg
*/
@Delegate
@Path("/organizations/{orgname}")
ChefAsyncClient getChefClientForOrg(@PathParam("orgname") String orgname);
/**
* @see ChefClient#createUser
*/
@POST
@Path("/users")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> createUser(@BinderParam(BindToJsonPayload.class) User user);
/**
* @see ChefClient#updateUser
*/
@PUT
@Path("/users/{username}")
@ResponseParser(ParseUserFromJson.class)
ListenableFuture<User> updateUser(
@PathParam("username") @ParamParser(Username.class) @BinderParam(BindToJsonPayload.class) User user);
/**
* @see ChefClient#getUser
*/
@GET
@Path("/users/{username}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@ResponseParser(ParseUserFromJson.class)
ListenableFuture<User> getUser(@PathParam("username") String username);
/**
* @see ChefClient#deleteUser
*/
@DELETE
@Path("/users/{username}")
@ResponseParser(ParseUserFromJson.class)
ListenableFuture<User> deleteUser(@PathParam("username") String username);
/**
* @see ChefClient#createOrg
*/
@POST
@Path("/organizations")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> createOrg(@BinderParam(BindToJsonPayload.class) Organization org);
/**
* @see ChefClient#updateOrg
*/
@PUT
@Path("/organizations/{orgname}")
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> updateOrg(
@PathParam("orgname") @ParamParser(OrganizationName.class) @BinderParam(BindToJsonPayload.class) Organization org);
/**
* @see ChefClient#getOrg
*/
@GET
@Path("/organizations/{orgname}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> getOrg(@PathParam("orgname") String orgname);
/**
* @see ChefClient#deleteOrg
*/
@DELETE
@Path("/organizations/{orgname}")
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> deleteOrg(@PathParam("orgname") String orgname);
}

View File

@ -0,0 +1,152 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.chef.ChefClient;
import org.jclouds.chef.domain.Organization;
import org.jclouds.chef.domain.User;
import org.jclouds.concurrent.Timeout;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.Endpoint;
/**
* Provides synchronous access to the Opscode Platform.
* <p/>
*
* @see OpscodePlatformAsyncClient
* @see <a href="TODO: insert URL of Opscode Platform documentation" />
* @author Adrian Cole
*/
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface OpscodePlatformClient {
/**
* @return a chef client appropriate for the organization specified.
*/
@Delegate
@Path("/organizations/{orgname}")
@Endpoint(OpscodePlatform.class)
ChefClient getChefClientForOrg(@PathParam("orgname") String orgname);
/**
* creates a new user
*
* @return the private key of the user. You can then use this user 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 user.
*/
String createUser(User user);
/**
* updates an existing user. Note: you must have update rights on the user.
*
* @throws AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized user.
* <p/>
* 403 Forbidden if you do not have Update rights on the user.
* @throws ResourceNotFoundException
* if the user does not exist.
*/
User updateUser(User user);
/**
* retrieves an existing user. Note: you must have update rights on the user.
*
* @return null, if the user is not found
*/
User getUser(String username);
/**
* deletes an existing user. Note: you must have delete rights on the user.
*
* @return the last state of the User object in question. * @throws AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized user.
* <p/>
* 403 Forbidden if you do not have Delete rights on the user.
* @throws ResourceNotFoundException
* <p/>
* 404 Not Found if the user does not exist.
*/
User deleteUser(String username);
/**
* creates a new organization
*
* @return the private key of the organization. You can then use this organization name and
* private key to access the Opscode API.
* @throws AuthorizationException
* <p/>
* "401 Unauthorized" if the caller is not a recognized organization.
* <p/>
* "403 Forbidden" if the caller is not authorized to create a organization.
*/
String createOrg(Organization organization);
/**
* updates an existing organization. Note: you must have update rights on the organization.
*
* @throws AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized organization.
* <p/>
* 403 Forbidden if you do not have Update rights on the organization.
* @throws ResourceNotFoundException
* if the organization does not exist.
*/
Organization updateOrg(Organization organization);
/**
* retrieves an existing organization. Note: you must have update rights on the organization.
*
* @return null, if the organization is not found
*/
Organization getOrg(String organizationname);
/**
* deletes an existing organization. Note: you must have delete rights on the organization.
*
* @return the last state of the Organization object in question. * @throws
* AuthorizationException
* <p/>
* 401 Unauthorized if you are not a recognized organization.
* <p/>
* 403 Forbidden if you do not have Delete rights on the organization.
* @throws ResourceNotFoundException
* <p/>
* 404 Not Found if the organization does not exist.
*/
Organization deleteOrg(String organizationname);
}

View File

@ -0,0 +1,59 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import java.util.Properties;
import org.jclouds.chef.reference.ChefConstants;
import org.jclouds.opscodeplatform.config.OpscodePlatformContextModule;
import org.jclouds.opscodeplatform.config.OpscodePlatformRestClientModule;
import org.jclouds.rest.RestContextBuilder;
import com.google.inject.Module;
/**
*
* @author Adrian Cole
*/
public class OpscodePlatformContextBuilder extends RestContextBuilder<OpscodePlatformAsyncClient, OpscodePlatformClient> {
public OpscodePlatformContextBuilder(String providerName, Properties props) {
super(providerName, OpscodePlatformClient.class, OpscodePlatformAsyncClient.class, props);
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_IDENTITY));
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_RSA_KEY));
}
protected void addClientModule(List<Module> modules) {
modules.add(new OpscodePlatformRestClientModule());
}
@Override
protected void addContextModule(String providerName, List<Module> modules) {
modules.add(new OpscodePlatformContextModule(providerName));
}
}

View File

@ -0,0 +1,73 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import java.net.URI;
import java.util.Properties;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
import org.jclouds.rest.RestContext;
import com.google.inject.Module;
/**
* Creates {@link RestContext} for {@link OpscodePlatformClient} instances based on the most
* commonly requested arguments.
* <p/>
* Note that Threadsafe objects will be bound as singletons to the Injector or Context provided.
* <p/>
* <p/>
* If no <code>Module</code>s are specified, the default {@link JDKLoggingModule logging} and
* {@link JavaUrlHttpCommandExecutorServiceModule http transports} will be installed.
*
* @author Adrian Cole
* @see RestContext
* @see OpscodePlatformClient
* @see OpscodePlatformAsyncClient
*/
public class OpscodePlatformContextFactory {
public static RestContext<OpscodePlatformAsyncClient, OpscodePlatformClient> createContext(
String identity, String rsaKey, Module... modules) {
return new OpscodePlatformContextBuilder("opscodeplatform",
new OpscodePlatformPropertiesBuilder(identity, rsaKey).build()).withModules(modules)
.buildContext();
}
public static RestContext<OpscodePlatformAsyncClient, OpscodePlatformClient> createContext(
URI endpoint, String identity, String rsaKey, Module... modules) {
return new OpscodePlatformContextBuilder("opscodeplatform",
new OpscodePlatformPropertiesBuilder(identity, rsaKey).withEndpoint(endpoint)
.build()).withModules(modules).buildContext();
}
public static RestContext<OpscodePlatformAsyncClient, OpscodePlatformClient> createContext(
Properties properties, Module... modules) {
return new OpscodePlatformContextBuilder("opscodeplatform",
new OpscodePlatformPropertiesBuilder(properties).build()).withModules(modules)
.buildContext();
}
}

View File

@ -0,0 +1,64 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.opscodeplatform.reference.OpscodePlatformConstants.PROPERTY_OPSCODEPLATFORM_ENDPOINT;
import java.net.URI;
import java.util.Properties;
import org.jclouds.chef.internal.BaseChefPropertiesBuilder;
/**
* Builds properties used in Chef Clients
*
* @author Adrian Cole
*/
public class OpscodePlatformPropertiesBuilder extends BaseChefPropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_OPSCODEPLATFORM_ENDPOINT, "https://api.opscode.com");
return properties;
}
public OpscodePlatformPropertiesBuilder(Properties properties) {
super(properties);
}
public OpscodePlatformPropertiesBuilder(URI endpoint, String identity, String rsaKey) {
super(endpoint, identity, rsaKey);
}
public OpscodePlatformPropertiesBuilder(String identity, String rsaKey) {
super(URI.create("https://api.opscode.com"), identity, rsaKey);
}
public BaseChefPropertiesBuilder withEndpoint(URI endpoint) {
properties.setProperty(PROPERTY_OPSCODEPLATFORM_ENDPOINT, checkNotNull(endpoint, "endpoint")
.toString());
return this;
}
}

View File

@ -0,0 +1,62 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.opscodeplatform.config;
import java.net.URI;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.lifecycle.Closer;
import org.jclouds.opscodeplatform.OpscodePlatform;
import org.jclouds.opscodeplatform.OpscodePlatformAsyncClient;
import org.jclouds.opscodeplatform.OpscodePlatformClient;
import org.jclouds.opscodeplatform.reference.OpscodePlatformConstants;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.internal.RestContextImpl;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
/**
* Configures the OpscodePlatform connection, including logging and http transport.
*
* @author Adrian Cole
*/
public class OpscodePlatformContextModule extends AbstractModule {
public OpscodePlatformContextModule(String providerName) {
}
@Override
protected void configure() {
}
@Provides
@Singleton
RestContext<OpscodePlatformAsyncClient, OpscodePlatformClient> provideContext(Closer closer,
OpscodePlatformAsyncClient asyncApi, OpscodePlatformClient syncApi,
@OpscodePlatform URI endPoint,
@Named(OpscodePlatformConstants.PROPERTY_OPSCODEPLATFORM_ENDPOINT) String account) {
return new RestContextImpl<OpscodePlatformAsyncClient, OpscodePlatformClient>(closer,
asyncApi, syncApi, endPoint, account);
}
}

View File

@ -0,0 +1,88 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.
* ====================================================================
*/
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform.config;
import java.net.URI;
import java.util.Map;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.chef.ChefAsyncClient;
import org.jclouds.chef.ChefClient;
import org.jclouds.chef.config.BaseChefRestClientModule;
import org.jclouds.http.RequiresHttp;
import org.jclouds.opscodeplatform.OpscodePlatform;
import org.jclouds.opscodeplatform.OpscodePlatformAsyncClient;
import org.jclouds.opscodeplatform.OpscodePlatformClient;
import org.jclouds.opscodeplatform.reference.OpscodePlatformConstants;
import org.jclouds.rest.ConfiguresRestClient;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Provides;
/**
* Configures the Opscode Platform connection.
*
* @author Adrian Cole
*/
@RequiresHttp
@ConfiguresRestClient
public class OpscodePlatformRestClientModule extends
BaseChefRestClientModule<OpscodePlatformAsyncClient, OpscodePlatformClient> {
public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap
.<Class<?>, Class<?>> builder()//
.put(ChefClient.class, ChefAsyncClient.class)//
.build();
public OpscodePlatformRestClientModule() {
super(OpscodePlatformClient.class, OpscodePlatformAsyncClient.class, DELEGATE_MAP);
}
@Provides
@Singleton
@OpscodePlatform
protected URI provideURI(
@Named(OpscodePlatformConstants.PROPERTY_OPSCODEPLATFORM_ENDPOINT) String endpoint) {
return URI.create(endpoint);
}
}

View File

@ -0,0 +1,29 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.opscodeplatform.reference;
/**
* Configuration properties and constants used in Opscode Platform connections.
*
* @author Adrian Cole
*/
public interface OpscodePlatformConstants {
public static final String PROPERTY_OPSCODEPLATFORM_ENDPOINT = "jclouds.opscodeplatform.endpoint";
}

View File

@ -0,0 +1,307 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutionException;
import org.jclouds.chef.domain.Organization;
import org.jclouds.chef.domain.User;
import org.jclouds.chef.filters.SignedHeaderAuth;
import org.jclouds.chef.filters.SignedHeaderAuthTest;
import org.jclouds.chef.functions.ParseKeyFromJson;
import org.jclouds.chef.functions.ParseOrganizationFromJson;
import org.jclouds.chef.functions.ParseUserFromJson;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.TransformingHttpCommandExecutorService;
import org.jclouds.logging.config.NullLoggingModule;
import org.jclouds.opscodeplatform.config.OpscodePlatformRestClientModule;
import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.config.RestModule;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.util.Jsr330;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
/**
* Tests annotation parsing of {@code OpscodePlatformAsyncClient}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "opscodeplatform.OpscodePlatformAsyncClientTest")
public class OpscodePlatformAsyncClientTest extends RestClientTest<OpscodePlatformAsyncClient> {
public void testDelegatedChefCallsResolveProperly() throws SecurityException,
NoSuchMethodException, InterruptedException, ExecutionException {
final TransformingHttpCommandExecutorService httpExecutor = createMock(TransformingHttpCommandExecutorService.class);
Injector injector = Guice.createInjector(createModule(), new RestModule() {
@Override
protected void configure() {
bind(TransformingHttpCommandExecutorService.class).toInstance(httpExecutor);
super.configure();
}
}, new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor()));
replay(httpExecutor);
OpscodePlatformAsyncClient caller = injector.getInstance(OpscodePlatformAsyncClient.class);
try {
caller.getChefClientForOrg("goo").listClients().get();
assert false : "shouldn't have connected as this url should be dummy";
} catch (AssertionError e) {
assert e.getMessage().indexOf(
"[request=GET https://api.opscode.com/organizations/goo/clients HTTP/1.1]") != -1 : e
.getMessage();
}
}
public void testCreateUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("createUser", User.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, new User("myuser"));
assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/users HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 21\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"username\":\"myuser\"}");
// now make sure request filters apply by replaying
Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
Iterables.getOnlyElement(httpRequest.getFilters()).filter(httpRequest);
assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/users HTTP/1.1");
assertHeadersEqual(
httpRequest,
new StringBuilder("Accept: application/json")
.append("\n")
.append("Content-Length: 21")
.append("\n")
.append("Content-Type: application/json")
.append("\n")
.append(
"X-Ops-Authorization-1: kfrkDpfgNU26k70R1vl1bEWk0Q0f9Fs/3kxOX7gHd7iNoJq03u7RrcrAOSgL")
.append("\n")
.append(
"X-Ops-Authorization-2: ETj5JNeCk18BmFkHMAbCA9hXVo1T4rlHCpbuzAzFlFxUGAT4wj8UoO7V886X")
.append("\n")
.append(
"X-Ops-Authorization-3: Kf8DvihP6ElthCNuu1xuhN0B4GEmWC9+ut7UMLe0L2T34VzkbCtuInGbf42/")
.append("\n")
.append(
"X-Ops-Authorization-4: G7iu94/xFOT1gN9cex4pNyTnRCHzob4JVU1usxt/2g5grN2SyYwRS5+4MNLN")
.append("\n")
.append(
"X-Ops-Authorization-5: WY/iLUPb/9dwtiIQsnUOXqDrs28zNswZulQW4AzYRd7MczJVKU4y4+4XRcB4")
.append("\n")
.append(
"X-Ops-Authorization-6: 2+BFLT5o6P6G0D+eCu3zSuaqEJRucPJPaDGWdKIMag==")
.append("\n").append("X-Ops-Content-Hash: yLHOxvgIEtNw5UrZDxslOeMw1gw=")
.append("\n").append("X-Ops-Sign: version=1.0").append("\n").append(
"X-Ops-Timestamp: timestamp").append("\n").append(
"X-Ops-Userid: user").append("\n").toString());
assertPayloadEquals(httpRequest, "{\"username\":\"myuser\"}");
assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testUpdateUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("updateUser", User.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, new User("myuser"));
assertRequestLineEquals(httpRequest, "PUT https://api.opscode.com/users/myuser HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 21\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"username\":\"myuser\"}");
assertResponseParserClassEquals(method, httpRequest, ParseUserFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testGetUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("getUser", String.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, "myuser");
assertRequestLineEquals(httpRequest, "GET https://api.opscode.com/users/myuser HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseUserFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testDeleteUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("deleteUser", String.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, "myuser");
assertRequestLineEquals(httpRequest, "DELETE https://api.opscode.com/users/myuser HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseUserFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testCreateOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("createOrg", Organization.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, new Organization("myorganization"));
assertRequestLineEquals(httpRequest, "POST https://api.opscode.com/organizations HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 25\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"name\":\"myorganization\"}");
assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testUpdateOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("updateOrg", Organization.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, new Organization("myorganization"));
assertRequestLineEquals(httpRequest,
"PUT https://api.opscode.com/organizations/myorganization HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 25\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"name\":\"myorganization\"}");
assertResponseParserClassEquals(method, httpRequest, ParseOrganizationFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testGetOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("getOrg", String.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, "myorganization");
assertRequestLineEquals(httpRequest,
"GET https://api.opscode.com/organizations/myorganization HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseOrganizationFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnNullOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testDeleteOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = OpscodePlatformAsyncClient.class.getMethod("deleteOrg", String.class);
GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest = processor.createRequest(
method, "myorganization");
assertRequestLineEquals(httpRequest,
"DELETE https://api.opscode.com/organizations/myorganization HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseOrganizationFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
@Override
protected void checkFilters(GeneratedHttpRequest<OpscodePlatformAsyncClient> httpRequest) {
assertEquals(httpRequest.getFilters().size(), 1);
assertEquals(httpRequest.getFilters().get(0).getClass(), SignedHeaderAuth.class);
}
@Override
protected TypeLiteral<RestAnnotationProcessor<OpscodePlatformAsyncClient>> createTypeLiteral() {
return new TypeLiteral<RestAnnotationProcessor<OpscodePlatformAsyncClient>>() {
};
}
@Override
protected Module createModule() {
return new OpscodePlatformRestClientModule() {
@Override
protected void configure() {
Jsr330.bindProperties(binder(), new OpscodePlatformPropertiesBuilder("user",
SignedHeaderAuthTest.PRIVATE_KEY).build());
install(new NullLoggingModule());
super.configure();
}
@Override
protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
return "timestamp";
}
};
}
}

View File

@ -0,0 +1,166 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.opscodeplatform;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertNotNull;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import org.jclouds.chef.domain.User;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.RestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
/**
* Tests behavior of {@code OpscodePlatformClient}
*
* @author Adrian Cole
*/
@Test(groups = "live", testName = "chef.OpscodePlatformClientLiveTest")
public class OpscodePlatformClientLiveTest {
private RestContext<OpscodePlatformAsyncClient, OpscodePlatformClient> validatorConnection;
private RestContext<OpscodePlatformAsyncClient, OpscodePlatformClient> clientConnection;
private String orgname;
private String clientKey;
public static final String PREFIX = System.getProperty("user.name") + "-jcloudstest";
@BeforeClass(groups = { "live" })
public void setupClient() throws IOException {
orgname = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
String keyfile = System.getProperty("jclouds.test.key");
if (keyfile == null || keyfile.equals(""))
keyfile = "/etc/chef/validation.pem";
validatorConnection = createConnection(orgname + "-validator", Files.toString(new File(
keyfile), Charsets.UTF_8));
}
private RestContext<OpscodePlatformAsyncClient, OpscodePlatformClient> createConnection(
String identity, String key) throws IOException {
return OpscodePlatformContextFactory.createContext(identity, key, new Log4JLoggingModule());
}
@Test
public void testListClientsInOrg() throws Exception {
Set<String> clients = validatorConnection.getApi().getChefClientForOrg(orgname).listClients();
assertNotNull(clients);
assert clients.contains(orgname + "-validator");
}
@Test(dependsOnMethods = "testListClientsInOrg")
public void testCreateClientInOrg() throws Exception {
validatorConnection.getApi().getChefClientForOrg(orgname).deleteClient(PREFIX);
clientKey = validatorConnection.getApi().getChefClientForOrg(orgname).createClient(PREFIX);
assertNotNull(clientKey);
System.out.println(clientKey);
clientConnection = createConnection(PREFIX, clientKey);
clientConnection.getApi().getChefClientForOrg(orgname).clientExists(PREFIX);
}
@Test(dependsOnMethods = "testCreateClientInOrg")
public void testGenerateKeyForClientInOrg() throws Exception {
clientKey = validatorConnection.getApi().getChefClientForOrg(orgname).generateKeyForClient(
PREFIX);
assertNotNull(clientKey);
clientConnection.close();
clientConnection = createConnection(PREFIX, clientKey);
clientConnection.getApi().getChefClientForOrg(orgname).clientExists(PREFIX);
}
@Test(dependsOnMethods = "testCreateClientInOrg")
public void testClientExistsInOrg() throws Exception {
assertNotNull(validatorConnection.getApi().getChefClientForOrg(orgname).clientExists(PREFIX));
}
@Test(expectedExceptions = AuthorizationException.class)
public void testGetOrgFailsForValidationKey() throws Exception {
validatorConnection.getApi().getOrg(orgname);
}
@Test(dependsOnMethods = "testGenerateKeyForClientInOrg", expectedExceptions = AuthorizationException.class)
public void testGetOrgFailsForClient() throws Exception {
clientConnection.getApi().getOrg(orgname);
}
@Test(enabled = false)
public void testGetUser() throws Exception {
User user = validatorConnection.getApi().getUser(orgname);
assertNotNull(user);
}
@Test(enabled = false)
public void testCreateUser() throws Exception {
// TODO
}
@Test(enabled = false)
public void testUpdateUser() throws Exception {
// TODO
}
@Test(enabled = false)
public void testDeleteUser() throws Exception {
// TODO
}
@Test(enabled = false)
public void testCreateOrg() throws Exception {
// TODO
}
@Test(enabled = false)
public void testUpdateOrg() throws Exception {
// TODO
}
@Test(enabled = false)
public void testDeleteOrg() throws Exception {
// TODO
}
@Test(dependsOnMethods = "testGenerateKeyForClientInOrg")
public void testListCookbooksInOrg() throws Exception {
System.err.println(clientConnection.getApi().getChefClientForOrg(orgname).listCookbooks());
}
@AfterClass(groups = { "live" })
public void teardownClient() throws IOException {
if (clientConnection != null)
clientConnection.close();
if (validatorConnection != null)
validatorConnection.close();
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2009 Cloud Conscious, LLC.
<info@cloudconscious.com>
====================================================================
Licensed 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.
====================================================================
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--
For more configuration infromation and examples see the Apache
Log4j website: http://logging.apache.org/log4j/
-->
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
debug="false">
<!-- A time/date based rolling appender -->
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds-wire.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!--
The default pattern: Date Priority [Category]
Message${symbol_escape}n
-->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message${symbol_escape}n <param
name="ConversionPattern" value="%d %-5r %-5p [%c]
(%t:%x) %m%n"/>
-->
</layout>
</appender>
<!-- A time/date based rolling appender -->
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="target/test-data/jclouds.log" />
<param name="Append" value="true" />
<!-- Rollover at midnight each day -->
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!--
The default pattern: Date Priority [Category]
Message${symbol_escape}n
-->
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
<!--
The full pattern: Date MS Priority [Category]
(Thread:NDC) Message${symbol_escape}n <param
name="ConversionPattern" value="%d %-5r %-5p [%c]
(%t:%x) %m%n"/>
-->
</layout>
</appender>
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
<appender-ref ref="WIREFILE" />
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.jclouds">
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
<category name="jclouds.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<category name="jclouds.wire">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="WARN" />
</root>
</log4j:configuration>

View File

@ -51,6 +51,7 @@
<module>vcloud</module>
<module>gogrid</module>
<module>chef</module>
<module>opscodeplatform</module>
<module>ibmdev</module>
</modules>
<build>