mirror of https://github.com/apache/jclouds.git
commit
4171eb9d2c
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -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>() {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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";
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -54,5 +54,6 @@
|
|||
<module>greenqloud-storage</module>
|
||||
<module>iam</module>
|
||||
<module>aws-iam</module>
|
||||
<module>nodepool</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
Loading…
Reference in New Issue