Issue 191: added client and user support

This commit is contained in:
Adrian Cole 2010-06-04 16:38:59 -07:00
parent 8234c94f37
commit 551a3639b5
24 changed files with 807 additions and 153 deletions

View File

@ -23,20 +23,26 @@
*/
package org.jclouds.chef;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.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;
@ -47,7 +53,9 @@ 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;
@ -63,9 +71,53 @@ import com.google.common.util.concurrent.ListenableFuture;
@RequestFilters(SignedHeaderAuth.class)
@Consumes(MediaType.APPLICATION_JSON)
public interface ChefAsyncClient {
/**
* @see ChefClient#createClientInOrganization
*/
@POST
@Path("/organizations/{orgname}/clients")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> createClientInOrg(@PathParam("orgname") String orgname,
@BinderParam(BindClientnameToJsonPayload.class) String clientname);
/**
* @see ChefAsyncClient#createUser
* @see ChefClient#generateKeyForClientInOrg
*/
@PUT
@Path("/organizations/{orgname}/clients/{clientname}")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> generateKeyForClientInOrg(
@PathParam("orgname") String orgname,
@PathParam("clientname") @BinderParam(BindGenerateKeyForClientToJsonPayload.class) String clientname);
/**
* @see ChefClient#clientExistsInOrg
*/
@HEAD
@Path("/organizations/{orgname}/clients/{clientname}")
@ExceptionParser(ReturnFalseOnNotFoundOr404.class)
ListenableFuture<Boolean> clientExistsInOrg(@PathParam("orgname") String orgname,
@PathParam("clientname") String clientname);
/**
* @see ChefClient#deleteClientInOrg
*/
@DELETE
@Path("/organizations/{orgname}/clients/{clientname}")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
ListenableFuture<Void> deleteClientInOrg(@PathParam("orgname") String orgname,
@PathParam("clientname") String clientname);
/**
* @see ChefClient#createClientInOrganization
*/
@GET
@Path("/organizations/{orgname}/clients")
@ResponseParser(ParseKeySetFromJson.class)
ListenableFuture<Set<String>> listClientsInOrg(@PathParam("orgname") String orgname);
/**
* @see ChefClient#createUser
*/
@POST
@Path("/users")
@ -73,7 +125,7 @@ public interface ChefAsyncClient {
ListenableFuture<String> createUser(@BinderParam(BindToJsonPayload.class) User user);
/**
* @see ChefAsyncClient#updateUser
* @see ChefClient#updateUser
*/
@PUT
@Path("/users/{username}")
@ -82,7 +134,7 @@ public interface ChefAsyncClient {
@PathParam("username") @ParamParser(Username.class) @BinderParam(BindToJsonPayload.class) User user);
/**
* @see ChefAsyncClient#getUser
* @see ChefClient#getUser
*/
@GET
@Path("/users/{username}")
@ -91,7 +143,7 @@ public interface ChefAsyncClient {
ListenableFuture<User> getUser(@PathParam("username") String username);
/**
* @see ChefAsyncClient#deleteUser
* @see ChefClient#deleteUser
*/
@DELETE
@Path("/users/{username}")
@ -99,38 +151,37 @@ public interface ChefAsyncClient {
ListenableFuture<User> deleteUser(@PathParam("username") String username);
/**
* @see ChefAsyncClient#createOrganization
* @see ChefClient#createOrg
*/
@POST
@Path("/organizations")
@ResponseParser(ParseKeyFromJson.class)
ListenableFuture<String> createOrganization(
@BinderParam(BindToJsonPayload.class) Organization org);
ListenableFuture<String> createOrg(@BinderParam(BindToJsonPayload.class) Organization org);
/**
* @see ChefAsyncClient#updateOrganization
* @see ChefClient#updateOrg
*/
@PUT
@Path("/organizations/{orgname}")
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> updateOrganization(
ListenableFuture<Organization> updateOrg(
@PathParam("orgname") @ParamParser(OrganizationName.class) @BinderParam(BindToJsonPayload.class) Organization org);
/**
* @see ChefAsyncClient#getOrganization
* @see ChefClient#getOrg
*/
@GET
@Path("/organizations/{orgname}")
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> getOrganization(@PathParam("orgname") String orgname);
ListenableFuture<Organization> getOrg(@PathParam("orgname") String orgname);
/**
* @see ChefAsyncClient#deleteOrganization
* @see ChefClient#deleteOrg
*/
@DELETE
@Path("/organizations/{orgname}")
@ResponseParser(ParseOrganizationFromJson.class)
ListenableFuture<Organization> deleteOrganization(@PathParam("orgname") String orgname);
ListenableFuture<Organization> deleteOrg(@PathParam("orgname") String orgname);
}

View File

@ -41,11 +41,13 @@
*/
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;
@ -57,9 +59,34 @@ import org.jclouds.rest.ResourceNotFoundException;
* @see <a href="TODO: insert URL of Chef documentation" />
* @author Adrian Cole
*/
@Timeout(duration = 4, timeUnit = TimeUnit.SECONDS)
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface ChefClient {
/**
* creates a new client
*
* @return the private key of the client. You can then use this client name and private key to
* access the Opscode API.
* @throws AuthorizationException
* <p/>
* "401 Unauthorized" if the caller is not a recognized user.
* <p/>
* "403 Forbidden" if the caller is not authorized to create a client.
* @throws HttpResponseException
* "409 Conflict" if the client already exists
*/
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
String createClientInOrg(String orgname, String name);
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
String generateKeyForClientInOrg(String orgname, String name);
Set<String> listClientsInOrg(String orgname);
boolean clientExistsInOrg(String orgname, String name);
void deleteClientInOrg(String orgname, String name);
/**
* creates a new user
*
@ -118,7 +145,7 @@ public interface ChefClient {
* <p/>
* "403 Forbidden" if the caller is not authorized to create a organization.
*/
String createOrganization(Organization organization);
String createOrg(Organization organization);
/**
* updates an existing organization. Note: you must have update rights on the organization.
@ -131,14 +158,14 @@ public interface ChefClient {
* @throws ResourceNotFoundException
* if the organization does not exist.
*/
Organization updateOrganization(Organization organization);
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 getOrganization(String organizationname);
Organization getOrg(String organizationname);
/**
* deletes an existing organization. Note: you must have delete rights on the organization.
@ -153,5 +180,5 @@ public interface ChefClient {
* <p/>
* 404 Not Found if the organization does not exist.
*/
Organization deleteOrganization(String organizationname);
Organization deleteOrg(String organizationname);
}

View File

@ -49,8 +49,8 @@ public class ChefContextBuilder extends RestContextBuilder<ChefAsyncClient, Chef
super(providerName, new TypeLiteral<ChefAsyncClient>() {
}, new TypeLiteral<ChefClient>() {
}, props);
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_USER_ID));
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_PRIVATE_KEY));
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_IDENTITY));
checkNotNull(properties.getProperty(ChefConstants.PROPERTY_CHEF_RSA_KEY));
}
protected void addClientModule(List<Module> modules) {

View File

@ -49,15 +49,15 @@ import com.google.inject.Module;
*/
public class ChefContextFactory {
public static RestContext<ChefAsyncClient, ChefClient> createContext(String user, String password,
public static RestContext<ChefAsyncClient, ChefClient> createContext(String identity, String rsaKey,
Module... modules) {
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(user, password).build())
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(identity, rsaKey).build())
.withModules(modules).buildContext();
}
public static RestContext<ChefAsyncClient, ChefClient> createContext(URI endpoint, String user, String password,
public static RestContext<ChefAsyncClient, ChefClient> createContext(URI endpoint, String identity, String rsaKey,
Module... modules) {
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(user, password).withEndpoint(endpoint).build())
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(identity, rsaKey).withEndpoint(endpoint).build())
.withModules(modules).buildContext();
}

View File

@ -25,9 +25,9 @@ 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_PRIVATE_KEY;
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_USER_ID;
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_IDENTITY;
import java.net.URI;
import java.util.Properties;
@ -52,14 +52,14 @@ public class ChefPropertiesBuilder extends PropertiesBuilder {
super(properties);
}
public ChefPropertiesBuilder(String id, String secret) {
public ChefPropertiesBuilder(String identity, String rsaKey) {
super();
withCredentials(id, secret);
withCredentials(identity, rsaKey);
}
public ChefPropertiesBuilder withCredentials(String id, String secret) {
properties.setProperty(PROPERTY_CHEF_USER_ID, checkNotNull(id, "user"));
properties.setProperty(PROPERTY_CHEF_PRIVATE_KEY, checkNotNull(secret, "password"));
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;
}

View File

@ -0,0 +1,48 @@
/**
*
* 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.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 BindClientnameToJsonPayload 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("{\"clientname\":\"%s\"}", payload));
}
}

View File

@ -0,0 +1,49 @@
/**
*
* 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.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 BindGenerateKeyForClientToJsonPayload 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("{\"clientname\":\"%s\", \"private_key\": true}",
payload));
}
}

View File

@ -77,7 +77,7 @@ public class ChefContextModule extends AbstractModule {
@Provides
@Singleton
RestContext<ChefAsyncClient, ChefClient> provideContext(Closer closer, ChefAsyncClient asyncApi,
ChefClient syncApi, @Chef URI endPoint, @Named(ChefConstants.PROPERTY_CHEF_USER_ID) String account) {
ChefClient syncApi, @Chef URI endPoint, @Named(ChefConstants.PROPERTY_CHEF_IDENTITY) String account) {
return new RestContextImpl<ChefAsyncClient, ChefClient>(closer, asyncApi, syncApi, endPoint, account);
}

View File

@ -57,12 +57,17 @@ 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;
@ -95,7 +100,7 @@ public class ChefRestClientModule extends AbstractModule {
final DateService dateService) {
return new ExpirableSupplier<String>(new Supplier<String>() {
public String get() {
return dateService.iso8601DateFormat();
return dateService.iso8601SecondsDateFormat();
}
}, seconds, TimeUnit.SECONDS);
}
@ -108,7 +113,7 @@ public class ChefRestClientModule extends AbstractModule {
@Provides
@Singleton
public PrivateKey provideKey(@Named(ChefConstants.PROPERTY_CHEF_PRIVATE_KEY) String key)
public PrivateKey provideKey(@Named(ChefConstants.PROPERTY_CHEF_RSA_KEY) String key)
throws IOException {
// TODO do this without adding a provider
Security.addProvider(new BouncyCastleProvider());
@ -135,9 +140,14 @@ public class ChefRestClientModule extends AbstractModule {
protected URI provideURI(@Named(ChefConstants.PROPERTY_CHEF_ENDPOINT) String endpoint) {
return URI.create(endpoint);
}
protected void bindErrorHandlers() {
// TODO
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() {

View File

@ -36,6 +36,7 @@ public class User implements Comparable<User> {
@SerializedName("display_name")
private String displayName;
private String email;
private String password;
public User(String username) {
this.username = username;
@ -107,6 +108,7 @@ public class User implements Comparable<User> {
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
result = prime * result + ((middleName == null) ? 0 : middleName.hashCode());
result = prime * result + ((password == null) ? 0 : password.hashCode());
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@ -145,6 +147,11 @@ public class User implements Comparable<User> {
return false;
} else if (!middleName.equals(other.middleName))
return false;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (username == null) {
if (other.username != null)
return false;
@ -159,4 +166,12 @@ public class User implements Comparable<User> {
+ firstName + ", middleName=" + middleName + ", lastName=" + lastName + ", email="
+ email + "]";
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
}

View File

@ -77,7 +77,7 @@ public class SignedHeaderAuth implements HttpRequestFilter {
@Inject
public SignedHeaderAuth(SignatureWire signatureWire,
@Named(ChefConstants.PROPERTY_CHEF_USER_ID) String userId, PrivateKey privateKey,
@Named(ChefConstants.PROPERTY_CHEF_IDENTITY) String userId, PrivateKey privateKey,
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService) {
this.signatureWire = signatureWire;
this.userId = userId;

View File

@ -0,0 +1,71 @@
/**
*
* 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.functions;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Singleton;
import org.jclouds.http.HttpResponse;
import org.jclouds.util.Utils;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
/**
*
*
* @author Adrian Cole
*/
@Singleton
public class ParseErrorFromJsonOrNull implements Function<HttpResponse, String> {
Pattern pattern = Pattern.compile(".*error\": *\"([^\"]+)\".*");
@Override
public String apply(HttpResponse response) {
if (response.getContent() == null)
return null;
try {
return parse(Utils.toStringAndClose(response.getContent()));
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
response.getContent().close();
} catch (IOException e) {
Throwables.propagate(e);
}
}
}
public String parse(String in) {
Matcher matcher = pattern.matcher(in);
while (matcher.find()) {
return matcher.group(1);
}
return null;
}
}

View File

@ -42,7 +42,7 @@ import com.google.common.base.Throwables;
*/
@Singleton
public class ParseKeyFromJson implements Function<HttpResponse, String> {
Pattern pattern = Pattern.compile(".*private_key\": \"([^\"]+)\".*");
Pattern pattern = Pattern.compile(".*private_key\": *\"([^\"]+)\".*");
@Override
public String apply(HttpResponse response) {
@ -62,7 +62,7 @@ public class ParseKeyFromJson implements Function<HttpResponse, String> {
public String parse(String in) {
Matcher matcher = pattern.matcher(in);
while (matcher.find()) {
return matcher.group(1);
return matcher.group(1).replaceAll("\\\\n", "\n");
}
assert false : String.format("pattern: %s didn't match %s", pattern, in);
return null;

View File

@ -0,0 +1,81 @@
/**
*
* 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.functions;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.http.functions.ParseJson;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
/**
* @author Adrian Cole
*/
@Singleton
public class ParseKeySetFromJson extends ParseJson<Set<String>> {
@Inject
public ParseKeySetFromJson(Gson gson) {
super(gson);
}
@SuppressWarnings("unchecked")
@Override
protected Set<String> apply(InputStream stream) {
try {
Type map = new TypeToken<Map<String, String>>() {
}.getType();
return ((Map<String, String>) gson.fromJson(new InputStreamReader(stream, "UTF-8"), map))
.keySet();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("jclouds requires UTF-8 encoding", e);
}
}
}

View File

@ -0,0 +1,77 @@
/**
*
* 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.chef.handlers;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.chef.functions.ParseErrorFromJsonOrNull;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.logging.Logger;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import com.google.common.io.Closeables;
/**
* This will parse and set an appropriate exception on the command object.
*
* @author Adrian Cole
*
*/
@Singleton
public class ChefErrorHandler implements HttpErrorHandler {
@Resource
protected Logger logger = Logger.NULL;
private final ParseErrorFromJsonOrNull errorParser;
@Inject
ChefErrorHandler(ParseErrorFromJsonOrNull errorParser) {
this.errorParser = errorParser;
}
public void handleError(HttpCommand command, HttpResponse response) {
String message = errorParser.apply(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);
}
}
}

View File

@ -48,11 +48,20 @@ package org.jclouds.chef.reference;
*/
public interface ChefConstants {
public static final String PROPERTY_CHEF_ENDPOINT = "jclouds.chef.endpoint";
public static final String PROPERTY_CHEF_USER_ID = "jclouds.chef.user-id";
/**
* There are generally 3 types of identities
* <ul>
* <li>validator - used to create clients within an organization; {@code orgname}-validator</li>
* <li>client - scoped to an organization, used on nodes to run chef</li>
* <li>user - used to run commands like knife and access cookbook sites</li>
* </ul>
*
*/
public static final String PROPERTY_CHEF_IDENTITY = "jclouds.chef.identity";
/**
* The PEM-encoded key
*/
public static final String PROPERTY_CHEF_PRIVATE_KEY = "jclouds.chef.private-key";
public static final String PROPERTY_CHEF_RSA_KEY = "jclouds.chef.rsa-key";
/**
* how often to refresh timestamps in seconds.

View File

@ -35,12 +35,17 @@ 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;
import org.jclouds.util.Jsr330;
@ -58,6 +63,98 @@ 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");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ReturnTrueIf2xx.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnFalseOnNotFoundOr404.class);
checkFilters(httpRequest);
}
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");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, CloseContentAndReturn.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class);
checkFilters(httpRequest);
}
public void testGenerateKeyForClientInOrg() 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");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 44\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"clientname\":\"client\", \"private_key\": true}");
assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
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");
assertRequestLineEquals(httpRequest,
"POST https://api.opscode.com/organizations/org/clients HTTP/1.1");
assertHeadersEqual(httpRequest,
"Accept: application/json\nContent-Length: 23\nContent-Type: application/json\n");
assertPayloadEquals(httpRequest, "{\"clientname\":\"client\"}");
assertResponseParserClassEquals(method, httpRequest, ParseKeyFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testListClientsInOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("listClientsInOrg", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method, "org");
assertRequestLineEquals(httpRequest,
"GET https://api.opscode.com/organizations/org/clients HTTP/1.1");
assertHeadersEqual(httpRequest, "Accept: application/json\n");
assertPayloadEquals(httpRequest, null);
assertResponseParserClassEquals(method, httpRequest, ParseKeySetFromJson.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpRequest);
}
public void testCreateUser() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("createUser", User.class);
@ -163,9 +260,8 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testCreateOrganization() throws SecurityException, NoSuchMethodException,
IOException {
Method method = ChefAsyncClient.class.getMethod("createOrganization", Organization.class);
public void testCreateOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("createOrg", Organization.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
new Organization("myorganization"));
@ -182,9 +278,8 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testUpdateOrganization() throws SecurityException, NoSuchMethodException,
IOException {
Method method = ChefAsyncClient.class.getMethod("updateOrganization", Organization.class);
public void testUpdateOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("updateOrg", Organization.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
new Organization("myorganization"));
@ -202,8 +297,8 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testGetOrganization() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("getOrganization", String.class);
public void testGetOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("getOrg", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
"myorganization");
@ -220,9 +315,8 @@ public class ChefAsyncClientTest extends RestClientTest<ChefAsyncClient> {
}
public void testDeleteOrganization() throws SecurityException, NoSuchMethodException,
IOException {
Method method = ChefAsyncClient.class.getMethod("deleteOrganization", String.class);
public void testDeleteOrg() throws SecurityException, NoSuchMethodException, IOException {
Method method = ChefAsyncClient.class.getMethod("deleteOrg", String.class);
GeneratedHttpRequest<ChefAsyncClient> httpRequest = processor.createRequest(method,
"myorganization");

View File

@ -28,11 +28,14 @@ import static org.testng.Assert.assertNotNull;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import org.jclouds.chef.domain.Organization;
import org.jclouds.chef.domain.User;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.testng.annotations.BeforeGroups;
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;
@ -46,23 +49,73 @@ import com.google.common.io.Files;
@Test(groups = "live", testName = "chef.ChefClientLiveTest")
public class ChefClientLiveTest {
private ChefClient client;
private String username;
private RestContext<ChefAsyncClient, ChefClient> validatorConnection;
private RestContext<ChefAsyncClient, ChefClient> clientConnection;
@BeforeGroups(groups = { "live" })
private String orgname;
private String clientKey;
public static final String PREFIX = System.getProperty("user.name") + "-jcloudstest";
@BeforeClass(groups = { "live" })
public void setupClient() throws IOException {
username = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
orgname = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
String keyfile = System.getProperty("jclouds.test.key");
if (keyfile == null || keyfile.equals(""))
keyfile = System.getProperty("user.home") + "/chef/client.pem";
client = ChefContextFactory.createContext(username,
Files.toString(new File(keyfile), Charsets.UTF_8), new Log4JLoggingModule())
.getApi();
keyfile = System.getProperty("user.home") + "/chef/validation.pem";
validatorConnection = createConnection(orgname + "-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());
}
@Test
public void testListClientsInOrg() throws Exception {
Set<String> clients = validatorConnection.getApi().listClientsInOrg(orgname);
assertNotNull(clients);
assert clients.contains(orgname + "-validator");
}
@Test(dependsOnMethods = "testListClientsInOrg")
public void testCreateClientInOrg() throws Exception {
validatorConnection.getApi().deleteClientInOrg(orgname, PREFIX);
clientKey = validatorConnection.getApi().createClientInOrg(orgname, PREFIX);
assertNotNull(clientKey);
System.out.println(clientKey);
clientConnection = createConnection(PREFIX, clientKey);
clientConnection.getApi().clientExistsInOrg(orgname, PREFIX);
}
@Test(dependsOnMethods = "testCreateClientInOrg")
public void testGenerateKeyForClientInOrg() throws Exception {
clientKey = validatorConnection.getApi().generateKeyForClientInOrg(orgname, PREFIX);
assertNotNull(clientKey);
clientConnection.close();
clientConnection = createConnection(PREFIX, clientKey);
clientConnection.getApi().clientExistsInOrg(orgname, PREFIX);
}
@Test(dependsOnMethods = "testCreateClientInOrg")
public void testClientExistsInOrg() throws Exception {
assertNotNull(validatorConnection.getApi().clientExistsInOrg(orgname, 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 = client.getUser(username);
User user = validatorConnection.getApi().getUser(orgname);
assertNotNull(user);
}
@ -82,24 +135,26 @@ public class ChefClientLiveTest {
}
@Test(enabled = false)
public void testCreateOrganization() throws Exception {
// TODO
}
@Test
public void testGetOrganization() throws Exception {
Organization organization = client.getOrganization("jclouds");
assertNotNull(organization);
}
@Test(enabled = false)
public void testUpdateOrganization() throws Exception {
public void testCreateOrg() throws Exception {
// TODO
}
@Test(enabled = false)
public void testDeleteOrganization() throws Exception {
public void testUpdateOrg() throws Exception {
// TODO
}
@Test(enabled = false)
public void testDeleteOrg() throws Exception {
// TODO
}
@AfterClass(groups = { "live" })
public void teardownClient() throws IOException {
if (clientConnection != null)
clientConnection.close();
if (validatorConnection != null)
validatorConnection.close();
}
}

View File

@ -111,13 +111,18 @@ public class SignedHeaderAuthTest {
public static final Multimap<String, String> EXPECTED_SIGN_RESULT_EMPTY = ImmutableMultimap
.<String, String> builder().put("X-Ops-Content-Hash", X_OPS_CONTENT_HASH_EMPTY).put(
"X-Ops-Userid", USER_ID).put("X-Ops-Sign", "version=1.0").put(
"X-Ops-Authorization-1", "N6U75kopDK64cEFqrB6vw+PnubnXr0w5LQeXnIGNGLRP2LvifwIeisk7QxEx").put(
"X-Ops-Authorization-2", "mtpQOWAw8HvnWErjzuk9AvUsqVmWpv14ficvkaD79qsPMvbje+aLcIrCGT1P").put(
"X-Ops-Authorization-3", "3d2uvf4w7iqwzrIscPnkxLR6o6pymR90gvJXDPzV7Le0jbfD8kmZ8AAK0sGG").put(
"X-Ops-Authorization-4", "09F1ftW80bLatJTA66Cw2wBz261r6x/abZhIKFJFDWLzyQGJ8ZNOkUrDDtgI").put(
"X-Ops-Authorization-5", "svLVXpOJKZZfKunsElpWjjsyNt3k8vpI1Y4ANO8Eg2bmeCPeEK+YriGm5fbC").put(
"X-Ops-Authorization-6", "DzWNPylHJqMeGKVYwGQKpg62QDfe5yXh3wZLiQcXow==").put("X-Ops-Timestamp",
TIMESTAMP_ISO8601).build();
"X-Ops-Authorization-1",
"N6U75kopDK64cEFqrB6vw+PnubnXr0w5LQeXnIGNGLRP2LvifwIeisk7QxEx").put(
"X-Ops-Authorization-2",
"mtpQOWAw8HvnWErjzuk9AvUsqVmWpv14ficvkaD79qsPMvbje+aLcIrCGT1P").put(
"X-Ops-Authorization-3",
"3d2uvf4w7iqwzrIscPnkxLR6o6pymR90gvJXDPzV7Le0jbfD8kmZ8AAK0sGG").put(
"X-Ops-Authorization-4",
"09F1ftW80bLatJTA66Cw2wBz261r6x/abZhIKFJFDWLzyQGJ8ZNOkUrDDtgI").put(
"X-Ops-Authorization-5",
"svLVXpOJKZZfKunsElpWjjsyNt3k8vpI1Y4ANO8Eg2bmeCPeEK+YriGm5fbC").put(
"X-Ops-Authorization-6", "DzWNPylHJqMeGKVYwGQKpg62QDfe5yXh3wZLiQcXow==").put(
"X-Ops-Timestamp", TIMESTAMP_ISO8601).build();
public static String PUBLIC_KEY;
public static String PRIVATE_KEY;
@ -195,6 +200,12 @@ public class SignedHeaderAuthTest {
private SignedHeaderAuth signing_obj;
private EncryptionService encryptionService;
@Test
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";
KeyPair.class.cast(new PEMReader(new StringReader(key)).readObject());
}
/**
* before class, as we need to ensure that the filter is threadsafe.
*

View File

@ -37,4 +37,11 @@ public class ParseKeyFromJsonTest {
.toInputStream("{\n\"uri\": \"https://api.opscode.com/users/bobo\", \"private_key\": \"RSA_PRIVATE_KEY\",}"))),
"RSA_PRIVATE_KEY");
}
public void test2() {
String key = handler.apply(new HttpResponse(ParseKeyFromJsonTest.class
.getResourceAsStream("/newclient.txt")));
assert key.startsWith("-----BEGIN RSA PRIVATE KEY-----\n");
}
}

View File

@ -0,0 +1,41 @@
package org.jclouds.chef.functions;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.config.ParserModule;
import org.jclouds.util.Utils;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code ParseKeySetFromJson}
*
* @author Adrian Cole
*/
@Test(groups = "unit", sequential = true, testName = "chef.ParseKeySetFromJsonTest")
public class ParseKeySetFromJsonTest {
private ParseKeySetFromJson handler;
@BeforeTest
protected void setUpInjector() throws IOException {
Injector injector = Guice.createInjector(new ParserModule());
handler = injector.getInstance(ParseKeySetFromJson.class);
}
public void testRegex() {
assertEquals(
handler
.apply(new HttpResponse(
Utils
.toInputStream("{\n\"opscode-validator\": \"https://api.opscode.com/...\", \"pimp-validator\": \"https://api.opscode.com/...\"}"))),
ImmutableSet.of("opscode-validator","pimp-validator"));
}
}

View File

@ -1,32 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
<!--
Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
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
====================================================================
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.
====================================================================
-->
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/
-->
<!--
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">
debug="false">
<!-- A time/date based rolling appender -->
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
@ -39,72 +38,80 @@
<param name="Threshold" value="TRACE" />
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message${symbol_escape}n -->
<!--
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"/>
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>
<!-- 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>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.jclouds">
<priority value="DEBUG" />
<appender-ref ref="ASYNC" />
</category>
</category>
<category name="jclouds.headers">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category><!--
</category>
<category name="jclouds.wire">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
--><!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="WARN" />
</root>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<priority value="WARN" />
</root>
</log4j:configuration>

View File

@ -0,0 +1 @@
{"clientname":"adriancole-jcloudstest","private_key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAuzaE6azgUxwESX1rCGdJ5xpdrc1XC311bOGZBCE8NA+CpFh2\npopCBQwjpOnlgpdd/+C+TESl30ojauvVej9AbgJb30Jl7e7dEX4Brncnj03G+mo+\nG4osf7I2PA/6+9Ol7xamK9GL/cs8nOb17cRTWmhTRW7+3Rrli/s6wzqQXjGjWzgz\nthXv7FOPHA87UjJzrePBFta7+S8BxKCG2QaTxzNGytSAy8KBX8BUrSt5+X22QjEM\nQF3zA4TPtoWp/lcDRzCMdffMYoVPZzKqIeEFSexwvNlJ/qU6hbcyAxab1lYawjKU\nRgvPCflVYTIw6teHNqkyvTPX+lpIAVXigSVQXwIDAQABAoIBAHz81xvTSSjzaYFO\n9Gh13QcnuSdSEi0fo4f/zdLOBY2UVVo3nW9umskX46w0ZAAd4qn0d9gfdMZwjtjR\nfoLRO8i2VnPltnt1n64P/DtoXcb03EVPLQvh4feXGVJcMOpz0TKgYmyax+W3DE6M\ne+Az1JplUELo6crgLCSapA63SK85PEuWAcMUQg9s6MnzB/qXz95yJlzgjVMIJUyb\n9jFdq2s0gefTpK2cKeSYWQAFPd41Ea5v/3j0LN8qs/dImNnzxDXu+hi8+16/4PTK\npl+1bJXwE9YkWPdd39EfjVkk6q/HyFijK3VpHnOy7n3iaJTUKwBJLRsFrQ5Eor3U\nvNKyGXECgYEA3RZdFC6MRBAo76GKd9axbA0G9Bmct9pQT4B+od2AGGOdiYzYRWfF\nlyTgctY9dcfsX5DBQFUHNnYXMHHI0wHQk1m20UpFLa7IV3RWkW5JwYkbQHmeP4pn\np8GtJEXC+4PrT0Pc32acfWozArokUju7nLLazCPCDdfc8t9MPX1W230CgYEA2MbB\ndwdwLZx9zEqZ0MciRxjsOA30b6OYPOqMP1ADVeExPN8fHLCAQjwUExQa86+FMV4H\nOtu+DXlisp+TSTRQzpXMfGppupbK1j5cqz2sU37upKuz/uf0XyeyBLOi0y9/DMl5\njG2StLLIMawRqJRUuq/fyA/6oTzADNwoW6LjCgsCgYBGvCj7lAj8nc77HEwZG2+Y\ninJ3Ftq1V/vp88qQLzYUl4qHv7BSRGlLelj1ZOY1EMnnqYCq/IlaO14f+ceu+x2o\nh0OeooyPmSQwFuC7lvWyHhPCBSdEXRvc6HJk8Iz5u7NFoQjB0SqwVZIMhVGpncLg\n17h5J9emZjIi4p6Z7cgkYQKBgHt+/8in3Cif9qrj9S0TxVtrv2dPy+mt8ZUCqlOH\nad8LI9nh4v+dLfSN9YHI+nHJlL/DKatGdMeIV8obTvVtcHvAq3ZVyVYbggL8FB8a\nS4plzd7SUwDtdDKhkrFLBX/6lw7Z2P0/j0ySbaqetJCtsHeKqpp3P/mLen3ZDsTl\nzyJxAoGBAIxl1SGzu3lO3BQ5+EPaprNw3eN3nzG4WLQvnzZwpeAFS+E5pllMkIfs\nu01Vfv68NC4u6LFgdXSY1vQt6hiA5TNqQk0TyVfFAunbXgTekF6XqDPQUf1nq9aZ\nlMvo4vlaLDKBkhG5HJE/pIa0iB+RMZLS0GhxsIWerEDmYdHKM25o\n-----END RSA PRIVATE KEY-----\n","uri":"https://api.opscode.com/organizations/jclouds/clients/adriancole-jcloudstest","certificate":"-----BEGIN CERTIFICATE-----\nMIIClzCCAgCgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFjAUBgNVBAoM\nDU9wc2NvZGUsIEluYy4xHDAaBgNVBAsME0NlcnRpZmljYXRlIFNlcnZpY2UxMjAw\nBgNVBAMMKW9wc2NvZGUuY29tL2VtYWlsQWRkcmVzcz1hdXRoQG9wc2NvZGUuY29t\nMB4XDTEwMDYwNDIzMzM0NloXDTIwMDYwMTIzMzM0NlowADCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBALs2hOms4FMcBEl9awhnSecaXa3NVwt9dWzhmQQh\nPDQPgqRYdqaKQgUMI6Tp5YKXXf/gvkxEpd9KI2rr1Xo/QG4CW99CZe3u3RF+Aa53\nJ49NxvpqPhuKLH+yNjwP+vvTpe8WpivRi/3LPJzm9e3EU1poU0Vu/t0a5Yv7OsM6\nkF4xo1s4M7YV7+xTjxwPO1Iyc63jwRbWu/kvAcSghtkGk8czRsrUgMvCgV/AVK0r\nefl9tkIxDEBd8wOEz7aFqf5XA0cwjHX3zGKFT2cyqiHhBUnscLzZSf6lOoW3MgMW\nm9ZWGsIylEYLzwn5VWEyMOrXhzapMr0z1/paSAFV4oElUF8CAwEAATANBgkqhkiG\n9w0BAQUFAAOBgQCTllbpWNagYjCiaU5UnjIFXn0YyNfZzqCh8SQ0Asj8MtksVbFG\nAErp03+Cb9a7GTdNE7fIyPsLTnGzFhqTwKN+3jIj4wgxhrbYXF73x1+rDRyHjJu7\na7gdTEYZqWiAHdW47vXj69W1dB5e4vNm1F29gOSL/x7BMAyjLFWbdbKw0w==\n-----END CERTIFICATE-----\n","orgname":"jclouds}

View File

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