Issue 220: added stub compute provider

This commit is contained in:
Adrian Cole 2010-06-01 04:13:27 -06:00
parent f701e44049
commit 14e16fbfa6
8 changed files with 941 additions and 52 deletions

View File

@ -32,5 +32,7 @@ bluelock.contextbuilder=org.jclouds.vcloud.bluelock.BlueLockVCloudContextBuilder
bluelock.propertiesbuilder=org.jclouds.vcloud.bluelock.BlueLockVCloudPropertiesBuilder bluelock.propertiesbuilder=org.jclouds.vcloud.bluelock.BlueLockVCloudPropertiesBuilder
gogrid.propertiesbuilder=org.jclouds.gogrid.GoGridPropertiesBuilder gogrid.propertiesbuilder=org.jclouds.gogrid.GoGridPropertiesBuilder
gogrid.contextbuilder=org.jclouds.gogrid.GoGridContextBuilder gogrid.contextbuilder=org.jclouds.gogrid.GoGridContextBuilder
stub.propertiesbuilder=org.jclouds.compute.stub.StubComputeServicePropertiesBuilder
stub.contextbuilder=org.jclouds.compute.stub.StubComputeServiceContextBuilder
# example of where to change your endpoint # example of where to change your endpoint
# bluelock.endpoint=https://express3.bluelock.com/api # bluelock.endpoint=https://express3.bluelock.com/api

View File

@ -29,3 +29,22 @@ list, Alan Dipert and MeikelBrandmeyer."
(deftest os-families-test (deftest os-families-test
(is (some #{"centos"} (map str (os-families))))) (is (some #{"centos"} (map str (os-families)))))
(defn clean-stub-fixture
"This should allow basic tests to easily be run with another service."
[service account key & options]
(fn [f]
(with-compute-service [(apply compute-service service account key options)]
(doseq [node (nodes)]
(destroy-node (.getId node)))
(f))))
(use-fixtures :each (clean-stub-fixture "stub" "" ""))
(deftest compute-service?-test
(is (compute-service? *compute*)))
(deftest as-compute-service-test
(is (compute-service? (compute-service "stub" "user" "password")))
(is (compute-service? (as-compute-service *compute*)))
(is (compute-service? (as-compute-service (compute-context *compute*)))))

View File

@ -80,7 +80,7 @@ import com.google.inject.Module;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "live", sequential = true, testName = "compute.ComputeServiceLiveTest") @Test(groups = { "integration", "live" }, sequential = true, testName = "compute.ComputeServiceLiveTest")
public abstract class BaseComputeServiceLiveTest { public abstract class BaseComputeServiceLiveTest {
@BeforeClass @BeforeClass
abstract public void setServiceDefaults(); abstract public void setServiceDefaults();
@ -98,13 +98,12 @@ public abstract class BaseComputeServiceLiveTest {
protected Template template; protected Template template;
protected Map<String, String> keyPair; protected Map<String, String> keyPair;
@BeforeGroups(groups = { "live" }) @BeforeGroups(groups = { "integration", "live" })
public void setupClient() throws InterruptedException, ExecutionException, TimeoutException, public void setupClient() throws InterruptedException, ExecutionException, TimeoutException,
IOException { IOException {
if (tag == null) if (tag == null)
tag = checkNotNull(service, "service"); tag = checkNotNull(service, "service");
user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user"); setupCredentials();
password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key");
String secretKeyFile; String secretKeyFile;
try { try {
secretKeyFile = checkNotNull(System.getProperty("jclouds.test.ssh.keyfile"), secretKeyFile = checkNotNull(System.getProperty("jclouds.test.ssh.keyfile"),
@ -115,17 +114,25 @@ public abstract class BaseComputeServiceLiveTest {
checkSecretKeyFile(secretKeyFile); checkSecretKeyFile(secretKeyFile);
String secret = Files.toString(new File(secretKeyFile), Charsets.UTF_8); String secret = Files.toString(new File(secretKeyFile), Charsets.UTF_8);
assert secret.startsWith("-----BEGIN RSA PRIVATE KEY-----") : "invalid key:\n" + secret; assert secret.startsWith("-----BEGIN RSA PRIVATE KEY-----") : "invalid key:\n" + secret;
keyPair = ImmutableMap.<String, String> of("private", secret, "public", Files.toString(
new File(secretKeyFile + ".pub"), Charsets.UTF_8));
initializeContextAndClient(); initializeContextAndClient();
Injector injector = Guice.createInjector(getSshModule()); Injector injector = createSshClientInjector();
sshFactory = injector.getInstance(SshClient.Factory.class); sshFactory = injector.getInstance(SshClient.Factory.class);
SocketOpen socketOpen = injector.getInstance(SocketOpen.class); SocketOpen socketOpen = injector.getInstance(SocketOpen.class);
socketTester = new RetryablePredicate<IPSocket>(socketOpen, 60, 1, TimeUnit.SECONDS); socketTester = new RetryablePredicate<IPSocket>(socketOpen, 60, 1, TimeUnit.SECONDS);
injector.injectMembers(socketOpen); // add logger injector.injectMembers(socketOpen); // add logger
// keyPair = sshFactory.generateRSAKeyPair("", ""); // keyPair = sshFactory.generateRSAKeyPair("", "");
keyPair = ImmutableMap.<String, String> of("private", secret, "public", Files.toString( }
new File(secretKeyFile + ".pub"), Charsets.UTF_8));
protected void setupCredentials() {
user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key");
}
protected Injector createSshClientInjector() {
return Guice.createInjector(getSshModule());
} }
private void initializeContextAndClient() throws IOException { private void initializeContextAndClient() throws IOException {
@ -153,7 +160,7 @@ public abstract class BaseComputeServiceLiveTest {
ImmutableSet.<Module> of(new Log4JLoggingModule())).close(); ImmutableSet.<Module> of(new Log4JLoggingModule())).close();
} }
@Test(enabled = true, dependsOnMethods = "testCorrectAuthException") @Test(enabled = true)
public void testImagesCache() throws Exception { public void testImagesCache() throws Exception {
client.listImages(); client.listImages();
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
@ -162,6 +169,42 @@ public abstract class BaseComputeServiceLiveTest {
assert duration < 1000 : String.format("%dms to get images", duration); assert duration < 1000 : String.format("%dms to get images", duration);
} }
// since surefire and eclipse don't otherwise guarantee the order, we are
// starting this one alphabetically before create2nodes..
@Test(enabled = true, dependsOnMethods = "testImagesCache")
public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception {
String tag = this.tag + "run";
try {
client.destroyNodesMatching(NodePredicates.withTag(tag));
} catch (Exception e) {
}
TemplateOptions options = client.templateOptions().blockOnPort(22, 120);
try {
Set<? extends NodeMetadata> nodes = client.runNodesWithTag(tag, 1, options);
Credentials good = nodes.iterator().next().getCredentials();
assert good.account != null;
assert good.key != null;
Image image = Iterables.get(nodes, 0).getImage();
try {
Map<? extends NodeMetadata, ExecResponse> responses = runScriptWithCreds(tag, image
.getOsFamily(), new Credentials(good.account, "romeo"));
assert false : "shouldn't pass with a bad password\n" + responses;
} catch (RunScriptOnNodesException e) {
assert Throwables.getRootCause(e).getMessage().contains("Auth fail") : e;
}
runScriptWithCreds(tag, image.getOsFamily(), good);
checkNodes(nodes, tag);
} finally {
client.destroyNodesMatching(NodePredicates.withTag(tag));
}
}
@Test(enabled = true, dependsOnMethods = "testImagesCache") @Test(enabled = true, dependsOnMethods = "testImagesCache")
public void testTemplateMatch() throws Exception { public void testTemplateMatch() throws Exception {
template = buildTemplate(client.templateBuilder()); template = buildTemplate(client.templateBuilder());
@ -225,40 +268,6 @@ public abstract class BaseComputeServiceLiveTest {
assertEquals(node.getImage(), template.getImage()); assertEquals(node.getImage(), template.getImage());
} }
@Test(enabled = true, dependsOnMethods = "testCorrectAuthException")
public void testScriptExecutionAfterBootWithBasicTemplate() throws Exception {
String tag = this.tag + "run";
try {
client.destroyNodesMatching(NodePredicates.withTag(tag));
} catch (Exception e) {
}
TemplateOptions options = client.templateOptions().blockOnPort(22, 120);
try {
Set<? extends NodeMetadata> nodes = client.runNodesWithTag(tag, 1, options);
Credentials good = nodes.iterator().next().getCredentials();
assert good.account != null;
assert good.key != null;
Image image = Iterables.get(nodes, 0).getImage();
try {
Map<? extends NodeMetadata, ExecResponse> responses = runScriptWithCreds(tag, image
.getOsFamily(), new Credentials(good.account, "romeo"));
assert false : "shouldn't pass with a bad password\n" + responses;
} catch (RunScriptOnNodesException e) {
assert Throwables.getRootCause(e).getMessage().contains("Auth fail") : e;
}
runScriptWithCreds(tag, image.getOsFamily(), good);
checkNodes(nodes, tag);
} finally {
client.destroyNodesMatching(NodePredicates.withTag(tag));
}
}
protected Map<? extends NodeMetadata, ExecResponse> runScriptWithCreds(final String tag, protected Map<? extends NodeMetadata, ExecResponse> runScriptWithCreds(final String tag,
OsFamily osFamily, Credentials creds) throws RunScriptOnNodesException { OsFamily osFamily, Credentials creds) throws RunScriptOnNodesException {
try { try {

View File

@ -0,0 +1,310 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.compute;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.reportMatcher;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.concurrent.ConcurrentMap;
import org.easymock.IArgumentMatcher;
import org.jclouds.compute.domain.Architecture;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.stub.config.StubComputeServiceContextModule.StubNodeMetadata;
import org.jclouds.net.IPSocket;
import org.jclouds.predicates.SocketOpen;
import org.jclouds.rest.RestContext;
import org.jclouds.scriptbuilder.InitBuilder;
import org.jclouds.ssh.ExecResponse;
import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.SshException;
import org.jclouds.util.Utils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
/**
*
*
* @author Adrian Cole
*/
@Test(groups = "live", enabled = true, sequential = true, testName = "stub.StubComputeServiceIntegrationTest")
public class StubComputeServiceIntegrationTest extends BaseComputeServiceLiveTest {
private static final ExecResponse EXEC_GOOD = new ExecResponse("", "", 0);
private static final ExecResponse EXEC_BAD = new ExecResponse("", "", 1);
@BeforeClass
@Override
public void setServiceDefaults() {
service = "stub";
}
@Override
public void testCorrectAuthException() throws Exception {
}
@Test
public void testTemplateBuilder() {
Template defaultTemplate = client.templateBuilder().build();
assertEquals(defaultTemplate.getImage().getArchitecture(), Architecture.X86_64);
assertEquals(defaultTemplate.getImage().getOsFamily(), OsFamily.UBUNTU);
assertEquals(defaultTemplate.getLocation().getId(), "memory");
assertEquals(defaultTemplate.getSize().getCores(), 4.0d);
}
@Override
protected Injector createSshClientInjector() {
return Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
SshClient.Factory factory = createMock(SshClient.Factory.class);
SocketOpen open = createMock(SocketOpen.class);
SshClient client1 = createMock(SshClient.class);
SshClient client2 = createMock(SshClient.class);
SshClient client3 = createMock(SshClient.class);
SshClient client4 = createMock(SshClient.class);
expect(open.apply(new IPSocket("144.175.1.1", 22))).andReturn(true);
expect(open.apply(new IPSocket("144.175.1.2", 22))).andReturn(true);
expect(open.apply(new IPSocket("144.175.1.3", 22))).andReturn(true);
expect(open.apply(new IPSocket("144.175.1.4", 22))).andReturn(true);
expect(
factory.create(eq(new IPSocket("144.175.1.1", 22)), eq("root"), aryEq(keyPair
.get("private").getBytes()))).andReturn(client1).atLeastOnce();
expect(
factory.create(eq(new IPSocket("144.175.1.2", 22)), eq("root"), aryEq(keyPair
.get("private").getBytes()))).andReturn(client2).atLeastOnce();
expect(
factory.create(eq(new IPSocket("144.175.1.3", 22)), eq("root"), aryEq(keyPair
.get("private").getBytes()))).andReturn(client3).atLeastOnce();
expect(
factory.create(eq(new IPSocket("144.175.1.4", 22)), eq("root"), aryEq(keyPair
.get("private").getBytes()))).andReturn(client4).atLeastOnce();
helloAndJava(client1);
helloAndJava(client2);
helloAndJava(client3);
helloAndJava(client4);
replay(open);
replay(factory);
replay(client1);
replay(client2);
replay(client3);
replay(client4);
bind(SshClient.Factory.class).toInstance(factory);
bind(SocketOpen.class).toInstance(open);
}
private void helloAndJava(SshClient client) {
client.connect();
expect(client.exec("echo hello")).andReturn(new ExecResponse("hello", "", 0));
expect(client.exec("java -version")).andReturn(new ExecResponse("", "OpenJDK", 0));
client.disconnect();
}
});
}
@Override
protected Module getSshModule() {
return new AbstractModule() {
@Override
protected void configure() {
SshClient.Factory factory = createMock(SshClient.Factory.class);
SshClient client1 = createMock(SshClient.class);
SshClient client2 = createMock(SshClient.class);
SshClient client3 = createMock(SshClient.class);
SshClient client4 = createMock(SshClient.class);
expect(factory.create(new IPSocket("144.175.1.1", 22), "root", "romeo")).andThrow(
new SshException("Auth fail"));
expect(factory.create(new IPSocket("144.175.1.1", 22), "root", "password1")).andReturn(
client1).atLeastOnce();
client1.connect();
runScript(client1, "computeserv", 1);
client1.disconnect();
expect(factory.create(new IPSocket("144.175.1.2", 22), "root", "password2")).andReturn(
client2).atLeastOnce();
expect(factory.create(new IPSocket("144.175.1.3", 22), "root", "password3")).andReturn(
client3).atLeastOnce();
expect(factory.create(new IPSocket("144.175.1.4", 22), "root", "password4")).andReturn(
client4).atLeastOnce();
runScriptAndInstallSsh(client2, "runscript", 2);
runScriptAndInstallSsh(client3, "runscript", 3);
runScriptAndInstallSsh(client4, "runscript", 4);
replay(factory);
replay(client1);
replay(client2);
replay(client3);
replay(client4);
bind(SshClient.Factory.class).toInstance(factory);
}
private void runScriptAndInstallSsh(SshClient client, String scriptName, int nodeId) {
client.connect();
runScript(client, scriptName, nodeId);
expect(client.exec("mkdir .ssh")).andReturn(EXEC_GOOD);
expect(client.exec("cat .ssh/id_rsa.pub >> .ssh/authorized_keys")).andReturn(EXEC_GOOD);
expect(client.exec("chmod 600 .ssh/authorized_keys")).andReturn(EXEC_GOOD);
client.put(eq(".ssh/id_rsa.pub"), isEq(keyPair.get("public")));
expect(client.exec("mkdir .ssh")).andReturn(EXEC_GOOD);
client.put(eq(".ssh/id_rsa"), isEq(keyPair.get("private")));
expect(client.exec("chmod 600 .ssh/id_rsa")).andReturn(EXEC_GOOD);
client.disconnect();
client.disconnect();
}
private void runScript(SshClient client, String scriptName, int nodeId) {
client.put(eq("" + scriptName + ""), isEq(initScript(scriptName,
buildScript(OsFamily.UBUNTU))));
expect(client.exec("chmod 755 " + scriptName + "")).andReturn(EXEC_GOOD);
expect(client.getUsername()).andReturn("root").atLeastOnce();
expect(client.getHostAddress()).andReturn(nodeId + "").atLeastOnce();
expect(client.exec("./" + scriptName + " init")).andReturn(EXEC_GOOD);
expect(client.exec("./" + scriptName + " start")).andReturn(EXEC_GOOD);
expect(client.exec("./" + scriptName + " status")).andReturn(EXEC_GOOD);
// next status says the script is done, since not found.
expect(client.exec("./" + scriptName + " status")).andReturn(EXEC_BAD);
expect(client.exec("./" + scriptName + " tail")).andReturn(EXEC_GOOD);
expect(client.exec("./" + scriptName + " tailerr")).andReturn(EXEC_GOOD);
}
};
}
@Override
protected void setupCredentials() {
user = "stub";
password = "stub";
}
public static String initScript(String scriptName, String script) {
return new InitBuilder(scriptName, "/tmp/" + scriptName, "/tmp/" + scriptName, ImmutableMap
.<String, String> of(), Iterables.toArray(Splitter.on("\n").split(
new String(checkNotNull(script, "script"))), String.class))
.build(org.jclouds.scriptbuilder.domain.OsFamily.UNIX);
}
public static InputStream isEq(String value) {
reportMatcher(new InputStreamEquals(value));
return null;
}
public void testAssignability() throws Exception {
@SuppressWarnings("unused")
RestContext<ConcurrentMap<Integer, StubNodeMetadata>, ConcurrentMap<Integer, StubNodeMetadata>> stubContext = new ComputeServiceContextFactory()
.createContext(service, user, password).getProviderSpecificContext();
}
private static class InputStreamEquals implements IArgumentMatcher, Serializable {
private static final long serialVersionUID = 583055160049982067L;
private final Object expected;
public InputStreamEquals(Object expected) {
this.expected = expected;
}
public boolean matches(Object actual) {
if (this.expected == null) {
return actual == null;
}
try {
String real = Utils.toStringAndClose((InputStream) actual);
if (!expected.equals(real)) {
System.err.println(real);
return false;
}
return true;
} catch (IOException e) {
Throwables.propagate(e);
return false;
}
}
public void appendTo(StringBuffer buffer) {
appendQuoting(buffer);
buffer.append(expected);
appendQuoting(buffer);
}
private void appendQuoting(StringBuffer buffer) {
if (expected instanceof String) {
buffer.append("\"");
} else if (expected instanceof Character) {
buffer.append("'");
}
}
@Override
public boolean equals(Object o) {
if (o == null || !this.getClass().equals(o.getClass()))
return false;
InputStreamEquals other = (InputStreamEquals) o;
return this.expected == null && other.expected == null || this.expected != null
&& this.expected.equals(other.expected);
}
@Override
public int hashCode() {
throw new UnsupportedOperationException("hashCode() is not supported");
}
}
}

View File

@ -0,0 +1,55 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.compute.stub;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;
import org.jclouds.compute.ComputeServiceContextBuilder;
import org.jclouds.compute.stub.config.StubComputeServiceContextModule;
import org.jclouds.compute.stub.config.StubComputeServiceContextModule.StubNodeMetadata;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
/**
*
* @author Adrian Cole
*/
public class StubComputeServiceContextBuilder
extends
ComputeServiceContextBuilder<ConcurrentMap<Integer, StubNodeMetadata>, ConcurrentMap<Integer, StubNodeMetadata>> {
public StubComputeServiceContextBuilder(String providerName, Properties props) {
super(providerName, new TypeLiteral<ConcurrentMap<Integer, StubNodeMetadata>>() {
}, new TypeLiteral<ConcurrentMap<Integer, StubNodeMetadata>>() {
}, props);
}
@Override
protected void addContextModule(String providerName, List<Module> modules) {
modules.add(new StubComputeServiceContextModule(providerName));
}
@Override
protected void addClientModule(List<Module> modules) {
}
}

View File

@ -0,0 +1,49 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.compute.stub;
import java.net.URI;
import java.util.Properties;
import org.jclouds.PropertiesBuilder;
/**
* Builds properties used in stub compute services
*
* @author Adrian Cole
*/
public class StubComputeServicePropertiesBuilder extends PropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
return properties;
}
public StubComputeServicePropertiesBuilder(Properties properties) {
super(properties);
}
public StubComputeServicePropertiesBuilder withCredentials(String id, String secret) {
return this;
}
public StubComputeServicePropertiesBuilder withEndpoint(URI endpoint) {
return this;
}
}

View File

@ -0,0 +1,444 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.jclouds.compute.stub.config;
import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.compute.domain.OsFamily.UBUNTU;
import static org.jclouds.compute.predicates.ImagePredicates.architectureIn;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.LoadBalancerService;
import org.jclouds.compute.domain.Architecture;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeState;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Size;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.internal.ImageImpl;
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
import org.jclouds.compute.internal.ComputeServiceContextImpl;
import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.compute.predicates.ScriptStatusReturnsZero;
import org.jclouds.compute.predicates.ScriptStatusReturnsZero.CommandUsingClient;
import org.jclouds.compute.strategy.AddNodeWithTagStrategy;
import org.jclouds.compute.strategy.DestroyNodeStrategy;
import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
import org.jclouds.compute.strategy.ListNodesStrategy;
import org.jclouds.compute.strategy.RebootNodeStrategy;
import org.jclouds.concurrent.SingleThreaded;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope;
import org.jclouds.domain.internal.LocationImpl;
import org.jclouds.lifecycle.Closer;
import org.jclouds.net.IPSocket;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.predicates.SocketOpen;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.internal.RestContextImpl;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.util.Providers;
/**
*
* @author Adrian Cole
*/
@SingleThreaded
public class StubComputeServiceContextModule extends AbstractModule {
// STUB STUFF STATIC SO MULTIPLE CONTEXTS CAN SEE IT
private static final AtomicInteger nodeIds = new AtomicInteger(0);
private static final ConcurrentMap<Integer, StubNodeMetadata> nodes = new ConcurrentHashMap<Integer, StubNodeMetadata>();
@Provides
@Singleton
ConcurrentMap<Integer, StubNodeMetadata> provideNodes() {
return nodes;
}
@Provides
@Named("NODE_ID")
Integer provideNodeId() {
return nodeIds.incrementAndGet();
}
@Singleton
@Provides
@Named("PUBLIC_IP_PREFIX")
String publicIpPrefix() {
return "144.175.1.";
}
@Singleton
@Provides
@Named("PRIVATE_IP_PREFIX")
String privateIpPrefix() {
return "10.1.1.";
}
@Singleton
@Provides
@Named("PASSWORD_PREFIX")
String passwordPrefix() {
return "password";
}
@Singleton
@Provides
SocketOpen socketOpen(StubSocketOpen in) {
return in;
}
@Singleton
public static class StubSocketOpen implements SocketOpen {
private final ConcurrentMap<Integer, StubNodeMetadata> nodes;
private final String publicIpPrefix;
@Inject
public StubSocketOpen(ConcurrentMap<Integer, StubNodeMetadata> nodes,
@Named("PUBLIC_IP_PREFIX") String publicIpPrefix) {
this.nodes = nodes;
this.publicIpPrefix = publicIpPrefix;
}
@Override
public boolean apply(IPSocket input) {
if (input.getAddress().indexOf(publicIpPrefix) == -1)
return false;
String id = input.getAddress().replace(publicIpPrefix, "");
int intId = Integer.parseInt(id);
StubNodeMetadata node = nodes.get(intId);
return node != null && node.getState() == NodeState.RUNNING;
}
}
@Provides
@Singleton
RestContext<ConcurrentMap<Integer, StubNodeMetadata>, ConcurrentMap<Integer, StubNodeMetadata>> provideRestContext(
Closer closer) {
return new RestContextImpl<ConcurrentMap<Integer, StubNodeMetadata>, ConcurrentMap<Integer, StubNodeMetadata>>(
closer, nodes, nodes, URI.create("http://stub"), System.getProperty("user.name"));
}
// NORMAL STUFF
private final String providerName;
public StubComputeServiceContextModule(String providerName) {
this.providerName = providerName;
}
@Override
protected void configure() {
bind(new TypeLiteral<ComputeServiceContext>() {
})
.to(
new TypeLiteral<ComputeServiceContextImpl<ConcurrentMap<Integer, StubNodeMetadata>, ConcurrentMap<Integer, StubNodeMetadata>>>() {
}).in(Scopes.SINGLETON);
bind(AddNodeWithTagStrategy.class).to(StubAddNodeWithTagStrategy.class);
bind(ListNodesStrategy.class).to(StubListNodesStrategy.class);
bind(GetNodeMetadataStrategy.class).to(StubGetNodeMetadataStrategy.class);
bind(RebootNodeStrategy.class).to(StubRebootNodeStrategy.class);
bind(DestroyNodeStrategy.class).to(StubDestroyNodeStrategy.class);
bind(LoadBalancerService.class).toProvider(Providers.<LoadBalancerService> of(null));
}
@Provides
@Singleton
protected Predicate<IPSocket> socketTester(SocketOpen open) {
return new RetryablePredicate<IPSocket>(open, 130, 10, TimeUnit.MILLISECONDS);
}
@Provides
@Named("DEFAULT")
protected TemplateBuilder provideTemplate(TemplateBuilder template) {
return template.osFamily(UBUNTU);
}
public static class StubNodeMetadata extends NodeMetadataImpl {
/** The serialVersionUID */
private static final long serialVersionUID = 5538798859671465494L;
private NodeState state;
private final ExecutorService service;
public StubNodeMetadata(String providerId, String name, String id, Location location,
URI uri, Map<String, String> userMetadata, String tag, Image image, NodeState state,
Iterable<String> publicAddresses, Iterable<String> privateAddresses,
Map<String, String> extra, Credentials credentials, ExecutorService service) {
super(providerId, name, id, location, uri, userMetadata, tag, image, state,
publicAddresses, privateAddresses, extra, credentials);
this.setState(state, 0);
this.service = service;
}
public void setState(final NodeState state, final long millis) {
if (millis == 0l)
this.state = state;
else
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Throwables.propagate(e);
}
StubNodeMetadata.this.state = state;
}
});
}
@Override
public NodeState getState() {
return state;
}
}
@Singleton
public static class StubAddNodeWithTagStrategy implements AddNodeWithTagStrategy {
private final Location location;
private final ExecutorService service;
private final ConcurrentMap<Integer, StubNodeMetadata> nodes;
private final Provider<Integer> idProvider;
private final String publicIpPrefix;
private final String privateIpPrefix;
private final String passwordPrefix;
@Inject
public StubAddNodeWithTagStrategy(ConcurrentMap<Integer, StubNodeMetadata> nodes,
Location location, @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service,
@Named("NODE_ID") Provider<Integer> idProvider,
@Named("PUBLIC_IP_PREFIX") String publicIpPrefix,
@Named("PRIVATE_IP_PREFIX") String privateIpPrefix,
@Named("PASSWORD_PREFIX") String passwordPrefix) {
this.nodes = nodes;
this.location = location;
this.service = service;
this.idProvider = idProvider;
this.publicIpPrefix = publicIpPrefix;
this.privateIpPrefix = privateIpPrefix;
this.passwordPrefix = passwordPrefix;
}
@Override
public NodeMetadata execute(String tag, String name, Template template) {
checkArgument(location.equals(template.getLocation()), "invalid location: "
+ template.getLocation());
int id = idProvider.get();
StubNodeMetadata node = new StubNodeMetadata(id + "", name, id + "", location, null,
ImmutableMap.<String, String> of(), tag, template.getImage(), NodeState.PENDING,
ImmutableSet.<String> of(publicIpPrefix + id), ImmutableSet
.<String> of(privateIpPrefix + id), ImmutableMap.<String, String> of(),
new Credentials("root", passwordPrefix + id), service);
node.setState(NodeState.RUNNING, 100);
nodes.put(id, node);
return node;
}
}
@Singleton
public static class StubGetNodeMetadataStrategy implements GetNodeMetadataStrategy {
private final ConcurrentMap<Integer, StubNodeMetadata> nodes;
@Inject
protected StubGetNodeMetadataStrategy(ConcurrentMap<Integer, StubNodeMetadata> nodes) {
this.nodes = nodes;
}
@Override
public NodeMetadata execute(String id) {
return nodes.get(Integer.parseInt(id));
}
}
@Singleton
public static class StubListNodesStrategy implements ListNodesStrategy {
private final ConcurrentMap<Integer, StubNodeMetadata> nodes;
@Inject
protected StubListNodesStrategy(ConcurrentMap<Integer, StubNodeMetadata> nodes) {
this.nodes = nodes;
}
@Override
public Iterable<? extends ComputeMetadata> list() {
return listDetailsOnNodesMatching(NodePredicates.all());
}
@Override
public Iterable<? extends NodeMetadata> listDetailsOnNodesMatching(
Predicate<ComputeMetadata> filter) {
return Iterables.filter(nodes.values(), filter);
}
}
@Singleton
public static class StubRebootNodeStrategy implements RebootNodeStrategy {
private final ConcurrentMap<Integer, StubNodeMetadata> nodes;
@Inject
protected StubRebootNodeStrategy(ConcurrentMap<Integer, StubNodeMetadata> nodes) {
this.nodes = nodes;
}
@Override
public boolean execute(String id) {
StubNodeMetadata node = nodes.get(Integer.parseInt(id));
if (node == null)
throw new ResourceNotFoundException("node not found: " + id);
node.setState(NodeState.PENDING, 0);
node.setState(NodeState.RUNNING, 50);
return true;
}
}
@Singleton
public static class StubDestroyNodeStrategy implements DestroyNodeStrategy {
private final ConcurrentMap<Integer, StubNodeMetadata> nodes;
private final ExecutorService service;
@Inject
protected StubDestroyNodeStrategy(ConcurrentMap<Integer, StubNodeMetadata> nodes,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService service) {
this.nodes = nodes;
this.service = service;
}
@Override
public boolean execute(String id) {
final int nodeId = Integer.parseInt(id);
StubNodeMetadata node = nodes.get(nodeId);
if (node == null)
return true;
node.setState(NodeState.PENDING, 0);
node.setState(NodeState.TERMINATED, 50);
service.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Throwables.propagate(e);
} finally {
nodes.remove(nodeId);
}
}
});
return true;
}
}
@Provides
@Named("NAMING_CONVENTION")
@Singleton
protected String provideNamingConvention() {
return "%s-%s";
}
@Provides
@Singleton
@Named("NOT_RUNNING")
protected Predicate<CommandUsingClient> runScriptRunning(ScriptStatusReturnsZero stateRunning) {
return new RetryablePredicate<CommandUsingClient>(Predicates.not(stateRunning), 600, 3,
TimeUnit.SECONDS);
}
@Provides
@Singleton
protected Set<? extends Size> provideSizes() {
return ImmutableSet.of(new StubSize("small", 1, 1740, 160, ImmutableSet
.of(Architecture.X86_32)), new StubSize("medium", 4, 7680, 850, ImmutableSet
.of(Architecture.X86_64)), new StubSize("large", 8, 15360, 1690, ImmutableSet
.of(Architecture.X86_64)));
}
private static class StubSize extends org.jclouds.compute.domain.internal.SizeImpl {
/** The serialVersionUID */
private static final long serialVersionUID = -1842135761654973637L;
StubSize(String type, int cores, int ram, int disk,
Iterable<Architecture> supportedArchitectures) {
super(type, type, type, null, null, ImmutableMap.<String, String> of(), cores, ram, disk,
architectureIn(supportedArchitectures));
}
}
@Provides
@Singleton
protected Set<? extends Image> provideImages(Location defaultLocation) {
return ImmutableSet.of(new ImageImpl("1", OsFamily.UBUNTU.name(), "1", defaultLocation, null,
ImmutableMap.<String, String> of(), "stub ubuntu 32", "", OsFamily.UBUNTU,
"ubuntu 64", Architecture.X86_64, new Credentials("root", null)), new ImageImpl("2",
OsFamily.UBUNTU.name(), "2", defaultLocation, null, ImmutableMap
.<String, String> of(), "stub ubuntu 64", "", OsFamily.UBUNTU, "ubuntu 64",
Architecture.X86_64, new Credentials("root", null)), new ImageImpl("3",
OsFamily.CENTOS.name(), "3", defaultLocation, null, ImmutableMap
.<String, String> of(), "stub centos 64", "", OsFamily.CENTOS, "centos 64",
Architecture.X86_64, new Credentials("root", null)));
}
@Provides
@Singleton
Location getLocation() {
Location provider = new LocationImpl(LocationScope.PROVIDER, providerName, providerName, null);
return new LocationImpl(LocationScope.ZONE, "memory", "memory", provider);
}
@Provides
@Singleton
Set<? extends Location> provideLocations(Location location) {
return ImmutableSet.of(location);
}
}

View File

@ -100,8 +100,8 @@
<url>http://clojars.org/repo</url> <url>http://clojars.org/repo</url>
</repository> </repository>
<repository> <repository>
<id>ning.http.client</id> <id>sonatype-github-releases</id>
<url>http://oss.sonatype.org/service/local/repositories/snapshots/content</url> <url>http://oss.sonatype.org/content/repositories/github-releases</url>
</repository> </repository>
</repositories> </repositories>
@ -284,7 +284,8 @@
<plugin> <plugin>
<groupId>com.theoryinpractise</groupId> <groupId>com.theoryinpractise</groupId>
<artifactId>clojure-maven-plugin</artifactId> <artifactId>clojure-maven-plugin</artifactId>
<version>1.3.3-SNAPSHOT</version> <!-- snapshot repo is blank, so reverted to 1.3.2 -->
<version>1.3.2</version>
<executions> <executions>
<execution> <execution>
<id>compile-clojure</id> <id>compile-clojure</id>