mirror of https://github.com/apache/jclouds.git
Issue 191: started on chef api support
This commit is contained in:
parent
66d6326dd9
commit
7e12d36870
|
@ -56,7 +56,6 @@ import org.jclouds.aws.filters.FormSigner;
|
|||
import org.jclouds.aws.handlers.AWSClientErrorRetryHandler;
|
||||
import org.jclouds.aws.handlers.AWSRedirectionRetryHandler;
|
||||
import org.jclouds.aws.handlers.ParseAWSErrorFromXmlContent;
|
||||
import org.jclouds.aws.util.RequestSigner;
|
||||
import org.jclouds.concurrent.internal.SyncProxy;
|
||||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
|
@ -70,6 +69,7 @@ import org.jclouds.net.IPSocket;
|
|||
import org.jclouds.predicates.RetryablePredicate;
|
||||
import org.jclouds.predicates.SocketOpen;
|
||||
import org.jclouds.rest.ConfiguresRestClient;
|
||||
import org.jclouds.rest.RequestSigner;
|
||||
import org.jclouds.rest.RestClientFactory;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
|
|
@ -42,7 +42,6 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.aws.reference.AWSConstants;
|
||||
import org.jclouds.aws.util.RequestSigner;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.encryption.EncryptionService;
|
||||
import org.jclouds.http.HttpException;
|
||||
|
@ -51,6 +50,7 @@ import org.jclouds.http.HttpRequestFilter;
|
|||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.internal.SignatureWire;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.rest.RequestSigner;
|
||||
import org.jclouds.rest.internal.GeneratedHttpRequest;
|
||||
import org.jclouds.rest.internal.RestAnnotationProcessor;
|
||||
import org.jclouds.util.Utils;
|
||||
|
@ -103,7 +103,7 @@ public class FormSigner implements HttpRequestFilter, RequestSigner {
|
|||
addSigningParams(decodedParams);
|
||||
validateParams(decodedParams);
|
||||
String stringToSign = createStringToSign(request, decodedParams);
|
||||
String signature = signString(stringToSign);
|
||||
String signature = sign(stringToSign);
|
||||
addSignature(decodedParams, signature);
|
||||
setPayload(request, decodedParams);
|
||||
HttpUtils.logRequest(signatureLog, request, "<<");
|
||||
|
@ -153,7 +153,7 @@ public class FormSigner implements HttpRequestFilter, RequestSigner {
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public String signString(String stringToSign) {
|
||||
public String sign(String stringToSign) {
|
||||
String signature;
|
||||
try {
|
||||
signature = encryptionService.hmacSha256Base64(stringToSign, secretKey.getBytes());
|
||||
|
|
|
@ -35,7 +35,6 @@ import org.jclouds.aws.s3.S3AsyncClient;
|
|||
import org.jclouds.aws.s3.S3Client;
|
||||
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
|
||||
import org.jclouds.aws.s3.reference.S3Constants;
|
||||
import org.jclouds.aws.util.RequestSigner;
|
||||
import org.jclouds.concurrent.ExpirableSupplier;
|
||||
import org.jclouds.concurrent.internal.SyncProxy;
|
||||
import org.jclouds.date.DateService;
|
||||
|
@ -47,6 +46,7 @@ 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.RequestSigner;
|
||||
import org.jclouds.rest.RestClientFactory;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
|
|
@ -35,7 +35,6 @@ import javax.ws.rs.core.HttpHeaders;
|
|||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.aws.s3.reference.S3Constants;
|
||||
import org.jclouds.aws.util.RequestSigner;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.encryption.EncryptionService;
|
||||
import org.jclouds.http.HttpException;
|
||||
|
@ -44,6 +43,7 @@ import org.jclouds.http.HttpRequestFilter;
|
|||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.internal.SignatureWire;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.rest.RequestSigner;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -108,14 +108,14 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
|
|||
|
||||
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign)
|
||||
throws HttpException {
|
||||
String signature = signString(toSign);
|
||||
String signature = sign(toSign);
|
||||
if (signatureWire.enabled())
|
||||
signatureWire.input(Utils.toInputStream(signature));
|
||||
request.getHeaders().replaceValues(HttpHeaders.AUTHORIZATION,
|
||||
Collections.singletonList("AWS " + accessKey + ":" + signature));
|
||||
}
|
||||
|
||||
public String signString(String toSign) {
|
||||
public String sign(String toSign) {
|
||||
String signature;
|
||||
try {
|
||||
signature = encryptionService.hmacSha1Base64(toSign, secretKey.getBytes());
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.jclouds.aws.sqs.SQS;
|
|||
import org.jclouds.aws.sqs.SQSAsyncClient;
|
||||
import org.jclouds.aws.sqs.SQSClient;
|
||||
import org.jclouds.aws.sqs.reference.SQSConstants;
|
||||
import org.jclouds.aws.util.RequestSigner;
|
||||
import org.jclouds.concurrent.internal.SyncProxy;
|
||||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
|
@ -45,6 +44,7 @@ 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.RequestSigner;
|
||||
import org.jclouds.rest.RestClientFactory;
|
||||
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.jclouds.aws.xml.ErrorHandler;
|
|||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.rest.RequestSigner;
|
||||
|
||||
/**
|
||||
* Needed to sign and verify requests and responses.
|
||||
|
@ -51,7 +52,7 @@ public class AWSUtils {
|
|||
AWSError error = (AWSError) factory.create(errorHandlerProvider.get()).parse(content);
|
||||
if ("SignatureDoesNotMatch".equals(error.getCode())) {
|
||||
error.setStringSigned(signer.createStringToSign(command.getRequest()));
|
||||
error.setSignature(signer.signString(error.getStringSigned()));
|
||||
error.setSignature(signer.sign(error.getStringSigned()));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# use glob syntax.
|
||||
syntax: glob
|
||||
target
|
||||
.settings
|
||||
.classpath
|
||||
.project
|
||||
jclouds-chef.iml
|
||||
jclouds-chef.ipr
|
||||
jclouds-chef.iws
|
|
@ -0,0 +1,97 @@
|
|||
<?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-chef</artifactId>
|
||||
<name>jclouds Chef core</name>
|
||||
<description>jclouds components to access Chef</description>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk/chef</connection>
|
||||
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk/chef</developerConnection>
|
||||
<url>http://jclouds.googlecode.com/svn/trunk/chef</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.test.user>user</jclouds.test.user>
|
||||
<jclouds.test.key>key</jclouds.test.key>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15</artifactId>
|
||||
<version>1.44</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.14</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>jclouds-log4j</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -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.chef;
|
||||
|
||||
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 Chef resource.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
|
||||
@Qualifier
|
||||
public @interface Chef {
|
||||
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
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.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.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 Chef via their REST API.
|
||||
* <p/>
|
||||
*
|
||||
* @see ChefClient
|
||||
* @see <a href="TODO: insert URL of provider documentation" />
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Endpoint(Chef.class)
|
||||
@RequestFilters(SignedHeaderAuth.class)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public interface ChefAsyncClient {
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#createUser
|
||||
*/
|
||||
@POST
|
||||
@Path("/users")
|
||||
@ResponseParser(ParseKeyFromJson.class)
|
||||
ListenableFuture<String> createUser(@BinderParam(BindToJsonPayload.class) User user);
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#updateUser
|
||||
*/
|
||||
@PUT
|
||||
@Path("/users/{username}")
|
||||
@ResponseParser(ParseUserFromJson.class)
|
||||
ListenableFuture<User> updateUser(
|
||||
@PathParam("username") @ParamParser(Username.class) @BinderParam(BindToJsonPayload.class) User user);
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#getUser
|
||||
*/
|
||||
@GET
|
||||
@Path("/users/{username}")
|
||||
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
|
||||
@ResponseParser(ParseUserFromJson.class)
|
||||
ListenableFuture<User> getUser(@PathParam("username") String username);
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#deleteUser
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/users/{username}")
|
||||
@ResponseParser(ParseUserFromJson.class)
|
||||
ListenableFuture<User> deleteUser(@PathParam("username") String username);
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#createOrganization
|
||||
*/
|
||||
@POST
|
||||
@Path("/organizations")
|
||||
@ResponseParser(ParseKeyFromJson.class)
|
||||
ListenableFuture<String> createOrganization(
|
||||
@BinderParam(BindToJsonPayload.class) Organization org);
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#updateOrganization
|
||||
*/
|
||||
@PUT
|
||||
@Path("/organizations/{orgname}")
|
||||
@ResponseParser(ParseOrganizationFromJson.class)
|
||||
ListenableFuture<Organization> updateOrganization(
|
||||
@PathParam("orgname") @ParamParser(OrganizationName.class) @BinderParam(BindToJsonPayload.class) Organization org);
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#getOrganization
|
||||
*/
|
||||
@GET
|
||||
@Path("/organizations/{orgname}")
|
||||
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
|
||||
@ResponseParser(ParseOrganizationFromJson.class)
|
||||
ListenableFuture<Organization> getOrganization(@PathParam("orgname") String orgname);
|
||||
|
||||
/**
|
||||
* @see ChefAsyncClient#deleteOrganization
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/organizations/{orgname}")
|
||||
@ResponseParser(ParseOrganizationFromJson.class)
|
||||
ListenableFuture<Organization> deleteOrganization(@PathParam("orgname") String orgname);
|
||||
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
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.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* Provides synchronous access to Chef.
|
||||
* <p/>
|
||||
*
|
||||
* @see ChefAsyncClient
|
||||
* @see <a href="TODO: insert URL of Chef documentation" />
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Timeout(duration = 4, timeUnit = TimeUnit.SECONDS)
|
||||
public interface ChefClient {
|
||||
|
||||
/**
|
||||
* 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 createOrganization(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 updateOrganization(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);
|
||||
|
||||
/**
|
||||
* 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 deleteOrganization(String organizationname);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
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 com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class ChefContextBuilder extends RestContextBuilder<ChefAsyncClient, ChefClient> {
|
||||
|
||||
public ChefContextBuilder(String providerName, Properties props) {
|
||||
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));
|
||||
}
|
||||
|
||||
protected void addClientModule(List<Module> modules) {
|
||||
modules.add(new ChefRestClientModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addContextModule(String providerName, List<Module> modules) {
|
||||
modules.add(new ChefContextModule(providerName));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
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 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/>
|
||||
* <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 ChefClient
|
||||
* @see ChefAsyncClient
|
||||
*/
|
||||
public class ChefContextFactory {
|
||||
|
||||
public static RestContext<ChefAsyncClient, ChefClient> createContext(String user, String password,
|
||||
Module... modules) {
|
||||
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(user, password).build())
|
||||
.withModules(modules).buildContext();
|
||||
}
|
||||
|
||||
public static RestContext<ChefAsyncClient, ChefClient> createContext(URI endpoint, String user, String password,
|
||||
Module... modules) {
|
||||
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(user, password).withEndpoint(endpoint).build())
|
||||
.withModules(modules).buildContext();
|
||||
}
|
||||
|
||||
public static RestContext<ChefAsyncClient, ChefClient> createContext(Properties properties, Module... modules) {
|
||||
return new ChefContextBuilder("chef", new ChefPropertiesBuilder(properties).build())
|
||||
.withModules(modules).buildContext();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
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_TIMESTAMP_INTERVAL;
|
||||
import static org.jclouds.chef.reference.ChefConstants.PROPERTY_CHEF_USER_ID;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jclouds.PropertiesBuilder;
|
||||
|
||||
/**
|
||||
* Builds properties used in Chef Clients
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class ChefPropertiesBuilder extends PropertiesBuilder {
|
||||
@Override
|
||||
protected Properties defaultProperties() {
|
||||
Properties properties = super.defaultProperties();
|
||||
properties.setProperty(PROPERTY_CHEF_ENDPOINT, "https://api.opscode.com");
|
||||
properties.setProperty(PROPERTY_CHEF_TIMESTAMP_INTERVAL, "1");
|
||||
return properties;
|
||||
}
|
||||
|
||||
public ChefPropertiesBuilder(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
public ChefPropertiesBuilder(String id, String secret) {
|
||||
super();
|
||||
withCredentials(id, secret);
|
||||
}
|
||||
|
||||
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"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChefPropertiesBuilder withEndpoint(URI endpoint) {
|
||||
properties.setProperty(PROPERTY_CHEF_ENDPOINT, checkNotNull(endpoint, "endpoint").toString());
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
*
|
||||
* 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.net.URI;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.lifecycle.Closer;
|
||||
import org.jclouds.rest.RestContext;
|
||||
import org.jclouds.rest.internal.RestContextImpl;
|
||||
import org.jclouds.chef.Chef;
|
||||
import org.jclouds.chef.ChefAsyncClient;
|
||||
import org.jclouds.chef.ChefClient;
|
||||
import org.jclouds.chef.reference.ChefConstants;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
/**
|
||||
* Configures the Chef connection, including logging and http transport.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class ChefContextModule extends AbstractModule {
|
||||
|
||||
public ChefContextModule(String providerName) {
|
||||
// providerName ignored right now
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
// example of how to customize bindings
|
||||
// bind(DateAdapter.class).to(CDateAdapter.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
RestContext<ChefAsyncClient, ChefClient> provideContext(Closer closer, ChefAsyncClient asyncApi,
|
||||
ChefClient syncApi, @Chef URI endPoint, @Named(ChefConstants.PROPERTY_CHEF_USER_ID) String account) {
|
||||
return new RestContextImpl<ChefAsyncClient, ChefClient>(closer, asyncApi, syncApi, endPoint, account);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
*
|
||||
* 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.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.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.RequiresHttp;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Configures the Chef connection.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@RequiresHttp
|
||||
@ConfiguresRestClient
|
||||
public class ChefRestClientModule extends AbstractModule {
|
||||
|
||||
@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.iso8601DateFormat();
|
||||
}
|
||||
}, seconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindErrorHandlers();
|
||||
bindRetryHandlers();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public PrivateKey provideKey(@Named(ChefConstants.PROPERTY_CHEF_PRIVATE_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);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Chef
|
||||
protected URI provideURI(@Named(ChefConstants.PROPERTY_CHEF_ENDPOINT) String endpoint) {
|
||||
return URI.create(endpoint);
|
||||
}
|
||||
|
||||
protected void bindErrorHandlers() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
protected void bindRetryHandlers() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 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.domain;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* User object.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class Organization implements Comparable<Organization> {
|
||||
private String name;
|
||||
@SerializedName("full_name")
|
||||
private String fullName;
|
||||
@SerializedName("org_type")
|
||||
private String orgType;
|
||||
private String clientname;
|
||||
|
||||
public Organization(String name) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Organization() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Organization o) {
|
||||
return name.compareTo(o.name);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getOrgType() {
|
||||
return orgType;
|
||||
}
|
||||
|
||||
public void setOrgType(String orgType) {
|
||||
this.orgType = orgType;
|
||||
}
|
||||
|
||||
public String getClientname() {
|
||||
return clientname;
|
||||
}
|
||||
|
||||
public void setClientname(String clientname) {
|
||||
this.clientname = clientname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((clientname == null) ? 0 : clientname.hashCode());
|
||||
result = prime * result + ((fullName == null) ? 0 : fullName.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((orgType == null) ? 0 : orgType.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Organization other = (Organization) obj;
|
||||
if (clientname == null) {
|
||||
if (other.clientname != null)
|
||||
return false;
|
||||
} else if (!clientname.equals(other.clientname))
|
||||
return false;
|
||||
if (fullName == null) {
|
||||
if (other.fullName != null)
|
||||
return false;
|
||||
} else if (!fullName.equals(other.fullName))
|
||||
return false;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
if (orgType == null) {
|
||||
if (other.orgType != null)
|
||||
return false;
|
||||
} else if (!orgType.equals(other.orgType))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Organization [clientname=" + clientname + ", fullName=" + fullName + ", name=" + name
|
||||
+ ", orgType=" + orgType + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 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.domain;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* User object.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class User implements Comparable<User> {
|
||||
private String username;
|
||||
@SerializedName("first_name")
|
||||
private String firstName;
|
||||
@SerializedName("middle_name")
|
||||
private String middleName;
|
||||
@SerializedName("last_name")
|
||||
private String lastName;
|
||||
@SerializedName("display_name")
|
||||
private String displayName;
|
||||
private String email;
|
||||
|
||||
public User(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public User() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(User o) {
|
||||
return username.compareTo(o.username);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getMiddleName() {
|
||||
return middleName;
|
||||
}
|
||||
|
||||
public void setMiddleName(String middleName) {
|
||||
this.middleName = middleName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
|
||||
result = prime * result + ((email == null) ? 0 : email.hashCode());
|
||||
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 + ((username == null) ? 0 : username.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
User other = (User) obj;
|
||||
if (displayName == null) {
|
||||
if (other.displayName != null)
|
||||
return false;
|
||||
} else if (!displayName.equals(other.displayName))
|
||||
return false;
|
||||
if (email == null) {
|
||||
if (other.email != null)
|
||||
return false;
|
||||
} else if (!email.equals(other.email))
|
||||
return false;
|
||||
if (firstName == null) {
|
||||
if (other.firstName != null)
|
||||
return false;
|
||||
} else if (!firstName.equals(other.firstName))
|
||||
return false;
|
||||
if (lastName == null) {
|
||||
if (other.lastName != null)
|
||||
return false;
|
||||
} else if (!lastName.equals(other.lastName))
|
||||
return false;
|
||||
if (middleName == null) {
|
||||
if (other.middleName != null)
|
||||
return false;
|
||||
} else if (!middleName.equals(other.middleName))
|
||||
return false;
|
||||
if (username == null) {
|
||||
if (other.username != null)
|
||||
return false;
|
||||
} else if (!username.equals(other.username))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User [username=" + username + ", displayName=" + displayName + ", firstName="
|
||||
+ firstName + ", middleName=" + middleName + ", lastName=" + lastName + ", email="
|
||||
+ email + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/**
|
||||
*
|
||||
* 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.filters;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.chef.reference.ChefConstants;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.encryption.EncryptionService;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpRequestFilter;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.Payload;
|
||||
import org.jclouds.http.Payloads;
|
||||
import org.jclouds.http.internal.SignatureWire;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Ported from mixlib-authentication in order to sign Chef requests.
|
||||
*
|
||||
* @see <a href= "http://github.com/opscode/mixlib-authentication" />
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
@Singleton
|
||||
public class SignedHeaderAuth implements HttpRequestFilter {
|
||||
public static final String SIGNING_DESCRIPTION = "version=1.0";
|
||||
|
||||
private final SignatureWire signatureWire;
|
||||
private final String userId;
|
||||
private final PrivateKey privateKey;
|
||||
private final Provider<String> timeStampProvider;
|
||||
private final EncryptionService encryptionService;
|
||||
private final String emptyStringHash;
|
||||
|
||||
@Resource
|
||||
@Named(Constants.LOGGER_SIGNATURE)
|
||||
Logger signatureLog = Logger.NULL;
|
||||
|
||||
@Inject
|
||||
public SignedHeaderAuth(SignatureWire signatureWire,
|
||||
@Named(ChefConstants.PROPERTY_CHEF_USER_ID) String userId, PrivateKey privateKey,
|
||||
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService) {
|
||||
this.signatureWire = signatureWire;
|
||||
this.userId = userId;
|
||||
this.privateKey = privateKey;
|
||||
this.timeStampProvider = timeStampProvider;
|
||||
this.encryptionService = encryptionService;
|
||||
this.emptyStringHash = hashBody(Payloads.newStringPayload(""));
|
||||
}
|
||||
|
||||
public void filter(HttpRequest request) throws HttpException {
|
||||
|
||||
String contentHash = hashBody(request.getPayload());
|
||||
request.getHeaders().replaceValues("X-Ops-Content-Hash",
|
||||
Collections.singletonList(contentHash));
|
||||
String timestamp = timeStampProvider.get();
|
||||
String toSign = createStringToSign(request.getMethod(), hashPath(request.getEndpoint()
|
||||
.getPath()), contentHash, timestamp);
|
||||
request.getHeaders().replaceValues("X-Ops-Userid", Collections.singletonList(userId));
|
||||
request.getHeaders().replaceValues("X-Ops-Sign",
|
||||
Collections.singletonList(SIGNING_DESCRIPTION));
|
||||
calculateAndReplaceAuthorizationHeaders(request, toSign);
|
||||
request.getHeaders().replaceValues("X-Ops-Timestamp", Collections.singletonList(timestamp));
|
||||
HttpUtils.logRequest(signatureLog, request, "<<");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void calculateAndReplaceAuthorizationHeaders(HttpRequest request, String toSign)
|
||||
throws HttpException {
|
||||
String signature = sign(toSign);
|
||||
if (signatureWire.enabled())
|
||||
signatureWire.input(Utils.toInputStream(signature));
|
||||
String[] signatureLines = Iterables.toArray(Splitter.fixedLength(60).split(signature),
|
||||
String.class);
|
||||
for (int i = 0; i < signatureLines.length; i++) {
|
||||
request.getHeaders().replaceValues("X-Ops-Authorization-" + (i + 1),
|
||||
Collections.singletonList(signatureLines[i]));
|
||||
}
|
||||
}
|
||||
|
||||
public String createStringToSign(String httpMethod, String hashedPath, String contentHash,
|
||||
String timestamp) {
|
||||
|
||||
return new StringBuilder().append("Method:").append(httpMethod).append("\n").append(
|
||||
"Hashed Path:").append(hashedPath).append("\n").append("X-Ops-Content-Hash:")
|
||||
.append(contentHash).append("\n").append("X-Ops-Timestamp:").append(timestamp)
|
||||
.append("\n").append("X-Ops-UserId:").append(userId).toString();
|
||||
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String hashPath(String path) {
|
||||
try {
|
||||
return encryptionService.sha1Base64(canonicalPath(path));
|
||||
} catch (Exception e) {
|
||||
Throwables.propagateIfPossible(e);
|
||||
throw new HttpException("error creating sigature for path: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the canonicalized path, which collapses multiple slashes (/) and removes a trailing
|
||||
* slash unless the path is only "/"
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String canonicalPath(String path) {
|
||||
path = path.replaceAll("\\/+", "/");
|
||||
return path.endsWith("/") && path.length() > 1 ? path.substring(0, path.length() - 1) : path;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String hashBody(Payload payload) {
|
||||
if (payload == null)
|
||||
return emptyStringHash;
|
||||
checkArgument(payload != null, "payload was null");
|
||||
checkArgument(payload.isRepeatable(), "payload must be repeatable");
|
||||
try {
|
||||
return encryptionService.sha1Base64(Utils.toStringAndClose(payload.getContent()));
|
||||
} catch (Exception e) {
|
||||
Throwables.propagateIfPossible(e);
|
||||
throw new HttpException("error creating sigature for payload: " + payload, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String sign(String toSign) {
|
||||
try {
|
||||
byte[] encrypted = encryptionService.rsaPrivateEncrypt(toSign, privateKey);
|
||||
return encryptionService.toBase64String(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new HttpException("error signing request", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 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.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.chef.domain.Organization;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class OrganizationName implements Function<Object, String> {
|
||||
|
||||
public String apply(Object from) {
|
||||
return ((Organization) from).getName();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
*
|
||||
* 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 ParseKeyFromJson implements Function<HttpResponse, String> {
|
||||
Pattern pattern = Pattern.compile(".*private_key\": \"([^\"]+)\".*");
|
||||
|
||||
@Override
|
||||
public String apply(HttpResponse response) {
|
||||
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);
|
||||
}
|
||||
assert false : String.format("pattern: %s didn't match %s", pattern, in);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
*
|
||||
* 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 javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.chef.domain.Organization;
|
||||
import org.jclouds.http.functions.ParseJson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class ParseOrganizationFromJson extends ParseJson<Organization> {
|
||||
@Inject
|
||||
public ParseOrganizationFromJson(Gson gson) {
|
||||
super(gson);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Organization apply(InputStream stream) {
|
||||
try {
|
||||
return gson.fromJson(new InputStreamReader(stream, "UTF-8"), Organization.class);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("jclouds requires UTF-8 encoding", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
*
|
||||
* 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 javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.chef.domain.User;
|
||||
import org.jclouds.http.functions.ParseJson;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class ParseUserFromJson extends ParseJson<User> {
|
||||
@Inject
|
||||
public ParseUserFromJson(Gson gson) {
|
||||
super(gson);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected User apply(InputStream stream) {
|
||||
|
||||
try {
|
||||
return gson.fromJson(new InputStreamReader(stream, "UTF-8"), User.class);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException("jclouds requires UTF-8 encoding", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 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.functions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.chef.domain.User;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class Username implements Function<Object, String> {
|
||||
|
||||
public String apply(Object from) {
|
||||
return ((User) from).getUsername();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
*
|
||||
* 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.reference;
|
||||
|
||||
/**
|
||||
* Configuration properties and constants used in Chef connections.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
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";
|
||||
/**
|
||||
* The PEM-encoded key
|
||||
*/
|
||||
public static final String PROPERTY_CHEF_PRIVATE_KEY = "jclouds.chef.private-key";
|
||||
|
||||
/**
|
||||
* how often to refresh timestamps in seconds.
|
||||
*/
|
||||
public static final String PROPERTY_CHEF_TIMESTAMP_INTERVAL = "jclouds.chef.timestamp-interval";
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
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.ParseOrganizationFromJson;
|
||||
import org.jclouds.chef.functions.ParseUserFromJson;
|
||||
import org.jclouds.date.TimeStamp;
|
||||
import org.jclouds.logging.config.NullLoggingModule;
|
||||
import org.jclouds.rest.RestClientTest;
|
||||
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.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
* Tests annotation parsing of {@code ChefAsyncClient}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", testName = "chef.ChefAsyncClientTest")
|
||||
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 testCreateOrganization() throws SecurityException, NoSuchMethodException,
|
||||
IOException {
|
||||
Method method = ChefAsyncClient.class.getMethod("createOrganization", 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 testUpdateOrganization() throws SecurityException, NoSuchMethodException,
|
||||
IOException {
|
||||
Method method = ChefAsyncClient.class.getMethod("updateOrganization", 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 testGetOrganization() throws SecurityException, NoSuchMethodException, IOException {
|
||||
Method method = ChefAsyncClient.class.getMethod("getOrganization", 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 testDeleteOrganization() throws SecurityException, NoSuchMethodException,
|
||||
IOException {
|
||||
Method method = ChefAsyncClient.class.getMethod("deleteOrganization", 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);
|
||||
assertEquals(httpRequest.getFilters().get(0).getClass(), SignedHeaderAuth.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeLiteral<RestAnnotationProcessor<ChefAsyncClient>> createTypeLiteral() {
|
||||
return new TypeLiteral<RestAnnotationProcessor<ChefAsyncClient>>() {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Module createModule() {
|
||||
return new ChefRestClientModule() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
Jsr330.bindProperties(binder(), new ChefPropertiesBuilder(new Properties())
|
||||
.withCredentials("user", SignedHeaderAuthTest.PRIVATE_KEY).build());
|
||||
install(new NullLoggingModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
|
||||
return "timestamp";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
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.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code ChefClient}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "live", testName = "chef.ChefClientLiveTest")
|
||||
public class ChefClientLiveTest {
|
||||
|
||||
private ChefClient client;
|
||||
private String username;
|
||||
|
||||
@BeforeGroups(groups = { "live" })
|
||||
public void setupClient() throws IOException {
|
||||
username = 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();
|
||||
}
|
||||
|
||||
@Test(enabled = false)
|
||||
public void testGetUser() throws Exception {
|
||||
User user = client.getUser(username);
|
||||
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 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 {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Test(enabled = false)
|
||||
public void testDeleteOrganization() throws Exception {
|
||||
// TODO
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
/**
|
||||
*
|
||||
* 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.filters;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
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 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.internal.SignatureWire;
|
||||
import org.jclouds.util.Jsr330;
|
||||
import org.jclouds.util.Utils;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", testName = "chef.SignedHeaderAuthTest")
|
||||
public class SignedHeaderAuthTest {
|
||||
|
||||
public static final String USER_ID = "spec-user";
|
||||
public static final String BODY = "Spec Body";
|
||||
// Base64.encode64(Digest::SHA1.digest("Spec Body")).chomp
|
||||
public static final String HASHED_BODY = "DFteJZPVv6WKdQmMqZUQUumUyRs=";
|
||||
public static final String TIMESTAMP_ISO8601 = "2009-01-01T12:00:00Z";
|
||||
|
||||
public static final String PATH = "/organizations/clownco";
|
||||
// Base64.encode64(Digest::SHA1.digest("/organizations/clownco")).chomp
|
||||
|
||||
public static final String HASHED_CANONICAL_PATH = "YtBWDn1blGGuFIuKksdwXzHU9oE=";
|
||||
public static final String REQUESTING_ACTOR_ID = "c0f8a68c52bffa1020222a56b23cccfa";
|
||||
|
||||
// Content hash is ???TODO
|
||||
public static final String X_OPS_CONTENT_HASH = "DFteJZPVv6WKdQmMqZUQUumUyRs=";
|
||||
|
||||
public static final String[] X_OPS_AUTHORIZATION_LINES = new String[] {
|
||||
"jVHrNniWzpbez/eGWjFnO6lINRIuKOg40ZTIQudcFe47Z9e/HvrszfVXlKG4",
|
||||
"NMzYZgyooSvU85qkIUmKuCqgG2AIlvYa2Q/2ctrMhoaHhLOCWWoqYNMaEqPc",
|
||||
"3tKHE+CfvP+WuPdWk4jv4wpIkAz6ZLxToxcGhXmZbXpk56YTmqgBW2cbbw4O",
|
||||
"IWPZDHSiPcw//AYNgW1CCDptt+UFuaFYbtqZegcBd2n/jzcWODA7zL4KWEUy",
|
||||
"9q4rlh/+1tBReg60QdsmDRsw/cdO1GZrKtuCwbuD4+nbRdVBKv72rqHX9cu0",
|
||||
"utju9jzczCyB+sSAQWrxSsXB/b8vV2qs0l4VD2ML+w==" };
|
||||
|
||||
// We expect Mixlib::Authentication::SignedHeaderAuth//sign to return this
|
||||
// if passed the BODY above.
|
||||
public static final Multimap<String, String> EXPECTED_SIGN_RESULT = ImmutableMultimap
|
||||
.<String, String> builder().put("X-Ops-Content-Hash", X_OPS_CONTENT_HASH).put(
|
||||
"X-Ops-Userid", USER_ID).put("X-Ops-Sign", "version=1.0").put(
|
||||
"X-Ops-Authorization-1", X_OPS_AUTHORIZATION_LINES[0]).put(
|
||||
"X-Ops-Authorization-2", X_OPS_AUTHORIZATION_LINES[1]).put(
|
||||
"X-Ops-Authorization-3", X_OPS_AUTHORIZATION_LINES[2]).put(
|
||||
"X-Ops-Authorization-4", X_OPS_AUTHORIZATION_LINES[3]).put(
|
||||
"X-Ops-Authorization-5", X_OPS_AUTHORIZATION_LINES[4]).put(
|
||||
"X-Ops-Authorization-6", X_OPS_AUTHORIZATION_LINES[5]).put("X-Ops-Timestamp",
|
||||
TIMESTAMP_ISO8601).build();
|
||||
|
||||
// Content hash for empty string
|
||||
public static final String X_OPS_CONTENT_HASH_EMPTY = "2jmj7l5rSw0yVb/vlWAYkK/YBwk=";
|
||||
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();
|
||||
|
||||
public static String PUBLIC_KEY;
|
||||
public static String PRIVATE_KEY;
|
||||
|
||||
static {
|
||||
try {
|
||||
PUBLIC_KEY = Utils.toStringAndClose(SignedHeaderAuthTest.class
|
||||
.getResourceAsStream("/pubkey.txt"));
|
||||
|
||||
PRIVATE_KEY = Utils.toStringAndClose(SignedHeaderAuthTest.class
|
||||
.getResourceAsStream("/privkey.txt"));
|
||||
} catch (IOException e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void canonicalizedPathRemovesMultipleSlashes() {
|
||||
assertEquals(signing_obj.canonicalPath("///"), "/");
|
||||
}
|
||||
|
||||
@Test
|
||||
void canonicalizedPathRemovesTrailingSlash() {
|
||||
assertEquals(signing_obj.canonicalPath("/path/"), "/path");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGenerateTheCorrectStringToSignAndSignature() {
|
||||
|
||||
URI host = URI.create("http://localhost/" + PATH);
|
||||
HttpRequest request = new HttpRequest(HttpMethod.POST, host);
|
||||
request.setPayload(BODY);
|
||||
|
||||
String expected_string_to_sign = new StringBuilder().append("Method:POST").append("\n")
|
||||
.append("Hashed Path:").append(HASHED_CANONICAL_PATH).append("\n").append(
|
||||
"X-Ops-Content-Hash:").append(HASHED_BODY).append("\n").append(
|
||||
"X-Ops-Timestamp:").append(TIMESTAMP_ISO8601).append("\n").append(
|
||||
"X-Ops-UserId:").append(USER_ID).toString();
|
||||
|
||||
assertEquals(signing_obj.createStringToSign("POST", HASHED_CANONICAL_PATH, HASHED_BODY,
|
||||
TIMESTAMP_ISO8601), expected_string_to_sign);
|
||||
assertEquals(signing_obj.sign(expected_string_to_sign), Joiner.on("").join(
|
||||
X_OPS_AUTHORIZATION_LINES));
|
||||
|
||||
signing_obj.filter(request);
|
||||
Multimap<String, String> headersWithoutContentLength = LinkedHashMultimap.create(request
|
||||
.getHeaders());
|
||||
headersWithoutContentLength.removeAll(HttpHeaders.CONTENT_LENGTH);
|
||||
assertEquals(headersWithoutContentLength.values(), EXPECTED_SIGN_RESULT.values());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGenerateTheCorrectStringToSignAndSignatureWithNoBody() {
|
||||
|
||||
URI host = URI.create("http://localhost/" + PATH);
|
||||
HttpRequest request = new HttpRequest(HttpMethod.DELETE, host);
|
||||
|
||||
signing_obj.filter(request);
|
||||
Multimap<String, String> headersWithoutContentLength = LinkedHashMultimap.create(request
|
||||
.getHeaders());
|
||||
assertEquals(headersWithoutContentLength.entries(), EXPECTED_SIGN_RESULT_EMPTY.entries());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotChokeWhenSigningARequestForAResourceWithALongName() {
|
||||
StringBuilder path = new StringBuilder("nodes/");
|
||||
for (int i = 0; i < 100; i++)
|
||||
path.append('A');
|
||||
URI host = URI.create("http://localhost/" + path.toString());
|
||||
HttpRequest request = new HttpRequest(HttpMethod.PUT, host);
|
||||
request.setPayload(BODY);
|
||||
signing_obj.filter(request);
|
||||
}
|
||||
|
||||
private SignedHeaderAuth signing_obj;
|
||||
private EncryptionService encryptionService;
|
||||
|
||||
/**
|
||||
* before class, as we need to ensure that the filter is threadsafe.
|
||||
*
|
||||
* @throws IOException
|
||||
*
|
||||
*/
|
||||
@BeforeClass
|
||||
protected void createFilter() throws IOException {
|
||||
Injector injector = Guice.createInjector(new ChefRestClientModule(),
|
||||
new ExecutorServiceModule(sameThreadExecutor(), sameThreadExecutor()),
|
||||
new ParserModule(), new AbstractModule() {
|
||||
|
||||
protected void configure() {
|
||||
Jsr330.bindProperties(binder(), checkNotNull(
|
||||
new ChefPropertiesBuilder("foo", "bar")).build());
|
||||
bind(UriBuilder.class).to(UriBuilderImpl.class);
|
||||
}
|
||||
});
|
||||
encryptionService = injector.getInstance(EncryptionService.class);
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
KeyPair pair = KeyPair.class.cast(new PEMReader(new StringReader(PRIVATE_KEY)).readObject());
|
||||
|
||||
PrivateKey privateKey = pair.getPrivate();
|
||||
|
||||
signing_obj = new SignedHeaderAuth(new SignatureWire(), USER_ID, privateKey,
|
||||
new Provider<String>() {
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return TIMESTAMP_ISO8601;
|
||||
}
|
||||
|
||||
}, encryptionService);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
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.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code ParseKeyFromJson}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", sequential = true, testName = "chef.ParseKeyFromJsonTest")
|
||||
public class ParseKeyFromJsonTest {
|
||||
|
||||
private ParseKeyFromJson handler;
|
||||
|
||||
@BeforeTest
|
||||
protected void setUpInjector() throws IOException {
|
||||
Injector injector = Guice.createInjector(new ParserModule());
|
||||
handler = injector.getInstance(ParseKeyFromJson.class);
|
||||
}
|
||||
|
||||
public void testRegex() {
|
||||
assertEquals(
|
||||
handler
|
||||
.apply(new HttpResponse(
|
||||
Utils
|
||||
.toInputStream("{\n\"uri\": \"https://api.opscode.com/users/bobo\", \"private_key\": \"RSA_PRIVATE_KEY\",}"))),
|
||||
"RSA_PRIVATE_KEY");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.jclouds.chef.functions;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jclouds.chef.domain.Organization;
|
||||
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.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code ParseOrganizationFromJson}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", sequential = true, testName = "chef.ParseOrganizationFromJsonTest")
|
||||
public class ParseOrganizationFromJsonTest {
|
||||
|
||||
private ParseOrganizationFromJson handler;
|
||||
|
||||
@BeforeTest
|
||||
protected void setUpInjector() throws IOException {
|
||||
Injector injector = Guice.createInjector(new ParserModule());
|
||||
handler = injector.getInstance(ParseOrganizationFromJson.class);
|
||||
}
|
||||
|
||||
public void test() {
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName("opscode");
|
||||
org.setFullName("Opscode, Inc.");
|
||||
org.setOrgType("Business");
|
||||
org.setClientname("opscode-validator");
|
||||
|
||||
String toParse = "{\"name\": \"opscode\",\"full_name\": \"Opscode, Inc.\", \"org_type\": \"Business\",\"clientname\": \"opscode-validator\" }";
|
||||
|
||||
assertEquals(handler.apply(new HttpResponse(Utils.toInputStream(toParse))), org);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.jclouds.chef.functions;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jclouds.chef.domain.User;
|
||||
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.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@code ParseUserFromJson}
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit", sequential = true, testName = "chef.ParseUserFromJsonTest")
|
||||
public class ParseUserFromJsonTest {
|
||||
|
||||
private ParseUserFromJson handler;
|
||||
|
||||
@BeforeTest
|
||||
protected void setUpInjector() throws IOException {
|
||||
Injector injector = Guice.createInjector(new ParserModule());
|
||||
handler = injector.getInstance(ParseUserFromJson.class);
|
||||
}
|
||||
|
||||
public void test() {
|
||||
|
||||
User user = new User();
|
||||
user.setUsername("bobo");
|
||||
user.setFirstName("Bobo");
|
||||
user.setMiddleName("Tiberion");
|
||||
user.setLastName("Clown");
|
||||
user.setDisplayName("Bobo T. Clown");
|
||||
user.setEmail("bobo@clownco.com");
|
||||
|
||||
String toParse = "{\n\"username\": \"bobo\",\n\"first_name\": \"Bobo\",\n\"middle_name\": \"Tiberion\",\n\"last_name\": \"Clown\",\n\"display_name\": \"Bobo T. Clown\",\n\"email\": \"bobo@clownco.com\" \n}";
|
||||
|
||||
assertEquals(handler.apply(new HttpResponse(Utils.toInputStream(toParse))), user);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?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>
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA0ueqo76MXuP6XqZBILFziH/9AI7C6PaN5W0dSvkr9yInyGHS
|
||||
z/IR1+4tqvP2qlfKVKI4CP6BFH251Ft9qMUBuAsnlAVQ1z0exDtIFFOyQCdR7iXm
|
||||
jBIWMSS4buBwRQXwDK7id1OxtU23qVJv+xwEV0IzaaSJmaGLIbvRBD+qatfUuQJB
|
||||
MU/04DdJIwvLtZBYdC2219m5dUBQaa4bimL+YN9EcsDzD9h9UxQo5ReK7b3cNMzJ
|
||||
BKJWLzFBcJuePMzAnLFktr/RufX4wpXe6XJxoVPaHo72GorLkwnQ0HYMTY8rehT4
|
||||
mDi1FI969LHCFFaFHSAaRnwdXaQkJmSfcxzCYQIDAQABAoIBAQCW3I4sKN5B9jOe
|
||||
xq/pkeWBq4OvhW8Ys1yW0zFT8t6nHbB1XrwscQygd8gE9BPqj3e0iIEqtdphbPmj
|
||||
VHqTYbC0FI6QDClifV7noTwTBjeIOlgZ0NSUN0/WgVzIOxUz2mZ2vBZUovKILPqG
|
||||
TOi7J7RXMoySMdcXpP1f+PgvYNcnKsT72UcWaSXEV8/zo+Zm/qdGPVWwJonri5Mp
|
||||
DVm5EQSENBiRyt028rU6ElXORNmoQpVjDVqZ1gipzXkifdjGyENw2rt4V/iKYD7V
|
||||
5iqXOsvP6Cemf4gbrjunAgDG08S00kiUgvVWcdXW+dlsR2nCvH4DOEe3AYYh/aH8
|
||||
DxEE7FbtAoGBAPcNO8fJ56mNw0ow4Qg38C+Zss/afhBOCfX4O/SZKv/roRn5+gRM
|
||||
KRJYSVXNnsjPI1plzqR4OCyOrjAhtuvL4a0DinDzf1+fiztyNohwYsW1vYmqn3ti
|
||||
EN0GhSgE7ppZjqvLQ3f3LUTxynhA0U+k9wflb4irIlViTUlCsOPkrNJDAoGBANqL
|
||||
Q+vvuGSsmRLU/Cenjy+Mjj6+QENg51dz34o8JKuVKIPKU8pNnyeLa5fat0qD2MHm
|
||||
OB9opeQOcw0dStodxr6DB3wi83bpjeU6BWUGITNiWEaZEBrQ0aiqNJJKrrHm8fAZ
|
||||
9o4l4oHc4hI0kYVYYDuxtKuVJrzZiEapTwoOcYiLAoGBAI/EWbeIHZIj9zOjgjEA
|
||||
LHvm25HtulLOtyk2jd1njQhlHNk7CW2azIPqcLLH99EwCYi/miNH+pijZ2aHGCXb
|
||||
/bZrSxM0ADmrZKDxdB6uGCyp+GS2sBxjEyEsfCyvwhJ8b3Q100tqwiNO+d5FCglp
|
||||
HICx2dgUjuRVUliBwOK93nx1AoGAUI8RhIEjOYkeDAESyhNMBr0LGjnLOosX+/as
|
||||
qiotYkpjWuFULbibOFp+WMW41vDvD9qrSXir3fstkeIAW5KqVkO6mJnRoT3Knnra
|
||||
zjiKOITCAZQeiaP8BO5o3pxE9TMqb9VCO3ffnPstIoTaN4syPg7tiGo8k1SklVeH
|
||||
2S8lzq0CgYAKG2fljIYWQvGH628rp4ZcXS4hWmYohOxsnl1YrszbJ+hzR+IQOhGl
|
||||
YlkUQYXhy9JixmUUKtH+NXkKX7Lyc8XYw5ETr7JBT3ifs+G7HruDjVG78EJVojbd
|
||||
8uLA+DdQm5mg4vd1GTiSK65q/3EeoBlUaVor3HhLFki+i9qpT8CBsg==
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ueqo76MXuP6XqZBILFz
|
||||
iH/9AI7C6PaN5W0dSvkr9yInyGHSz/IR1+4tqvP2qlfKVKI4CP6BFH251Ft9qMUB
|
||||
uAsnlAVQ1z0exDtIFFOyQCdR7iXmjBIWMSS4buBwRQXwDK7id1OxtU23qVJv+xwE
|
||||
V0IzaaSJmaGLIbvRBD+qatfUuQJBMU/04DdJIwvLtZBYdC2219m5dUBQaa4bimL+
|
||||
YN9EcsDzD9h9UxQo5ReK7b3cNMzJBKJWLzFBcJuePMzAnLFktr/RufX4wpXe6XJx
|
||||
oVPaHo72GorLkwnQ0HYMTY8rehT4mDi1FI969LHCFFaFHSAaRnwdXaQkJmSfcxzC
|
||||
YQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
|
@ -0,0 +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">
|
||||
<classes>
|
||||
<class name="org.jclouds.chef.ChefClientLiveTest"/>
|
||||
</classes>
|
||||
</test>
|
||||
</suite>
|
|
@ -26,9 +26,14 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import org.jclouds.encryption.internal.JCEEncryptionService;
|
||||
|
||||
import com.google.inject.ImplementedBy;
|
||||
|
@ -47,6 +52,9 @@ public interface EncryptionService {
|
|||
String hmacSha256Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException,
|
||||
NoSuchProviderException, InvalidKeyException;
|
||||
|
||||
String sha1Base64(String toEncode) throws NoSuchAlgorithmException, NoSuchProviderException,
|
||||
InvalidKeyException;
|
||||
|
||||
String hmacSha1Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException,
|
||||
NoSuchProviderException, InvalidKeyException;
|
||||
|
||||
|
@ -92,4 +100,8 @@ public interface EncryptionService {
|
|||
|
||||
byte[] fromBase64String(String encoded);
|
||||
|
||||
byte[] rsaPrivateEncrypt(String toSign, Key privateKey) throws NoSuchAlgorithmException,
|
||||
NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
|
||||
BadPaddingException;
|
||||
|
||||
}
|
|
@ -24,11 +24,16 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import com.google.common.io.Closeables;
|
||||
|
@ -97,7 +102,7 @@ public class JCEEncryptionService extends BaseEncryptionService {
|
|||
}
|
||||
|
||||
public String toBase64String(byte[] resBuf) {
|
||||
return Base64.encodeBytes(resBuf);
|
||||
return Base64.encodeBytes(resBuf, Base64.DONT_BREAK_LINES);
|
||||
}
|
||||
|
||||
public MD5InputStreamResult generateMD5Result(InputStream toEncode) {
|
||||
|
@ -154,4 +159,21 @@ public class JCEEncryptionService extends BaseEncryptionService {
|
|||
return digest.digest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1Base64(String toEncode) throws NoSuchAlgorithmException,
|
||||
NoSuchProviderException, InvalidKeyException {
|
||||
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
|
||||
byte[] digest = sha1.digest(toEncode.getBytes());
|
||||
return toBase64String(digest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] rsaPrivateEncrypt(String toSign, Key key) throws NoSuchAlgorithmException,
|
||||
NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
|
||||
BadPaddingException {
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(toSign.getBytes());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ package org.jclouds.http;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
|
||||
|
@ -35,7 +35,7 @@ public class HttpMessage {
|
|||
/**
|
||||
* synchronized as there is no concurrent version. Headers may change in flight due to redirects.
|
||||
*/
|
||||
protected Multimap<String, String> headers = Multimaps.synchronizedMultimap(HashMultimap
|
||||
protected Multimap<String, String> headers = Multimaps.synchronizedMultimap(LinkedHashMultimap
|
||||
.<String, String> create());
|
||||
|
||||
public Multimap<String, String> getHeaders() {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* limitations under the License.
|
||||
* ====================================================================
|
||||
*/
|
||||
package org.jclouds.aws.util;
|
||||
package org.jclouds.rest;
|
||||
|
||||
import org.jclouds.http.HttpRequest;
|
||||
|
||||
|
@ -28,6 +28,6 @@ public interface RequestSigner {
|
|||
|
||||
String createStringToSign(HttpRequest input);
|
||||
|
||||
String signString(String toSign);
|
||||
String sign(String toSign);
|
||||
|
||||
}
|
|
@ -23,9 +23,14 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
|
@ -156,4 +161,25 @@ public class BouncyCastleEncryptionService extends BaseEncryptionService {
|
|||
return resBuf;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sha1Base64(String toEncode) throws NoSuchAlgorithmException,
|
||||
NoSuchProviderException, InvalidKeyException {
|
||||
byte[] plainBytes = toEncode.getBytes();
|
||||
Digest digest = new SHA1Digest();
|
||||
byte[] resBuf = new byte[digest.getDigestSize()];
|
||||
digest.update(plainBytes, 0, plainBytes.length);
|
||||
digest.doFinal(resBuf, 0);
|
||||
return toBase64String(resBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] rsaPrivateEncrypt(String toSign, Key key) throws NoSuchAlgorithmException,
|
||||
NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
|
||||
BadPaddingException {
|
||||
// TODO convert this to BC code
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(toSign.getBytes());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue