Merge pull request #688 from dralves/nodepool

Nodepool
This commit is contained in:
Adrian Cole 2012-06-25 22:06:02 -07:00
commit 4171eb9d2c
26 changed files with 1537 additions and 872 deletions

View File

@ -1,32 +1,83 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jclouds-project</artifactId>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-project</artifactId>
<version>1.5.0-SNAPSHOT</version>
<relativePath>../../project/pom.xml</relativePath>
</parent>
<groupId>org.jclouds.labs</groupId>
<artifactId>nodepool</artifactId>
<name>jclouds nodepool</name>
<name>jcloud nodepool api</name>
<description>jclouds components to access an implementation of Joyent SDC</description>
<packaging>bundle</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jsch.version>0.1.46</jsch.version>
<test.aws-ec2.identity>${test.aws.identity}</test.aws-ec2.identity>
<test.aws-ec2.credential>${test.aws.credential}</test.aws-ec2.credential>
<jclouds.nodepool.backend-provider>byon</jclouds.nodepool.backend-provider>
<jclouds.nodepool.backend-modules>org.jclouds.logging.slf4j.config.SLF4JLoggingModule,org.jclouds.sshj.config.SshjSshClientModule</jclouds.nodepool.backend-modules>
<test.nodepool.endpoint>FIXME_ENDPOINT</test.nodepool.endpoint>
<test.nodepool.api-version></test.nodepool.api-version>
<test.nodepool.build-version></test.nodepool.build-version>
<test.nodepool.identity>FIXME_IDENTITY</test.nodepool.identity>
<test.nodepool.credential>FIXME_CREDENTIALS</test.nodepool.credential>
<jclouds.osgi.export>org.jclouds.nodepool*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
<jclouds.osgi.import>
org.jclouds.rest.internal;version="${project.version}",
org.jclouds*;version="${project.version}",
*
</jclouds.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-blobstore</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds.api</groupId>
<artifactId>filesystem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds.api</groupId>
<artifactId>byon</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-core</artifactId>
@ -34,28 +85,9 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds.provider</groupId>
<artifactId>aws-ec2</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-enterprise</artifactId>
<artifactId>jclouds-slf4j</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
@ -66,21 +98,45 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jclouds</groupId>
<artifactId>jclouds-scriptbuilder</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jclouds.driver</groupId>
<artifactId>jclouds-jsch</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>live</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<test.nodepool.endpoint>${test.nodepool.endpoint}</test.nodepool.endpoint>
<test.nodepool.api-version>${test.nodepool.api-version}</test.nodepool.api-version>
<test.nodepool.build-version>${test.nodepool.build-version}</test.nodepool.build-version>
<test.nodepool.identity>${test.nodepool.identity}</test.nodepool.identity>
<test.nodepool.credential>${test.nodepool.credential}</test.nodepool.credential>
<jclouds.nodepool.backend-provider>${jclouds.nodepool.backend-provider}</jclouds.nodepool.backend-provider>
<jclouds.nodepool.backend-modules>${jclouds.nodepool.backend-modules}</jclouds.nodepool.backend-modules>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,42 @@
/*
* 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.nodepool;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
/**
* Designates that this Resource qualifies an object to the back-end of the pool
*
* @author Adrian Cole
*/
@Target( { ANNOTATION_TYPE, FIELD, METHOD, PARAMETER })
@Retention(RUNTIME)
@Qualifier
public @interface Backend {
}

View File

@ -18,18 +18,26 @@
*/
package org.jclouds.nodepool;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_GROUP_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MAX_SIZE_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MIN_SIZE_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.REMOVE_DESTROYED_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_GROUP;
import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_MODULES;
import static org.jclouds.nodepool.config.NodePoolProperties.MAX_SIZE;
import static org.jclouds.nodepool.config.NodePoolProperties.METADATA_CONTAINER;
import static org.jclouds.nodepool.config.NodePoolProperties.MIN_SIZE;
import static org.jclouds.nodepool.config.NodePoolProperties.REMOVE_DESTROYED;
import java.net.URI;
import java.util.Properties;
import org.jclouds.apis.internal.BaseApiMetadata;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.nodepool.config.BindBackendComputeService;
import org.jclouds.nodepool.config.BindInputStreamToFilesystemBlobStore;
import org.jclouds.nodepool.config.NodePoolComputeServiceContextModule;
import org.jclouds.rest.internal.BaseRestApiMetadata;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
public class NodePoolApiMetadata extends BaseApiMetadata {
/** The serialVersionUID */
@ -54,19 +62,28 @@ public class NodePoolApiMetadata extends BaseApiMetadata {
public static Properties defaultProperties() {
Properties properties = BaseRestApiMetadata.defaultProperties();
properties.setProperty(BACKING_GROUP_PROPERTY, "nodepool");
properties.setProperty(MAX_SIZE_PROPERTY, 10 + "");
properties.setProperty(MIN_SIZE_PROPERTY, 5 + "");
properties.setProperty(REMOVE_DESTROYED_PROPERTY, "false");
properties.setProperty(BACKEND_GROUP, "nodepool");
properties.setProperty(METADATA_CONTAINER, "nodes");
properties.setProperty(BACKEND_MODULES, "org.jclouds.logging.slf4j.config.SLF4JLoggingModule,org.jclouds.sshj.config.SshjSshClientModule");
properties.setProperty(MAX_SIZE, 10 + "");
properties.setProperty(MIN_SIZE, 5 + "");
properties.setProperty(REMOVE_DESTROYED, "false");
return properties;
}
public static class Builder extends BaseApiMetadata.Builder {
protected Builder() {
id("nodepool").name("node pool provider wrapper").identityName("Unused").defaultIdentity("nodepool")
.defaultEndpoint("nodepool").documentation(
URI.create("http://www.jclouds.org/documentation/userguide/compute")).view(
ComputeServiceContext.class).defaultProperties(NodePoolApiMetadata.defaultProperties());
id("nodepool")
.name("node pool provider wrapper")
.identityName("backend identity")
.endpointName("backend endpoint").defaultEndpoint("fixme")
.documentation(URI.create("http://www.jclouds.org/documentation/userguide/compute"))
.view(ComputeServiceContext.class)
.defaultModules(ImmutableSet.<Class<? extends Module>> builder()
.add(NodePoolComputeServiceContextModule.class)
.add(BindInputStreamToFilesystemBlobStore.class)
.add(BindBackendComputeService.class).build())
.defaultProperties(NodePoolApiMetadata.defaultProperties());
}
@Override

View File

@ -1,80 +0,0 @@
/*
* 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.nodepool;
import java.io.Closeable;
import java.io.IOException;
import org.jclouds.compute.ComputeService;
import org.jclouds.nodepool.config.NodePoolComputeServiceProperties;
import org.jclouds.nodepool.internal.EagerNodePoolComputeService;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.ImplementedBy;
/**
* A {@link ComputeService} wrapper that uses a pool of pre-loaded nodes to speed up creation times.
*
* This interface extends the ComputeService with a backing pool of nodes, configured during
* construction. The {@link #startPool()} and {@link #close()} methods are used to create and
* destroy the pool and its associated nodes.
*
* @author Andrew Kennedy
* @author Gustavo Morozowski
* @author David Alves
*
* @see <a href="https://github.com/jclouds/jclouds/wiki/NodePool-Notes">NodePool Notes</a>
* @see NodePoolComputeServiceProperties
* @since 1.5.0
*/
@ImplementedBy(EagerNodePoolComputeService.class)
public interface NodePoolComputeService extends ComputeService, Closeable {
/**
* Starts the pool, may or may not start the actual nodes, depending on the implementation, i.e.
* the returned Set may be empty.
*/
ListenableFuture<Void> startPool();
/**
* Returns true of the pool has been started by calling the {@link #startPool()} method.
*/
boolean isStarted();
/**
* Returns the number of ready (pre-allocated) nodes in the pool.
*/
int ready();
/**
* Returns the current size of the pool (nodes allocated on the backing compute service)
*/
int size();
/**
* Returns the maximum amout of node the pool will allocate in the backing compute service.
*/
int maxSize();
/**
* Close the pool and destroy all associated nodes.
*/
void close() throws IOException;
}

View File

@ -0,0 +1,39 @@
/*
* 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.nodepool;
import org.jclouds.compute.JCloudsNativeComputeServiceAdapter;
import org.jclouds.nodepool.internal.EagerNodePoolComputeServiceAdapter;
import com.google.inject.ImplementedBy;
@ImplementedBy(EagerNodePoolComputeServiceAdapter.class)
public interface NodePoolComputeServiceAdapter extends JCloudsNativeComputeServiceAdapter {
int idleNodes();
int maxNodes();
int minNodes();
int usedNodes();
int currentSize();
}

View File

@ -0,0 +1,48 @@
/*
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.nodepool;
import javax.inject.Singleton;
import org.jclouds.Context;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.Utils;
import org.jclouds.compute.internal.ComputeServiceContextImpl;
import org.jclouds.location.Provider;
import com.google.common.reflect.TypeToken;
import com.google.inject.Inject;
@Singleton
public class NodePoolComputeServiceContext extends ComputeServiceContextImpl {
private final NodePoolComputeServiceAdapter adapter;
@Inject
public NodePoolComputeServiceContext(@Provider Context backend, @Provider TypeToken<? extends Context> backendType,
ComputeService computeService, Utils utils, NodePoolComputeServiceAdapter adapter) {
super(backend, backendType, computeService, utils);
this.adapter = adapter;
}
public NodePoolStats getPoolStats() {
return new NodePoolStats(adapter.currentSize(), adapter.idleNodes(), adapter.usedNodes(), adapter.maxNodes(),
adapter.minNodes());
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.nodepool;
/**
* NodePool statistics and status.
*
* @author David Alves
*
*/
public class NodePoolStats {
private final int currentSize;
private final int idleNodes;
private final int usedNodes;
private final int maxNodes;
private final int minNodes;
NodePoolStats(int currentSize, int idleNodes, int usedNodes, int maxNodes, int minNodes) {
this.currentSize = currentSize;
this.idleNodes = idleNodes;
this.usedNodes = usedNodes;
this.maxNodes = maxNodes;
this.minNodes = minNodes;
}
/**
* The number of nodes currently allocated in the backend provider and in the pool.
*/
public int currentSize() {
return currentSize;
}
/**
* The number of nodes in the pool not being used.
*/
public int idleNodes() {
return idleNodes;
}
/**
* The number of nodes in the pool that are currently being used.
*/
public int usedNodes() {
return usedNodes;
}
/**
* The maximum size the pool will reach.
*/
public int maxNodes() {
return maxNodes;
}
/**
* The minimum size of the pool.
*/
public int minNodes() {
return minNodes;
}
}

View File

@ -0,0 +1,129 @@
/*
* 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.nodepool.config;
import java.net.URI;
import java.util.Properties;
import java.util.Set;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.Template;
import org.jclouds.domain.Credentials;
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
import org.jclouds.lifecycle.Closer;
import org.jclouds.location.Provider;
import org.jclouds.nodepool.Backend;
import org.jclouds.rest.annotations.ApiVersion;
import org.jclouds.rest.annotations.BuildVersion;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.inject.Exposed;
import com.google.inject.Module;
import com.google.inject.Provides;
public class BindBackendComputeService extends BindJcloudsModules {
@Provides
@Singleton
@Backend
protected String provideBackendProvider(@Named(NodePoolProperties.BACKEND_PROVIDER) String provider){
return provider;
}
// things wrapped in suppliers are intentional. They can cause network i/o
// and shouldn't be invoked until after the injector is created.
@Provides
@Singleton
@Backend
@Exposed
protected Supplier<ComputeService> makeBackendComputeService(@Backend final String provider,
@Backend final Set<Module> modules, @Provider final Credentials creds,
@Backend final Supplier<Properties> overrides, final Closer closer) {
return Suppliers.memoize(new Supplier<ComputeService>() {
@Override
public ComputeService get() {
ComputeServiceContext ctx = ContextBuilder.newBuilder(provider)
.credentials(creds.identity, creds.credential)
.overrides(overrides.get())
.modules(modules)
.buildView(ComputeServiceContext.class);
closer.addToClose(ctx);
return ctx.getComputeService();
}
});
}
private static final Predicate<String> keys = new Predicate<String>() {
@Override
public boolean apply(String input) {
return !input.startsWith("jclouds.nodepool") && !input.startsWith("nodepool");
}
};
@Provides
@Singleton
@Backend
protected Supplier<Properties> propertiesFor(final FilterStringsBoundToInjectorByName filterStringsBoundByName,
@Backend final String provider, @Provider final Supplier<URI> endpoint, @ApiVersion final String apiVersion,
@BuildVersion final String buildVersion) {
return Suppliers.memoize(new Supplier<Properties>() {
@Override
public Properties get() {
Properties props = new Properties();
props.putAll(filterStringsBoundByName.apply(keys));
props.put(provider + ".endpoint", endpoint.get().toASCIIString());
props.put(provider + ".api-version", apiVersion);
props.put(provider + ".build-version", buildVersion);
return props;
}
});
}
@Provides
@Singleton
@Backend
@Exposed
protected Supplier<Template> makeBackendTemplate(@Backend Supplier<ComputeService> compute) {
return Suppliers.memoize(Suppliers.compose(new Function<ComputeService, Template>() {
@Override
public Template apply(ComputeService input) {
return input.templateBuilder().build();
}
}, compute));
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.nodepool.config;
import java.io.File;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.ContextBuilder;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.filesystem.reference.FilesystemConstants;
import org.jclouds.lifecycle.Closer;
import org.jclouds.nodepool.Backend;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.inject.Exposed;
import com.google.inject.Module;
import com.google.inject.Provides;
public class BindInputStreamToFilesystemBlobStore extends BindJcloudsModules {
@Override
protected void configure() {
}
@Provides
@Singleton
@Exposed
protected Supplier<Map<String, InputStream>> provideInputStreamMapFromBlobStore(Supplier<BlobStoreContext> in,
@Named(NodePoolProperties.METADATA_CONTAINER) final String container) {
return Suppliers.memoize(Suppliers.compose(new Function<BlobStoreContext, Map<String, InputStream>>() {
@Override
public Map<String, InputStream> apply(BlobStoreContext input) {
input.getBlobStore().createContainerInLocation(null, container);
return input.createInputStreamMap(container);
}
}, in));
}
@Provides
@Singleton
protected Supplier<BlobStoreContext> makeBlobStoreContext(@Named(NodePoolProperties.BASEDIR) final String basedir,
@Backend final Set<Module> modules, final Closer closer) {
final Properties overrides = new Properties();
overrides.setProperty(FilesystemConstants.PROPERTY_BASEDIR, basedir);
return Suppliers.memoize(new Supplier<BlobStoreContext>() {
@Override
public BlobStoreContext get() {
// GAE alert!
new File(basedir).mkdirs();
BlobStoreContext returnVal = ContextBuilder.newBuilder("filesystem")
.overrides(overrides)
.modules(modules)
.buildView(BlobStoreContext.class);
closer.addToClose(returnVal);
return returnVal;
}
});
}
}

View File

@ -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.nodepool.config;
import java.util.Set;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.nodepool.Backend;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Module;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
public class BindJcloudsModules extends PrivateModule {
@Override
protected void configure() {
}
@Provides
@Singleton
@Backend
protected Set<Module> provideBackendModules(@Named(NodePoolProperties.BACKEND_MODULES) String moduleString) {
return ImmutableSet.copyOf(Iterables.transform(Splitter.on(',').split(moduleString),
new Function<String, Module>() {
@Override
public Module apply(String input) {
try {
return Module.class.cast(Class.forName(input).newInstance());
} catch (InstantiationException e) {
throw Throwables.propagate(e);
} catch (IllegalAccessException e) {
throw Throwables.propagate(e);
} catch (ClassNotFoundException e) {
throw Throwables.propagate(e);
}
}
}));
}
}

View File

@ -0,0 +1,26 @@
package org.jclouds.nodepool.config;
import org.jclouds.compute.config.JCloudsNativeComputeServiceAdapterContextModule;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.domain.Location;
import org.jclouds.nodepool.NodePoolComputeServiceAdapter;
import org.jclouds.nodepool.internal.JsonNodeMetadataStore;
import org.jclouds.nodepool.internal.NodeMetadataStore;
public class NodePoolComputeServiceContextModule extends JCloudsNativeComputeServiceAdapterContextModule {
public NodePoolComputeServiceContextModule() {
super(NodePoolComputeServiceAdapter.class);
}
@Override
protected void configure() {
super.configure();
bind(NodeMetadataStore.class).to(JsonNodeMetadataStore.class);
install(new LocationsFromComputeServiceAdapterModule<NodeMetadata, Hardware, Image, Location>() {
});
}
}

View File

@ -21,33 +21,49 @@ package org.jclouds.nodepool.config;
/**
* Constants for the {@link org.jclouds.nodepool.NodePoolComputeService}.
*/
public interface NodePoolComputeServiceProperties {
public interface NodePoolProperties {
/**
* Property to set the name of the backing group used for pooled nodes.
* Property to set the name of the backend group used for pooled nodes.
*/
public static final String BACKING_GROUP_PROPERTY = "jclouds.nodepool.backing-group";
public static final String BACKEND_GROUP = "jclouds.nodepool.backend-group";
/**
* Property to set the template that will be used to create the nodes in the pool.
* Property to set the provider or api of backend the pool
*/
public static final String BACKING_TEMPLATE_PROPERTY = "jclouds.nodepool.backing-template";
public static final String BACKEND_PROVIDER = "jclouds.nodepool.backend-provider";
/**
* Property to set the modules the backend will use for ssh and logging, comma delimited
*/
public static final String BACKEND_MODULES = "jclouds.nodepool.backend-modules";
/**
* Property to set the basedir where metadata will be stored
*/
public static final String BASEDIR = "jclouds.nodepool.basedir";
/**
* Property to set the container where metadata will be stored
*/
public static final String METADATA_CONTAINER = "jclouds.nodepool.metadata-container";
/**
* Property to set the maximum size of the pool. Set this to {@literal -1} to have an unlimited
* pool size.
*/
public static final String MAX_SIZE_PROPERTY = "jclouds.nodepool.max-size";
public static final String MAX_SIZE = "jclouds.nodepool.max-size";
/**
* Property to set the minimum (initial) size of the pool.
*/
public static final String MIN_SIZE_PROPERTY = "jclouds.nodepool.min-size";
public static final String MIN_SIZE = "jclouds.nodepool.min-size";
/**
* Property to set the pool behaviour to remove destroyed nodes rather than returning them to the
* pool for re-use.
*/
public static final String REMOVE_DESTROYED_PROPERTY = "jclouds.nodepool.remove-destroyed";
public static final String REMOVE_DESTROYED = "jclouds.nodepool.remove-destroyed";
}

View File

@ -1,335 +0,0 @@
package org.jclouds.nodepool.internal;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.find;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.RunScriptOnNodesException;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.extensions.ImageExtension;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Location;
import org.jclouds.nodepool.NodePoolComputeService;
import org.jclouds.scriptbuilder.domain.Statement;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture;
/**
* A base class for {@link NodePoolComputeService}, takes care of keeping (not changing assignments)
* and of everything that does not change the pool.
*
* @author David Alves
*
*/
public abstract class BaseNodePoolComputeService implements NodePoolComputeService {
protected final ComputeService backingComputeService;
protected final String poolGroupName;
protected final Template template;
protected final Image image;
protected final Hardware hardware;
protected final Location location;
// assignments of nodes to group names
protected final Multimap<String, NodeMetadata> assignments = HashMultimap.create();
protected final AtomicBoolean started = new AtomicBoolean(false);
public BaseNodePoolComputeService(ComputeServiceContext backingComputeServiceContext, String poolGroupNamePrefix,
Template backingTemplate) {
this.backingComputeService = backingComputeServiceContext.getComputeService();
this.poolGroupName = poolGroupNamePrefix;
this.template = backingTemplate == null ? this.backingComputeService.templateBuilder().build() : backingTemplate;
this.image = this.template.getImage();
this.hardware = this.template.getHardware();
this.location = this.template.getLocation();
}
/**
* Checks which nodes match the users predicate and builds a predicate that returns true to their
* specific ids.
*
* @param filter
* @return
*/
private Predicate<NodeMetadata> transformUserPredicateSpecificIdPredicate(Predicate<NodeMetadata> filter) {
Iterable<Map.Entry<String, NodeMetadata>> relevantAssginemnts = filterAssignmentsBasedOnUserPredicate(filter);
final Set<String> ids = Sets.newHashSet();
for (Map.Entry<String, NodeMetadata> assignment : relevantAssginemnts) {
ids.add(assignment.getValue().getId());
}
return new Predicate<NodeMetadata>() {
@Override
public boolean apply(NodeMetadata input) {
return ids.contains(input.getId());
}
};
}
@Override
public boolean isStarted() {
return started.get();
}
// TODO this is n^2 expensive. s
private Map<? extends NodeMetadata, ExecResponse> transformBackendExecutionMapIntoFrontend(
Map<? extends NodeMetadata, ExecResponse> backendMap) {
Map<NodeMetadata, ExecResponse> frontendMap = Maps.newHashMapWithExpectedSize(backendMap.size());
for (Map.Entry<? extends NodeMetadata, ExecResponse> entry : backendMap.entrySet()) {
Map.Entry<String, NodeMetadata> assignmentEntry = findAssigmentEntry(entry.getKey().getId());
frontendMap
.put(toFrontendNodemetadata(assignmentEntry.getValue(), assignmentEntry.getKey()), entry.getValue());
}
return frontendMap;
}
protected Map.Entry<String, NodeMetadata> findAssigmentEntry(final String id) {
// TODO reverse lookup data structure would be faster but will pools be that big ?
return find(assignments.entries(), new Predicate<Map.Entry<String, NodeMetadata>>() {
@Override
public boolean apply(Entry<String, NodeMetadata> entry) {
return entry.getValue().getId().equals(id);
}
});
}
protected NodeMetadata toFrontendNodemetadata(NodeMetadata backendNodeMetadata, String group) {
return NodeMetadataBuilder.fromNodeMetadata(backendNodeMetadata).group(group).build();
}
/**
* Because a lot of predicates are based on group info we need that to check wether the predicate
* matches.
*/
protected Iterable<Map.Entry<String, NodeMetadata>> filterAssignmentsBasedOnUserPredicate(
final Predicate<NodeMetadata> userFilter) {
return filter(assignments.entries(), new Predicate<Map.Entry<String, NodeMetadata>>() {
@Override
public boolean apply(Entry<String, NodeMetadata> input) {
return userFilter.apply(toFrontendNodemetadata(input.getValue(), input.getKey()));
}
});
}
@Override
public NodeMetadata getNodeMetadata(String id) {
checkState(started.get(), "pool is not started");
Map.Entry<String, NodeMetadata> assigmentEntry = findAssigmentEntry(id);
return toFrontendNodemetadata(assigmentEntry.getValue(), assigmentEntry.getKey());
}
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
String runScript) throws RunScriptOnNodesException {
return runScriptOnNodesMatching(filter, runScript, new RunScriptOptions());
}
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
Statement runScript) throws RunScriptOnNodesException {
return runScriptOnNodesMatching(filter, runScript, new RunScriptOptions());
}
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
String runScript, RunScriptOptions options) throws RunScriptOnNodesException {
checkState(started.get(), "pool is not started");
return transformBackendExecutionMapIntoFrontend(backingComputeService.runScriptOnNodesMatching(
transformUserPredicateSpecificIdPredicate(filter), runScript, options));
}
@Override
public Map<? extends NodeMetadata, ExecResponse> runScriptOnNodesMatching(Predicate<NodeMetadata> filter,
Statement runScript, RunScriptOptions options) throws RunScriptOnNodesException {
checkState(started.get(), "pool is not started");
return transformBackendExecutionMapIntoFrontend(backingComputeService.runScriptOnNodesMatching(
transformUserPredicateSpecificIdPredicate(filter), runScript, options));
}
@Override
public Set<? extends ComputeMetadata> listNodes() {
checkState(started.get(), "pool is not started");
return listNodesDetailsMatching(Predicates.alwaysTrue());
}
@SuppressWarnings( { "rawtypes", "unchecked" })
@Override
public Set<? extends NodeMetadata> listNodesDetailsMatching(Predicate filter) {
checkState(started.get(), "pool is not started");
return FluentIterable.from(filterAssignmentsBasedOnUserPredicate(filter)).transform(
new Function<Map.Entry<String, NodeMetadata>, NodeMetadata>() {
@Override
public NodeMetadata apply(Entry<String, NodeMetadata> input) {
return toFrontendNodemetadata(input.getValue(), input.getKey());
}
}).toImmutableSet();
}
@Override
public void rebootNodesMatching(final Predicate<NodeMetadata> filter) {
checkState(started.get(), "pool is not started");
backingComputeService.rebootNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
}
@Override
public void resumeNodesMatching(Predicate<NodeMetadata> filter) {
checkState(started.get(), "pool is not started");
backingComputeService.resumeNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
}
@Override
public void suspendNodesMatching(Predicate<NodeMetadata> filter) {
checkState(started.get(), "pool is not started");
backingComputeService.suspendNodesMatching(transformUserPredicateSpecificIdPredicate(filter));
}
@Override
public ComputeServiceContext getContext() {
// not sure this is enough, should we have our own?
return backingComputeService.getContext();
}
// we ignore user provided templates and options
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template)
throws RunNodesException {
return createNodesInGroup(group, count);
}
@Override
public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions)
throws RunNodesException {
return createNodesInGroup(group, count);
}
@Override
public TemplateBuilder templateBuilder() {
return backingComputeService.templateBuilder().fromTemplate(template);
}
@Override
public TemplateOptions templateOptions() {
return backingComputeService.templateOptions();
}
@Override
public Set<? extends Hardware> listHardwareProfiles() {
return ImmutableSet.of(hardware);
}
@Override
public Set<? extends Image> listImages() {
return ImmutableSet.of(image);
}
@Override
public Image getImage(String id) {
return image.getId().equals(id) ? image : null;
}
@Override
public Set<? extends Location> listAssignableLocations() {
return ImmutableSet.of(location);
}
@Override
public void suspendNode(String id) {
if (findAssigmentEntry(id) != null) {
backingComputeService.suspendNode(id);
}
throw new NoSuchElementException(id);
}
@Override
public void resumeNode(String id) {
if (findAssigmentEntry(id) != null) {
backingComputeService.resumeNode(id);
}
throw new NoSuchElementException(id);
}
@Override
public void rebootNode(String id) {
if (findAssigmentEntry(id) != null) {
backingComputeService.rebootNode(id);
}
throw new NoSuchElementException(id);
}
@Override
public ExecResponse runScriptOnNode(String id, Statement runScript) {
if (findAssigmentEntry(id) != null) {
return runScriptOnNode(id, runScript, new RunScriptOptions());
}
throw new NoSuchElementException(id);
}
@Override
public ExecResponse runScriptOnNode(String id, String runScript) {
if (findAssigmentEntry(id) != null) {
return runScriptOnNode(id, runScript, new RunScriptOptions());
}
throw new NoSuchElementException(id);
}
@Override
public ExecResponse runScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
if (findAssigmentEntry(id) != null) {
return backingComputeService.runScriptOnNode(id, runScript, options);
}
throw new NoSuchElementException(id);
}
@Override
public ListenableFuture<ExecResponse> submitScriptOnNode(String id, Statement runScript, RunScriptOptions options) {
if (findAssigmentEntry(id) != null) {
return backingComputeService.submitScriptOnNode(id, runScript, options);
}
throw new NoSuchElementException(id);
}
@Override
public ExecResponse runScriptOnNode(String id, String runScript, RunScriptOptions options) {
if (findAssigmentEntry(id) != null) {
return backingComputeService.runScriptOnNode(id, runScript, options);
}
throw new NoSuchElementException(id);
}
@Override
public Optional<ImageExtension> getImageExtension() {
return Optional.absent();
}
}

View File

@ -0,0 +1,140 @@
/*
* 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.nodepool.internal;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_GROUP;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.inject.Named;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.domain.Location;
import org.jclouds.nodepool.Backend;
import org.jclouds.nodepool.NodePoolComputeServiceAdapter;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* A base class for {@link NodePoolComputeService}, takes care of keeping (not
* changing assignments) and of everything that does not change the pool.
*
* @author David Alves
*
*/
public abstract class BaseNodePoolComputeServiceAdapter implements NodePoolComputeServiceAdapter {
protected final Supplier<ComputeService> backendComputeService;
protected final Supplier<Template> backendTemplate;
protected final String poolGroupName;
protected final NodeMetadataStore metadataStore;
public BaseNodePoolComputeServiceAdapter(@Backend Supplier<ComputeService> backendComputeService,
@Backend Supplier<Template> backendTemplate, @Named(BACKEND_GROUP) String poolGroupNamePrefix,
NodeMetadataStore metadataStore) {
this.backendComputeService = backendComputeService;
this.poolGroupName = poolGroupNamePrefix;
this.backendTemplate = backendTemplate;
this.metadataStore = metadataStore;
}
@Override
public NodeMetadata getNode(String id) {
NodeMetadata backendMetadata = backendComputeService.get().getNodeMetadata(id);
checkState(backendMetadata.getGroup().equals(backendMetadata));
return metadataStore.load(backendMetadata);
}
@Override
public Iterable<NodeMetadata> listNodes() {
return metadataStore.loadAll(getBackendNodes());
}
@Override
public Iterable<Hardware> listHardwareProfiles() {
return ImmutableSet.of(backendTemplate.get().getHardware());
}
@Override
public Iterable<Image> listImages() {
return ImmutableSet.of(backendTemplate.get().getImage());
}
@Override
public Iterable<Location> listLocations() {
return ImmutableSet.of(backendTemplate.get().getLocation());
}
@Override
public Image getImage(String id) {
Image backendImage = backendTemplate.get().getImage();
return backendImage.getId().equals(id) ? backendImage : null;
}
@Override
public void suspendNode(String id) {
if (getNode(id) != null) {
backendComputeService.get().suspendNode(id);
}
throw new NoSuchElementException(id);
}
@Override
public void resumeNode(String id) {
if (getNode(id) != null) {
backendComputeService.get().resumeNode(id);
}
throw new NoSuchElementException(id);
}
@Override
public void rebootNode(String id) {
if (getNode(id) != null) {
backendComputeService.get().rebootNode(id);
}
throw new NoSuchElementException(id);
}
protected Set<NodeMetadata> getBackendNodes() {
return ImmutableSet.copyOf(Iterables.filter(backendComputeService.get().listNodesDetailsMatching(
NodePredicates.all()), NodePredicates.inGroup(poolGroupName)));
}
protected void addToPool(int number) {
try {
backendComputeService.get().createNodesInGroup(poolGroupName, number, backendTemplate.get());
} catch (RunNodesException e) {
throw Throwables.propagate(e);
}
}
}

View File

@ -1,250 +0,0 @@
package org.jclouds.nodepool.internal;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.removeIf;
import static com.google.common.collect.Iterables.transform;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_GROUP_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.BACKING_TEMPLATE_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MAX_SIZE_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.MIN_SIZE_PROPERTY;
import static org.jclouds.nodepool.config.NodePoolComputeServiceProperties.REMOVE_DESTROYED_PROPERTY;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.Constants;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.concurrent.Futures;
import org.jclouds.logging.Logger;
import org.jclouds.nodepool.NodePoolComputeService;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture;
/**
* An eager {@link NodePoolComputeService}. Eagerly builds and maintains a pool of nodes. It's only
* "started" after min nodes are allocated and available.
*
* @author David Alves
*
*/
public class EagerNodePoolComputeService extends BaseNodePoolComputeService {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final int maxSize;
private final boolean reuseDestroyed;
private final int minSize;
private final ExecutorService executor;
// set of available nodes
private Set<NodeMetadata> available = Sets.newHashSet();
// lock associated with changes to the pool since they happen asynchronously
private final Lock lock = new ReentrantLock();
// all the nodes in the pool (associated or not)
private final Set<NodeMetadata> poolNodes = Sets.newLinkedHashSet();
@Inject
public EagerNodePoolComputeService(ComputeServiceContext backingComputeServiceContext,
@Named(BACKING_GROUP_PROPERTY) String poolGroupPrefix,
@Named(MAX_SIZE_PROPERTY) int maxSize, @Named(MIN_SIZE_PROPERTY) int minSize,
@Named(REMOVE_DESTROYED_PROPERTY) boolean readdDestroyed,
@Nullable @Named(BACKING_TEMPLATE_PROPERTY) Template backingTemplate,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor) {
super(backingComputeServiceContext, poolGroupPrefix, backingTemplate);
this.maxSize = maxSize;
this.minSize = minSize;
this.reuseDestroyed = readdDestroyed;
this.executor = executor;
}
@Override
public synchronized Set<? extends NodeMetadata> createNodesInGroup(String group, int count) throws RunNodesException {
checkState(started.get(), "pool is not started");
try {
return assignPoolNodes(group, count);
} catch (Exception e) {
Set<NodeMetadata> nodes = Collections.emptySet();
Map<String, Exception> executionExceptions = ImmutableMap.of("poolnode", e);
Map<NodeMetadata, Exception> failedNodes = ImmutableMap.of(new NodeMetadataBuilder().id("poolnode").status(
Status.ERROR).build(), e);
throw new RunNodesException(group, count, template, nodes, executionExceptions, failedNodes);
}
}
@Override
public synchronized void destroyNode(String id) {
checkState(started.get(), "pool is not started");
unassignNode(id);
}
@Override
public synchronized Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
checkState(started.get(), "pool is not started");
// copy the set of nodes to unassign because we'll be altering the assignments map.
Set<Map.Entry<String, NodeMetadata>> poolNodesToUnassign = Sets
.newHashSet(filterAssignmentsBasedOnUserPredicate(filter));
// TODO this should be done in parallel since it can take quite a while, moreover the contract
// for any destroy node action should probably be that the pool has at least minSize nodes
// before it returns. need to think it through a bit better.
for (Map.Entry<String, NodeMetadata> poolNode : poolNodesToUnassign) {
unassignNode(poolNode.getValue().getId());
}
return Sets.newHashSet(transform(poolNodesToUnassign,
new Function<Map.Entry<String, NodeMetadata>, NodeMetadata>() {
@Override
public NodeMetadata apply(final Map.Entry<String, NodeMetadata> input) {
assignments.remove(input.getKey(), input.getValue());
return toFrontendNodemetadata(input.getValue(), input.getKey());
}
}));
}
/**
* Adds nodes to the pool, using the pool's group name. Lock the pool so that no-one tries to
* increase/decrease until we're finished but we'll return from the method well before the pool
* as enough nodes.
*/
private ListenableFuture<Void> increasePoolSize(final int size) {
lock.lock();
logger.debug(">> increasing pool size, available: %s total: %s min; %s max: %s increasing to: %s", available
.size(), poolNodes.size(), minSize, maxSize, size);
return Futures.makeListenable(executor.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
Set<? extends NodeMetadata> original = backingComputeService.createNodesInGroup(poolGroupName, size,
template);
poolNodes.addAll(original);
available.addAll(original);
logger.debug("<< pool size increased, available: %s total: %s min; %s max: %s increasing to: %s",
available.size(), poolNodes.size(), minSize, maxSize, size);
if (started.compareAndSet(false, true)) {
logger.info("pool started, status: %s min; %s max: %s", available.size(), minSize, maxSize);
}
return null;
} finally {
lock.unlock();
}
}
}), executor);
}
/**
* Unassigns the node with the provided id. If the we're set to reuse the nodes it adds it to the
* available pool, if not is destroys the backing node, removes if from the poll and increases
* the pool size by one.
*/
private NodeMetadata unassignNode(final String nodeId) {
Map.Entry<String, NodeMetadata> entry = findAssigmentEntry(nodeId);
assignments.remove(entry.getKey(), entry.getValue());
// if we're reusing destroyed simply add to the available nodes
if (reuseDestroyed) {
available.add(entry.getValue());
return entry.getValue();
}
// if not we need to destroy the backing node
lock.lock();
try {
backingComputeService.destroyNode(nodeId);
removeIf(poolNodes, new Predicate<NodeMetadata>() {
@Override
public boolean apply(NodeMetadata input) {
return input.getId().equals(nodeId);
}
});
if (poolNodes.size() < minSize) {
increasePoolSize(1);
}
} finally {
lock.unlock();
}
return entry.getValue();
}
/**
* Used to assign size pool nodes to a group. If not enough nodes are available we check if we
* can increase the pool if that is enough, otherwise we complain.
*/
private Set<? extends NodeMetadata> assignPoolNodes(String groupName, int size) throws InterruptedException,
ExecutionException {
if (available.size() < size) {
if (poolNodes.size() + size > maxSize) {
// TODO think of a better exception
throw new IllegalStateException(
"not enough nodes available and cannot add enough nodes to pool [available: " + available.size()
+ " total: " + poolNodes.size() + " min: " + minSize + " max: " + maxSize
+ " requested: " + size + "]");
}
increasePoolSize(size - available.size()).get();
}
Set<NodeMetadata> groupNodes = Sets.newHashSet();
Iterator<NodeMetadata> iter = available.iterator();
for (int i = 0; i < size && iter.hasNext(); i++) {
NodeMetadata node = iter.next();
assignments.put(groupName, node);
iter.remove();
groupNodes.add(toFrontendNodemetadata(node, groupName));
}
return groupNodes;
}
@Override
public ListenableFuture<Void> startPool() {
return increasePoolSize(minSize);
}
@Override
public void close() {
// lock just to make sure we have the correct pool size
if (started.compareAndSet(true, false)) {
logger.info("Closing pooled compute service with {} nodes", size());
available.clear();
assignments.clear();
backingComputeService.destroyNodesMatching(NodePredicates.inGroup(poolGroupName));
}
}
@Override
public int ready() {
return available.size();
}
@Override
public int size() {
return poolNodes.size();
}
@Override
public int maxSize() {
return maxSize;
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.nodepool.internal;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_GROUP;
import static org.jclouds.nodepool.config.NodePoolProperties.MAX_SIZE;
import static org.jclouds.nodepool.config.NodePoolProperties.MIN_SIZE;
import static org.jclouds.nodepool.config.NodePoolProperties.REMOVE_DESTROYED;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.nodepool.Backend;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
/**
* An eager {@link NodePoolComputeService}. Eagerly builds and maintains a pool
* of nodes. It's only "started" after min nodes are allocated and available.
*
* @author David Alves
*
*/
@Singleton
public class EagerNodePoolComputeServiceAdapter extends BaseNodePoolComputeServiceAdapter {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final int maxSize;
private final int minSize;
private final boolean reuseDestroyed;
@Inject
public EagerNodePoolComputeServiceAdapter(@Backend Supplier<ComputeService> backendComputeService,
@Backend Supplier<Template> backendTemplate, @Named(BACKEND_GROUP) String poolGroupPrefix,
@Named(MAX_SIZE) int maxSize, @Named(MIN_SIZE) int minSize, @Named(REMOVE_DESTROYED) boolean readdDestroyed,
NodeMetadataStore storage) {
super(backendComputeService, backendTemplate, poolGroupPrefix, storage);
this.maxSize = maxSize;
this.minSize = minSize;
this.reuseDestroyed = readdDestroyed;
}
@PostConstruct
public void startEagerPool() {
Set<? extends NodeMetadata> backendNodes = getBackendNodes();
if (backendNodes.size() < minSize) {
addToPool(backendNodes.size() - minSize);
}
}
@Override
public NodeWithInitialCredentials createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
int count = 1;
synchronized (this) {
Set<NodeMetadata> backendNodes = getBackendNodes();
Set<NodeMetadata> frontendNodes = metadataStore.loadAll(backendNodes);
checkState(frontendNodes.size() + count < maxSize,
"cannot add more nodes to pool [requested: %s, current: %s, max: %s]", count, frontendNodes.size(),
maxSize);
SetView<NodeMetadata> availableNodes = Sets.difference(backendNodes, frontendNodes);
NodeMetadata node = metadataStore.store(Iterables.get(availableNodes, 0), template.getOptions(), group);
return new NodeWithInitialCredentials(node);
}
}
@Override
public synchronized void destroyNode(String id) {
checkState(getNode(id) != null);
metadataStore.deleteMapping(id);
if (!reuseDestroyed) {
backendComputeService.get().destroyNode(id);
addToPool(1);
}
}
@Override
public int currentSize() {
return getBackendNodes().size();
}
@Override
public int idleNodes() {
Set<NodeMetadata> backendNodes = getBackendNodes();
Set<NodeMetadata> frontendNodes = metadataStore.loadAll(backendNodes);
return backendNodes.size() - frontendNodes.size();
}
@Override
public int maxNodes() {
return maxSize;
}
@Override
public int minNodes() {
return minSize;
}
@Override
public int usedNodes() {
return metadataStore.loadAll(getBackendNodes()).size();
}
}

View File

@ -0,0 +1,116 @@
package org.jclouds.nodepool.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.json.Json;
import org.jclouds.util.Strings2;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* An implementation of {@link NodeMetadataStore} that stores all that is needed by building a json
* string.
*
* @author David Alves
*
*/
@Singleton
public class JsonNodeMetadataStore implements NodeMetadataStore {
private final Supplier<Map<String, InputStream>> storage;
private final Json json;
private static class JsonUserNodeMetadata {
private String userGroup;
private Set<String> userTags;
private Map<String, String> userMetadata;
private String user;
private String password;
private String privateKey;
private Boolean authenticateSudo;
}
@Inject
public JsonNodeMetadataStore(Supplier<Map<String, InputStream>> storage, Json json) {
this.storage = storage;
this.json = json;
}
@Override
public NodeMetadata store(NodeMetadata backendNodeMetadata, TemplateOptions userOptions, String userGroup) {
checkNotNull(backendNodeMetadata);
checkNotNull(userGroup);
checkNotNull(userOptions);
JsonUserNodeMetadata jsonMetadata = new JsonUserNodeMetadata();
jsonMetadata.user = userOptions.getLoginUser();
jsonMetadata.password = userOptions.getLoginPassword();
jsonMetadata.privateKey = userOptions.getLoginPrivateKey();
jsonMetadata.authenticateSudo = userOptions.shouldAuthenticateSudo();
jsonMetadata.userMetadata = userOptions.getUserMetadata();
jsonMetadata.userTags = userOptions.getTags();
jsonMetadata.userGroup = userGroup;
storage.get().put(backendNodeMetadata.getId(), Strings2.toInputStream(json.toJson(jsonMetadata)));
return buildFromJsonAndBackendMetadata(backendNodeMetadata, jsonMetadata);
}
@Override
public NodeMetadata load(NodeMetadata backendNodeMetadata) {
try {
InputStream storedMetadata = storage.get().get(checkNotNull(backendNodeMetadata).getId());
if (storedMetadata == null) {
return null;
}
String jsonMetadataAsString = Strings2.toStringAndClose(storedMetadata);
JsonUserNodeMetadata jsonMetadata = json.fromJson(jsonMetadataAsString, JsonUserNodeMetadata.class);
return buildFromJsonAndBackendMetadata(backendNodeMetadata, jsonMetadata);
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
private NodeMetadata buildFromJsonAndBackendMetadata(NodeMetadata backendNodeMetadata,
JsonUserNodeMetadata jsonMetadata) {
return NodeMetadataBuilder
.fromNodeMetadata(backendNodeMetadata)
.tags(jsonMetadata.userTags)
.group(jsonMetadata.userGroup)
.userMetadata(jsonMetadata.userMetadata)
.credentials(
new LoginCredentials(jsonMetadata.user, jsonMetadata.password, jsonMetadata.privateKey,
jsonMetadata.authenticateSudo)).build();
}
@Override
public void deleteAllMappings() {
storage.get().clear();
}
@Override
public void deleteMapping(String backendNodeId) {
storage.get().remove(backendNodeId);
}
public Set<NodeMetadata> loadAll(Set<NodeMetadata> backendNodes) {
return ImmutableSet.copyOf(Iterables.transform(backendNodes, new Function<NodeMetadata, NodeMetadata>() {
@Override
public NodeMetadata apply(NodeMetadata input) {
return load(input);
}
}));
}
}

View File

@ -0,0 +1,80 @@
/**
* 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.nodepool.internal;
import java.util.Set;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.TemplateOptions;
/**
* Stores/Loads frontend {@link NodeMetadata} mappings.
*
* @author David Alves
*
*/
public interface NodeMetadataStore {
/**
* Associates the provided user options and group with the provided backend {@link NodeMetadata},
* then build a frontend version of node metadata that has some fields from the backend node such
* as id, name or location, and some fields from the provided userOptions, such as userMetadata
* or tags.
*
* @param backendNode
* the backend node's {@link NodeMetadata}
* @param userOptions
* the user provided options
* @param userGroup
* the user selected group
* @return a version of NodeMetadata that includes information from the backend node and form the
* user provided options and group.
*/
public NodeMetadata store(NodeMetadata backendNode, TemplateOptions userOptions, String userGroup);
/**
* Removes the mapping from storage.
*
* @param backendNodeId
*/
public void deleteMapping(String backendNodeId);
/**
* Clears all mappings.
*/
public void deleteAllMappings();
/**
* Loads the previously stored user {@link NodeMetadata} corresponding to the provided backend
* {@link NodeMetadata}.
*
* @param backendNode
*
* @return the frontend {@link NodeMetadata} or null of this backend node has no mapping
*/
public NodeMetadata load(NodeMetadata backendNode);
/**
* Loads frontend {@link NodeMetadata} for all provided backend nodes.
*
* @param backendNodes
* @return
*/
public Set<NodeMetadata> loadAll(Set<NodeMetadata> backendNodes);
}

View File

@ -0,0 +1,70 @@
package org.jclouds.nodepool.internal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.TemplateOptions;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@Singleton
public class NodeMetadataStoreCache implements NodeMetadataStore {
private Map<String, NodeMetadata> frontendMetadataCache = new HashMap<String, NodeMetadata>();
private NodeMetadataStore backend;
@Inject
public NodeMetadataStoreCache(NodeMetadataStore backend) {
this.backend = backend;
}
@Override
public synchronized NodeMetadata store(NodeMetadata backendNode, TemplateOptions userOptions, String userGroup) {
NodeMetadata frontEndNode = backend.store(backendNode, userOptions, userGroup);
frontendMetadataCache.put(backendNode.getId(), frontEndNode);
return frontEndNode;
}
@Override
public synchronized void deleteMapping(String backendNodeId) {
frontendMetadataCache.remove(backendNodeId);
backend.deleteMapping(backendNodeId);
}
@Override
public synchronized void deleteAllMappings() {
frontendMetadataCache.clear();
backend.deleteAllMappings();
}
@Override
public synchronized NodeMetadata load(NodeMetadata backendNode) {
NodeMetadata frontendNode = frontendMetadataCache.get(backendNode.getId());
if (frontendNode == null) {
frontendNode = backend.load(backendNode);
if (frontendNode != null) {
frontendMetadataCache.put(backendNode.getId(), frontendNode);
}
}
return frontendNode;
}
@Override
public synchronized Set<NodeMetadata> loadAll(Set<NodeMetadata> backendNodes) {
return ImmutableSet.copyOf(Iterables.transform(backendNodes, new Function<NodeMetadata, NodeMetadata>() {
@Override
public NodeMetadata apply(NodeMetadata input) {
return load(input);
}
}));
}
}

View File

@ -0,0 +1,104 @@
/*
* 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.nodepool;
import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
import static org.jclouds.nodepool.config.NodePoolProperties.BACKEND_PROVIDER;
import static org.jclouds.nodepool.config.NodePoolProperties.BASEDIR;
import static org.jclouds.nodepool.config.NodePoolProperties.MAX_SIZE;
import static org.jclouds.nodepool.config.NodePoolProperties.MIN_SIZE;
import static org.jclouds.scriptbuilder.domain.Statements.exec;
import java.io.File;
import java.util.Properties;
import java.util.Set;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.internal.BaseComputeServiceContextLiveTest;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/**
*
* @author Adrian Cole
*/
@Test(groups = "live", testName ="BYONBackendLiveTest")
public class BYONBackendLiveTest extends BaseComputeServiceContextLiveTest {
final String basedir = "target/" + this.getClass().getSimpleName();
public BYONBackendLiveTest() {
provider = "nodepool";
}
@Override
protected Properties setupProperties() {
Properties contextProperties = super.setupProperties();
contextProperties.setProperty(BACKEND_PROVIDER, "byon");
contextProperties.setProperty(BASEDIR, basedir);
contextProperties.setProperty(MAX_SIZE, 1 + "");
contextProperties.setProperty(MIN_SIZE, 1 + "");
StringBuilder nodes = new StringBuilder();
nodes.append("nodes:\n");
nodes.append(" - id: mymachine\n");
nodes.append(" location_id: localhost\n");
nodes.append(" name: my local machine\n");
nodes.append(" hostname: localhost\n");
nodes.append(" os_arch: ").append(System.getProperty("os.arch")).append("\n");
nodes.append(" os_family: ").append(OsFamily.UNIX).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(" tags:\n");
nodes.append(" - local\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");
contextProperties.setProperty("byon.nodes", nodes.toString());
contextProperties.setProperty("byon.template", "locationId=localhost");
return contextProperties;
}
public void testCanRunCommandAsCurrentUser() throws Exception {
Set<? extends NodeMetadata> nodes = view.getComputeService().createNodesInGroup("goo", 1);
NodeMetadata node = Iterables.get(nodes, 0);
try {
ExecResponse response = view.getComputeService().runScriptOnNode(node.getId(), exec("id"),
wrapInInitScript(false).runAsRoot(false));
assert response.getOutput().trim().contains(System.getProperty("user.name")) : node + ": " + response;
} finally {
view.getComputeService().destroyNode(node.getId());
}
}
@Override
protected void tearDownContext() {
super.tearDownContext();
new File(basedir).delete();
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.nodepool;
import static org.testng.Assert.assertEquals;
import java.io.File;
import java.util.Properties;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeService;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.nodepool.Backend;
import org.jclouds.nodepool.config.NodePoolProperties;
import org.jclouds.rest.annotations.Credential;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
/**
*
*/
@Test(groups = "unit", testName = "NodePoolComputeServiceContextTest")
public class NodePoolComputeServiceContextTest {
@Test
public void testBinds() {
final String basedir = "target/" + this.getClass().getSimpleName();
new File(basedir).delete();
Properties overrides = new Properties();
overrides.setProperty(NodePoolProperties.BACKEND_PROVIDER, "stub");
overrides.setProperty(NodePoolProperties.BASEDIR, basedir);
// note no ssh module since we are stub and not trying ssh, yet
overrides.setProperty(NodePoolProperties.BACKEND_MODULES, SLF4JLoggingModule.class.getName());
ComputeService stub = ContextBuilder.newBuilder("nodepool")
.credentials("foo", "bar")
.endpoint("gooend")
.apiVersion("1.1")
.buildVersion("1.1-2")
.overrides(overrides)
.buildInjector().getInstance(Key.get(new TypeLiteral<Supplier<ComputeService>>(){}, Backend.class)).get();
assertEquals(stub.getContext().unwrap().getIdentity(), "foo");
assertEquals(stub.getContext().utils().injector().getInstance(Key.get(String.class, Credential.class)), "bar");
assertEquals(stub.getContext().unwrap().getProviderMetadata().getEndpoint(), "gooend");
assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getVersion(), "1.1");
assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getBuildVersion().get(), "1.1-2");
stub.getContext().close();
new File(basedir).delete();
}
}

View File

@ -1,72 +0,0 @@
package org.jclouds.nodepool;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.RunNodesException;
import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.nodepool.internal.EagerNodePoolComputeService;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.util.concurrent.ListenableFuture;
@Test(groups = "unit", testName = "NodePoolComputeServiceTest")
public class NodePoolComputeServiceTest {
private EagerNodePoolComputeService pooledComputeService;
@BeforeClass
public void setUp() {
ComputeServiceContext stubCtx = ContextBuilder.newBuilder("stub").buildView(ComputeServiceContext.class);
this.pooledComputeService = new EagerNodePoolComputeService(stubCtx, "pool", 10, 5, true, stubCtx
.getComputeService().templateBuilder().build(), stubCtx.utils().getUserExecutor());
}
public void testStartPool() throws InterruptedException, ExecutionException {
ListenableFuture<Void> future = this.pooledComputeService.startPool();
future.get();
assertEquals(pooledComputeService.ready(), 5);
assertEquals(pooledComputeService.size(), 5);
assertEquals(pooledComputeService.maxSize(), 10);
}
@Test(dependsOnMethods = "testStartPool", groups = { "unit", "poolStarted" })
public void testAllocateMinNodes() throws RunNodesException {
this.pooledComputeService.createNodesInGroup("1", 5);
// this pool is not supposed to add nodes past min until we request them
assertEquals(pooledComputeService.ready(), 0);
assertEquals(pooledComputeService.size(), 5);
}
@Test(dependsOnMethods = "testAllocateMinNodes", groups = { "unit", "poolStarted" })
public void testAllocateUpToMaxNodes() throws RunNodesException {
this.pooledComputeService.createNodesInGroup("2", 5);
assertEquals(pooledComputeService.ready(), 0);
assertEquals(pooledComputeService.size(), 10);
}
@Test(dependsOnMethods = "testAllocateUpToMaxNodes", groups = { "unit", "poolStarted" }, expectedExceptions = RunNodesException.class)
public void testAllocateMoreNodesFails() throws RunNodesException {
this.pooledComputeService.createNodesInGroup("3", 5);
System.out.println(this.pooledComputeService.size());
}
@Test(dependsOnMethods = "testAllocateUpToMaxNodes", groups = { "unit", "poolStarted" })
public void testDeallocatingNodesAndReallocating() throws RunNodesException {
this.pooledComputeService.destroyNodesMatching(NodePredicates.inGroup("2"));
assertEquals(pooledComputeService.ready(), 5);
this.pooledComputeService.createNodesInGroup("2", 5);
}
@Test(dependsOnGroups = "poolStarted")
public void testClose() throws IOException {
this.pooledComputeService.close();
assertEquals(0, pooledComputeService.ready());
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.nodepool.config;
import static org.testng.Assert.assertEquals;
import java.io.File;
import java.util.Properties;
import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeService;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.nodepool.Backend;
import org.jclouds.rest.annotations.Credential;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
/**
*
*/
@Test(groups = "unit", testName = "BindBackendComputeServiceTest")
public class BindBackendComputeServiceTest {
@Test
public void testBinds() {
final String basedir = "target/" + this.getClass().getSimpleName();
new File(basedir).delete();
Properties overrides = new Properties();
overrides.setProperty(NodePoolProperties.BACKEND_PROVIDER, "stub");
overrides.setProperty(NodePoolProperties.BASEDIR, basedir);
// note no ssh module since we are stub and not trying ssh, yet
overrides.setProperty(NodePoolProperties.BACKEND_MODULES, SLF4JLoggingModule.class.getName());
ComputeService stub = ContextBuilder.newBuilder("nodepool")
.credentials("foo", "bar")
.endpoint("gooend")
.apiVersion("1.1")
.buildVersion("1.1-2")
.overrides(overrides)
.buildInjector().getInstance(Key.get(new TypeLiteral<Supplier<ComputeService>>(){}, Backend.class)).get();
assertEquals(stub.getContext().unwrap().getIdentity(), "foo");
assertEquals(stub.getContext().utils().injector().getInstance(Key.get(String.class, Credential.class)), "bar");
assertEquals(stub.getContext().unwrap().getProviderMetadata().getEndpoint(), "gooend");
assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getVersion(), "1.1");
assertEquals(stub.getContext().unwrap().getProviderMetadata().getApiMetadata().getBuildVersion().get(), "1.1-2");
stub.getContext().close();
new File(basedir).delete();
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.nodepool.config;
import static org.testng.Assert.assertEquals;
import java.io.File;
import java.io.InputStream;
import java.util.Map;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSet;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
/**
*
*/
@Test(groups = "unit", testName = "BindInputStreamToFilesystemBlobStoreTest")
public class BindInputStreamToFilesystemBlobStoreTest {
@Test
public void testCreatesDir() {
final String basedir = "target/" + this.getClass().getSimpleName();
new File(basedir).delete();
Map<String, InputStream> file = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindConstant().annotatedWith(Names.named(NodePoolProperties.BASEDIR)).to(basedir);
bindConstant().annotatedWith(Names.named(NodePoolProperties.METADATA_CONTAINER)).to("barr");
bindConstant().annotatedWith(Names.named(NodePoolProperties.BACKEND_MODULES)).to(
SLF4JLoggingModule.class.getName());
}
}, new BindInputStreamToFilesystemBlobStore()).getInstance(
Key.get(new TypeLiteral<Supplier<Map<String, InputStream>>>() {
})).get();
assert (new File(basedir + "/barr").exists());
assertEquals(file.keySet(), ImmutableSet.of());
new File(basedir).delete();
}
}

View File

@ -1,71 +0,0 @@
<?xml version="1.0"?>
<configuration scan="false">
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds-wire.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds-compute.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="SSHFILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds-ssh.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%-4r [%thread] %-5level - %msg%n</Pattern>
</encoder>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="CONSOLE" />
</root>
<logger name="org.jclouds">
<level value="DEBUG" />
<appender-ref ref="FILE" />
</logger>
<logger name="jclouds.wire">
<level value="DEBUG" />
<appender-ref ref="WIREFILE" />
</logger>
<logger name="jclouds.headers">
<level value="DEBUG" />
<appender-ref ref="WIREFILE" />
</logger>
<logger name="jclouds.compute">
<level value="DEBUG" />
<appender-ref ref="COMPUTEFILE" />
</logger>
<logger name="jclouds.ssh">
<level value="DEBUG" />
<appender-ref ref="SSHFILE" />
</logger>
</configuration>

View File

@ -54,5 +54,6 @@
<module>greenqloud-storage</module>
<module>iam</module>
<module>aws-iam</module>
<module>nodepool</module>
</modules>
</project>