/* * 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.apache.brooklyn.core.mgmt.persist.jclouds; import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.openstack.reference.AuthHeaders.URL_SUFFIX; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.List; import java.util.Map.Entry; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.internal.BrooklynProperties; import org.apache.brooklyn.core.location.LocationConfigKeys; import org.apache.brooklyn.core.location.cloud.CloudLocationConfig; import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.http.HttpTool; import org.apache.brooklyn.util.http.HttpToolResponse; import org.apache.brooklyn.util.text.Identifiers; import org.apache.brooklyn.util.time.Duration; import org.apache.brooklyn.util.time.Time; import org.apache.http.client.HttpClient; import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.domain.PageSet; import org.jclouds.blobstore.domain.StorageMetadata; import org.jclouds.domain.Credentials; import org.jclouds.openstack.domain.AuthenticationResponse; import org.jclouds.openstack.reference.AuthHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.apache.brooklyn.location.jclouds.JcloudsLocation; import org.apache.brooklyn.location.jclouds.JcloudsUtil; import com.google.common.base.Preconditions; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import com.google.inject.Inject; @Test(groups={"Live", "Live-sanity"}) public class BlobStoreExpiryTest { private static final Logger log = LoggerFactory.getLogger(BlobStoreExpiryTest.class); /** * Live tests as written require a location defined as follows: * * <code> * brooklyn.location.named.brooklyn-jclouds-objstore-test-1==jclouds:swift:https://ams01.objectstorage.softlayer.net/auth/v1.0 * brooklyn.location.named.brooklyn-jclouds-objstore-test-1.identity=IBMOS1234-5:yourname * brooklyn.location.named.brooklyn-jclouds-objstore-test-1.credential=0123abcd....... * </code> */ public static final String PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC = BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC; public static final String CONTAINER_PREFIX = "brooklyn-persistence-test"; private String locationSpec = PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC; private JcloudsLocation location; private BlobStoreContext context; private ManagementContext mgmt; private String testContainerName; private String identity; private String credential; private String provider; private String endpoint; public synchronized BlobStoreContext getSwiftBlobStoreContext() { if (context==null) { if (location==null) { Preconditions.checkNotNull(locationSpec, "locationSpec required for remote object store when location is null"); Preconditions.checkNotNull(mgmt, "mgmt required for remote object store when location is null"); location = (JcloudsLocation) mgmt.getLocationRegistry().resolve(locationSpec); } identity = checkNotNull(location.getConfig(LocationConfigKeys.ACCESS_IDENTITY), "identity must not be null"); credential = checkNotNull(location.getConfig(LocationConfigKeys.ACCESS_CREDENTIAL), "credential must not be null"); provider = checkNotNull(location.getConfig(LocationConfigKeys.CLOUD_PROVIDER), "provider must not be null"); endpoint = location.getConfig(CloudLocationConfig.CLOUD_ENDPOINT); context = JcloudsUtil.newBlobstoreContext(provider, endpoint, identity, credential); } return context; } @BeforeMethod(alwaysRun=true) public void setup() { testContainerName = CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(8); mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault()); } @AfterMethod(alwaysRun=true) public void teardown() { Entities.destroyAll(mgmt); if (context!=null) context.close(); context = null; } public void testRenewAuthSucceedsInSwiftObjectStore() throws Exception { doTestRenewAuth(); } protected void doTestRenewAuth() throws Exception { getSwiftBlobStoreContext(); injectShortLivedTokenForSwiftAuth(); context.getBlobStore().createContainerInLocation(null, testContainerName); assertContainerFound(); log.info("created container, now sleeping for expiration"); Time.sleep(Duration.TEN_SECONDS); assertContainerFound(); context.getBlobStore().deleteContainer(testContainerName); } private void assertContainerFound() { PageSet<? extends StorageMetadata> ps = context.getBlobStore().list(); BlobStoreTest.assertHasItemNamed(ps, testContainerName); } private void injectShortLivedTokenForSwiftAuth() throws Exception { URL endpointUrl = new URL(endpoint); HttpToolResponse tokenHttpResponse1 = requestTokenWithExplicitLifetime(endpointUrl, identity, credential, Duration.FIVE_SECONDS); Builder<String, URI> servicesMapBuilder = ImmutableMap.builder(); for (Entry<String, List<String>> entry : tokenHttpResponse1.getHeaderLists().entrySet()) { if (entry.getKey().toLowerCase().endsWith(URL_SUFFIX.toLowerCase()) || entry.getKey().toLowerCase().endsWith("X-Auth-Token-Expires".toLowerCase())){ servicesMapBuilder.put(entry.getKey(), URI.create(entry.getValue().iterator().next())); } } AuthenticationResponse authResponse = new AuthenticationResponse(tokenHttpResponse1.getHeaderLists().get(AuthHeaders.AUTH_TOKEN).get(0), servicesMapBuilder.build()); getAuthCache().put(new Credentials(identity, credential), authResponse); } private LoadingCache<Credentials, AuthenticationResponse> getAuthCache() { return context.utils().injector().getInstance(CachePeeker.class).authenticationResponseCache; } public static class CachePeeker { private final LoadingCache<Credentials, AuthenticationResponse> authenticationResponseCache; @Inject protected CachePeeker(LoadingCache<Credentials, AuthenticationResponse> authenticationResponseCache) { this.authenticationResponseCache = authenticationResponseCache; } } public static HttpToolResponse requestTokenWithExplicitLifetime(URL url, String user, String key, Duration expiration) throws URISyntaxException { HttpClient client = HttpTool.httpClientBuilder().build(); HttpToolResponse response = HttpTool.httpGet(client, url.toURI(), MutableMap.<String,String>of() .add(AuthHeaders.AUTH_USER, user) .add(AuthHeaders.AUTH_KEY, key) .add("Host", url.getHost()) .add("X-Auth-New-Token", "" + true) .add("X-Auth-Token-Lifetime", "" + expiration.toSeconds()) ); // curl -v https://ams01.objectstorage.softlayer.net/auth/v1.0/v1.0 -H "X-Auth-User: IBMOS12345-2:username" -H "X-Auth-Key: <API KEY>" -H "Host: ams01.objectstorage.softlayer.net" -H "X-Auth-New-Token: true" -H "X-Auth-Token-Lifetime: 15" log.info("Requested token with explicit lifetime: "+expiration+" at "+url+"\n"+response+"\n"+response.getHeaderLists()); return response; } }