diff --git a/common/openstack/src/main/java/org/jclouds/openstack/services/Extension.java b/common/openstack/src/main/java/org/jclouds/openstack/services/Extension.java new file mode 100644 index 0000000000..06965f8048 --- /dev/null +++ b/common/openstack/src/main/java/org/jclouds/openstack/services/Extension.java @@ -0,0 +1,77 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.openstack.services; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * An extension of a {@link ServiceType service}. In order for us to understand + * the context of the extension, we must consider the extensions call. + * + *
+ * For our purposes, the minimal context of an extension is the type of the + * service it extends ex. {@link ServiceType#COMPUTE}, and its namespace ex. http://docs.openstack.org + * /ext/keypairs/api/v1.1. + * + * @author Adrian Cole + * + * @see ServiceType + * @see + * @see + * @see + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Qualifier +public @interface Extension { + + /** + * the service type this is an extension of. + * + *

note

+ * + * This isn't necessarily one of the built-in {@link ServiceType services}, + * it could be an extension of a custom service. + * + * @return the service type this is an extension of. + * + */ + String of(); + + /** + * namespace ex.
http + * ://docs.openstack.org /ext/keypairs/api/v1.1. + * + * @return the namespace of the extension + */ + String namespace(); + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java index 288cbf8c75..eb3eb3e6d5 100644 --- a/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java +++ b/core/src/main/java/org/jclouds/concurrent/internal/SyncProxy.java @@ -35,8 +35,11 @@ import javax.inject.Named; import org.jclouds.concurrent.Timeout; import org.jclouds.internal.ClassMethodArgs; import org.jclouds.rest.annotations.Delegate; +import org.jclouds.util.Optionals2; import org.jclouds.util.Throwables2; +import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -51,14 +54,15 @@ import com.google.inject.ProvisionException; public class SyncProxy implements InvocationHandler { @SuppressWarnings("unchecked") - public static T proxy(Class clazz, Object async, + public static T proxy(Function> optionalConverter, Class clazz, Object async, @Named("sync") LoadingCache delegateMap, Map, Class> sync2Async, Map timeouts) throws IllegalArgumentException, SecurityException, NoSuchMethodException { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, - new SyncProxy(clazz, async, delegateMap, sync2Async, timeouts)); + new SyncProxy(optionalConverter, clazz, async, delegateMap, sync2Async, timeouts)); } + private final Function> optionalConverter; private final Object delegate; private final Class declaring; private final Map methodMap; @@ -69,10 +73,11 @@ public class SyncProxy implements InvocationHandler { private static final Set objectMethods = ImmutableSet.copyOf(Object.class.getMethods()); @Inject - private SyncProxy(Class declaring, Object async, + private SyncProxy(Function> optionalConverter, Class declaring, Object async, @Named("sync") LoadingCache delegateMap, Map, Class> sync2Async, final Map timeouts) throws SecurityException, NoSuchMethodException { + this.optionalConverter = optionalConverter; this.delegateMap = delegateMap; this.delegate = async; this.declaring = declaring; @@ -125,10 +130,19 @@ public class SyncProxy implements InvocationHandler { } else if (method.getName().equals("toString")) { return this.toString(); } else if (method.isAnnotationPresent(Delegate.class)) { - Class asyncClass = sync2Async.get(method.getReturnType()); - checkState(asyncClass != null, "please configure corresponding async class for " + method.getReturnType() + Class syncClass = Optionals2.returnTypeOrTypeOfOptional(method); + // get the return type of the asynchronous class associated with this client + // ex. FloatingIPClient is associated with FloatingIPAsyncClient + Class asyncClass = sync2Async.get(syncClass); + checkState(asyncClass != null, "please configure corresponding async class for " + syncClass + " in your RestClientModule"); + // pass any parameters necessary to get a relevant instance of that async class + // ex. getClientForRegion("north") might return an instance whose endpoint is + // different that "south" Object returnVal = delegateMap.get(new ClassMethodArgs(asyncClass, method, args)); + if (Optionals2.isReturnTypeOptional(method)){ + return optionalConverter.apply(returnVal); + } return returnVal; } else if (syncMethodMap.containsKey(method)) { return syncMethodMap.get(method).invoke(delegate, args); diff --git a/core/src/main/java/org/jclouds/rest/config/BinderUtils.java b/core/src/main/java/org/jclouds/rest/config/BinderUtils.java index 790b6dbe87..d64723f6b8 100644 --- a/core/src/main/java/org/jclouds/rest/config/BinderUtils.java +++ b/core/src/main/java/org/jclouds/rest/config/BinderUtils.java @@ -71,19 +71,19 @@ public class BinderUtils { * interface for the sync client (ex. LoginClient) * @param asyncClientType * interface for the async client (ex. LoginAsyncClient) - * @param delegates + * @param sync2Async * presuming your clients are annotated with @Delegate, contains the sync to async * classes relating to these methods */ public static void bindClientAndAsyncClient(Binder binder, Class syncClientType, Class asyncClientType, - Map, Class> delegates) { - bindClient(binder, syncClientType, asyncClientType, delegates); + Map, Class> sync2Async) { + bindClient(binder, syncClientType, asyncClientType, sync2Async); bindAsyncClient(binder, asyncClientType); } public static void bindClient(Binder binder, Class syncClientType, Class asyncClientType, - Map, Class> delegates) { - Provider asyncProvider = new ClientProvider(syncClientType, asyncClientType, delegates); + Map, Class> sync2Async) { + Provider asyncProvider = new ClientProvider(syncClientType, asyncClientType, sync2Async); binder.requestInjection(asyncProvider); binder.bind(syncClientType).toProvider(asyncProvider); } diff --git a/core/src/main/java/org/jclouds/rest/config/ClientProvider.java b/core/src/main/java/org/jclouds/rest/config/ClientProvider.java index c8275155b3..bd55d77f9c 100644 --- a/core/src/main/java/org/jclouds/rest/config/ClientProvider.java +++ b/core/src/main/java/org/jclouds/rest/config/ClientProvider.java @@ -26,6 +26,8 @@ import javax.inject.Singleton; import org.jclouds.concurrent.internal.SyncProxy; import org.jclouds.internal.ClassMethodArgs; +import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.common.cache.LoadingCache; import com.google.inject.Injector; @@ -59,17 +61,18 @@ public class ClientProvider implements Provider { @Singleton public S get() { A client = (A) injector.getInstance(Key.get(asyncClientType)); + Function> optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { + })); LoadingCache delegateMap = injector.getInstance(Key.get( new TypeLiteral>() { }, Names.named("sync"))); + Map timeoutsMap = injector.getInstance(Key.get(new TypeLiteral>() { + }, Names.named("TIMEOUTS"))); try { - return (S) SyncProxy.proxy(syncClientType, client, delegateMap, sync2Async, - injector.getInstance(Key.get(new TypeLiteral>() { - }, Names.named("TIMEOUTS")))); + return (S) SyncProxy.proxy(optionalConverter, syncClientType, client, delegateMap, sync2Async, + timeoutsMap); } catch (Exception e) { - Throwables.propagate(e); - assert false : "should have propagated"; - return null; + throw Throwables.propagate(e); } } } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java b/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java index 211eacce80..93ea413792 100644 --- a/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java +++ b/core/src/main/java/org/jclouds/rest/config/CreateClientForCaller.java @@ -33,7 +33,10 @@ import com.google.inject.TypeLiteral; import com.google.inject.name.Names; import org.jclouds.concurrent.internal.SyncProxy; import org.jclouds.internal.ClassMethodArgs; +import org.jclouds.util.Optionals2; +import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.common.cache.LoadingCache; import com.google.common.cache.CacheLoader; @@ -61,19 +64,19 @@ public class CreateClientForCaller extends CacheLoader @Override public Object load(ClassMethodArgs from) throws ExecutionException { - Class syncClass = from.getMethod().getReturnType(); + Class syncClass = Optionals2.returnTypeOrTypeOfOptional(from.getMethod()); Class asyncClass = sync2Async.get(syncClass); checkState(asyncClass != null, "configuration error, sync class " + syncClass + " not mapped to an async class"); Object asyncClient = asyncMap.get(from); checkState(asyncClient != null, "configuration error, sync client for " + from + " not found"); + Function> optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { + })); + Map timeoutsMap = injector.getInstance(Key.get(new TypeLiteral>() { + }, Names.named("TIMEOUTS"))); try { - return SyncProxy.proxy(syncClass, asyncClient, delegateMap.get(), sync2Async, - injector.getInstance(Key.get(new TypeLiteral>() { - }, Names.named("TIMEOUTS")))); + return SyncProxy.proxy(optionalConverter, syncClass, asyncClient, delegateMap.get(), sync2Async, timeoutsMap); } catch (Exception e) { - Throwables.propagate(e); - assert false : "should have propagated"; - return null; + throw Throwables.propagate(e); } } diff --git a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java index 93c9f777d8..a2bc447326 100644 --- a/core/src/main/java/org/jclouds/rest/config/RestClientModule.java +++ b/core/src/main/java/org/jclouds/rest/config/RestClientModule.java @@ -31,8 +31,11 @@ import org.jclouds.location.config.LocationModule; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.RestContext; +import org.jclouds.rest.functions.ImplicitOptionalConverter; import org.jclouds.rest.internal.RestContextImpl; +import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.common.cache.CacheBuilder; import com.google.common.cache.LoadingCache; @@ -55,14 +58,14 @@ public class RestClientModule extends AbstractModule { protected final Class asyncClientType; protected final Class syncClientType; - protected final Map, Class> delegates; + protected final Map, Class> sync2Async; protected final AtomicReference authException = new AtomicReference(); public RestClientModule(Class syncClientType, Class asyncClientType, - Map, Class> delegates) { + Map, Class> sync2Async) { this.asyncClientType = asyncClientType; this.syncClientType = syncClientType; - this.delegates = delegates; + this.sync2Async = sync2Async; } public RestClientModule(Class syncClientType, Class asyncClientType) { @@ -77,6 +80,7 @@ public class RestClientModule extends AbstractModule { @SuppressWarnings({ "unchecked", "rawtypes" }) @Override protected void configure() { + bind(new TypeLiteral>>(){}).to(ImplicitOptionalConverter.class); // this will help short circuit scenarios that can otherwise lock out users bind(new TypeLiteral>(){}).toInstance(authException); // Ensures the restcontext can be looked up without generic types. @@ -136,7 +140,7 @@ public class RestClientModule extends AbstractModule { protected void bindClient() { BinderUtils.bindClient(binder(), syncClientType, asyncClientType, - delegates); + sync2Async); } @@ -145,7 +149,7 @@ public class RestClientModule extends AbstractModule { @Named("sync") LoadingCache provideSyncDelegateMap( CreateClientForCaller createClientForCaller) { - createClientForCaller.sync2Async = delegates; + createClientForCaller.sync2Async = sync2Async; return CacheBuilder.newBuilder().build(createClientForCaller); } diff --git a/core/src/main/java/org/jclouds/rest/functions/AlwaysPresentImplicitOptionalConverter.java b/core/src/main/java/org/jclouds/rest/functions/AlwaysPresentImplicitOptionalConverter.java new file mode 100644 index 0000000000..7580f00d50 --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/functions/AlwaysPresentImplicitOptionalConverter.java @@ -0,0 +1,34 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.rest.functions; + +import com.google.common.base.Optional; + +/** + * + * @author Adrian Cole + */ +public class AlwaysPresentImplicitOptionalConverter implements ImplicitOptionalConverter { + + @Override + public Optional apply(Object input) { + return Optional.of(input); + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/functions/ImplicitOptionalConverter.java b/core/src/main/java/org/jclouds/rest/functions/ImplicitOptionalConverter.java new file mode 100644 index 0000000000..d6e49d96b9 --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/functions/ImplicitOptionalConverter.java @@ -0,0 +1,32 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.rest.functions; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.inject.ImplementedBy; + +/** + * + * @author Adrian Cole + */ +@ImplementedBy(AlwaysPresentImplicitOptionalConverter.class) +public interface ImplicitOptionalConverter extends Function> { + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java b/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java index 4685db7bd3..f4cf02bd10 100644 --- a/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java +++ b/core/src/main/java/org/jclouds/rest/internal/AsyncRestClientProxy.java @@ -41,9 +41,11 @@ import org.jclouds.logging.Logger; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.InvocationContext; import org.jclouds.rest.annotations.Delegate; +import org.jclouds.util.Optionals2; import org.jclouds.util.Throwables2; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.cache.LoadingCache; @@ -97,6 +99,7 @@ public class AsyncRestClientProxy implements InvocationHandler { @Resource protected Logger logger = Logger.NULL; + private final Function> optionalConverter; private final LoadingCache delegateMap; @SuppressWarnings("unchecked") @@ -104,6 +107,8 @@ public class AsyncRestClientProxy implements InvocationHandler { public AsyncRestClientProxy(Injector injector, Factory factory, RestAnnotationProcessor util, TypeLiteral typeLiteral, @Named("async") LoadingCache delegateMap) { this.injector = injector; + this.optionalConverter = injector.getInstance(Key.get(new TypeLiteral>>() { + })); this.annotationProcessor = util; this.declaring = (Class) typeLiteral.getRawType(); this.commandFactory = factory; @@ -144,7 +149,12 @@ public class AsyncRestClientProxy implements InvocationHandler { } public Object propagateContextToDelegate(Method method, Object[] args) throws ExecutionException { - return delegateMap.get(new ClassMethodArgs(method.getReturnType(), method, args)); + Class asyncClass = Optionals2.returnTypeOrTypeOfOptional(method); + Object returnVal = delegateMap.get(new ClassMethodArgs(asyncClass, method, args)); + if (Optionals2.isReturnTypeOptional(method)){ + return optionalConverter.apply(returnVal); + } + return returnVal; } public Object lookupValueFromGuice(Method method) { diff --git a/core/src/main/java/org/jclouds/util/Optionals2.java b/core/src/main/java/org/jclouds/util/Optionals2.java new file mode 100644 index 0000000000..2ec4b1efc8 --- /dev/null +++ b/core/src/main/java/org/jclouds/util/Optionals2.java @@ -0,0 +1,50 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.util; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; + +import com.google.common.base.Optional; + +/** + * + * @author Adrian Cole + */ +public class Optionals2 { + + public static Class returnTypeOrTypeOfOptional(Method method) { + boolean optional = isReturnTypeOptional(method); + Class syncClass; + if (optional) { + ParameterizedType futureType = ParameterizedType.class.cast(method.getGenericReturnType()); + // TODO: error checking in case this is a type, not a class. + syncClass = Class.class.cast(futureType.getActualTypeArguments()[0]); + } else { + syncClass = method.getReturnType(); + } + return syncClass; + } + + public static boolean isReturnTypeOptional(Method method) { + boolean optional = method.getReturnType().isAssignableFrom(Optional.class); + return optional; + } + +} diff --git a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java index 7a29a04e25..de9fe2dafb 100644 --- a/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java +++ b/core/src/test/java/org/jclouds/concurrent/internal/SyncProxyTest.java @@ -32,13 +32,14 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Futures; import org.jclouds.concurrent.Timeout; import org.jclouds.internal.ClassMethodArgs; +import org.jclouds.rest.functions.AlwaysPresentImplicitOptionalConverter; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import com.google.common.base.Functions; -import com.google.common.cache.LoadingCache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; @@ -185,7 +186,7 @@ public class SyncProxyTest { public void setUp() throws IllegalArgumentException, SecurityException, NoSuchMethodException { LoadingCache cache = CacheBuilder.newBuilder().build( CacheLoader.from(Functions. constant(null))); - sync = SyncProxy.proxy(Sync.class, new Async(), cache, ImmutableMap., Class> of(), + sync = SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), Sync.class, new Async(), cache, ImmutableMap., Class> of(), ImmutableMap.of("Sync.takeXMillisecondsPropOverride", 250L)); // just to warm up sync.string(); @@ -260,7 +261,7 @@ public class SyncProxyTest { IOException { LoadingCache cache = CacheBuilder.newBuilder().build( CacheLoader.from(Functions. constant(null))); - SyncProxy.proxy(SyncWrongException.class, new Async(), cache, ImmutableMap., Class> of(), + SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), SyncWrongException.class, new Async(), cache, ImmutableMap., Class> of(), ImmutableMap. of()); } @@ -280,7 +281,7 @@ public class SyncProxyTest { IOException { LoadingCache cache = CacheBuilder.newBuilder().build( CacheLoader.from(Functions. constant(null))); - SyncProxy.proxy(SyncNoTimeOut.class, new Async(), cache, ImmutableMap., Class> of(), + SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), SyncNoTimeOut.class, new Async(), cache, ImmutableMap., Class> of(), ImmutableMap. of()); } @@ -301,7 +302,7 @@ public class SyncProxyTest { public void testClassOverridePropTimeout() throws Exception { LoadingCache cache = CacheBuilder.newBuilder().build( CacheLoader.from(Functions. constant(null))); - final SyncClassOverride sync2 = SyncProxy.proxy(SyncClassOverride.class, new Async(), cache, + final SyncClassOverride sync2 = SyncProxy.proxy(new AlwaysPresentImplicitOptionalConverter(), SyncClassOverride.class, new Async(), cache, ImmutableMap., Class> of(), ImmutableMap. of("SyncClassOverride", 100L)); assertEquals(sync2.takeXMillisecondsPropOverride(200), "foo"); diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java index 15fd9dba94..d3c90b7065 100644 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -135,6 +135,7 @@ import org.jclouds.rest.binders.BindMapToMatrixParams; import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.binders.BindToStringPayload; import org.jclouds.rest.config.RestClientModule; +import org.jclouds.rest.functions.ImplicitOptionalConverter; import org.jclouds.util.Strings2; import org.jclouds.xml.XMLParser; import org.testng.Assert; @@ -144,6 +145,7 @@ import org.testng.annotations.Test; import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; @@ -184,8 +186,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { protected void configure() { super.configure(); bind(new TypeLiteral>() { - }).annotatedWith(Localhost2.class).toInstance( - Suppliers.ofInstance(URI.create("http://localhost:1111"))); + }).annotatedWith(Localhost2.class).toInstance(Suppliers.ofInstance(URI.create("http://localhost:1111"))); bind(IOExceptionRetryHandler.class).toInstance(IOExceptionRetryHandler.NEVER_RETRY); } @@ -201,17 +202,20 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @Endpoint(Localhost2.class) @Timeout(duration = 10, timeUnit = TimeUnit.NANOSECONDS) public static interface Caller { - + // tests that we can pull from suppliers @Provides @Localhost2 URI getURI(); - + @Delegate public Callee getCallee(); @Delegate public Callee getCallee(@EndpointParam URI endpoint); + + @Delegate + public Optional getOptionalCallee(@EndpointParam URI endpoint); } @Timeout(duration = 10, timeUnit = TimeUnit.NANOSECONDS) @@ -224,21 +228,25 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @Provides @Localhost2 URI getURI(); - + @Delegate public AsyncCallee getCallee(); @Delegate public AsyncCallee getCallee(@EndpointParam URI endpoint); + + @Delegate + public Optional getOptionalCallee(@EndpointParam URI endpoint); } - public void testAsyncDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException { + public void testAsyncDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, + ExecutionException { Injector child = injectorForCaller(new HttpCommandExecutorService() { @Override public Future submit(HttpCommand command) { assertEquals(command.getCurrentRequest().getRequestLine(), - "GET http://localhost:9999/client/1/foo HTTP/1.1"); + "GET http://localhost:9999/client/1/foo HTTP/1.1"); return Futures.immediateFuture(HttpResponse.builder().build()); } @@ -255,13 +263,14 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } - public void testDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException { + public void testDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, + ExecutionException { Injector child = injectorForCaller(new HttpCommandExecutorService() { @Override public Future submit(HttpCommand command) { assertEquals(command.getCurrentRequest().getRequestLine(), - "GET http://localhost:1111/client/1/foo HTTP/1.1"); + "GET http://localhost:1111/client/1/foo HTTP/1.1"); return Futures.immediateFuture(HttpResponse.builder().build()); } @@ -278,15 +287,14 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { child.getInstance(Caller.class).getCallee().onePath("foo"); } - - public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException { + public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, + ExecutionException { Injector child = injectorForCaller(new HttpCommandExecutorService() { @Override public Future submit(HttpCommand command) { - assertEquals(command.getCurrentRequest().getRequestLine(), - "GET http://howdyboys/client/1/foo HTTP/1.1"); + assertEquals(command.getCurrentRequest().getRequestLine(), "GET http://howdyboys/client/1/foo HTTP/1.1"); return Futures.immediateFuture(HttpResponse.builder().build()); } @@ -305,13 +313,77 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } - public void testDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException { + public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPathOptionalPresent() + throws InterruptedException, ExecutionException { Injector child = injectorForCaller(new HttpCommandExecutorService() { @Override public Future submit(HttpCommand command) { - assertEquals(command.getCurrentRequest().getRequestLine(), - "GET http://howdyboys/client/1/foo HTTP/1.1"); + assertEquals(command.getCurrentRequest().getRequestLine(), "GET http://howdyboys/client/1/foo HTTP/1.1"); + return Futures.immediateFuture(HttpResponse.builder().build()); + } + + }); + + try { + child.getInstance(AsyncCallee.class); + assert false : "Callee shouldn't be bound yet"; + } catch (ConfigurationException e) { + + } + + child.getInstance(AsyncCaller.class).getOptionalCallee(URI.create("http://howdyboys")).get().onePath("foo").get(); + + assertEquals(child.getInstance(AsyncCaller.class).getURI(), URI.create("http://localhost:1111")); + + } + + public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPathCanOverrideOptionalBehaviour() + throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { + + @Override + public Future submit(HttpCommand command) { + assertEquals(command.getCurrentRequest().getRequestLine(), "GET http://howdyboys/client/1/foo HTTP/1.1"); + return Futures.immediateFuture(HttpResponse.builder().build()); + } + + }, new AbstractModule() { + + @Override + protected void configure() { + bind(ImplicitOptionalConverter.class).toInstance(new ImplicitOptionalConverter() { + + @Override + public Optional apply(Object input) { + return Optional.absent(); + } + + }); + } + + }); + + try { + child.getInstance(AsyncCallee.class); + assert false : "Callee shouldn't be bound yet"; + } catch (ConfigurationException e) { + + } + + assert !child.getInstance(AsyncCaller.class).getOptionalCallee(URI.create("http://howdyboys")).isPresent(); + + assertEquals(child.getInstance(AsyncCaller.class).getURI(), URI.create("http://localhost:1111")); + + } + + public void testDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, + ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { + + @Override + public Future submit(HttpCommand command) { + assertEquals(command.getCurrentRequest().getRequestLine(), "GET http://howdyboys/client/1/foo HTTP/1.1"); return Futures.immediateFuture(HttpResponse.builder().build()); } @@ -327,12 +399,21 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { assertEquals(child.getInstance(Caller.class).getURI(), URI.create("http://localhost:1111")); } - - private Injector injectorForCaller(HttpCommandExecutorService service) { - RestContextSpec contextSpec = contextSpec("test", "http://localhost:9999", "1", "", "", - "userfoo", null, Caller.class, AsyncCaller.class, ImmutableSet. of(new MockModule(service), - new NullLoggingModule(), new CallerModule())); + private Injector injectorForCaller(HttpCommandExecutorService service, Module... modules) { + + RestContextSpec contextSpec = contextSpec( + "test", + "http://localhost:9999", + "1", + "", + "", + "userfoo", + null, + Caller.class, + AsyncCaller.class, + ImmutableSet. builder().add(new MockModule(service)).add(new NullLoggingModule()) + .add(new CallerModule()).addAll(ImmutableSet. copyOf(modules)).build()); return createContextBuilder(contextSpec).buildInjector(); @@ -1822,9 +1903,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { public void oneTransformerWithContext() throws SecurityException, NoSuchMethodException { RestAnnotationProcessor processor = factory(TestTransformers.class); Method method = TestTransformers.class.getMethod("oneTransformerWithContext"); - GeneratedHttpRequest request = GeneratedHttpRequest.requestBuilder().method("GET") - .endpoint(URI.create("http://localhost")).declaring(TestTransformers.class).javaMethod(method) - .args(new Object[] {}).build(); + GeneratedHttpRequest request = GeneratedHttpRequest. requestBuilder() + .method("GET").endpoint(URI.create("http://localhost")).declaring(TestTransformers.class) + .javaMethod(method).args(new Object[] {}).build(); Function transformer = processor.createResponseParser(method, request); assertEquals(transformer.getClass(), ReturnStringIf200Context.class); assertEquals(((ReturnStringIf200Context) transformer).request, request); @@ -2354,9 +2435,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { public void testCreateJAXBResponseParserWithAnnotation() throws SecurityException, NoSuchMethodException { RestAnnotationProcessor processor = factory(TestJAXBResponseParser.class); Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAnnotation"); - GeneratedHttpRequest request = GeneratedHttpRequest.requestBuilder() - .method("GET").endpoint(URI.create("http://localhost")).declaring(TestJAXBResponseParser.class) - .javaMethod(method).args(new Object[] {}).build(); + GeneratedHttpRequest request = GeneratedHttpRequest + . requestBuilder().method("GET").endpoint(URI.create("http://localhost")) + .declaring(TestJAXBResponseParser.class).javaMethod(method).args(new Object[] {}).build(); Function transformer = processor.createResponseParser(method, request); assertEquals(transformer.getClass(), ParseXMLWithJAXB.class); } @@ -2365,9 +2446,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { public void testCreateJAXBResponseParserWithAcceptHeader() throws SecurityException, NoSuchMethodException { RestAnnotationProcessor processor = factory(TestJAXBResponseParser.class); Method method = TestJAXBResponseParser.class.getMethod("jaxbGetWithAcceptHeader"); - GeneratedHttpRequest request = GeneratedHttpRequest.requestBuilder() - .method("GET").endpoint(URI.create("http://localhost")).declaring(TestJAXBResponseParser.class) - .javaMethod(method).args(new Object[] {}).build(); + GeneratedHttpRequest request = GeneratedHttpRequest + . requestBuilder().method("GET").endpoint(URI.create("http://localhost")) + .declaring(TestJAXBResponseParser.class).javaMethod(method).args(new Object[] {}).build(); Function transformer = processor.createResponseParser(method, request); assertEquals(transformer.getClass(), ParseXMLWithJAXB.class); } @@ -2409,40 +2490,40 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } @Test(expectedExceptions = NullPointerException.class) - public void testAddHostNullWithHost() throws Exception{ - assertNull(RestAnnotationProcessor.addHostIfMissing(null,null)); + public void testAddHostNullWithHost() throws Exception { + assertNull(RestAnnotationProcessor.addHostIfMissing(null, null)); } @Test(expectedExceptions = IllegalArgumentException.class) - public void testAddHostWithHostHasNoHost() throws Exception{ - assertNull(RestAnnotationProcessor.addHostIfMissing(null,new URI("/no/host"))); + public void testAddHostWithHostHasNoHost() throws Exception { + assertNull(RestAnnotationProcessor.addHostIfMissing(null, new URI("/no/host"))); } @Test - public void testAddHostNullOriginal() throws Exception{ - assertNull(RestAnnotationProcessor.addHostIfMissing(null,new URI("http://foo"))); + public void testAddHostNullOriginal() throws Exception { + assertNull(RestAnnotationProcessor.addHostIfMissing(null, new URI("http://foo"))); } @Test - public void testAddHostOriginalHasHost() throws Exception{ + public void testAddHostOriginalHasHost() throws Exception { - URI original = new URI("http://hashost/foo"); - URI result = RestAnnotationProcessor.addHostIfMissing(original,new URI("http://foo")); - assertEquals(original,result); + URI original = new URI("http://hashost/foo"); + URI result = RestAnnotationProcessor.addHostIfMissing(original, new URI("http://foo")); + assertEquals(original, result); } @Test - public void testAddHostIfMissing() throws Exception{ - URI result = RestAnnotationProcessor.addHostIfMissing(new URI("/bar"),new URI("http://foo")); - assertEquals(new URI("http://foo/bar"),result); + public void testAddHostIfMissing() throws Exception { + URI result = RestAnnotationProcessor.addHostIfMissing(new URI("/bar"), new URI("http://foo")); + assertEquals(new URI("http://foo/bar"), result); } DateService dateService = new SimpleDateFormatDateService(); @BeforeClass void setupFactory() { - RestContextSpec contextSpec = contextSpec("test", "http://localhost:9999", "1", "", "", "userfoo", - null, Callee.class, AsyncCallee.class, + RestContextSpec contextSpec = contextSpec("test", "http://localhost:9999", "1", "", "", + "userfoo", null, Callee.class, AsyncCallee.class, ImmutableSet. of(new MockModule(), new NullLoggingModule(), new AbstractModule() { @Override @@ -2453,7 +2534,7 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { }).annotatedWith(Names.named("bar")).toInstance(ImmutableSet.of("bar")); bind(new TypeLiteral>() { }).annotatedWith(Localhost2.class).toInstance( - Suppliers.ofInstance(URI.create("http://localhost:1111"))); + Suppliers.ofInstance(URI.create("http://localhost:1111"))); } @SuppressWarnings("unused") diff --git a/core/src/test/java/org/jclouds/util/Optionals2Test.java b/core/src/test/java/org/jclouds/util/Optionals2Test.java new file mode 100644 index 0000000000..9a2ffbddc6 --- /dev/null +++ b/core/src/test/java/org/jclouds/util/Optionals2Test.java @@ -0,0 +1,66 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.util; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.lang.reflect.Method; + +import org.testng.annotations.Test; + +import com.google.common.base.Optional; + +/** + * @author Adrian Cole + */ +@Test +public class Optionals2Test { + + public static interface Test { + Optional getOptional(); + + String getNotOptional(); + } + + public void testReturnTypeOrTypeOfOptionalWhenOptional() throws SecurityException, NoSuchMethodException { + Method method = Test.class.getMethod("getOptional"); + + assertEquals(Optionals2.returnTypeOrTypeOfOptional(method), String.class); + } + + public void testReturnTypeOrTypeOfOptionalWhenNotOptional() throws SecurityException, NoSuchMethodException { + Method method = Test.class.getMethod("getNotOptional"); + + assertEquals(Optionals2.returnTypeOrTypeOfOptional(method), String.class); + } + + public void testIsReturnTypeOptionalWhenOptional() throws SecurityException, NoSuchMethodException { + Method method = Test.class.getMethod("getOptional"); + + assertTrue(Optionals2.isReturnTypeOptional(method)); + } + + public void testIsReturnTypeOptionalWhenNotOptional() throws SecurityException, NoSuchMethodException { + Method method = Test.class.getMethod("getNotOptional"); + + assertFalse(Optionals2.isReturnTypeOptional(method)); + } +} diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java index 71534e7aef..10c8e6db5d 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaAsyncClient.java @@ -33,6 +33,7 @@ import org.jclouds.openstack.nova.v1_1.features.ServerAsyncClient; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; +import com.google.common.base.Optional; import com.google.inject.Provides; /** @@ -85,7 +86,7 @@ public interface NovaAsyncClient { * Provides asynchronous access to Floating IP features. */ @Delegate - FloatingIPAsyncClient getFloatingIPClientForRegion( + Optional getFloatingIPExtensionForRegion( @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region); /** diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java index 2cf57d5f40..e2e899e48b 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/NovaClient.java @@ -35,6 +35,7 @@ import org.jclouds.openstack.nova.v1_1.features.ServerClient; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.EndpointParam; +import com.google.common.base.Optional; import com.google.inject.Provides; /** @@ -87,7 +88,7 @@ public interface NovaClient { * Provides synchronous access to Floating IP features. */ @Delegate - FloatingIPClient getFloatingIPClientForRegion( + Optional getFloatingIPExtensionForRegion( @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region); /** diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java index 7cf5ef9611..725622493a 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/ExtensionNamespaces.java @@ -18,7 +18,6 @@ */ package org.jclouds.openstack.nova.v1_1.extensions; -import java.net.URI; /** * Extension namespaces @@ -27,8 +26,8 @@ import java.net.URI; * @see */ public interface ExtensionNamespaces { - public static URI KEYPAIRS = URI.create("http://docs.openstack.org/ext/keypairs/api/v1.1"); - public static URI VOLUMES = URI.create("http://docs.openstack.org/ext/volumes/api/v1.1"); - public static URI SECURITY_GROUPS = URI.create("http://docs.openstack.org/ext/securitygroups/api/v1.1"); - public static URI FLOATING_IPS = URI.create("http://docs.openstack.org/ext/floating_ips/api/v1.1"); + public static final String KEYPAIRS = "http://docs.openstack.org/ext/keypairs/api/v1.1"; + public static final String VOLUMES = "http://docs.openstack.org/ext/volumes/api/v1.1"; + public static final String SECURITY_GROUPS = "http://docs.openstack.org/ext/securitygroups/api/v1.1"; + public static final String FLOATING_IPS = "http://docs.openstack.org/ext/floating_ips/api/v1.1"; } diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClient.java index 9bbb413dfa..93e3df0d24 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClient.java @@ -32,6 +32,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v1_1.domain.FloatingIP; import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; @@ -50,10 +52,13 @@ import com.google.common.util.concurrent.ListenableFuture; * @see FloatingIPClient * @author Jeremy Daggett * @see ExtensionAsyncClient - * @see + * @see * @see * @see */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.FLOATING_IPS) @SkipEncoding({ '/', '=' }) @RequestFilters(AuthenticateRequest.class) public interface FloatingIPAsyncClient { @@ -98,7 +103,7 @@ public interface FloatingIPAsyncClient { @ExceptionParser(ReturnNullOnNotFoundOr404.class) @Path("/os-floating-ips/{id}") ListenableFuture deallocate(@PathParam("id") String id); - + /** * @see FloatingIPClient#addFloatingIP */ @@ -107,9 +112,7 @@ public interface FloatingIPAsyncClient { @Consumes @Produces(MediaType.APPLICATION_JSON) @Payload("%7B\"addFloatingIp\":%7B\"address\":\"{address}\"%7D%7D") - ListenableFuture addFloatingIP( - @PathParam("server") String serverId, - @PayloadParam("address") String address); + ListenableFuture addFloatingIP(@PathParam("server") String serverId, @PayloadParam("address") String address); /** * @see FloatingIPClient#removeFloatingIP @@ -119,8 +122,6 @@ public interface FloatingIPAsyncClient { @Consumes @Produces(MediaType.APPLICATION_JSON) @Payload("%7B\"removeFloatingIp\":%7B\"address\":\"{address}\"%7D%7D") - ListenableFuture removeFloatingIP( - @PathParam("server") String serverId, - @PayloadParam("address") String address); + ListenableFuture removeFloatingIP(@PathParam("server") String serverId, @PayloadParam("address") String address); } diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClient.java index 59934d94f3..6c53a719e9 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClient.java @@ -23,6 +23,8 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.nova.v1_1.domain.FloatingIP; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; /** * Provides synchronous access to Floating IPs. @@ -31,6 +33,7 @@ import org.jclouds.openstack.nova.v1_1.domain.FloatingIP; * @see FloatingIPAsyncClient * @author Jeremy Daggett */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.FLOATING_IPS) @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) public interface FloatingIPClient { diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairAsyncClient.java index 5c7eaf7aa5..1635f50ad6 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairAsyncClient.java @@ -33,6 +33,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v1_1.domain.KeyPair; import org.jclouds.openstack.nova.v1_1.features.ExtensionAsyncClient; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; @@ -55,6 +57,7 @@ import com.google.common.util.concurrent.ListenableFuture; * @see * @see */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.KEYPAIRS) @SkipEncoding({ '/', '=' }) @RequestFilters(AuthenticateRequest.class) public interface KeyPairAsyncClient { diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClient.java index 075272b3a8..cca7c89bbd 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClient.java @@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.nova.v1_1.domain.KeyPair; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; /** * Provides synchronous access to Security Groups. @@ -32,6 +34,7 @@ import org.jclouds.openstack.nova.v1_1.domain.KeyPair; * @see KeyPairAsyncClient * @author Jeremy Daggett */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.KEYPAIRS) @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) public interface KeyPairClient { diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java index a163861a58..c7c7a7c313 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupAsyncClient.java @@ -32,6 +32,8 @@ import javax.ws.rs.core.MediaType; import org.jclouds.openstack.filters.AuthenticateRequest; import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup; import org.jclouds.openstack.nova.v1_1.domain.SecurityGroupRule; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; @@ -54,6 +56,7 @@ import com.google.common.util.concurrent.ListenableFuture; * @see * @see */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.SECURITY_GROUPS) @SkipEncoding({ '/', '=' }) @RequestFilters(AuthenticateRequest.class) public interface SecurityGroupAsyncClient { diff --git a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClient.java b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClient.java index 8b78950119..fc14bf12ef 100644 --- a/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClient.java +++ b/labs/openstack-nova/src/main/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClient.java @@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup; import org.jclouds.openstack.nova.v1_1.domain.SecurityGroupRule; +import org.jclouds.openstack.services.Extension; +import org.jclouds.openstack.services.ServiceType; /** * Provides synchronous access to Security Groups. @@ -32,6 +34,7 @@ import org.jclouds.openstack.nova.v1_1.domain.SecurityGroupRule; * @see SecurityGroupAsyncClient * @author Jeremy Daggett */ +@Extension(of = ServiceType.COMPUTE, namespace = ExtensionNamespaces.SECURITY_GROUPS) @Timeout(duration = 30, timeUnit = TimeUnit.SECONDS) public interface SecurityGroupClient { diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java index 47ce6a984b..a1c9846fed 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/PasswordAuthenticationExpectTest.java @@ -26,7 +26,7 @@ import java.util.Properties; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseServerListTest; import org.testng.annotations.Test; @@ -39,7 +39,7 @@ import com.google.common.collect.ImmutableSet; * @author Adrian Cole */ @Test(groups = "unit", testName = "PasswordAuthenticationExpectTest") -public class PasswordAuthenticationExpectTest extends BaseNovaRestClientExpectTest { +public class PasswordAuthenticationExpectTest extends BaseNovaClientExpectTest { /** * this reflects the properties that a user would pass to createContext diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClientExpectTest.java new file mode 100644 index 0000000000..73540324ee --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPAsyncClientExpectTest.java @@ -0,0 +1,160 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.openstack.nova.v1_1.extensions; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.net.URI; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.openstack.nova.v1_1.NovaAsyncClient; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaAsyncClientExpectTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseFloatingIPListTest; +import org.jclouds.openstack.nova.v1_1.parse.ParseFloatingIPTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; + +/** + * Tests annotation parsing of {@code FloatingIPAsyncClient} + * + * @author Michael Arnold + */ +@Test(groups = "unit", testName = "FloatingIPAsyncClientExpectTest") +public class FloatingIPAsyncClientExpectTest extends BaseNovaAsyncClientExpectTest { + + public void testListFloatingIPsWhenResponseIs2xx() throws Exception { + HttpRequest listFloatingIPs = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .headers( + ImmutableMultimap. builder().put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listFloatingIPsResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/floatingip_list.json")).build(); + + NovaAsyncClient clientWhenFloatingIPsExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey, + responseWithKeystoneAccess, listFloatingIPs, listFloatingIPsResponse); + + assertEquals(clientWhenFloatingIPsExist.getConfiguredRegions(), ImmutableSet.of("North")); + + assertEquals(clientWhenFloatingIPsExist.getFloatingIPExtensionForRegion("North").get().listFloatingIPs().get() + .toString(), new ParseFloatingIPListTest().expected().toString()); + } + + public void testListFloatingIPsWhenResponseIs404() throws Exception { + HttpRequest listFloatingIPs = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .headers( + ImmutableMultimap. builder().put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse listFloatingIPsResponse = HttpResponse.builder().statusCode(404).build(); + + NovaAsyncClient clientWhenNoServersExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey, + responseWithKeystoneAccess, listFloatingIPs, listFloatingIPsResponse); + + assertTrue(clientWhenNoServersExist.getFloatingIPExtensionForRegion("North").get().listFloatingIPs().get() + .isEmpty()); + } + + public void testGetFloatingIPWhenResponseIs2xx() throws Exception { + HttpRequest getFloatingIP = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1")) + .headers( + ImmutableMultimap. builder().put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse getFloatingIPResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/floatingip_details.json")).build(); + + NovaAsyncClient clientWhenFloatingIPsExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey, + responseWithKeystoneAccess, getFloatingIP, getFloatingIPResponse); + + assertEquals(clientWhenFloatingIPsExist.getFloatingIPExtensionForRegion("North").get().getFloatingIP("1").get() + .toString(), new ParseFloatingIPTest().expected().toString()); + } + + public void testGetFloatingIPWhenResponseIs404() throws Exception { + HttpRequest getFloatingIP = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips/1")) + .headers( + ImmutableMultimap. builder().put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()).build(); + + HttpResponse getFloatingIPResponse = HttpResponse.builder().statusCode(404).build(); + + NovaAsyncClient clientWhenNoServersExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey, + responseWithKeystoneAccess, getFloatingIP, getFloatingIPResponse); + + assertNull(clientWhenNoServersExist.getFloatingIPExtensionForRegion("North").get().getFloatingIP("1").get()); + } + + public void testAllocateWhenResponseIs2xx() throws Exception { + HttpRequest allocateFloatingIP = HttpRequest + .builder() + .method("POST") + .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .headers( + ImmutableMultimap. builder().put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()) + .payload(payloadFromStringWithContentType("{}", "application/json")).build(); + + HttpResponse allocateFloatingIPResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/floatingip_details.json")).build(); + + NovaAsyncClient clientWhenFloatingIPsExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey, + responseWithKeystoneAccess, allocateFloatingIP, allocateFloatingIPResponse); + + assertEquals(clientWhenFloatingIPsExist.getFloatingIPExtensionForRegion("North").get().allocate().get() + .toString(), new ParseFloatingIPTest().expected().toString()); + + } + + public void testAllocateWhenResponseIs404() throws Exception { + HttpRequest allocateFloatingIP = HttpRequest + .builder() + .method("POST") + .endpoint(URI.create("https://compute.north.host/v1.1/3456/os-floating-ips")) + .headers( + ImmutableMultimap. builder().put("Accept", "application/json") + .put("X-Auth-Token", authToken).build()) + .payload(payloadFromStringWithContentType("{}", "application/json")).build(); + + HttpResponse allocateFloatingIPResponse = HttpResponse.builder().statusCode(404).build(); + + NovaAsyncClient clientWhenNoServersExist = requestsSendResponses(keystoneAuthWithAccessKeyAndSecretKey, + responseWithKeystoneAccess, allocateFloatingIP, allocateFloatingIPResponse); + + assertNull(clientWhenNoServersExist.getFloatingIPExtensionForRegion("North").get().allocate().get()); + } + +} \ No newline at end of file diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java index e373ed1295..1c345af2ca 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientExpectTest.java @@ -27,7 +27,7 @@ import java.net.URI; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseFloatingIPListTest; import org.jclouds.openstack.nova.v1_1.parse.ParseFloatingIPTest; import org.testng.annotations.Test; @@ -41,7 +41,7 @@ import com.google.common.collect.ImmutableSet; * @author Michael Arnold */ @Test(groups = "unit", testName = "FloatingIPClientExpectTest") -public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest { +public class FloatingIPClientExpectTest extends BaseNovaClientExpectTest { public void testListFloatingIPsWhenResponseIs2xx() throws Exception { HttpRequest listFloatingIPs = HttpRequest @@ -64,7 +64,7 @@ public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest { assertEquals(clientWhenFloatingIPsExist.getConfiguredRegions(), ImmutableSet.of("North")); - assertEquals(clientWhenFloatingIPsExist.getFloatingIPClientForRegion("North") + assertEquals(clientWhenFloatingIPsExist.getFloatingIPExtensionForRegion("North").get() .listFloatingIPs().toString(), new ParseFloatingIPListTest().expected() .toString()); } @@ -87,7 +87,7 @@ public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest { keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, listFloatingIPs, listFloatingIPsResponse); - assertTrue(clientWhenNoServersExist.getFloatingIPClientForRegion("North") + assertTrue(clientWhenNoServersExist.getFloatingIPExtensionForRegion("North").get() .listFloatingIPs().isEmpty()); } @@ -109,7 +109,7 @@ public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest { keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, getFloatingIP, getFloatingIPResponse); - assertEquals(clientWhenFloatingIPsExist.getFloatingIPClientForRegion("North") + assertEquals(clientWhenFloatingIPsExist.getFloatingIPExtensionForRegion("North").get() .getFloatingIP("1").toString(), new ParseFloatingIPTest().expected().toString()); } @@ -131,7 +131,7 @@ public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest { keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, getFloatingIP, getFloatingIPResponse); - assertNull(clientWhenNoServersExist.getFloatingIPClientForRegion("North") + assertNull(clientWhenNoServersExist.getFloatingIPExtensionForRegion("North").get() .getFloatingIP("1")); } @@ -154,7 +154,7 @@ public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest { keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, allocateFloatingIP, allocateFloatingIPResponse); - assertEquals(clientWhenFloatingIPsExist.getFloatingIPClientForRegion("North") + assertEquals(clientWhenFloatingIPsExist.getFloatingIPExtensionForRegion("North").get() .allocate().toString(), new ParseFloatingIPTest().expected().toString()); @@ -178,7 +178,7 @@ public class FloatingIPClientExpectTest extends BaseNovaRestClientExpectTest { keystoneAuthWithAccessKeyAndSecretKey, responseWithKeystoneAccess, allocateFloatingIP, allocateFloatingIPResponse); - assertNull(clientWhenNoServersExist.getFloatingIPClientForRegion("North") + assertNull(clientWhenNoServersExist.getFloatingIPExtensionForRegion("North").get() .allocate()); } diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientLiveTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientLiveTest.java index da46fdff87..b4508bf549 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientLiveTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/FloatingIPClientLiveTest.java @@ -25,6 +25,7 @@ import static org.testng.Assert.assertTrue; import java.util.Set; +import com.google.common.base.Optional; import com.google.common.collect.Iterables; import org.jclouds.openstack.nova.v1_1.domain.Address; import org.jclouds.openstack.nova.v1_1.domain.FloatingIP; @@ -52,8 +53,10 @@ public class FloatingIPClientLiveTest extends BaseNovaClientLiveTest { @Test public void testListFloatingIPs() throws Exception { for (String regionId : context.getApi().getConfiguredRegions()) { - - FloatingIPClient client = context.getApi().getFloatingIPClientForRegion(regionId); + Optional clientOption = context.getApi().getFloatingIPExtensionForRegion(regionId); + if (!clientOption.isPresent()) + continue; + FloatingIPClient client = clientOption.get(); Set response = client.listFloatingIPs(); assert null != response; assertTrue(response.size() >= 0); @@ -73,7 +76,10 @@ public class FloatingIPClientLiveTest extends BaseNovaClientLiveTest { @Test public void testAllocateAndDeallocateFloatingIPs() throws Exception { for (String regionId : context.getApi().getConfiguredRegions()) { - FloatingIPClient client = context.getApi().getFloatingIPClientForRegion(regionId); + Optional clientOption = context.getApi().getFloatingIPExtensionForRegion(regionId); + if (!clientOption.isPresent()) + continue; + FloatingIPClient client = clientOption.get(); FloatingIP floatingIP = client.allocate(); assertNotNull(floatingIP); @@ -101,7 +107,10 @@ public class FloatingIPClientLiveTest extends BaseNovaClientLiveTest { @Test public void testAddAndRemoveFloatingIp() throws Exception { for (String regionId : context.getApi().getConfiguredRegions()) { - FloatingIPClient client = context.getApi().getFloatingIPClientForRegion(regionId); + Optional clientOption = context.getApi().getFloatingIPExtensionForRegion(regionId); + if (!clientOption.isPresent()) + continue; + FloatingIPClient client = clientOption.get(); ServerClient serverClient = context.getApi().getServerClientForRegion(regionId); Server server = serverClient.createServer("test", imageIdForRegion(regionId), flavorRefForRegion(regionId)); blockUntilServerActive(server.getId(), serverClient); diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java index c9a7b70d43..f6f3fdab33 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/KeyPairClientExpectTest.java @@ -23,7 +23,7 @@ import com.google.common.collect.ImmutableSet; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseKeyPairListTest; import org.jclouds.openstack.nova.v1_1.parse.ParseKeyPairTest; import org.testng.annotations.Test; @@ -40,7 +40,7 @@ import static org.testng.Assert.assertTrue; * @author Michael Arnold */ @Test(groups = "unit", testName = "KeyPairClientExpectTest") -public class KeyPairClientExpectTest extends BaseNovaRestClientExpectTest { +public class KeyPairClientExpectTest extends BaseNovaClientExpectTest { public void testListKeyPairsWhenResponseIs2xx() throws Exception { HttpRequest listKeyPairs = HttpRequest diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java index 0496d1df25..cdb3ec0857 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/extensions/SecurityGroupClientExpectTest.java @@ -16,17 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableSet; +package org.jclouds.openstack.nova.v1_1.extensions; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseSecurityGroupListTest; import org.jclouds.openstack.nova.v1_1.parse.ParseSecurityGroupTest; import org.testng.annotations.Test; -import java.net.URI; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; @@ -38,7 +43,7 @@ import static org.testng.Assert.assertTrue; * @author Michael Arnold */ @Test(groups = "unit", testName = "SecurityGroupClientExpectTest") -public class SecurityGroupClientExpectTest extends BaseNovaRestClientExpectTest { +public class SecurityGroupClientExpectTest extends BaseNovaClientExpectTest { public void testListSecurityGroupsWhenResponseIs2xx() throws Exception { HttpRequest listSecurityGroups = HttpRequest .builder() diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java index 259c9732ba..8becc98cd2 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ExtensionClientExpectTest.java @@ -27,7 +27,7 @@ import java.net.URI; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseExtensionListTest; import org.jclouds.openstack.nova.v1_1.parse.ParseExtensionTest; import org.testng.annotations.Test; @@ -41,7 +41,7 @@ import com.google.common.collect.ImmutableSet; * @author Adrian Cole */ @Test(groups = "unit", testName = "ExtensionClientExpectTest") -public class ExtensionClientExpectTest extends BaseNovaRestClientExpectTest { +public class ExtensionClientExpectTest extends BaseNovaClientExpectTest { public void testListExtensionsWhenResponseIs2xx() throws Exception { HttpRequest listExtensions = HttpRequest diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java index 134082d018..9fe65ccb6c 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/FlavorClientExpectTest.java @@ -27,7 +27,7 @@ import java.net.URI; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseFlavorListTest; import org.jclouds.openstack.nova.v1_1.parse.ParseFlavorTest; import org.testng.annotations.Test; @@ -41,7 +41,7 @@ import com.google.common.collect.ImmutableSet; * @author Jeremy Daggett */ @Test(groups = "unit", testName = "FlavorClientExpectTest") -public class FlavorClientExpectTest extends BaseNovaRestClientExpectTest { +public class FlavorClientExpectTest extends BaseNovaClientExpectTest { public void testListFlavorsWhenResponseIs2xx() throws Exception { HttpRequest listFlavors = HttpRequest diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java index bad3704f80..ccdbeae5aa 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ImageClientExpectTest.java @@ -23,7 +23,7 @@ import com.google.common.collect.ImmutableSet; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseImageTest; import org.jclouds.openstack.nova.v1_1.parse.ParseImageListTest; import org.testng.annotations.Test; @@ -40,7 +40,7 @@ import static org.testng.Assert.assertTrue; * @author Michael Arnold */ @Test(groups = "unit", testName = "ImageAsyncClientTest") -public class ImageClientExpectTest extends BaseNovaRestClientExpectTest { +public class ImageClientExpectTest extends BaseNovaClientExpectTest { public void testListImagesWhenResponseIs2xx() throws Exception { HttpRequest listImages = HttpRequest .builder() diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java index 37eae40461..ddaf66b9ba 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/features/ServerClientExpectTest.java @@ -26,7 +26,7 @@ import java.net.URI; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.openstack.nova.v1_1.internal.BaseNovaRestClientExpectTest; +import org.jclouds.openstack.nova.v1_1.internal.BaseNovaClientExpectTest; import org.jclouds.openstack.nova.v1_1.parse.ParseServerListTest; import org.testng.annotations.Test; @@ -39,7 +39,7 @@ import com.google.common.collect.ImmutableSet; * @author Adrian Cole */ @Test(groups = "unit", testName = "ServerAsyncClientTest") -public class ServerClientExpectTest extends BaseNovaRestClientExpectTest { +public class ServerClientExpectTest extends BaseNovaClientExpectTest { public void testListServersWhenResponseIs2xx() throws Exception { HttpRequest listServers = HttpRequest.builder().method("GET").endpoint( diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaAsyncClientTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaAsyncClientExpectTest.java similarity index 56% rename from labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaAsyncClientTest.java rename to labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaAsyncClientExpectTest.java index 70a99fc69e..00720cdce1 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaAsyncClientTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaAsyncClientExpectTest.java @@ -18,33 +18,32 @@ */ package org.jclouds.openstack.nova.v1_1.internal; -import static org.testng.Assert.assertEquals; +import static org.jclouds.rest.RestContextFactory.createContext; import java.util.Properties; import org.jclouds.http.HttpRequest; -import org.jclouds.openstack.filters.AuthenticateRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.openstack.nova.v1_1.NovaAsyncClient; import org.jclouds.openstack.nova.v1_1.NovaClient; -import org.jclouds.rest.RestClientTest; import org.jclouds.rest.RestContextFactory; import org.jclouds.rest.RestContextSpec; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; + /** + * Base class for writing KeyStone Rest Client Expect tests + * * @author Adrian Cole */ -public abstract class BaseNovaAsyncClientTest extends RestClientTest { - - @Override - protected void checkFilters(HttpRequest request) { - assertEquals(request.getFilters().size(), 1); - assertEquals(request.getFilters().get(0).getClass(), AuthenticateRequest.class); +public class BaseNovaAsyncClientExpectTest extends BaseNovaExpectTest { + public NovaAsyncClient createClient(Function fn, Module module, Properties props) { + RestContextSpec contextSpec = new RestContextFactory(setupRestProperties()) + .createContextSpec(provider, identity, credential, new Properties()); + return createContext(contextSpec, + ImmutableSet. of(new ExpectModule(fn), new NullLoggingModule(), module), props).getAsyncApi(); } - - @Override - public RestContextSpec createContextSpec() { - Properties props = new Properties(); - return new RestContextFactory().createContextSpec("openstack-nova", "accountId", "accessKey", props); - } - } diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientExpectTest.java new file mode 100644 index 0000000000..08c6c9052a --- /dev/null +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaClientExpectTest.java @@ -0,0 +1,30 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.openstack.nova.v1_1.internal; + +import org.jclouds.openstack.nova.v1_1.NovaClient; + +/** + * Base class for writing KeyStone Rest Client Expect tests + * + * @author Adrian Cole + */ +public class BaseNovaClientExpectTest extends BaseNovaExpectTest { + +} diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceExpectTest.java index 8979c839b9..4c032e0196 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaComputeServiceExpectTest.java @@ -25,43 +25,22 @@ import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.logging.config.NullLoggingModule; -import org.jclouds.openstack.keystone.v2_0.internal.KeystoneFixture; -import org.jclouds.rest.BaseRestClientExpectTest; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.inject.Module; /** - * Base class for writing KeyStone Expect tests with the ComputeService abstraction - * + * Base class for writing KeyStone Expect tests with the ComputeService + * abstraction + * * @author Matt Stephenson */ -public class BaseNovaComputeServiceExpectTest extends BaseRestClientExpectTest -{ - protected HttpRequest keystoneAuthWithUsernameAndPassword; - protected HttpRequest keystoneAuthWithAccessKeyAndSecretKey; - protected String authToken; - protected HttpResponse responseWithKeystoneAccess; - - public BaseNovaComputeServiceExpectTest() - { - provider = "openstack-nova"; - keystoneAuthWithUsernameAndPassword = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPassword(identity, - credential); - keystoneAuthWithAccessKeyAndSecretKey = KeystoneFixture.INSTANCE.initialAuthWithAccessKeyAndSecretKey(identity, - credential); - authToken = KeystoneFixture.INSTANCE.getAuthToken(); - responseWithKeystoneAccess = KeystoneFixture.INSTANCE.responseWithAccess(); - // now, createContext arg will need tenant prefix - identity = KeystoneFixture.INSTANCE.getTenantName() + ":" + identity; - } - +public class BaseNovaComputeServiceExpectTest extends BaseNovaExpectTest { @Override - public ComputeService createClient(Function fn, Module module, Properties props) - { + public ComputeService createClient(Function fn, Module module, Properties props) { return new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, identity, credential, - ImmutableSet.of(new ExpectModule(fn), new NullLoggingModule(), module), props).getComputeService(); + ImmutableSet. of(new ExpectModule(fn), new NullLoggingModule(), module), props).getComputeService(); } } diff --git a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaRestClientExpectTest.java b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java similarity index 88% rename from labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaRestClientExpectTest.java rename to labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java index 7afeb2985d..c3ee24c12d 100644 --- a/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaRestClientExpectTest.java +++ b/labs/openstack-nova/src/test/java/org/jclouds/openstack/nova/v1_1/internal/BaseNovaExpectTest.java @@ -21,21 +21,20 @@ package org.jclouds.openstack.nova.v1_1.internal; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.openstack.keystone.v2_0.internal.KeystoneFixture; -import org.jclouds.openstack.nova.v1_1.NovaClient; import org.jclouds.rest.BaseRestClientExpectTest; /** - * Base class for writing KeyStone Rest Client Expect tests + * Base class for writing Nova Expect tests * * @author Adrian Cole */ -public class BaseNovaRestClientExpectTest extends BaseRestClientExpectTest { +public class BaseNovaExpectTest extends BaseRestClientExpectTest { protected HttpRequest keystoneAuthWithUsernameAndPassword; protected HttpRequest keystoneAuthWithAccessKeyAndSecretKey; protected String authToken; protected HttpResponse responseWithKeystoneAccess; - public BaseNovaRestClientExpectTest() { + public BaseNovaExpectTest() { provider = "openstack-nova"; keystoneAuthWithUsernameAndPassword = KeystoneFixture.INSTANCE.initialAuthWithUsernameAndPassword(identity, credential); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientExpectTest.java index 7cd70f7151..35e4d84c84 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/AdminCatalogClientExpectTest.java @@ -74,7 +74,7 @@ public class AdminCatalogClientExpectTest extends BaseVCloudDirectorRestClientEx assertEquals(client.getAdminCatalogClient().createCatalog(catalogRef.getHref(), source), expected); } - @Test + @Test(enabled = false)//TODO public void testGetCatalog() { VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() @@ -90,7 +90,7 @@ public class AdminCatalogClientExpectTest extends BaseVCloudDirectorRestClientEx assertEquals(client.getAdminCatalogClient().getCatalog(catalogRef.getHref()), expected); } - @Test + @Test(enabled = false)//TODO public void testModifyCatalog() { VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() @@ -107,7 +107,7 @@ public class AdminCatalogClientExpectTest extends BaseVCloudDirectorRestClientEx assertEquals(client.getAdminCatalogClient().updateCatalog(catalogRef.getHref(), expected), expected); } - @Test + @Test(enabled = false)//TODO public void testGetOwner() { VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java index 301dbd7050..07d253ce55 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/MediaClientExpectTest.java @@ -51,7 +51,7 @@ import com.google.common.collect.ImmutableSet; @Test(groups = { "unit", "media" }, singleThreaded = true, testName = "MediaClientExpectTest") public class MediaClientExpectTest extends BaseVCloudDirectorRestClientExpectTest { - @Test + @Test(enabled = false)//TODO public void testCreateMedia() { URI uploadLink = URI.create(endpoint + "/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f/media"); @@ -77,7 +77,7 @@ public class MediaClientExpectTest extends BaseVCloudDirectorRestClientExpectTes assertEquals(client.getMediaClient().createMedia(uploadLink, source), expected); } - @Test + @Test(enabled = false)//TODO public void testCloneMedia() { URI cloneUri = URI.create(endpoint + "/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f/action/cloneMedia"); @@ -107,7 +107,7 @@ public class MediaClientExpectTest extends BaseVCloudDirectorRestClientExpectTes assertEquals(client.getMediaClient().cloneMedia(cloneUri, params), expected); } - @Test + @Test(enabled = false)//TODO public void testGetMedia() { URI mediaUri = URI.create(endpoint + "/media/794eb334-754e-4917-b5a0-5df85cbd61d1"); @@ -211,7 +211,7 @@ public class MediaClientExpectTest extends BaseVCloudDirectorRestClientExpectTes } } - @Test + @Test(enabled = false)//TODO public void testUpdateMedia() { URI mediaUri = URI.create(endpoint + "/media/794eb334-754e-4917-b5a0-5df85cbd61d1"); @@ -345,7 +345,7 @@ public class MediaClientExpectTest extends BaseVCloudDirectorRestClientExpectTes assertEquals(client.getMediaClient().getMetadataClient().deleteMetadataEntry(mediaUri, "key"), expectedTask); } - @Test + @Test(enabled = false)//TODO public void testGetOwner() { URI mediaUri = URI.create(endpoint + "/media/794eb334-754e-4917-b5a0-5df85cbd61d1"); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/QueryClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/QueryClientExpectTest.java index b87ff1b077..22d652d95c 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/QueryClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/QueryClientExpectTest.java @@ -120,7 +120,7 @@ public class QueryClientExpectTest extends BaseVCloudDirectorRestClientExpectTes assertEquals(client.getQueryClient().catalogsQueryAll(), expected); } - @Test + @Test(enabled = false)//TODO public void testQueryAllCatalogReferences() { HttpRequest queryRequest = HttpRequest.builder() .method("GET") diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientExpectTest.java index ba04271a53..641012a02c 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppClientExpectTest.java @@ -74,7 +74,7 @@ public class VAppClientExpectTest extends BaseVCloudDirectorRestClientExpectTest public void before() { } - @Test + @Test(enabled = false)//TODO public void testGetVapp() { VCloudDirectorClient client = orderedRequestsSendResponses(loginRequest, sessionResponse, new VcloudHttpRequestPrimer() diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java index 0de912bf4d..681dcc2b72 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VAppTemplateClientExpectTest.java @@ -80,6 +80,7 @@ public class VAppTemplateClientExpectTest extends BaseVCloudDirectorRestClientEx TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); } + @Test(enabled = false)//TODO public void testVAppTemplate() { final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9"; URI uri = URI.create(endpoint + templateId); @@ -117,7 +118,8 @@ public class VAppTemplateClientExpectTest extends BaseVCloudDirectorRestClientEx client.getVAppTemplate(uri); } - @Test(expectedExceptions = VCloudDirectorException.class) + //TODO + @Test(enabled = false, expectedExceptions = VCloudDirectorException.class) public void testErrorEditVAppTemplate() { final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9"; URI uri = URI.create(endpoint + templateId); @@ -252,6 +254,7 @@ public class VAppTemplateClientExpectTest extends BaseVCloudDirectorRestClientEx client.relocateVappTemplate(uri, params); } + @Test(enabled = false)//TODO public void testCustomizationSection() { final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9"; URI uri = URI.create(endpoint + templateId); @@ -272,7 +275,8 @@ public class VAppTemplateClientExpectTest extends BaseVCloudDirectorRestClientEx assertNotNull(task); } - @Test(expectedExceptions = VCloudDirectorException.class) + //TODO + @Test(enabled = false, expectedExceptions = VCloudDirectorException.class) public void testErrorGetCustomizationSection() { final String templateId = "/vAppTemplate/vappTemplate-ef4415e6-d413-4cbb-9262-f9bbec5f2ea9"; URI uri = URI.create(endpoint + templateId); diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientExpectTest.java index 7cd22b235e..16d9b285b1 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/VdcClientExpectTest.java @@ -293,7 +293,7 @@ public class VdcClientExpectTest extends BaseVCloudDirectorRestClientExpectTest assertEquals(client.getVdcClient().uploadVAppTemplate(vdcURI, params), expected); } - @Test + @Test(enabled = false)//TODO public void testCreateMedia() { URI vdcUri = URI.create(endpoint + "/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f"); @@ -319,7 +319,7 @@ public class VdcClientExpectTest extends BaseVCloudDirectorRestClientExpectTest assertEquals(client.getVdcClient().createMedia(vdcUri, source), expected); } - @Test + @Test(enabled = false)//TODO public void testCloneMedia() { URI vdcUri = URI.create(endpoint + "/vdc/e9cd3387-ac57-4d27-a481-9bee75e0690f"); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java index 165832a332..4f6c728155 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxPropertiesBuilder.java @@ -19,18 +19,16 @@ package org.jclouds.virtualbox; -import static org.jclouds.Constants.*; +import static org.jclouds.Constants.PROPERTY_API_VERSION; import static org.jclouds.Constants.PROPERTY_BUILD_VERSION; -import static org.jclouds.Constants.PROPERTY_CREDENTIAL; import static org.jclouds.Constants.PROPERTY_ENDPOINT; -import static org.jclouds.Constants.PROPERTY_IDENTITY; import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_IMAGE_AUTHENTICATE_SUDO; import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_IMAGE_LOGIN_USER; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_DIR; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGES_DESCRIPTOR; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL; -import static org.jclouds.virtualbox.config.VirtualBoxConstants.*; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR; import java.io.File; import java.util.Properties; @@ -60,11 +58,9 @@ public class VirtualBoxPropertiesBuilder extends PropertiesBuilder { properties.put(PROPERTY_API_VERSION, "4.1.4"); properties.put(PROPERTY_BUILD_VERSION, "4.1.8r75467"); - properties.put(PROPERTY_IDENTITY, "administrator"); - properties.put(PROPERTY_CREDENTIAL, "12345"); - properties.put(PROPERTY_IMAGE_LOGIN_USER, "toor:password"); properties.put(PROPERTY_IMAGE_AUTHENTICATE_SUDO, "true"); + properties.put(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE, " " + "/install/vmlinuz noapic preseed/url=PRECONFIGURATION_URL " @@ -82,7 +78,7 @@ public class VirtualBoxPropertiesBuilder extends PropertiesBuilder { properties.put(VIRTUALBOX_IMAGES_DESCRIPTOR, yamlDescriptor); - properties.put(VIRTUALBOX_PRECONFIGURATION_URL, "http://10.0.2.2:8080/src/test/resources/preseed.cfg"); + properties.put(VIRTUALBOX_PRECONFIGURATION_URL, "http://10.0.2.2:23232/preseed.cfg"); return properties; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java index 1f15abbb0b..5ae08a2338 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java @@ -96,7 +96,7 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter>() { - }).to((Class) MastersCache.class); + }).to((Class) MastersLoadingCache.class); // the master creating function bind(new TypeLiteral>() { }).to((Class) CreateAndInstallVm.class); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java index abca1e02b0..4584b6221b 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java @@ -24,7 +24,6 @@ import java.util.List; import javax.annotation.Resource; import javax.inject.Named; -import javax.inject.Singleton; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; @@ -100,6 +99,7 @@ public class CloneAndRegisterMachineFromIMachineIfNotAlreadyExists implements Fu .getVBox() .createMachine(settingsFile, vmSpec.getVmName(), vmSpec.getOsTypeId(), vmSpec.getVmId(), vmSpec.isForceOverwrite()); + List options = new ArrayList(); if (isLinkedClone) options.add(CloneOptions.Link); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java index 53d7810ea3..336225926f 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java @@ -44,12 +44,10 @@ import org.jclouds.virtualbox.util.MachineController; import org.jclouds.virtualbox.util.MachineUtils; import org.virtualbox_4_1.IMachine; import org.virtualbox_4_1.LockType; -import org.virtualbox_4_1.VirtualBoxManager; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Splitter; -import com.google.common.base.Supplier; import com.google.common.cache.LoadingCache; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -62,7 +60,6 @@ public class CreateAndInstallVm implements Function { @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - private final Supplier manager; private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists; private final GuestAdditionsInstaller guestAdditionsInstaller; private final Predicate sshResponds; @@ -75,7 +72,6 @@ public class CreateAndInstallVm implements Function { @Inject public CreateAndInstallVm( - Supplier manager, CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, GuestAdditionsInstaller guestAdditionsInstaller, IMachineToNodeMetadata imachineToNodeMetadata, @@ -83,7 +79,6 @@ public class CreateAndInstallVm implements Function { Function sshClientForIMachine, MachineUtils machineUtils, @Preconfiguration LoadingCache preConfiguration, MachineController machineController) { - this.manager = manager; this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; this.sshResponds = sshResponds; this.sshClientForIMachine = sshClientForIMachine; @@ -113,6 +108,7 @@ public class CreateAndInstallVm implements Function { configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence); + SshClient client = sshClientForIMachine.apply(vm); logger.debug(">> awaiting installation to finish node(%s)", vmName); @@ -121,8 +117,8 @@ public class CreateAndInstallVm implements Function { "timed out waiting for guest %s to be accessible via ssh", vmName); - //logger.debug(">> awaiting installation of guest additions on vm: %s", vmName); - //checkState(guestAdditionsInstaller.apply(vm)); + logger.debug(">> awaiting installation of guest additions on vm: %s", vmName); + checkState(guestAdditionsInstaller.apply(vm)); logger.debug(">> awaiting post-installation actions on vm: %s", vmName); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java index 5b9f4086d8..c65c41872a 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java @@ -23,17 +23,12 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule.machineToNodeState; -import java.util.HashSet; -import java.util.Set; - import javax.annotation.Resource; import javax.inject.Named; -import org.jclouds.compute.domain.HardwareBuilder; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; -import org.jclouds.compute.domain.Processor; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationScope; diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersCache.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java similarity index 95% rename from labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersCache.java rename to labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java index 57dbd6a4cb..85b81bf3ac 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersCache.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/MastersLoadingCache.java @@ -64,18 +64,21 @@ import com.google.common.base.Function; import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.cache.AbstractLoadingCache; +import com.google.common.cache.LoadingCache; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; /** - * Does most of the work wrt to creating the master image. + * A {@link LoadingCache} for masters. If the requested master has been previously created this + * returns it, if not it coordinates its creation including downloading isos and creating + * cache/config directories. * * @author dralves * */ @Singleton -public class MastersCache extends AbstractLoadingCache { - +public class MastersLoadingCache extends AbstractLoadingCache { + // TODO parameterize public static final int MASTER_PORT = 2222; @@ -94,7 +97,7 @@ public class MastersCache extends AbstractLoadingCache { private String version; @Inject - public MastersCache(@Named(Constants.PROPERTY_BUILD_VERSION) String version, + public MastersLoadingCache(@Named(Constants.PROPERTY_BUILD_VERSION) String version, @Named(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE) String installationKeySequence, @Named(VIRTUALBOX_WORKINGDIR) String workingDir, Function masterLoader, Supplier> yamlMapper, Supplier manager, @@ -114,7 +117,7 @@ public class MastersCache extends AbstractLoadingCache { this.version = Iterables.get(Splitter.on('r').split(version), 0); this.isoDownloader = isoDownloader; } - + @PostConstruct public void createCacheDirStructure() { if (!new File(workingDir).exists()) { @@ -201,7 +204,7 @@ public class MastersCache extends AbstractLoadingCache { } return null; } - + private String getFilePathOrDownload(String httpUrl) throws ExecutionException { String fileName = httpUrl.substring(httpUrl.lastIndexOf('/') + 1, httpUrl.length()); File localFile = new File(isosDir, fileName); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java index 92c483fd1c..9372e9bbca 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java @@ -43,10 +43,12 @@ import org.jclouds.virtualbox.domain.NetworkInterfaceCard; import org.jclouds.virtualbox.domain.NetworkSpec; import org.jclouds.virtualbox.domain.NodeSpec; import org.jclouds.virtualbox.domain.VmSpec; +import org.jclouds.virtualbox.statements.DeleteGShadowLock; import org.jclouds.virtualbox.statements.SetIpAddress; import org.jclouds.virtualbox.util.MachineUtils; import org.virtualbox_4_1.CleanupMode; import org.virtualbox_4_1.IMachine; +import org.virtualbox_4_1.IProgress; import org.virtualbox_4_1.ISession; import org.virtualbox_4_1.NetworkAttachmentType; import org.virtualbox_4_1.VirtualBoxManager; @@ -54,149 +56,140 @@ import org.virtualbox_4_1.VirtualBoxManager; import com.google.common.base.Function; import com.google.common.base.Supplier; +/** + * Creates nodes, by cloning a master vm and based on the provided {@link NodeSpec}. Must be + * synchronized mainly because of snapshot creation (must be synchronized on a per-master-basis). + * + * @author dralves + * + */ @Singleton -public class NodeCreator implements - Function> { +public class NodeCreator implements Function> { - // TODO parameterize - public static final int NODE_PORT_INIT = 3000; + // TODO parameterize + public static final int NODE_PORT_INIT = 3000; - // TODO parameterize - public static final String VMS_NETWORK = "33.33.33."; + // TODO parameterize + public static final String VMS_NETWORK = "33.33.33."; - // TODO parameterize - public static final String HOST_ONLY_IFACE_NAME = "vboxnet0"; + // TODO parameterize + public static final String HOST_ONLY_IFACE_NAME = "vboxnet0"; - // TODO parameterize - public static final boolean USE_LINKED = true; + // TODO parameterize + public static final boolean USE_LINKED = true; - private final Supplier manager; - private final Function cloner; - private final AtomicInteger nodePorts; - private final AtomicInteger nodeIps; - private MachineUtils machineUtils; - private Function imachineToNodeMetadata; + // TODO parameterize + public static final ExecutionType EXECUTION_TYPE = ExecutionType.HEADLESS; - private final RunScriptOnNode.Factory scriptRunnerFactory; - private final Supplier hostSupplier; + private final Supplier manager; + private final Function cloner; + private final AtomicInteger nodePorts; + private final AtomicInteger nodeIps; + private MachineUtils machineUtils; + private Function imachineToNodeMetadata; - @Inject - public NodeCreator(Supplier manager, - Function cloner, MachineUtils machineUtils, - Function imachineToNodeMetadata, - RunScriptOnNode.Factory scriptRunnerFactory, - Supplier hostSupplier) { - this.manager = manager; - this.cloner = cloner; - this.nodePorts = new AtomicInteger(NODE_PORT_INIT); - this.nodeIps = new AtomicInteger(1); - this.machineUtils = machineUtils; - this.imachineToNodeMetadata = imachineToNodeMetadata; - this.scriptRunnerFactory = scriptRunnerFactory; - this.hostSupplier = hostSupplier; + private final RunScriptOnNode.Factory scriptRunnerFactory; + private final Supplier hostSupplier; - } + @Inject + public NodeCreator(Supplier manager, Function cloner, + MachineUtils machineUtils, Function imachineToNodeMetadata, + RunScriptOnNode.Factory scriptRunnerFactory, Supplier hostSupplier) { + this.manager = manager; + this.cloner = cloner; + this.nodePorts = new AtomicInteger(NODE_PORT_INIT + 1); + this.nodeIps = new AtomicInteger(1); + this.machineUtils = machineUtils; + this.imachineToNodeMetadata = imachineToNodeMetadata; + this.scriptRunnerFactory = scriptRunnerFactory; + this.hostSupplier = hostSupplier; - @Override - public NodeAndInitialCredentials apply(NodeSpec nodeSpec) { + } - checkNotNull(nodeSpec, "NodeSpec"); + @Override + public synchronized NodeAndInitialCredentials apply(NodeSpec nodeSpec) { - Master master = nodeSpec.getMaster(); - checkNotNull(master, "Master"); + checkNotNull(nodeSpec, "NodeSpec"); - if (master.getMachine().getCurrentSnapshot() != null) { - ISession session; - try { - session = manager.get().openMachineSession(master.getMachine()); - } catch (Exception e) { - throw new RuntimeException( - "error opening vbox machine session: " + e.getMessage(), - e); - } - session.getConsole().deleteSnapshot( - master.getMachine().getCurrentSnapshot().getId()); - session.unlockMachine(); - } - String masterNameWithoutPrefix = master.getSpec().getVmSpec() - .getVmName().replace(VIRTUALBOX_IMAGE_PREFIX, ""); + Master master = nodeSpec.getMaster(); + checkNotNull(master, "Master"); - String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix - + "-" + nodeSpec.getTag() + "-" + nodeSpec.getName(); + if (master.getMachine().getCurrentSnapshot() != null) { + ISession session; + try { + session = manager.get().openMachineSession(master.getMachine()); + } catch (Exception e) { + throw new RuntimeException("error opening vbox machine session: " + e.getMessage(), e); + } + IProgress progress = session.getConsole().deleteSnapshot(master.getMachine().getCurrentSnapshot().getId()); + progress.waitForCompletion(-1); + session.unlockMachine(); + } + String masterNameWithoutPrefix = master.getSpec().getVmSpec().getVmName().replace(VIRTUALBOX_IMAGE_PREFIX, ""); - VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName) - .memoryMB(512).cleanUpMode(CleanupMode.Full) - .forceOverwrite(true).build(); + String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix + "-" + nodeSpec.getTag() + "-" + + nodeSpec.getName(); - - // CASE NAT + HOST-ONLY - NetworkAdapter natAdapter = NetworkAdapter - .builder() - .networkAttachmentType(NetworkAttachmentType.NAT) - .tcpRedirectRule("127.0.0.1", this.nodePorts.getAndIncrement(), - "", 22).build(); - NetworkInterfaceCard natIfaceCard = NetworkInterfaceCard.builder() - .addNetworkAdapter(natAdapter).slot(0L).build(); + VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(512).cleanUpMode(CleanupMode.Full) + .forceOverwrite(true).build(); - NetworkAdapter hostOnlyAdapter = NetworkAdapter.builder() - .networkAttachmentType(NetworkAttachmentType.HostOnly) - .staticIp(VMS_NETWORK + this.nodeIps.getAndIncrement()).build(); + // CASE NAT + HOST-ONLY + NetworkAdapter natAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT) + .tcpRedirectRule("127.0.0.1", this.nodePorts.getAndIncrement(), "", 22).build(); + NetworkInterfaceCard natIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(natAdapter).slot(0L).build(); - NetworkInterfaceCard hostOnlyIfaceCard = NetworkInterfaceCard.builder() - .addNetworkAdapter(hostOnlyAdapter) - .addHostInterfaceName(HOST_ONLY_IFACE_NAME).slot(1L).build(); - - NetworkSpec networkSpec = createNetworkSpecForHostOnlyNATNICs(natIfaceCard, hostOnlyIfaceCard); - //// - - // CASE BRIDGED - //NetworkSpec networkSpec = createNetworkSpecForBridgedNIC(); + NetworkAdapter hostOnlyAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.HostOnly) + .staticIp(VMS_NETWORK + this.nodeIps.getAndIncrement()).build(); - CloneSpec cloneSpec = CloneSpec.builder().linked(USE_LINKED) - .master(master.getMachine()).network(networkSpec) - .vm(cloneVmSpec).build(); + NetworkInterfaceCard hostOnlyIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(hostOnlyAdapter) + .addHostInterfaceName(HOST_ONLY_IFACE_NAME).slot(1L).build(); - IMachine cloned = cloner.apply(cloneSpec); + NetworkSpec networkSpec = createNetworkSpecForHostOnlyNATNICs(natIfaceCard, hostOnlyIfaceCard); + // // - new LaunchMachineIfNotAlreadyRunning(manager.get(), ExecutionType.GUI, - "").apply(cloned); + // CASE BRIDGED + // NetworkSpec networkSpec = createNetworkSpecForBridgedNIC(); - - // CASE NAT + HOST-ONLY - machineUtils.runScriptOnNode(imachineToNodeMetadata.apply(cloned), - new SetIpAddress(hostOnlyIfaceCard), RunScriptOptions.NONE); - //// - + CloneSpec cloneSpec = CloneSpec.builder().linked(USE_LINKED).master(master.getMachine()).network(networkSpec) + .vm(cloneVmSpec).build(); - // TODO get credentials from somewhere else (they are also HC in - // IMachineToSshClient) - NodeAndInitialCredentials nodeAndInitialCredentials = new NodeAndInitialCredentials( - cloned, cloneName, LoginCredentials.builder().user("toor") - .password("password").authenticateSudo(true).build()); + IMachine cloned = cloner.apply(cloneSpec); - return nodeAndInitialCredentials; - } + new LaunchMachineIfNotAlreadyRunning(manager.get(), EXECUTION_TYPE, "").apply(cloned); - private NetworkSpec createNetworkSpecForHostOnlyNATNICs(NetworkInterfaceCard natIfaceCard, NetworkInterfaceCard hostOnlyIfaceCard) { - return NetworkSpec.builder().addNIC(natIfaceCard) - .addNIC(hostOnlyIfaceCard).build(); - } + // see DeleteGShadowLock for a detailed explanation + machineUtils + .runScriptOnNode(imachineToNodeMetadata.apply(cloned), new DeleteGShadowLock(), RunScriptOptions.NONE); - private NetworkSpec createNetworkSpecForBridgedNIC() { - List activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces( - scriptRunnerFactory).apply(hostSupplier.get()); - BridgedIf bridgedActiveInterface = checkNotNull( - activeBridgedInterfaces.get(0), "activeBridgedIf"); + // CASE NAT + HOST-ONLY + machineUtils.runScriptOnNode(imachineToNodeMetadata.apply(cloned), new SetIpAddress(hostOnlyIfaceCard), + RunScriptOptions.NONE); + // // - NetworkAdapter bridgedAdapter = NetworkAdapter.builder() - .networkAttachmentType(NetworkAttachmentType.Bridged).build(); - NetworkInterfaceCard bridgedNIC = NetworkInterfaceCard.builder() - .addNetworkAdapter(bridgedAdapter) - .addHostInterfaceName(bridgedActiveInterface.getName()) - .slot(0L).build(); + // TODO get credentials from somewhere else (they are also HC in + // IMachineToSshClient) + NodeAndInitialCredentials nodeAndInitialCredentials = new NodeAndInitialCredentials(cloned, + cloneName, LoginCredentials.builder().user("toor").password("password").authenticateSudo(true).build()); - NetworkSpec networkSpec = NetworkSpec.builder().addNIC(bridgedNIC) - .build(); - return networkSpec; - } + return nodeAndInitialCredentials; + } + + private NetworkSpec createNetworkSpecForHostOnlyNATNICs(NetworkInterfaceCard natIfaceCard, + NetworkInterfaceCard hostOnlyIfaceCard) { + return NetworkSpec.builder().addNIC(natIfaceCard).addNIC(hostOnlyIfaceCard).build(); + } + + private NetworkSpec createNetworkSpecForBridgedNIC() { + List activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces(scriptRunnerFactory) + .apply(hostSupplier.get()); + BridgedIf bridgedActiveInterface = checkNotNull(activeBridgedInterfaces.get(0), "activeBridgedIf"); + + NetworkAdapter bridgedAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.Bridged) + .build(); + NetworkInterfaceCard bridgedNIC = NetworkInterfaceCard.builder().addNetworkAdapter(bridgedAdapter) + .addHostInterfaceName(bridgedActiveInterface.getName()).slot(0L).build(); + + NetworkSpec networkSpec = NetworkSpec.builder().addNIC(bridgedNIC).build(); + return networkSpec; + } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java index 03311963ab..e496cb5b94 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java @@ -40,16 +40,14 @@ import com.google.common.base.Throwables; */ public class TakeSnapshotIfNotAlreadyAttached implements Function { - @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - + private Supplier manager; private String snapshotName; private String snapshotDesc; - public TakeSnapshotIfNotAlreadyAttached(Supplier manager, String snapshotName, String snapshotDesc) { this.manager = manager; this.snapshotName = snapshotName; @@ -60,18 +58,34 @@ public class TakeSnapshotIfNotAlreadyAttached implements Function { - @Resource + @javax.annotation.Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; private Server jetty; @@ -60,7 +74,7 @@ public class StartJettyIfNotAlreadyRunning extends CacheLoader { } @Override - public URI load(IsoSpec isoSpec) throws Exception { + public URI load(IsoSpec isoSpec) throws Exception { try { start(); } catch (Exception e) { @@ -74,24 +88,36 @@ public class StartJettyIfNotAlreadyRunning extends CacheLoader { if (jetty.getState().equals(Server.STARTED)) { logger.debug("not starting jetty, as existing host is serving %s", preconfigurationUrl); } else { - logger.debug(">> starting jetty to serve %s", preconfigurationUrl); - ResourceHandler resourceHandler = new ResourceHandler(); - resourceHandler.setDirectoriesListed(true); - resourceHandler.setWelcomeFiles(new String[]{"index.html"}); - - resourceHandler.setResourceBase(""); - HandlerList handlers = new HandlerList(); - handlers.setHandlers(new Handler[]{resourceHandler, new DefaultHandler()}); - jetty.setHandler(handlers); - try { + + // find the the parent dir inside the jar to serve the file from + + final String preseedFile = IOUtils + .toString(Resource.newSystemResource("preseed.cfg").getURL().openStream()); + + checkState(preseedFile != null); + + // since we're only serving the preseed.cfg file respond to all requests with it + jetty.setHandler(new AbstractHandler() { + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + response.setContentType("text/plain;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + baseRequest.setHandled(true); + response.getWriter().println(preseedFile); + } + + }); + jetty.start(); + } catch (Exception e) { logger.error(e, "Server jetty could not be started for %s", preconfigurationUrl); + throw Throwables.propagate(e); } - logger.debug("<< serving %s", resourceHandler.getBaseResource()); } - } @PreDestroy() diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java index 3afb07c8a2..9cf475f378 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/StartVBoxIfNotAlreadyRunning.java @@ -25,6 +25,7 @@ import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot; import java.net.URI; +import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.inject.Inject; @@ -58,8 +59,8 @@ public class StartVBoxIfNotAlreadyRunning implements Supplier private final RetryIfSocketNotYetOpen socketTester; private final Supplier host; private final Supplier providerSupplier; - private final String identity; - private final String credential; +// private final String identity; +// private final String credential; private final Function, VirtualBoxManager> managerForNode; private transient VirtualBoxManager manager; @@ -67,14 +68,15 @@ public class StartVBoxIfNotAlreadyRunning implements Supplier @Inject public StartVBoxIfNotAlreadyRunning(Function, VirtualBoxManager> managerForNode, Factory runScriptOnNodeFactory, RetryIfSocketNotYetOpen socketTester, Supplier host, - @Provider Supplier providerSupplier, @Identity String identity, @Credential String credential) { + @Provider Supplier providerSupplier, @Nullable @Identity String identity, + @Nullable @Credential String credential) { this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory"); this.socketTester = checkNotNull(socketTester, "socketTester"); this.socketTester.seconds(3L); this.host = checkNotNull(host, "host"); this.providerSupplier = checkNotNull(providerSupplier, "endpoint to virtualbox websrvd is needed"); - this.identity = checkNotNull(identity, "identity"); - this.credential = checkNotNull(credential, "credential"); +// this.identity = checkNotNull(identity, "identity"); +// this.credential = checkNotNull(credential, "credential"); this.managerForNode = checkNotNull(managerForNode, "managerForNode"); } @@ -99,7 +101,7 @@ public class StartVBoxIfNotAlreadyRunning implements Supplier } } manager = managerForNode.apply(host); - manager.connect(provider.toASCIIString(), identity, credential); + manager.connect(provider.toASCIIString(), "", ""); if (logger.isDebugEnabled()) if (manager.getSessionObject().getState() != SessionState.Unlocked) logger.warn("manager is not in unlocked state " + manager.getSessionObject().getState()); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/DeleteGShadowLock.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/DeleteGShadowLock.java new file mode 100644 index 0000000000..d2b6e26acc --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/DeleteGShadowLock.java @@ -0,0 +1,48 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.virtualbox.statements; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.collect.ImmutableList; + +/** + * Deletes /etc/gshadow.lock. see https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/732864. + * + * @author dralves + * + */ +public class DeleteGShadowLock implements Statement { + + @Override + public Iterable functionDependencies(OsFamily family) { + return ImmutableList.of(); + } + + @Override + public String render(OsFamily family) { + if (checkNotNull(family, "family") == OsFamily.WINDOWS) + throw new UnsupportedOperationException("windows not yet implemented"); + return "rm -f /etc/passwd.lock /etc/group.lock /etc/gshadow.lock"; + } + +} diff --git a/labs/virtualbox/src/test/resources/preseed.cfg b/labs/virtualbox/src/main/resources/preseed.cfg similarity index 100% rename from labs/virtualbox/src/test/resources/preseed.cfg rename to labs/virtualbox/src/main/resources/preseed.cfg diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/StartJettyIfNotAlreadyRunningLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/StartJettyIfNotAlreadyRunningLiveTest.java new file mode 100644 index 0000000000..5a18cbe3eb --- /dev/null +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/StartJettyIfNotAlreadyRunningLiveTest.java @@ -0,0 +1,62 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.virtualbox; + +import static junit.framework.Assert.assertEquals; + +import java.net.URI; +import java.net.URL; +import java.util.Properties; + +import org.apache.commons.io.IOUtils; +import org.eclipse.jetty.server.Server; +import org.jclouds.virtualbox.config.VirtualBoxConstants; +import org.jclouds.virtualbox.functions.admin.StartJettyIfNotAlreadyRunning; +import org.testng.annotations.Test; + +/** + * Tests that jetty is able to serve the preseed.cfg file. This test is here to have access to the + * defaultProperties() method in {@link VirtualBoxPropertiesBuilder}. + * + * @author dralves + * + */ +@Test(groups = "live", singleThreaded = true, testName = "StartJettyIfNotAlreadyRunningLiveTest") +public class StartJettyIfNotAlreadyRunningLiveTest { + + @Test + public void testJettyServerServesPreseedFile() throws Exception { + Properties props = new VirtualBoxPropertiesBuilder().defaultProperties(); + + String preconfigurationUrl = props.getProperty(VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL); + + int port = URI.create(preconfigurationUrl).getPort(); + + Server server = new Server(port); + + StartJettyIfNotAlreadyRunning starter = new StartJettyIfNotAlreadyRunning(preconfigurationUrl, server); + + starter.load(null); + + String preseedFileFromJetty = IOUtils.toString(new URL("http://127.0.0.1:" + port + "/preseed.cfg").openStream()); + String preseedFileFromFile = IOUtils + .toString(this.getClass().getClassLoader().getResourceAsStream("preseed.cfg")) + "\n"; + assertEquals(preseedFileFromFile, preseedFileFromJetty); + } +} diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java index b4fe0f1577..656a61a17f 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java @@ -31,9 +31,11 @@ import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.RunNodesException; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.scriptbuilder.statements.login.AdminAccess; import org.jclouds.ssh.SshClient; import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.BeforeClass; @@ -58,7 +60,7 @@ public class VirtualBoxExperimentLiveTest { @BeforeClass public void setUp() { - context = new ComputeServiceContextFactory().createContext("virtualbox", "toor", "password", + context = new ComputeServiceContextFactory().createContext("virtualbox", "", "", ImmutableSet. of(new SLF4JLoggingModule(), new SshjSshClientModule())); } @@ -66,7 +68,8 @@ public class VirtualBoxExperimentLiveTest { public void testLaunchCluster() throws RunNodesException { int numNodes = 4; final String clusterName = "test-launch-cluster"; - Set nodes = context.getComputeService().createNodesInGroup(clusterName, numNodes); + Set nodes = context.getComputeService().createNodesInGroup(clusterName, numNodes, + TemplateOptions.Builder.runScript(AdminAccess.standard())); assertEquals(numNodes, nodes.size(), "wrong number of nodes"); for (NodeMetadata node : nodes) { logger.debug("Created Node: %s", node);