mirror of https://github.com/apache/jclouds.git
Issue 220: added stub compute provider
This commit is contained in:
parent
f701e44049
commit
14e16fbfa6
|
@ -32,5 +32,7 @@ bluelock.contextbuilder=org.jclouds.vcloud.bluelock.BlueLockVCloudContextBuilder
|
|||
bluelock.propertiesbuilder=org.jclouds.vcloud.bluelock.BlueLockVCloudPropertiesBuilder
|
||||
gogrid.propertiesbuilder=org.jclouds.gogrid.GoGridPropertiesBuilder
|
||||
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
|
||||
# bluelock.endpoint=https://express3.bluelock.com/api
|
||||
|
|
|
@ -29,3 +29,22 @@ list, Alan Dipert and MeikelBrandmeyer."
|
|||
(deftest os-families-test
|
||||
(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*)))))
|
|
@ -80,7 +80,7 @@ import com.google.inject.Module;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "live", sequential = true, testName = "compute.ComputeServiceLiveTest")
|
||||
@Test(groups = { "integration", "live" }, sequential = true, testName = "compute.ComputeServiceLiveTest")
|
||||
public abstract class BaseComputeServiceLiveTest {
|
||||
@BeforeClass
|
||||
abstract public void setServiceDefaults();
|
||||
|
@ -98,13 +98,12 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
protected Template template;
|
||||
protected Map<String, String> keyPair;
|
||||
|
||||
@BeforeGroups(groups = { "live" })
|
||||
@BeforeGroups(groups = { "integration", "live" })
|
||||
public void setupClient() throws InterruptedException, ExecutionException, TimeoutException,
|
||||
IOException {
|
||||
if (tag == null)
|
||||
tag = checkNotNull(service, "service");
|
||||
user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user");
|
||||
password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key");
|
||||
setupCredentials();
|
||||
String secretKeyFile;
|
||||
try {
|
||||
secretKeyFile = checkNotNull(System.getProperty("jclouds.test.ssh.keyfile"),
|
||||
|
@ -115,17 +114,25 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
checkSecretKeyFile(secretKeyFile);
|
||||
String secret = Files.toString(new File(secretKeyFile), Charsets.UTF_8);
|
||||
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();
|
||||
|
||||
Injector injector = Guice.createInjector(getSshModule());
|
||||
Injector injector = createSshClientInjector();
|
||||
sshFactory = injector.getInstance(SshClient.Factory.class);
|
||||
SocketOpen socketOpen = injector.getInstance(SocketOpen.class);
|
||||
socketTester = new RetryablePredicate<IPSocket>(socketOpen, 60, 1, TimeUnit.SECONDS);
|
||||
injector.injectMembers(socketOpen); // add logger
|
||||
// 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 {
|
||||
|
@ -153,7 +160,7 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
ImmutableSet.<Module> of(new Log4JLoggingModule())).close();
|
||||
}
|
||||
|
||||
@Test(enabled = true, dependsOnMethods = "testCorrectAuthException")
|
||||
@Test(enabled = true)
|
||||
public void testImagesCache() throws Exception {
|
||||
client.listImages();
|
||||
long time = System.currentTimeMillis();
|
||||
|
@ -162,6 +169,42 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
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")
|
||||
public void testTemplateMatch() throws Exception {
|
||||
template = buildTemplate(client.templateBuilder());
|
||||
|
@ -225,40 +268,6 @@ public abstract class BaseComputeServiceLiveTest {
|
|||
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,
|
||||
OsFamily osFamily, Credentials creds) throws RunScriptOnNodesException {
|
||||
try {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -100,8 +100,8 @@
|
|||
<url>http://clojars.org/repo</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>ning.http.client</id>
|
||||
<url>http://oss.sonatype.org/service/local/repositories/snapshots/content</url>
|
||||
<id>sonatype-github-releases</id>
|
||||
<url>http://oss.sonatype.org/content/repositories/github-releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
@ -284,7 +284,8 @@
|
|||
<plugin>
|
||||
<groupId>com.theoryinpractise</groupId>
|
||||
<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>
|
||||
<execution>
|
||||
<id>compile-clojure</id>
|
||||
|
|
Loading…
Reference in New Issue