mirror of https://github.com/apache/jclouds.git
JCLOUDS-457: Created the skeleton of the Glacier API.
This commit is contained in:
commit
d4af465ca8
|
@ -0,0 +1,128 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<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.apache.jclouds.labs</groupId>
|
||||||
|
<artifactId>jclouds-labs-aws</artifactId>
|
||||||
|
<version>1.8.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>org.apache.jclouds.labs</groupId>
|
||||||
|
<artifactId>glacier</artifactId>
|
||||||
|
<name>jclouds glacier api</name>
|
||||||
|
<description>jclouds components to access an implementation of glacier</description>
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<test.glacier.endpoint>https://glacier.eu-west-1.amazonaws.com</test.glacier.endpoint>
|
||||||
|
<test.glacier.api-version>2012-06-01</test.glacier.api-version>
|
||||||
|
<test.glacier.build-version />
|
||||||
|
<test.glacier.identity>${test.aws.identity}</test.glacier.identity>
|
||||||
|
<test.glacier.credential>${test.aws.credential}</test.glacier.credential>
|
||||||
|
|
||||||
|
<jclouds.osgi.export>org.jclouds.glacier*;version="${project.version}"</jclouds.osgi.export>
|
||||||
|
<jclouds.osgi.import>
|
||||||
|
org.jclouds.labs*;version="${project.version}",
|
||||||
|
org.jclouds*;version="${project.version}",
|
||||||
|
*
|
||||||
|
</jclouds.osgi.import>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.jclouds.api</groupId>
|
||||||
|
<artifactId>sts</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<type>jar</type>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.jclouds</groupId>
|
||||||
|
<artifactId>jclouds-blobstore</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<type>jar</type>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.jclouds</groupId>
|
||||||
|
<artifactId>jclouds-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.jclouds</groupId>
|
||||||
|
<artifactId>jclouds-blobstore</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.jclouds.driver</groupId>
|
||||||
|
<artifactId>jclouds-log4j</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>log4j</groupId>
|
||||||
|
<artifactId>log4j</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.mockwebserver</groupId>
|
||||||
|
<artifactId>mockwebserver</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>live</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>integration</id>
|
||||||
|
<phase>integration-test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>test</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<jclouds.blobstore.httpstream.url>${jclouds.blobstore.httpstream.url}</jclouds.blobstore.httpstream.url>
|
||||||
|
<jclouds.blobstore.httpstream.md5>${jclouds.blobstore.httpstream.md5}</jclouds.blobstore.httpstream.md5>
|
||||||
|
<test.glacier.endpoint>${test.glacier.endpoint}</test.glacier.endpoint>
|
||||||
|
<test.glacier.api-version>${test.glacier.api-version}</test.glacier.api-version>
|
||||||
|
<test.glacier.build-version>${test.glacier.build-version}</test.glacier.build-version>
|
||||||
|
<test.glacier.identity>${test.glacier.identity}</test.glacier.identity>
|
||||||
|
<test.glacier.credential>${test.glacier.credential}</test.glacier.credential>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier;
|
||||||
|
|
||||||
|
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
|
||||||
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.BlobStoreContext;
|
||||||
|
import org.jclouds.glacier.config.GlacierRestClientModule;
|
||||||
|
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||||
|
import org.jclouds.rest.internal.BaseRestApiMetadata;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of ApiMetadata for Amazon Glacier API
|
||||||
|
*
|
||||||
|
* @author Roman Coedo
|
||||||
|
*/
|
||||||
|
public class GlacierApiMetadata extends BaseRestApiMetadata {
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static final TypeToken<org.jclouds.rest.RestContext<GlacierClient, GlacierAsyncClient>> CONTEXT_TOKEN = new TypeToken<org.jclouds.rest.RestContext<GlacierClient, GlacierAsyncClient>>() {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder toBuilder() {
|
||||||
|
return builder().fromApiMetadata(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlacierApiMetadata() {
|
||||||
|
this(builder());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GlacierApiMetadata(Builder builder) {
|
||||||
|
super(new Builder());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties defaultProperties() {
|
||||||
|
Properties properties = BaseRestApiMetadata.defaultProperties();
|
||||||
|
properties.setProperty(PROPERTY_HEADER_TAG, GlacierHeaders.DEFAULT_AMAZON_HEADERTAG);
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder extends BaseRestApiMetadata.Builder<Builder> {
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
protected Builder() {
|
||||||
|
super(GlacierClient.class, GlacierAsyncClient.class);
|
||||||
|
id("glacier")
|
||||||
|
.name("Amazon Glacier API")
|
||||||
|
.identityName("Access Key ID")
|
||||||
|
.credentialName("Secret Access Key")
|
||||||
|
.defaultEndpoint("https://glacier.us-east-1.amazonaws.com")
|
||||||
|
.documentation(URI.create("http://docs.aws.amazon.com/amazonglacier/latest/dev/amazon-glacier-api.html"))
|
||||||
|
.version("2012-06-01")
|
||||||
|
.defaultProperties(GlacierApiMetadata.defaultProperties())
|
||||||
|
.context(CONTEXT_TOKEN)
|
||||||
|
.view(typeToken(BlobStoreContext.class))
|
||||||
|
.defaultModules(ImmutableSet.<Class<? extends Module>> of(GlacierRestClientModule.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GlacierApiMetadata build() {
|
||||||
|
return new GlacierApiMetadata(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Builder self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier;
|
||||||
|
|
||||||
|
import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.attr.BlobScope;
|
||||||
|
import org.jclouds.glacier.filters.RequestAuthorizeSignature;
|
||||||
|
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||||
|
import org.jclouds.rest.annotations.Headers;
|
||||||
|
import org.jclouds.rest.annotations.RequestFilters;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
|
||||||
|
@Headers(keys = GlacierHeaders.VERSION, values = "2012-06-01")
|
||||||
|
@RequestFilters(RequestAuthorizeSignature.class)
|
||||||
|
@BlobScope(CONTAINER)
|
||||||
|
public interface GlacierAsyncClient extends Closeable {
|
||||||
|
|
||||||
|
@Named("CreateVault")
|
||||||
|
@PUT
|
||||||
|
@Path("/-/vaults/{vault}")
|
||||||
|
ListenableFuture<URI> createVault(@PathParam("vault") String vaultName);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
public interface GlacierClient extends Closeable {
|
||||||
|
|
||||||
|
URI createVault(String vaultName);
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier.config;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import org.jclouds.Constants;
|
||||||
|
import org.jclouds.date.DateService;
|
||||||
|
import org.jclouds.date.TimeStamp;
|
||||||
|
import org.jclouds.glacier.GlacierAsyncClient;
|
||||||
|
import org.jclouds.glacier.GlacierClient;
|
||||||
|
import org.jclouds.glacier.filters.RequestAuthorizeSignature;
|
||||||
|
import org.jclouds.rest.ConfiguresRestClient;
|
||||||
|
import org.jclouds.rest.config.RestClientModule;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.Scopes;
|
||||||
|
|
||||||
|
@ConfiguresRestClient
|
||||||
|
public class GlacierRestClientModule extends RestClientModule<GlacierClient, GlacierAsyncClient> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
super.configure();
|
||||||
|
bind(RequestAuthorizeSignature.class).in(Scopes.SINGLETON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@TimeStamp
|
||||||
|
protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
|
||||||
|
return cache.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@TimeStamp
|
||||||
|
Supplier<String> provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
|
||||||
|
final DateService dateService) {
|
||||||
|
return Suppliers.memoizeWithExpiration(new Supplier<String>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return dateService.iso8601SecondsDateFormat().replaceAll("[-:]", "");
|
||||||
|
}
|
||||||
|
}, seconds, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier.filters;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
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.crypto.Crypto;
|
||||||
|
import org.jclouds.date.TimeStamp;
|
||||||
|
import org.jclouds.domain.Credentials;
|
||||||
|
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||||
|
import org.jclouds.glacier.util.AWSRequestSignerV4;
|
||||||
|
import org.jclouds.http.HttpException;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.net.HttpHeaders;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class RequestAuthorizeSignature implements HttpRequestFilter {
|
||||||
|
|
||||||
|
private final AWSRequestSignerV4 signer;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
@Named(Constants.LOGGER_SIGNATURE)
|
||||||
|
Logger signatureLog = Logger.NULL;
|
||||||
|
|
||||||
|
private final Provider<String> timeStampProvider;
|
||||||
|
private final HttpUtils utils;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RequestAuthorizeSignature(@TimeStamp Provider<String> timeStampProvider,
|
||||||
|
@org.jclouds.location.Provider Supplier<Credentials> creds, Crypto crypto, HttpUtils utils) {
|
||||||
|
checkNotNull(creds);
|
||||||
|
this.signer = new AWSRequestSignerV4(creds.get().identity, creds.get().credential, checkNotNull(crypto));
|
||||||
|
this.timeStampProvider = checkNotNull(timeStampProvider);
|
||||||
|
this.utils = checkNotNull(utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpRequest filter(HttpRequest request) throws HttpException {
|
||||||
|
request = request.toBuilder().removeHeader(HttpHeaders.DATE)
|
||||||
|
.replaceHeader(GlacierHeaders.ALTERNATE_DATE, timeStampProvider.get())
|
||||||
|
.replaceHeader(HttpHeaders.HOST, request.getEndpoint().getHost()).build();
|
||||||
|
utils.logRequest(signatureLog, request, ">>");
|
||||||
|
request = this.signer.sign(request);
|
||||||
|
utils.logRequest(signatureLog, request, "<<");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier.reference;
|
||||||
|
|
||||||
|
public final class GlacierHeaders {
|
||||||
|
|
||||||
|
public static final String DEFAULT_AMAZON_HEADERTAG = "amz";
|
||||||
|
public static final String HEADER_PREFIX = "x-" + DEFAULT_AMAZON_HEADERTAG + "-";
|
||||||
|
public static final String VERSION = HEADER_PREFIX + "glacier-version";
|
||||||
|
public static final String ALTERNATE_DATE = HEADER_PREFIX + "date";
|
||||||
|
|
||||||
|
private GlacierHeaders() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier.util;
|
||||||
|
|
||||||
|
import static com.google.common.base.Charsets.UTF_8;
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
|
import org.jclouds.crypto.Crypto;
|
||||||
|
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||||
|
import org.jclouds.http.HttpException;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.SortedSetMultimap;
|
||||||
|
import com.google.common.collect.TreeMultimap;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.google.common.net.HttpHeaders;
|
||||||
|
|
||||||
|
// TODO: Query parameters, not necessary for Glacier
|
||||||
|
// TODO: Endpoint on buildCredentialScope is being read from the static string. Uncool.
|
||||||
|
/**
|
||||||
|
* Signs requests using the AWSv4 signing algorithm
|
||||||
|
*
|
||||||
|
* @see <a href="http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html" />
|
||||||
|
* @author Roman Coedo
|
||||||
|
*/
|
||||||
|
public class AWSRequestSignerV4 {
|
||||||
|
|
||||||
|
public static final String AUTH_TAG = "AWS4";
|
||||||
|
public static final String HEADER_TAG = "x-amz-";
|
||||||
|
public static final String ALGORITHM = AUTH_TAG + "-HMAC-SHA256";
|
||||||
|
public static final String TERMINATION_STRING = "aws4_request";
|
||||||
|
public static final String REGION = "us-east-1";
|
||||||
|
public static final String SERVICE = "glacier";
|
||||||
|
|
||||||
|
private final Crypto crypto;
|
||||||
|
private final String identity;
|
||||||
|
private final String credential;
|
||||||
|
|
||||||
|
public AWSRequestSignerV4(String identity, String credential, Crypto crypto) {
|
||||||
|
this.crypto = checkNotNull(crypto);
|
||||||
|
this.identity = checkNotNull(identity);
|
||||||
|
this.credential = checkNotNull(credential);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildHashedCanonicalRequest(String method, String endpoint, String hashedPayload,
|
||||||
|
String canonicalizedHeadersString, String signedHeaders) {
|
||||||
|
return sha256((method + "\n" + endpoint + "\n" + "" + "\n" + canonicalizedHeadersString + "\n" + signedHeaders
|
||||||
|
+ "\n" + hashedPayload).getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String createStringToSign(String date, String credentialScope, String hashedCanonicalRequest) {
|
||||||
|
return ALGORITHM + "\n" + date + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatDateWithoutTimestamp(String date) {
|
||||||
|
return date.substring(0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildCredentialScope(String dateWithoutTimeStamp) {
|
||||||
|
return dateWithoutTimeStamp + "/" + REGION + "/" + SERVICE + "/" + TERMINATION_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Multimap<String, String> buildCanonicalizedHeadersMap(HttpRequest request) {
|
||||||
|
Multimap<String, String> headers = request.getHeaders();
|
||||||
|
SortedSetMultimap<String, String> canonicalizedHeaders = TreeMultimap.create();
|
||||||
|
for (Entry<String, String> header : headers.entries()) {
|
||||||
|
if (header.getKey() == null)
|
||||||
|
continue;
|
||||||
|
String key = header.getKey().toString().toLowerCase(Locale.getDefault());
|
||||||
|
// Ignore any headers that are not particularly interesting.
|
||||||
|
if (key.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE) || key.equalsIgnoreCase(HttpHeaders.CONTENT_MD5)
|
||||||
|
|| key.equalsIgnoreCase(HttpHeaders.HOST) || key.startsWith(HEADER_TAG)) {
|
||||||
|
canonicalizedHeaders.put(key, header.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canonicalizedHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildCanonicalizedHeadersString(Multimap<String, String> canonicalizedHeadersMap) {
|
||||||
|
StringBuilder canonicalizedHeadersBuffer = new StringBuilder();
|
||||||
|
for (Entry<String, String> header : canonicalizedHeadersMap.entries()) {
|
||||||
|
String key = header.getKey();
|
||||||
|
canonicalizedHeadersBuffer.append(key.toLowerCase()).append(':').append(header.getValue()).append('\n');
|
||||||
|
}
|
||||||
|
return canonicalizedHeadersBuffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildSignedHeaders(Multimap<String, String> canonicalizedHeadersMap) {
|
||||||
|
return Joiner.on(';').join(Iterables.transform(canonicalizedHeadersMap.keySet(), new Function<String, String>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apply(String input) {
|
||||||
|
return input.toLowerCase();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sha256(byte[] unhashedBytes) {
|
||||||
|
return Hashing.sha256().hashBytes(unhashedBytes).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildHashedPayload(HttpRequest request) {
|
||||||
|
try {
|
||||||
|
byte[] unhashedBytes = request.getPayload() == null ? "".getBytes() : ByteStreams.toByteArray(request
|
||||||
|
.getPayload().getInput());
|
||||||
|
return sha256(unhashedBytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new HttpException("Error signing request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildAuthHeader(String accessKey, String credentialScope, String signedHeaders,
|
||||||
|
String signature) {
|
||||||
|
return ALGORITHM + " " + "Credential=" + accessKey + "/" + credentialScope + "," + "SignedHeaders="
|
||||||
|
+ signedHeaders + "," + "Signature=" + signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] hmacSha256(byte[] key, String s) {
|
||||||
|
try {
|
||||||
|
Mac hmacSHA256 = crypto.hmacSHA256(key);
|
||||||
|
return hmacSHA256.doFinal(s.getBytes());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new HttpException("Error signing request", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildSignature(String dateWithoutTimestamp, String stringToSign) {
|
||||||
|
byte[] kSecret = (AUTH_TAG + credential).getBytes(UTF_8);
|
||||||
|
byte[] kDate = hmacSha256(kSecret, dateWithoutTimestamp);
|
||||||
|
byte[] kRegion = hmacSha256(kDate, REGION);
|
||||||
|
byte[] kService = hmacSha256(kRegion, SERVICE);
|
||||||
|
byte[] kSigning = hmacSha256(kService, TERMINATION_STRING);
|
||||||
|
return BaseEncoding.base16().encode(hmacSha256(kSigning, stringToSign)).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest sign(HttpRequest request) {
|
||||||
|
// Grab the needed data to build the signature
|
||||||
|
Multimap<String, String> canonicalizedHeadersMap = buildCanonicalizedHeadersMap(request);
|
||||||
|
String canonicalizedHeadersString = buildCanonicalizedHeadersString(canonicalizedHeadersMap);
|
||||||
|
String signedHeaders = buildSignedHeaders(canonicalizedHeadersMap);
|
||||||
|
String date = request.getFirstHeaderOrNull(GlacierHeaders.ALTERNATE_DATE);
|
||||||
|
String dateWithoutTimestamp = formatDateWithoutTimestamp(date);
|
||||||
|
String method = request.getMethod();
|
||||||
|
String endpoint = request.getEndpoint().getRawPath();
|
||||||
|
String credentialScope = buildCredentialScope(dateWithoutTimestamp);
|
||||||
|
String hashedPayload = buildHashedPayload(request);
|
||||||
|
|
||||||
|
// Task 1: Create a Canonical Request For Signature Version 4.
|
||||||
|
String hashedCanonicalRequest = buildHashedCanonicalRequest(method, endpoint, hashedPayload,
|
||||||
|
canonicalizedHeadersString, signedHeaders);
|
||||||
|
|
||||||
|
// Task 2: Create a String to Sign for Signature Version 4.
|
||||||
|
String stringToSign = createStringToSign(date, credentialScope, hashedCanonicalRequest);
|
||||||
|
|
||||||
|
// Task 3: Calculate the AWS Signature Version 4.
|
||||||
|
String signature = buildSignature(dateWithoutTimestamp, stringToSign);
|
||||||
|
|
||||||
|
// Sign the request
|
||||||
|
String authHeader = buildAuthHeader(identity, credentialScope, signedHeaders, signature);
|
||||||
|
request = request.toBuilder().replaceHeader(HttpHeaders.AUTHORIZATION, authHeader).build();
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.jclouds.glacier.GlacierApiMetadata
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier;
|
||||||
|
|
||||||
|
import org.jclouds.blobstore.internal.BaseBlobStoreApiMetadataTest;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Roman Coedo
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit", testName = "GlacierApiMetadataTest")
|
||||||
|
public class GlacierApiMetadataTest extends BaseBlobStoreApiMetadataTest {
|
||||||
|
|
||||||
|
public GlacierApiMetadataTest() {
|
||||||
|
super(new GlacierApiMetadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier;
|
||||||
|
|
||||||
|
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||||
|
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
|
||||||
|
import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jclouds.ContextBuilder;
|
||||||
|
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Module;
|
||||||
|
import com.google.mockwebserver.MockResponse;
|
||||||
|
import com.google.mockwebserver.MockWebServer;
|
||||||
|
import com.google.mockwebserver.RecordedRequest;
|
||||||
|
|
||||||
|
public class GlacierClientMockTest {
|
||||||
|
|
||||||
|
private static final String VAULT_NAME = "ConcreteVaultName";
|
||||||
|
|
||||||
|
private static final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(),
|
||||||
|
sameThreadExecutor()));
|
||||||
|
|
||||||
|
static GlacierClient getGlacierClient(URL server) {
|
||||||
|
Properties overrides = new Properties();
|
||||||
|
// prevent expect-100 bug http://code.google.com/p/mockwebserver/issues/detail?id=6
|
||||||
|
overrides.setProperty(PROPERTY_SO_TIMEOUT, "0");
|
||||||
|
overrides.setProperty(PROPERTY_MAX_RETRIES, "1");
|
||||||
|
return ContextBuilder.newBuilder("glacier").credentials("accessKey", "secretKey").endpoint(server.toString())
|
||||||
|
.modules(modules).overrides(overrides).buildApi(GlacierClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateVault() throws IOException, InterruptedException {
|
||||||
|
// Prepare the response
|
||||||
|
MockResponse mr = new MockResponse();
|
||||||
|
mr.setResponseCode(201);
|
||||||
|
mr.addHeader("x-amzn-RequestId", "AAABZpJrTyioDC_HsOmHae8EZp_uBSJr6cnGOLKp_XJCl-Q");
|
||||||
|
mr.addHeader("Date", "Sun, 25 Mar 2012 12:02:00 GMT");
|
||||||
|
mr.addHeader("Location", "/111122223333/vaults/" + VAULT_NAME);
|
||||||
|
MockWebServer server = new MockWebServer();
|
||||||
|
server.enqueue(mr);
|
||||||
|
server.play();
|
||||||
|
|
||||||
|
// Send the request and check the response
|
||||||
|
try {
|
||||||
|
GlacierClient client = getGlacierClient(server.getUrl("/"));
|
||||||
|
URI responseUri = client.createVault(VAULT_NAME);
|
||||||
|
assertEquals(responseUri.toString(), server.getUrl("/") + "111122223333/vaults/" + VAULT_NAME);
|
||||||
|
RecordedRequest request = server.takeRequest();
|
||||||
|
assertEquals(request.getRequestLine(), "PUT /-/vaults/" + VAULT_NAME + " HTTP/1.1");
|
||||||
|
} finally {
|
||||||
|
server.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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.glacier.util;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
import org.jclouds.encryption.internal.JCECrypto;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.TreeMultimap;
|
||||||
|
|
||||||
|
@Test(groups = "unit", testName = "AWSRequestSignerV4Test")
|
||||||
|
public class AWSRequestSignerV4Test {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignatureCalculation() throws NoSuchAlgorithmException, CertificateException {
|
||||||
|
String auth = "AWS4-HMAC-SHA256 " + "Credential=AKIAIOSFODNN7EXAMPLE/20120525/us-east-1/glacier/aws4_request,"
|
||||||
|
+ "SignedHeaders=host;x-amz-date;x-amz-glacier-version,"
|
||||||
|
+ "Signature=3ce5b2f2fffac9262b4da9256f8d086b4aaf42eba5f111c21681a65a127b7c2a";
|
||||||
|
String identity = "AKIAIOSFODNN7EXAMPLE";
|
||||||
|
String credential = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
|
||||||
|
AWSRequestSignerV4 signer = new AWSRequestSignerV4(identity, credential, new JCECrypto());
|
||||||
|
HttpRequest request = signer.sign(createRequest());
|
||||||
|
assertEquals(request.getFirstHeaderOrNull("Authorization"), auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpRequest createRequest() {
|
||||||
|
Multimap<String, String> headers = TreeMultimap.create();
|
||||||
|
headers.put("Host", "glacier.us-east-1.amazonaws.com");
|
||||||
|
headers.put("x-amz-date", "20120525T002453Z");
|
||||||
|
headers.put("x-amz-glacier-version", "2012-06-01");
|
||||||
|
HttpRequest request = HttpRequest.builder().method("PUT")
|
||||||
|
.endpoint("https://glacier.us-east-1.amazonaws.com/-/vaults/examplevault").headers(headers).build();
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.jclouds.glacier.GlacierApiMetadata
|
Loading…
Reference in New Issue