From b0e2dccd43fa73dc04718fd31dd69193d353d2dd Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 22 Sep 2011 10:41:19 -0700 Subject: [PATCH 1/6] Issue 692: cloudsigma nodes working with ubuntu 10.04 --- .../CloudSigmaComputeServiceContextModule.java | 2 +- .../compute/CloudSigmaComputeServiceLiveTest.java | 13 ++++++++++++- .../compute/CloudSigmaTemplateBuilderLiveTest.java | 9 ++++----- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java index fb8e669544..ad590c6880 100644 --- a/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java +++ b/providers/cloudsigma-zrh/src/main/java/org/jclouds/cloudsigma/compute/config/CloudSigmaComputeServiceContextModule.java @@ -78,7 +78,7 @@ public class CloudSigmaComputeServiceContextModule protected TemplateBuilder provideTemplate(Injector injector, TemplateBuilder template) { // until there is a way to query by drive info that can suggest which // drives are ssh boot - return template.imageId("a3011b07-3f04-467e-9390-f9a85d859de1").minRam(1024); + return template.imageId("f3c7c665-cd54-4a78-8fd2-7ec2f028cf29").minRam(1024); // return // template.osFamily(UBUNTU).osVersionMatches("1[10].[10][04]").os64Bit(true).minRam(1024); } diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceLiveTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceLiveTest.java index b1ec33f721..4f78412f28 100644 --- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceLiveTest.java +++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaComputeServiceLiveTest.java @@ -19,6 +19,8 @@ package org.jclouds.cloudsigma.compute; import org.jclouds.compute.BaseComputeServiceLiveTest; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.Test; @@ -37,5 +39,14 @@ public class CloudSigmaComputeServiceLiveTest extends BaseComputeServiceLiveTest protected Module getSshModule() { return new SshjSshClientModule(); } - + + protected void checkResponseEqualsHostname(ExecResponse execResponse, NodeMetadata node1) { + // hostname is not predictable based on node metadata + assert execResponse.getOutput().trim().equals("ubuntu"); + } + + @Override + public void testOptionToNotBlock() { + // start call has to block until we have a pool of reserved pre-cloned drives. + } } diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaTemplateBuilderLiveTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaTemplateBuilderLiveTest.java index 09ec32c06d..011146ca35 100644 --- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaTemplateBuilderLiveTest.java +++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/compute/CloudSigmaTemplateBuilderLiveTest.java @@ -52,9 +52,8 @@ public class CloudSigmaTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTe public boolean apply(OsFamilyVersion64Bit input) { switch (input.family) { case UBUNTU: - return input.version.equals("11.04") || (input.version.equals("11.04") && !input.is64Bit) - || !input.version.equals("11.10") - && (input.version.equals("") || !(input.version.matches("^[89].*")) && input.is64Bit); + return input.version.equals("11.04") || (input.version.equals("10.04") && !input.is64Bit) + || (input.version.equals("10.10") && input.is64Bit) || input.version.equals(""); case SOLARIS: return !input.is64Bit; case DEBIAN: @@ -77,8 +76,8 @@ public class CloudSigmaTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTe @Override public void testDefaultTemplateBuilder() throws IOException { Template defaultTemplate = context.getComputeService().templateBuilder().build(); - assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "11.04"); - assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true); + assertEquals(defaultTemplate.getImage().getOperatingSystem().getVersion(), "10.04"); + assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), false); assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU); assertEquals(getCores(defaultTemplate.getHardware()), 1.0d); } From 78d2564ba3df2064b0fbe418fe809509b5b582ae Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 22 Sep 2011 10:43:04 -0700 Subject: [PATCH 2/6] added negative tests so that we know what to update when guava 10.0 final is out --- .../org/jclouds/functions/CacheLearningTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/test/java/org/jclouds/functions/CacheLearningTest.java b/core/src/test/java/org/jclouds/functions/CacheLearningTest.java index 58113af471..e6fb56e6af 100644 --- a/core/src/test/java/org/jclouds/functions/CacheLearningTest.java +++ b/core/src/test/java/org/jclouds/functions/CacheLearningTest.java @@ -59,6 +59,20 @@ public class CacheLearningTest { } catch (NullPointerException e) { assertEquals(e.getMessage(), "testLoader returned null for key foo."); } + + try { + assertEquals(cache.activeEntries(Integer.MAX_VALUE).size(), 1); + assert false : "I suppose this works now! Go hunt asMap().keySet()!"; + } catch (UnsupportedOperationException e) { + } + + try { + cache.asMap().put("foo", "bar"); + assertEquals(cache.get("foo"), "bar"); + assert false : "I suppose this works now! Go hunt for invalidate calls!"; + } catch (UnsupportedOperationException e) { + } + try { cache.get("exception"); } catch (ExecutionException e) { From 07ac8e76851ca78a79812a9146b4c94f4a1eb148 Mon Sep 17 00:00:00 2001 From: andreaturli Date: Thu, 22 Sep 2011 19:10:37 +0100 Subject: [PATCH 3/6] issue 384: use arp to retrieve IP address --- .../experiment/VirtualboxLiveTest.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxLiveTest.java index 980a9fb1b9..e3595d143f 100644 --- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxLiveTest.java +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxLiveTest.java @@ -25,6 +25,7 @@ import static org.testng.Assert.assertEquals; import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; @@ -34,17 +35,23 @@ import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.ArrayList; import java.util.List; +import java.util.Properties; import java.util.concurrent.TimeUnit; import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.OsFamily; import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.domain.Credentials; +import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule; import org.jclouds.logging.Logger; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.net.IPSocket; import org.jclouds.predicates.InetSocketAddressConnect; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.ssh.SshClient; +import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeGroups; import org.testng.annotations.BeforeMethod; @@ -63,8 +70,10 @@ import org.virtualbox_4_1.VirtualBoxManager; import com.google.common.base.Predicate; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.inject.Injector; +import com.google.inject.Module; @Test(groups = "live", testName = "virtualbox.VirtualboxLiveTest") public class VirtualboxLiveTest { @@ -227,7 +236,7 @@ public class VirtualboxLiveTest { mutable.getNetworkAdapter(new Long(0)).setAttachmentType(NetworkAttachmentType.Bridged); mutable.getNetworkAdapter(new Long(0)).setAdapterType(NetworkAdapterType.Am79C973); mutable.getNetworkAdapter(new Long(0)).setMACAddress("0800279DA478"); - mutable.getNetworkAdapter(new Long(0)).setBridgedInterface("virbr0"); + mutable.getNetworkAdapter(new Long(0)).setBridgedInterface(hostInterface.trim()); mutable.getNetworkAdapter(new Long(0)).setEnabled(true); mutable.saveSettings(); session.unlockMachine(); @@ -247,15 +256,15 @@ public class VirtualboxLiveTest { String simplifiedMacAddressOfClonedVM = macAddressOfClonedVM; - /* + if(isOSX(hostId)) { if(simplifiedMacAddressOfClonedVM.contains("00")) simplifiedMacAddressOfClonedVM = new StringBuffer(simplifiedMacAddressOfClonedVM).delete(simplifiedMacAddressOfClonedVM.indexOf("00"), simplifiedMacAddressOfClonedVM.indexOf("00") + 1).toString(); if(simplifiedMacAddressOfClonedVM.contains("0")) if(simplifiedMacAddressOfClonedVM.indexOf("0") + 1 != ':' && simplifiedMacAddressOfClonedVM.indexOf("0") - 1 != ':') simplifiedMacAddressOfClonedVM = new StringBuffer(simplifiedMacAddressOfClonedVM).delete(simplifiedMacAddressOfClonedVM.indexOf("0"), simplifiedMacAddressOfClonedVM.indexOf("0") + 1).toString(); - */ - runScriptOnNode(hostId, "for i in $(seq 1 254) ; do ping -c 1 -t 1 192.168.122.$i & done", runAsRoot(false).wrapInInitScript(false)); + } + runScriptOnNode(hostId, "for i in $(seq 1 254) ; do ping -c 1 -t 1 192.168.1.$i & done", runAsRoot(false).wrapInInitScript(false)); String arpLine = runScriptOnNode(hostId, "arp -an | grep " + simplifiedMacAddressOfClonedVM, runAsRoot(false).wrapInInitScript(false)).getOutput(); String ipAddress = arpLine.substring(arpLine.indexOf("(") + 1, arpLine.indexOf(")")); @@ -271,10 +280,9 @@ public class VirtualboxLiveTest { } } */ - //TODO - IPSocket socket = new IPSocket(ipAddress, 22); - checkSSH(socket); + runScriptOnNode(guestId, "echo ciao"); + } private void launchVMProcess(IMachine machine, ISession session) { From 6ad732cda78b8013200cc20bfdeef45ebde26a93 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 22 Sep 2011 13:09:45 -0700 Subject: [PATCH 4/6] Issue 693:add ability to supply node object directly to byon --- .../BYONComputeServiceContextBuilder.java | 19 ++ .../BYONComputeServiceContextModule.java | 15 +- .../byon/config/CacheNodeStoreModule.java | 64 +++++++ .../byon/config/ConfiguresNodeStore.java | 2 +- .../byon/config/YamlNodeStoreModule.java | 41 ++++- .../byon/functions/NodesFromYamlStream.java | 26 ++- .../internal/BYONComputeServiceAdapter.java | 20 +- .../suppliers/NodesParsedFromSupplier.java | 12 +- .../jclouds/byon/BYONComputeServiceTest.java | 54 +++--- .../byon/config/CacheNodeStoreModuleTest.java | 171 ++++++++++++++++++ .../byon/config/YamlNodeStoreModuleTest.java | 81 ++++++--- .../functions/NodeToNodeMetadataTest.java | 15 +- .../byon/functions/NodesFromYamlTest.java | 6 +- .../PlacementGroupClientLiveTest.java | 2 +- .../cloudsigma/CloudSigmaClientLiveTest.java | 6 +- 15 files changed, 434 insertions(+), 100 deletions(-) create mode 100644 apis/byon/src/main/java/org/jclouds/byon/config/CacheNodeStoreModule.java create mode 100644 apis/byon/src/test/java/org/jclouds/byon/config/CacheNodeStoreModuleTest.java diff --git a/apis/byon/src/main/java/org/jclouds/byon/BYONComputeServiceContextBuilder.java b/apis/byon/src/main/java/org/jclouds/byon/BYONComputeServiceContextBuilder.java index 0aa8ea7890..15bdb626a9 100644 --- a/apis/byon/src/main/java/org/jclouds/byon/BYONComputeServiceContextBuilder.java +++ b/apis/byon/src/main/java/org/jclouds/byon/BYONComputeServiceContextBuilder.java @@ -22,9 +22,13 @@ import java.util.List; import java.util.Properties; import org.jclouds.byon.config.BYONComputeServiceContextModule; +import org.jclouds.byon.config.ConfiguresNodeStore; +import org.jclouds.byon.config.YamlNodeStoreModule; import org.jclouds.compute.StandaloneComputeServiceContextBuilder; +import com.google.common.base.Predicate; import com.google.common.base.Supplier; +import com.google.common.collect.Iterables; import com.google.inject.Module; /** @@ -41,6 +45,21 @@ public class BYONComputeServiceContextBuilder extends StandaloneComputeServiceCo @Override protected void addContextModule(List modules) { modules.add(new BYONComputeServiceContextModule()); + addNodeStoreModuleIfNotPresent(modules); } + protected void addNodeStoreModuleIfNotPresent(List modules) { + if (!Iterables.any(modules, new Predicate() { + public boolean apply(Module input) { + return input.getClass().isAnnotationPresent(ConfiguresNodeStore.class); + } + + })) { + addNodeStoreModule(modules); + } + } + + protected void addNodeStoreModule(List modules) { + modules.add(new YamlNodeStoreModule()); + } } diff --git a/apis/byon/src/main/java/org/jclouds/byon/config/BYONComputeServiceContextModule.java b/apis/byon/src/main/java/org/jclouds/byon/config/BYONComputeServiceContextModule.java index 0aa24df25d..7bff8b097d 100644 --- a/apis/byon/src/main/java/org/jclouds/byon/config/BYONComputeServiceContextModule.java +++ b/apis/byon/src/main/java/org/jclouds/byon/config/BYONComputeServiceContextModule.java @@ -20,14 +20,11 @@ package org.jclouds.byon.config; import java.io.InputStream; import java.net.URI; -import java.util.Map; import javax.inject.Singleton; import org.jclouds.byon.Node; -import org.jclouds.byon.functions.NodesFromYamlStream; import org.jclouds.byon.internal.BYONComputeServiceAdapter; -import org.jclouds.byon.suppliers.NodesParsedFromSupplier; import org.jclouds.byon.suppliers.SupplyFromProviderURIOrNodesProperty; import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule; import org.jclouds.concurrent.SingleThreaded; @@ -37,6 +34,7 @@ import org.jclouds.location.suppliers.OnlyLocationOrFirstZone; import com.google.common.base.Function; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.inject.Provides; import com.google.inject.TypeLiteral; @@ -47,7 +45,7 @@ import com.google.inject.TypeLiteral; @SuppressWarnings("unchecked") @SingleThreaded public class BYONComputeServiceContextModule extends - JCloudsNativeComputeServiceAdapterContextModule { + JCloudsNativeComputeServiceAdapterContextModule { public BYONComputeServiceContextModule() { super(Supplier.class, Supplier.class, BYONComputeServiceAdapter.class); @@ -55,7 +53,7 @@ public class BYONComputeServiceContextModule extends @Provides @Singleton - Supplier provideApi(Supplier> in) { + Supplier provideApi(Supplier> in) { return in; } @@ -64,15 +62,12 @@ public class BYONComputeServiceContextModule extends super.configure(); bind(new TypeLiteral>() { }).to(OnlyLocationOrFirstZone.class); - bind(new TypeLiteral>>() { - }).to(NodesParsedFromSupplier.class); + bind(new TypeLiteral>() { + }).to(SupplyFromProviderURIOrNodesProperty.class); bind(new TypeLiteral>() { }).annotatedWith(Provider.class).to(SupplyFromProviderURIOrNodesProperty.class); bind(new TypeLiteral>() { }).to(SupplyFromProviderURIOrNodesProperty.class); - // TODO make this somehow overridable via user request - bind(new TypeLiteral>>() { - }).to(NodesFromYamlStream.class); } } diff --git a/apis/byon/src/main/java/org/jclouds/byon/config/CacheNodeStoreModule.java b/apis/byon/src/main/java/org/jclouds/byon/config/CacheNodeStoreModule.java new file mode 100644 index 0000000000..7e33379cec --- /dev/null +++ b/apis/byon/src/main/java/org/jclouds/byon/config/CacheNodeStoreModule.java @@ -0,0 +1,64 @@ +/** + * 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.byon.config; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; + +import org.jclouds.byon.Node; + +import com.google.common.annotations.Beta; +import com.google.common.base.Functions; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.inject.AbstractModule; +import com.google.inject.TypeLiteral; + +/** + * + * @author Adrian Cole + */ +@ConfiguresNodeStore +@Beta +public class CacheNodeStoreModule extends AbstractModule { + private final Cache backing; + + public CacheNodeStoreModule(Cache backing) { + this.backing = checkNotNull(backing, "backing"); + } + + public CacheNodeStoreModule(Map backing) { + this(CacheBuilder.newBuilder().build(CacheLoader.from(Functions.forMap(backing)))); + for (String node : backing.keySet()) + this.backing.getUnchecked(node); + } + + @Override + protected void configure() { + bind(new TypeLiteral>() { + }).toInstance(backing); + bind(new TypeLiteral>>() { + }).toInstance(Suppliers.> ofInstance(backing)); + } + +} \ No newline at end of file diff --git a/apis/byon/src/main/java/org/jclouds/byon/config/ConfiguresNodeStore.java b/apis/byon/src/main/java/org/jclouds/byon/config/ConfiguresNodeStore.java index 2afcb75175..319cf76fcd 100644 --- a/apis/byon/src/main/java/org/jclouds/byon/config/ConfiguresNodeStore.java +++ b/apis/byon/src/main/java/org/jclouds/byon/config/ConfiguresNodeStore.java @@ -27,7 +27,7 @@ import java.lang.annotation.Target; import com.google.common.annotations.Beta; /** - * designates the module configures a {@code Map} + * designates the module configures a {@code Cache} * * @author Adrian Cole * diff --git a/apis/byon/src/main/java/org/jclouds/byon/config/YamlNodeStoreModule.java b/apis/byon/src/main/java/org/jclouds/byon/config/YamlNodeStoreModule.java index 5d4f062caf..cd902170fc 100644 --- a/apis/byon/src/main/java/org/jclouds/byon/config/YamlNodeStoreModule.java +++ b/apis/byon/src/main/java/org/jclouds/byon/config/YamlNodeStoreModule.java @@ -22,19 +22,30 @@ import java.io.InputStream; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.byon.Node; import org.jclouds.byon.domain.YamlNode; +import org.jclouds.byon.functions.NodesFromYamlStream; +import org.jclouds.byon.suppliers.NodesParsedFromSupplier; import org.jclouds.collect.TransformingMap; import org.jclouds.io.CopyInputStreamInputSupplierMap; +import org.jclouds.io.CopyInputStreamIntoSupplier; import com.google.common.annotations.Beta; import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.base.Supplier; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.io.InputSupplier; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; /** * @@ -56,6 +67,10 @@ public class YamlNodeStoreModule extends AbstractModule { @Override protected void configure() { + bind(new TypeLiteral>>() { + }).to(NodesParsedFromSupplier.class); + bind(new TypeLiteral>>() { + }).to(NodesFromYamlStream.class); bind(new TypeLiteral>() { }).toInstance(org.jclouds.byon.domain.YamlNode.yamlNodeToInputStream); bind(new TypeLiteral>() { @@ -66,27 +81,37 @@ public class YamlNodeStoreModule extends AbstractModule { }).toInstance(org.jclouds.byon.domain.YamlNode.toNode); if (backing != null) { bind(new TypeLiteral>() { - }).toInstance(backing); + }).annotatedWith(Names.named("yaml")).toInstance(backing); } else { bind(new TypeLiteral>>() { - }).toInstance(BACKING); + }).annotatedWith(Names.named("yaml")).toInstance(BACKING); bind(new TypeLiteral>() { - }).to(new TypeLiteral() { + }).annotatedWith(Names.named("yaml")).to(new TypeLiteral() { }); } + + } + + @Singleton + public static class YAMLCopyInputStreamInputSupplierMap extends CopyInputStreamInputSupplierMap { + @Inject + public YAMLCopyInputStreamInputSupplierMap(@Named("yaml") Map> toMap, + CopyInputStreamIntoSupplier putFunction) { + super(toMap, putFunction); + } } @Provides @Singleton - protected Map provideNodeStore(Map backing, - Function yamlSerializer, Function yamlDeserializer) { - return new TransformingMap(backing, yamlDeserializer, yamlSerializer); + protected Cache provideNodeStore(Map backing, Function yamlSerializer, + Function yamlDeserializer) { + return CacheBuilder.newBuilder().build(CacheLoader.from(Functions.forMap(new TransformingMap(backing, yamlDeserializer, yamlSerializer)))); } @Provides @Singleton - protected Map provideYamlStore(Map backing, - Function yamlSerializer, Function yamlDeserializer) { + protected Map provideYamlStore(@Named("yaml") Map backing, + Function yamlSerializer, Function yamlDeserializer) { return new TransformingMap(backing, yamlDeserializer, yamlSerializer); } } \ No newline at end of file diff --git a/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java b/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java index 8f22a5140c..880457d349 100644 --- a/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java +++ b/apis/byon/src/main/java/org/jclouds/byon/functions/NodesFromYamlStream.java @@ -34,6 +34,10 @@ import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.Constructor; import com.google.common.base.Function; +import com.google.common.base.Functions; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; @@ -65,7 +69,7 @@ import com.google.common.collect.Maps; * @author Adrian Cole */ @Singleton -public class NodesFromYamlStream implements Function> { +public class NodesFromYamlStream implements Function> { /** * Type-safe config class for YAML @@ -76,7 +80,7 @@ public class NodesFromYamlStream implements Function apply(InputStream source) { + public Cache apply(InputStream source) { Constructor constructor = new Constructor(Config.class); @@ -87,17 +91,23 @@ public class NodesFromYamlStream implements Function() { - public String apply(Node node) { - return node.getId(); - } - }); + Map backingMap = Maps.uniqueIndex(Iterables.transform(config.nodes, YamlNode.toNode), + new Function() { + public String apply(Node node) { + return node.getId(); + } + }); + Cache cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.forMap(backingMap))); + for (String node : backingMap.keySet()) + cache.getUnchecked(node); + return cache; } } diff --git a/apis/byon/src/main/java/org/jclouds/byon/internal/BYONComputeServiceAdapter.java b/apis/byon/src/main/java/org/jclouds/byon/internal/BYONComputeServiceAdapter.java index 890336e459..4f9190bab6 100644 --- a/apis/byon/src/main/java/org/jclouds/byon/internal/BYONComputeServiceAdapter.java +++ b/apis/byon/src/main/java/org/jclouds/byon/internal/BYONComputeServiceAdapter.java @@ -42,9 +42,11 @@ import org.jclouds.location.suppliers.JustProvider; import com.google.common.base.Function; import com.google.common.base.Predicates; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.UncheckedExecutionException; /** * @@ -52,12 +54,12 @@ import com.google.common.collect.ImmutableSet.Builder; */ @Singleton public class BYONComputeServiceAdapter implements JCloudsNativeComputeServiceAdapter { - private final Supplier> nodes; + private final Supplier> nodes; private final NodeToNodeMetadata converter; private final JustProvider locationSupplier; @Inject - public BYONComputeServiceAdapter(Supplier> nodes, NodeToNodeMetadata converter, + public BYONComputeServiceAdapter(Supplier> nodes, NodeToNodeMetadata converter, JustProvider locationSupplier) { this.nodes = checkNotNull(nodes, "nodes"); this.converter = checkNotNull(converter, "converter"); @@ -82,14 +84,14 @@ public class BYONComputeServiceAdapter implements JCloudsNativeComputeServiceAda @Override public Iterable listNodes() { - return Iterables.transform(nodes.get().values(), converter); + return Iterables.transform(nodes.get().asMap().values(), converter); } @Override public Iterable listLocations() { Builder locations = ImmutableSet.builder(); Location provider = Iterables.getOnlyElement(locationSupplier.get()); - Set zones = ImmutableSet.copyOf(Iterables.filter(Iterables.transform(nodes.get().values(), + Set zones = ImmutableSet.copyOf(Iterables.filter(Iterables.transform(nodes.get().asMap().values(), new Function() { @Override @@ -109,7 +111,13 @@ public class BYONComputeServiceAdapter implements JCloudsNativeComputeServiceAda @Override public NodeMetadata getNode(String id) { - Node node = nodes.get().get(id); + + Node node = null; + try { + node = nodes.get().getUnchecked(id); + } catch (UncheckedExecutionException e) { + + } return node != null ? converter.apply(node) : null; } diff --git a/apis/byon/src/main/java/org/jclouds/byon/suppliers/NodesParsedFromSupplier.java b/apis/byon/src/main/java/org/jclouds/byon/suppliers/NodesParsedFromSupplier.java index 94cfc8d495..0cbf0e532a 100644 --- a/apis/byon/src/main/java/org/jclouds/byon/suppliers/NodesParsedFromSupplier.java +++ b/apis/byon/src/main/java/org/jclouds/byon/suppliers/NodesParsedFromSupplier.java @@ -22,7 +22,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import java.io.InputStream; -import java.util.Map; import javax.annotation.Resource; import javax.inject.Inject; @@ -34,28 +33,29 @@ import org.jclouds.logging.Logger; import com.google.common.base.Function; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; /** * * @author Adrian Cole */ @Singleton -public class NodesParsedFromSupplier implements Supplier> { +public class NodesParsedFromSupplier implements Supplier> { @Resource protected Logger logger = Logger.NULL; private final Supplier supplier; - private final Function> parser; + private final Function> parser; @Inject - NodesParsedFromSupplier(@Provider Supplier supplier, Function> parser) { + NodesParsedFromSupplier(@Provider Supplier supplier, Function> parser) { this.supplier = checkNotNull(supplier, "supplier"); this.parser = checkNotNull(parser, "parser"); } @Override - public Map get() { - Map nodes = parser.apply(supplier.get()); + public Cache get() { + Cache nodes = parser.apply(supplier.get()); checkState(nodes != null && nodes.size() > 0, "no nodes parsed from supplier: %s", supplier); return nodes; } diff --git a/apis/byon/src/test/java/org/jclouds/byon/BYONComputeServiceTest.java b/apis/byon/src/test/java/org/jclouds/byon/BYONComputeServiceTest.java index 45ab6d8a8f..de1a4958bc 100644 --- a/apis/byon/src/test/java/org/jclouds/byon/BYONComputeServiceTest.java +++ b/apis/byon/src/test/java/org/jclouds/byon/BYONComputeServiceTest.java @@ -24,17 +24,17 @@ import static org.jclouds.byon.functions.NodeToNodeMetadataTest.zoneCalled; import static org.testng.Assert.assertEquals; import java.net.URI; -import java.util.Map; import java.util.Properties; +import org.jclouds.byon.config.CacheNodeStoreModule; import org.jclouds.byon.functions.NodesFromYamlTest; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.domain.Location; -import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.Test; import com.google.common.base.Supplier; +import com.google.common.cache.Cache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.inject.Module; @@ -43,41 +43,48 @@ import com.google.inject.Module; * * @author Adrian Cole */ -@Test(groups = "live") +@Test(singleThreaded = true, testName = "BYONComputeServiceTest") public class BYONComputeServiceTest { + @Test + public void testNodesParseNodeMap() throws Exception { + assertNodesParse( + "foo", + ImmutableSet. of(new CacheNodeStoreModule(ImmutableMap. of( + NodesFromYamlTest.TEST1.getId(), NodesFromYamlTest.TEST1)))); + } + @Test public void testNodesParseWithFileUrl() throws Exception { - assertNodesParse("file://" + getClass().getResource("/test1.yaml").getPath()); + assertNodesParse("file://" + getClass().getResource("/test1.yaml").getPath(), ImmutableSet. of()); } @Test public void testNodesParseWithClasspathUrl() throws Exception { - assertNodesParse("classpath:///test1.yaml"); + assertNodesParse("classpath:///test1.yaml", ImmutableSet. of()); } - private void assertNodesParse(String endpoint) { + private void assertNodesParse(String endpoint, Iterable modules) { ComputeServiceContext context = null; try { Location providerLocation = expectedProviderLocationFromResource(endpoint); Properties props = new Properties(); props.setProperty("byon.endpoint", endpoint); - context = new ComputeServiceContextFactory().createContext("byon", "foo", "bar", ImmutableSet - . of(new SshjSshClientModule()), props); + context = new ComputeServiceContextFactory().createContext("byon", "foo", "bar", modules, props); assertEquals(context.getProviderSpecificContext().getEndpoint(), URI.create(endpoint)); @SuppressWarnings("unchecked") - Supplier> supplier = (Supplier>) context.getProviderSpecificContext() - .getApi(); + Supplier> supplier = (Supplier>) context.getProviderSpecificContext() + .getApi(); assertEquals(supplier.get().size(), context.getComputeService().listNodes().size()); - assertEquals(supplier.get(), ImmutableMap. of(NodesFromYamlTest.TEST1.getId(), - NodesFromYamlTest.TEST1)); + assertEquals(supplier.get().asMap(), + ImmutableMap. of(NodesFromYamlTest.TEST1.getId(), NodesFromYamlTest.TEST1)); - assertEquals(context.getComputeService().listNodes(), ImmutableSet - .of(expectedNodeMetadataFromResource(endpoint))); + assertEquals(context.getComputeService().listNodes(), + ImmutableSet.of(expectedNodeMetadataFromResource(endpoint))); assertEquals(context.getComputeService().listAssignableLocations(), ImmutableSet.of(providerLocation)); } finally { if (context != null) @@ -91,26 +98,27 @@ public class BYONComputeServiceTest { String endpoint = "file://" + getClass().getResource("/test_location.yaml").getPath(); Properties props = new Properties(); props.setProperty("byon.endpoint", endpoint); - context = new ComputeServiceContextFactory().createContext("byon", "foo", "bar", ImmutableSet - . of(new SshjSshClientModule()), props); + context = new ComputeServiceContextFactory().createContext("byon", "foo", "bar", + ImmutableSet. of(), props); assertEquals(context.getProviderSpecificContext().getEndpoint(), URI.create(endpoint)); @SuppressWarnings("unchecked") - Supplier> supplier = (Supplier>) context.getProviderSpecificContext() - .getApi(); + Supplier> supplier = (Supplier>) context.getProviderSpecificContext() + .getApi(); assertEquals(supplier.get().size(), context.getComputeService().listNodes().size()); - assertEquals(supplier.get(), ImmutableMap. of(NodesFromYamlTest.TEST2.getId(), - NodesFromYamlTest.TEST2, NodesFromYamlTest.TEST3.getId(), NodesFromYamlTest.TEST3)); + assertEquals(supplier.get().asMap(), ImmutableMap. of(NodesFromYamlTest.TEST2.getId(), + NodesFromYamlTest.TEST2, NodesFromYamlTest.TEST3.getId(), NodesFromYamlTest.TEST3)); Location providerLocation = expectedProviderLocationFromResource(endpoint); Location virginia = zoneCalled("virginia", providerLocation); Location maryland = zoneCalled("maryland", providerLocation); - assertEquals(context.getComputeService().listNodes(), ImmutableSet.of(expectedNodeMetadataFromResource(1, - endpoint, virginia), expectedNodeMetadataFromResource(2, endpoint, maryland, 2022))); - + assertEquals(context.getComputeService().listNodes(), ImmutableSet.of( + expectedNodeMetadataFromResource(1, endpoint, virginia), + expectedNodeMetadataFromResource(2, endpoint, maryland, 2022))); + assertEquals(context.getComputeService().listAssignableLocations(), ImmutableSet.of(virginia, maryland)); } finally { if (context != null) diff --git a/apis/byon/src/test/java/org/jclouds/byon/config/CacheNodeStoreModuleTest.java b/apis/byon/src/test/java/org/jclouds/byon/config/CacheNodeStoreModuleTest.java new file mode 100644 index 0000000000..af64f18645 --- /dev/null +++ b/apis/byon/src/test/java/org/jclouds/byon/config/CacheNodeStoreModuleTest.java @@ -0,0 +1,171 @@ +/** + * 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.byon.config; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import org.jclouds.byon.Node; +import org.jclouds.location.Provider; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import com.google.common.base.Functions; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", singleThreaded = true, testName = "CacheNodeStoreModuleTest") +public class CacheNodeStoreModuleTest { + + @DataProvider(name = "names") + public Object[][] createData() { + return new Object[][] { { "instance1", "bear" }, { "instance2", "apple" }, { "instance2", "francis" }, + { "instance4", "robot" } }; + } + + public void testProvidedMapWithValue() throws IOException { + Map map = Maps.newConcurrentMap(); + + map.put("test", Node.builder().id("instance1").name("instancename").build()); + checkConsistent(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); + checkConsistent(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); + remove(map, getStore(createInjectorWithProvidedMap(map)), "test"); + + } + + public void testProvidedConsistentAcrossRepeatedWrites() throws IOException { + Map map = Maps.newConcurrentMap(); + + Injector injector = createInjectorWithProvidedMap(map); + assertEquals(injector.getInstance(Key.get(new TypeLiteral>() { + })).asMap(), map); + Cache store = getStore(injector); + + for (int i = 0; i < 10; i++) + check(map, store, "test" + i, "instance1" + i, "instancename" + i); + + } + + public void testProvidedConsistentAcrossMultipleInjectors() throws IOException { + Map map = Maps.newConcurrentMap(); + + put(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); + checkConsistent(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); + checkConsistent(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); + remove(map, getStore(createInjectorWithProvidedMap(map)), "test"); + + } + + public void testProvidedCacheConsistentAcrossMultipleInjectors() throws IOException { + Map map = Maps.newConcurrentMap(); + + Cache cache = CacheBuilder.newBuilder().build(CacheLoader.from(Functions.forMap(map))); + + put(map, getStore(createInjectorWithProvidedCache(cache)), "test", "instance1", "instancename"); + checkConsistent(map, getStore(createInjectorWithProvidedCache(cache)), "test", "instance1", "instancename"); + checkConsistent(map, getStore(createInjectorWithProvidedCache(cache)), "test", "instance1", "instancename"); + remove(map, getStore(createInjectorWithProvidedCache(cache)), "test"); + + } + + private Cache getStore(Injector injector) { + return injector.getInstance(Key.get(new TypeLiteral>() { + })); + } + + private Injector createInjectorWithProvidedMap(Map map) { + return Guice.createInjector(new CacheNodeStoreModule(map), new AbstractModule() { + + @Override + public void configure() { + bind(new TypeLiteral>() { + }).annotatedWith(Provider.class).toInstance(Suppliers. ofInstance(null)); + } + + }); + } + + private Injector createInjectorWithProvidedCache(Cache cache) { + return Guice.createInjector(new CacheNodeStoreModule(cache), new AbstractModule() { + + @Override + public void configure() { + bind(new TypeLiteral>() { + }).annotatedWith(Provider.class).toInstance(Suppliers. ofInstance(null)); + } + + }); + } + + private void check(Map map, Cache store, String key, String id, String name) + throws IOException { + put(map, store, key, id, name); + checkConsistent(map, store, key, id, name); + remove(map, store, key); + } + + private void remove(Map map, Cache store, String key) { + store.invalidate(key); + assertEquals(store.size(), 0); + map.remove(key); + assertEquals(map.size(), 0); + try { + assertEquals(store.getUnchecked(key), null); + assert false : "should not work as null is invalid"; + } catch (UncheckedExecutionException e) { + + } + assertEquals(map.get(key), null); + } + + private void checkConsistent(Map map, Cache store, String key, String id, String name) + throws IOException { + assertEquals(map.size(), 1); + if (store.size() == 0) + store.getUnchecked(key); + assertEquals(store.size(), 1); + // checkRepeatedRead + assertEquals(store.getUnchecked(key), Node.builder().id(id).name(name).build()); + assertEquals(store.getUnchecked(key), Node.builder().id(id).name(name).build()); + } + + private void put(Map map, Cache store, String key, String id, String name) { + assertEquals(store.size(), 0); + assertEquals(map.size(), 0); + map.put(key, Node.builder().id(id).name(name).build()); + store.getUnchecked(key); + } +} \ No newline at end of file diff --git a/apis/byon/src/test/java/org/jclouds/byon/config/YamlNodeStoreModuleTest.java b/apis/byon/src/test/java/org/jclouds/byon/config/YamlNodeStoreModuleTest.java index 25ac42e2a1..97e285f8dc 100644 --- a/apis/byon/src/test/java/org/jclouds/byon/config/YamlNodeStoreModuleTest.java +++ b/apis/byon/src/test/java/org/jclouds/byon/config/YamlNodeStoreModuleTest.java @@ -28,16 +28,23 @@ import java.util.concurrent.ConcurrentHashMap; import org.jclouds.byon.Node; import org.jclouds.io.CopyInputStreamInputSupplierMap; +import org.jclouds.location.Provider; import org.jclouds.util.Strings2; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import org.yaml.snakeyaml.Yaml; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; import com.google.common.io.InputSupplier; +import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; /** * @@ -45,12 +52,12 @@ import com.google.inject.TypeLiteral; */ @Test(groups = "unit", singleThreaded = true) public class YamlNodeStoreModuleTest { - Yaml json = createInjector().getInstance(Yaml.class); + Yaml yaml = createInjector().getInstance(Yaml.class); @DataProvider(name = "names") public Object[][] createData() { return new Object[][] { { "instance1", "bear" }, { "instance2", "apple" }, { "instance2", "francis" }, - { "instance4", "robot" } }; + { "instance4", "robot" } }; } @Test(dataProvider = "names") @@ -62,7 +69,7 @@ public class YamlNodeStoreModuleTest { public void testProvidedMapWithValue() throws IOException { Map map = new CopyInputStreamInputSupplierMap( - new ConcurrentHashMap>()); + new ConcurrentHashMap>()); map.put("test", new ByteArrayInputStream("id: instance1\nname: instancename\n".getBytes())); checkConsistent(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); @@ -73,12 +80,12 @@ public class YamlNodeStoreModuleTest { public void testProvidedConsistentAcrossRepeatedWrites() throws IOException { Map map = new CopyInputStreamInputSupplierMap( - new ConcurrentHashMap>()); + new ConcurrentHashMap>()); Injector injector = createInjectorWithProvidedMap(map); assertEquals(injector.getInstance(Key.get(new TypeLiteral>() { - })), map); - Map store = getStore(injector); + }, Names.named("yaml"))), map); + Cache store = getStore(injector); for (int i = 0; i < 10; i++) check(map, store, "test" + i, "instance1" + i, "instancename" + i); @@ -87,7 +94,7 @@ public class YamlNodeStoreModuleTest { public void testProvidedConsistentAcrossMultipleInjectors() throws IOException { Map map = new CopyInputStreamInputSupplierMap( - new ConcurrentHashMap>()); + new ConcurrentHashMap>()); put(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); checkConsistent(map, getStore(createInjectorWithProvidedMap(map)), "test", "instance1", "instancename"); @@ -100,52 +107,77 @@ public class YamlNodeStoreModuleTest { Map map = getMap(createInjector()); put(map, getStore(createInjector()), "test", "instance1", "instancename"); + checkConsistent(map, getStore(createInjector()), "test", "instance1", "instancename"); checkConsistent(map, getStore(createInjector()), "test", "instance1", "instancename"); remove(map, getStore(createInjector()), "test"); } - protected Map getStore(Injector injector) { - return injector.getInstance(Key.get(new TypeLiteral>() { + protected Cache getStore(Injector injector) { + return injector.getInstance(Key.get(new TypeLiteral>() { })); } protected Map getMap(Injector injector) { return injector.getInstance(Key.get(new TypeLiteral>() { - })); + }, Names.named("yaml"))); } protected Injector createInjectorWithProvidedMap(Map map) { - return Guice.createInjector(new YamlNodeStoreModule(map)); + return Guice.createInjector(new YamlNodeStoreModule(map), new AbstractModule() { + + @Override + protected void configure() { + bind(new TypeLiteral>() { + }).annotatedWith(Provider.class).toInstance(Suppliers. ofInstance(null)); + } + + }); } protected Injector createInjector() { - return Guice.createInjector(new YamlNodeStoreModule()); + return Guice.createInjector(new YamlNodeStoreModule(), new AbstractModule() { + + @Override + protected void configure() { + bind(new TypeLiteral>() { + }).annotatedWith(Provider.class).toInstance(Suppliers. ofInstance(null)); + } + + }); } - protected void check(Map map, Map store, String key, String id, String name) - throws IOException { + protected void check(Map map, Cache store, String key, String id, String name) + throws IOException { put(map, store, key, id, name); checkConsistent(map, store, key, id, name); remove(map, store, key); } - protected void remove(Map map, Map store, String key) { - store.remove(key); + protected void remove(Map map, Cache store, String key) { + store.invalidate(key); assertEquals(store.size(), 0); + map.remove(key); assertEquals(map.size(), 0); - assertEquals(store.get(key), null); + try { + assertEquals(store.getUnchecked(key), null); + assert false : "should not work as null is invalid"; + } catch (UncheckedExecutionException e) { + + } assertEquals(map.get(key), null); } - protected void checkConsistent(Map map, Map store, String key, String id, - String name) throws IOException { - assertEquals(store.size(), 1); + protected void checkConsistent(Map map, Cache store, String key, String id, + String name) throws IOException { assertEquals(map.size(), 1); + if (store.size() == 0) + store.getUnchecked(key); + assertEquals(store.size(), 1); // checkRepeatedRead - assertEquals(store.get(key), Node.builder().id(id).name(name).build()); - assertEquals(store.get(key), Node.builder().id(id).name(name).build()); + assertEquals(store.getUnchecked(key), Node.builder().id(id).name(name).build()); + assertEquals(store.getUnchecked(key), Node.builder().id(id).name(name).build()); // checkRepeatedRead checkToYaml(map, key, id, name); checkToYaml(map, key, id, name); @@ -155,9 +187,10 @@ public class YamlNodeStoreModuleTest { assertEquals(Strings2.toStringAndClose(map.get(key)), String.format("id: %s\nname: %s\n", id, name)); } - protected void put(Map map, Map store, String key, String id, String name) { + protected void put(Map map, Cache store, String key, String id, String name) { assertEquals(store.size(), 0); assertEquals(map.size(), 0); - store.put(key, Node.builder().id(id).name(name).build()); + map.put(key, new ByteArrayInputStream(String.format("id: %s\nname: %s\n", id, name).getBytes())); + store.getUnchecked(key); } } \ No newline at end of file diff --git a/apis/byon/src/test/java/org/jclouds/byon/functions/NodeToNodeMetadataTest.java b/apis/byon/src/test/java/org/jclouds/byon/functions/NodeToNodeMetadataTest.java index 4358e38530..1fc15839e3 100644 --- a/apis/byon/src/test/java/org/jclouds/byon/functions/NodeToNodeMetadataTest.java +++ b/apis/byon/src/test/java/org/jclouds/byon/functions/NodeToNodeMetadataTest.java @@ -45,6 +45,7 @@ import com.google.common.collect.Maps; * * @author Adrian Cole */ +@Test(singleThreaded = true, testName = "NodeToNodeMetadataTest") public class NodeToNodeMetadataTest { public static Location expectedProviderLocationFromResource(String resource) { return new LocationBuilder().scope(LocationScope.PROVIDER).id("byon").description(resource).build(); @@ -60,9 +61,9 @@ public class NodeToNodeMetadataTest { Map credentialStore = Maps.newLinkedHashMap(); - NodeToNodeMetadata parser = new NodeToNodeMetadata(Suppliers.ofInstance(provider), Suppliers - .> ofInstance(ImmutableSet.of(provider, zoneCalled("virginia", provider))), - new SupplyFromProviderURIOrNodesProperty(URI.create("test")), credentialStore); + NodeToNodeMetadata parser = new NodeToNodeMetadata(Suppliers.ofInstance(provider), + Suppliers.> ofInstance(ImmutableSet.of(provider, zoneCalled("virginia", provider))), + new SupplyFromProviderURIOrNodesProperty(URI.create("test")), credentialStore); public static NodeMetadata expectedNodeMetadataFromResource(String resource) { return expectedNodeMetadataFromResource(resource, expectedProviderLocationFromResource(resource)); @@ -75,7 +76,7 @@ public class NodeToNodeMetadataTest { public static NodeMetadata expectedNodeMetadataFromResource(int id, String resource, Location location) { return expectedNodeMetadataFromResource(id, resource, location, 22); } - + public static NodeMetadata expectedNodeMetadataFromResource(int id, String resource, Location location, int loginPort) { return new NodeMetadataBuilder() .ids("cluster-" + id) @@ -99,11 +100,11 @@ public class NodeToNodeMetadataTest { @Test public void testNodesParseLocation() throws Exception { - assertEquals(parser.apply(NodesFromYamlTest.TEST2), expectedNodeMetadataFromResource(resource, zoneCalled( - "virginia", provider))); + assertEquals(parser.apply(NodesFromYamlTest.TEST2), + expectedNodeMetadataFromResource(resource, zoneCalled("virginia", provider))); assertEquals(credentialStore, ImmutableMap.of("node#cluster-1", new Credentials("myUser", NodesFromYamlTest.key))); } - + @Test public void testNodesParseLoginPort() throws Exception { assertEquals(parser.apply(NodesFromYamlTest.TEST3), expectedNodeMetadataFromResource(2, resource, provider, 2022)); diff --git a/apis/byon/src/test/java/org/jclouds/byon/functions/NodesFromYamlTest.java b/apis/byon/src/test/java/org/jclouds/byon/functions/NodesFromYamlTest.java index fc1417aee2..27603cf1bb 100644 --- a/apis/byon/src/test/java/org/jclouds/byon/functions/NodesFromYamlTest.java +++ b/apis/byon/src/test/java/org/jclouds/byon/functions/NodesFromYamlTest.java @@ -58,7 +58,7 @@ public class NodesFromYamlTest { InputStream is = getClass().getResourceAsStream("/test1.yaml"); NodesFromYamlStream parser = new NodesFromYamlStream(); - assertEquals(parser.apply(is), ImmutableMap.of(TEST1.getId(), TEST1)); + assertEquals(parser.apply(is).asMap(), ImmutableMap.of(TEST1.getId(), TEST1)); } @Test @@ -67,7 +67,7 @@ public class NodesFromYamlTest { InputStream is = getClass().getResourceAsStream("/test_location.yaml"); NodesFromYamlStream parser = new NodesFromYamlStream(); - assertEquals(parser.apply(is), ImmutableMap.of(TEST2.getId(), TEST2, TEST3.getId(), TEST3)); + assertEquals(parser.apply(is).asMap(), ImmutableMap.of(TEST2.getId(), TEST2, TEST3.getId(), TEST3)); } @Test @@ -76,7 +76,7 @@ public class NodesFromYamlTest { InputStream is = getClass().getResourceAsStream("/test_with_url.yaml"); NodesFromYamlStream parser = new NodesFromYamlStream(); - assertEquals(parser.apply(is), ImmutableMap.of(TEST1.getId(), TEST1)); + assertEquals(parser.apply(is).asMap(), ImmutableMap.of(TEST1.getId(), TEST1)); } @Test(expectedExceptions = IllegalStateException.class) diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java index 2652969beb..c1e061de9b 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/PlacementGroupClientLiveTest.java @@ -168,7 +168,7 @@ public class PlacementGroupClientLiveTest { public void testStartCCInstance() throws Exception { - Template template = context.getComputeService().templateBuilder().fastest().osVersionMatches("11.04").build(); + Template template = context.getComputeService().templateBuilder().fastest().osDescriptionMatches(".*/ubuntu-images/.*").osVersionMatches("11.04").build(); assert template != null : "The returned template was null, but it should have a value."; assertEquals(template.getHardware().getProviderId(), InstanceType.CC1_4XLARGE); assertEquals(template.getImage().getUserMetadata().get("rootDeviceType"), "ebs"); diff --git a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java index c50b8fdb3a..8b53fca3d6 100644 --- a/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java +++ b/providers/cloudsigma-zrh/src/test/java/org/jclouds/cloudsigma/CloudSigmaClientLiveTest.java @@ -73,7 +73,7 @@ import com.google.inject.Module; * * @author Adrian Cole */ -@Test(groups = "live", sequential = true) +@Test(groups = "live", singleThreaded = true) public class CloudSigmaClientLiveTest { protected long driveSize = 8 * 1024 * 1024 * 1024l; protected int maxDriveImageTime = 300; @@ -455,12 +455,12 @@ public class CloudSigmaClientLiveTest { } protected Credentials getSshCredentials(Server server) { - return new Credentials("cloudsigma", "cloudsigma"); + return new Credentials("root", vncPassword); } protected void prepareDrive() { client.destroyDrive(drive.getUuid()); - drive = client.cloneDrive("6a9cd9c2-4814-4953-8e86-f8ee6a3e57d5", drive.getName(), + drive = client.cloneDrive("f3c7c665-cd54-4a78-8fd2-7ec2f028cf29", drive.getName(), new CloneDriveOptions().size(driveSize)); assert driveNotClaimed.apply(drive) : client.getDriveInfo(drive.getUuid()); System.err.println("after prepare" + client.getDriveInfo(drive.getUuid())); From 40e7ca8858b8a718278082ffbab11e740e8ef43a Mon Sep 17 00:00:00 2001 From: Mattias Holmqvist Date: Fri, 23 Sep 2011 00:05:39 +0200 Subject: [PATCH 5/6] Refactored TestUtils to use CacheNodeStoreModule. --- .../virtualbox/experiment/TestUtils.java | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/TestUtils.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/TestUtils.java index 9ed46fe23d..b8513f5f56 100644 --- a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/TestUtils.java +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/TestUtils.java @@ -18,10 +18,11 @@ */ package org.jclouds.virtualbox.experiment; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Properties; - +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; +import org.jclouds.byon.Node; +import org.jclouds.byon.config.CacheNodeStoreModule; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.domain.OsFamily; @@ -29,41 +30,48 @@ import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule; import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.sshj.config.SshjSshClientModule; -import com.google.common.collect.ImmutableSet; -import com.google.inject.Module; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; public class TestUtils { - public static ComputeServiceContext computeServiceForLocalhost() throws FileNotFoundException, IOException { - Properties contextProperties = new Properties(); + public static ComputeServiceContext computeServiceForLocalhost() throws IOException { - StringBuilder nodes = new StringBuilder(); - nodes.append("nodes:\n"); - nodes.append(" - id: host\n"); - nodes.append(" name: host installing virtualbox\n"); - nodes.append(" hostname: localhost\n"); - nodes.append(" os_family: ").append(OsFamily.LINUX).append("\n"); - nodes.append(" os_description: ").append(System.getProperty("os.name")).append("\n"); - nodes.append(" os_version: ").append(System.getProperty("os.version")).append("\n"); - nodes.append(" group: ").append("ssh").append("\n"); - nodes.append(" username: ").append(System.getProperty("user.name")).append("\n"); - nodes.append(" credential_url: file://").append(System.getProperty("user.home")).append("/.ssh/id_rsa") - .append("\n"); - nodes.append("\n"); - nodes.append(" - id: guest\n"); - nodes.append(" name: new guest\n"); - nodes.append(" hostname: localhost\n"); - nodes.append(" login_port: 2222\n"); - nodes.append(" os_family: ubuntu").append("\n"); - nodes.append(" os_description: ubuntu/11.04").append("\n"); - nodes.append(" os_version: 11.04").append("\n"); - nodes.append(" group: guest").append("\n"); - nodes.append(" username: toor").append("\n"); - nodes.append(" sudo_password: password").append("\n"); - nodes.append(" credential: password").append("\n"); + Node host = Node.builder().id("host") + .name("host installing virtualbox") + .hostname("localhost") + .osFamily(OsFamily.LINUX.toString()) + .osDescription(System.getProperty("os.name")) + .osVersion(System.getProperty("os.version")) + .group("ssh") + .username(System.getProperty("user.name")) + .credentialUrl(privateKeyFile()) + .build(); + Node guest = Node.builder().id("guest") + .name("new guest") + .hostname("localhost") + .loginPort(2222) + .osFamily(OsFamily.UBUNTU.toString()) + .osDescription("ubuntu/11.04") + .osVersion(System.getProperty("11.04")) + .group("guest") + .username("toor") + .sudoPassword("password") + .credential("password") + .build(); - contextProperties.setProperty("byon.nodes", nodes.toString()); + final Map nodeMap = ImmutableMap.builder().put("host", host).put("guest", guest).build(); + return new ComputeServiceContextFactory().createContext("byon", "foo", "bar", ImmutableSet.of( + new SshjSshClientModule(), new SLF4JLoggingModule(), new BouncyCastleCryptoModule(), new CacheNodeStoreModule(nodeMap))); + } - return new ComputeServiceContextFactory().createContext("byon", "foo", "bar", ImmutableSet. of( - new SshjSshClientModule(), new SLF4JLoggingModule(), new BouncyCastleCryptoModule()), contextProperties); + private static URI privateKeyFile() { + try { + return new URI("file://" + System.getProperty("user.home") + "/.ssh/id_rsa"); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return null; } } From 79a8336b100ab1393f93c1c8a3ba99643a689ee5 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 22 Sep 2011 22:53:09 -0700 Subject: [PATCH 6/6] Issue 696:The security group 'X' does not exist error when creating nodes --- .../org/jclouds/ec2/EC2PropertiesBuilder.java | 2 + .../EC2ComputeServiceDependenciesModule.java | 14 +++ .../CreateSecurityGroupIfNeeded.java | 34 ++++-- .../predicates/SecurityGroupPresent.java | 70 +++++++++++ .../jclouds/ec2/domain/UserIdGroupPair.java | 6 + .../jclouds/ec2/reference/EC2Constants.java | 5 + .../CreateSecurityGroupIfNeededTest.java | 110 ++++++++++++++++++ 7 files changed, 230 insertions(+), 11 deletions(-) create mode 100644 apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/SecurityGroupPresent.java create mode 100644 apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeededTest.java diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java b/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java index 73bbbc873a..6a28aa6463 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/EC2PropertiesBuilder.java @@ -22,6 +22,7 @@ import static org.jclouds.Constants.PROPERTY_API_VERSION; import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG; import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG; import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS; +import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT; import java.util.Properties; @@ -40,6 +41,7 @@ public class EC2PropertiesBuilder extends PropertiesBuilder { properties.setProperty(PROPERTY_HEADER_TAG, "amz"); properties.setProperty(PROPERTY_API_VERSION, EC2AsyncClient.VERSION); properties.setProperty(PROPERTY_EC2_AMI_OWNERS, "*"); + properties.setProperty(PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT, "500"); return properties; } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java index 21f9f2f7c7..fd4c246188 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceDependenciesModule.java @@ -18,8 +18,11 @@ */ package org.jclouds.ec2.compute.config; +import static org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT; + import java.security.SecureRandom; import java.util.Map; +import java.util.concurrent.TimeUnit; import javax.inject.Named; import javax.inject.Singleton; @@ -44,13 +47,16 @@ import org.jclouds.ec2.compute.functions.RegionAndIdToImage; import org.jclouds.ec2.compute.functions.RunningInstanceToNodeMetadata; import org.jclouds.ec2.compute.internal.EC2TemplateBuilderImpl; import org.jclouds.ec2.compute.options.EC2TemplateOptions; +import org.jclouds.ec2.compute.predicates.SecurityGroupPresent; import org.jclouds.ec2.domain.InstanceState; import org.jclouds.ec2.domain.KeyPair; import org.jclouds.ec2.domain.RunningInstance; +import org.jclouds.predicates.RetryablePredicate; import org.jclouds.rest.RestContext; import org.jclouds.rest.internal.RestContextImpl; import com.google.common.base.Function; +import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -144,6 +150,14 @@ public class EC2ComputeServiceDependenciesModule extends AbstractModule { return CacheBuilder.newBuilder().build(in); } + @Provides + @Singleton + @Named("SECURITY") + protected Predicate securityGroupEventualConsistencyDelay(SecurityGroupPresent in, + @Named(PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT) long msDelay) { + return new RetryablePredicate(in, msDelay, 100l, TimeUnit.MILLISECONDS); + } + @Provides @Singleton // keys that we know about. diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java index 73d182776e..5fa6cc73a1 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeeded.java @@ -31,8 +31,10 @@ import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; import org.jclouds.ec2.domain.IpProtocol; import org.jclouds.ec2.domain.UserIdGroupPair; +import org.jclouds.ec2.services.SecurityGroupClient; import org.jclouds.logging.Logger; +import com.google.common.base.Predicate; import com.google.common.cache.CacheLoader; import com.google.common.collect.Iterables; @@ -45,16 +47,25 @@ public class CreateSecurityGroupIfNeeded extends CacheLoader securityGroupEventualConsistencyDelay; @Inject - public CreateSecurityGroupIfNeeded(EC2Client ec2Client) { - this.ec2Client = checkNotNull(ec2Client, "ec2Client"); + public CreateSecurityGroupIfNeeded(EC2Client ec2Client, + @Named("SECURITY") Predicate securityGroupEventualConsistencyDelay) { + this(checkNotNull(ec2Client, "ec2Client").getSecurityGroupServices(), securityGroupEventualConsistencyDelay); + } + + public CreateSecurityGroupIfNeeded(SecurityGroupClient securityClient, + @Named("SECURITY") Predicate securityGroupEventualConsistencyDelay) { + this.securityClient = checkNotNull(securityClient, "securityClient"); + this.securityGroupEventualConsistencyDelay = checkNotNull(securityGroupEventualConsistencyDelay, + "securityGroupEventualConsistencyDelay"); } @Override public String load(RegionAndName from) { - RegionNameAndIngressRules realFrom = RegionNameAndIngressRules.class.cast(from); + RegionNameAndIngressRules realFrom = RegionNameAndIngressRules.class.cast(from); createSecurityGroupInRegion(from.getRegion(), from.getName(), realFrom.getPorts()); return from.getName(); } @@ -64,7 +75,11 @@ public class CreateSecurityGroupIfNeeded extends CacheLoader> creating securityGroup region(%s) name(%s)", region, name); try { - ec2Client.getSecurityGroupServices().createSecurityGroupInRegion(region, name, name); + securityClient.createSecurityGroupInRegion(region, name, name); + boolean created = securityGroupEventualConsistencyDelay.apply(new RegionAndName(region, name)); + if (!created) + throw new RuntimeException(String.format("security group %s/%s is not available after creating", region, + name)); logger.debug("<< created securityGroup(%s)", name); for (int port : ports) { createIngressRuleForTCPPort(region, name, port); @@ -79,17 +94,14 @@ public class CreateSecurityGroupIfNeeded extends CacheLoader> authorizing securityGroup region(%s) name(%s) port(%s)", region, name, port); - ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, IpProtocol.TCP, port, - port, "0.0.0.0/0"); + securityClient.authorizeSecurityGroupIngressInRegion(region, name, IpProtocol.TCP, port, port, "0.0.0.0/0"); logger.debug("<< authorized securityGroup(%s)", name); } private void authorizeGroupToItself(String region, String name) { logger.debug(">> authorizing securityGroup region(%s) name(%s) permission to itself", region, name); - String myOwnerId = Iterables.get( - ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region, name), 0).getOwnerId(); - ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, - new UserIdGroupPair(myOwnerId, name)); + String myOwnerId = Iterables.get(securityClient.describeSecurityGroupsInRegion(region, name), 0).getOwnerId(); + securityClient.authorizeSecurityGroupIngressInRegion(region, name, new UserIdGroupPair(myOwnerId, name)); logger.debug("<< authorized securityGroup(%s)", name); } diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/SecurityGroupPresent.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/SecurityGroupPresent.java new file mode 100644 index 0000000000..8b38312c5a --- /dev/null +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/predicates/SecurityGroupPresent.java @@ -0,0 +1,70 @@ +/** + * 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.ec2.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.NoSuchElementException; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.ec2.EC2Client; +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.domain.SecurityGroup; +import org.jclouds.logging.Logger; +import org.jclouds.rest.ResourceNotFoundException; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class SecurityGroupPresent implements Predicate { + + private final EC2Client client; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + public SecurityGroupPresent(EC2Client client) { + this.client = checkNotNull(client, "client"); + } + + public boolean apply(RegionAndName securityGroup) { + logger.trace("looking for security group %s/%s", securityGroup.getRegion(), securityGroup.getName()); + try { + return refresh(securityGroup) != null; + } catch (ResourceNotFoundException e) { + return false; + } catch (NoSuchElementException e) { + return false; + } + } + + protected SecurityGroup refresh(RegionAndName securityGroup) { + return Iterables.getOnlyElement(client.getSecurityGroupServices().describeSecurityGroupsInRegion( + securityGroup.getRegion(), securityGroup.getName())); + } +} diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/UserIdGroupPair.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/UserIdGroupPair.java index 9e600a7a16..f055cd43f1 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/UserIdGroupPair.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/UserIdGroupPair.java @@ -37,6 +37,12 @@ public class UserIdGroupPair implements Comparable { } + @Override + public String toString() { + return "[userId=" + userId + ", groupName=" + groupName + "]"; + } + + /** * {@inheritDoc} */ diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java b/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java index f42afc1439..44a05464f0 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/reference/EC2Constants.java @@ -31,5 +31,10 @@ public interface EC2Constants { * the ami owners you wish to use in {@link ComputeService} */ public static final String PROPERTY_EC2_AMI_OWNERS = "jclouds.ec2.ami-owners"; + + /** + * Eventual consistency delay for retrieving a security group after it is created (in ms) + */ + public static final String PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT = "jclouds.ec2.timeout.securitygroup-present"; } diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeededTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeededTest.java new file mode 100644 index 0000000000..ceaea5e117 --- /dev/null +++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/functions/CreateSecurityGroupIfNeededTest.java @@ -0,0 +1,110 @@ +/** + * 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.ec2.compute.functions; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.createNiceMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import org.jclouds.ec2.compute.domain.RegionAndName; +import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules; +import org.jclouds.ec2.domain.IpProtocol; +import org.jclouds.ec2.domain.SecurityGroup; +import org.jclouds.ec2.domain.UserIdGroupPair; +import org.jclouds.ec2.services.SecurityGroupClient; +import org.testng.annotations.Test; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", singleThreaded = true, testName = "CreateSecurityGroupIfNeeded") +public class CreateSecurityGroupIfNeededTest { + + @SuppressWarnings("unchecked") + @Test + public void testWhenPort22AndToItselfAuthorizesIngressTwice() throws ExecutionException { + + SecurityGroupClient client = createMock(SecurityGroupClient.class); + Predicate tester = Predicates.alwaysTrue(); + + SecurityGroup group = createNiceMock(SecurityGroup.class); + Set groups = ImmutableSet. of(group); + + client.createSecurityGroupInRegion("region", "group", "group"); + client.authorizeSecurityGroupIngressInRegion("region", "group", IpProtocol.TCP, 22, 22, "0.0.0.0/0"); + expect(client.describeSecurityGroupsInRegion("region", "group")).andReturn(Set.class.cast(groups)); + expect(group.getOwnerId()).andReturn("ownerId"); + client.authorizeSecurityGroupIngressInRegion("region", "group", new UserIdGroupPair("ownerId", "group")); + + replay(client); + replay(group); + + CreateSecurityGroupIfNeeded function = new CreateSecurityGroupIfNeeded(client, tester); + + assertEquals("group", function.load(new RegionNameAndIngressRules("region", "group", new int[] { 22 }, true))); + + verify(client); + verify(group); + + } + + @Test + public void testIllegalStateExceptionCreatingGroupJustReturns() throws ExecutionException { + + SecurityGroupClient client = createMock(SecurityGroupClient.class); + Predicate tester = Predicates.alwaysTrue(); + + client.createSecurityGroupInRegion("region", "group", "group"); + expectLastCall().andThrow(new IllegalStateException()); + + replay(client); + + CreateSecurityGroupIfNeeded function = new CreateSecurityGroupIfNeeded(client, tester); + + assertEquals("group", function.load(new RegionNameAndIngressRules("region", "group", new int[] { 22 }, true))); + + verify(client); + + } + + @Test(expectedExceptions = RuntimeException.class) + public void testWhenEventualConsistencyExpiresIllegalStateException() throws ExecutionException { + + SecurityGroupClient client = createMock(SecurityGroupClient.class); + Predicate tester = Predicates.alwaysFalse(); + + client.createSecurityGroupInRegion("region", "group", "group"); + + replay(client); + + CreateSecurityGroupIfNeeded function = new CreateSecurityGroupIfNeeded(client, tester); + function.load(new RegionNameAndIngressRules("region", "group", new int[] { 22 }, true)); + } +}