http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/README.txt ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/README.txt b/dependencies/jclouds/apis/gce/1.8.1-stratos/README.txt new file mode 100644 index 0000000..0801104 --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/README.txt @@ -0,0 +1,77 @@ +====== +Stratos GCE provider 1.8.1 +====== + +This code in Stratos is copied from Jclouds GCE [1] +The jclouds GCE code has 2 directories oauth & google-compute-engine +In Stratos, these two directories are mered into one. + +[1] https://github.com/jclouds/jclouds-labs-google/tree/jclouds-labs-google-1.8.1 + + + + +====== +jclouds Google Compute Engine Provider +====== + + +Authenticating into the instances: +-------- + +User: +If no user is provided in GoogleComputeEngineTemplateOptions when launching an instance by default "jclouds" is used. + +Credential: + +GCE uses exclusively ssh keys to login into instances. +In order for an instance to be sshable a public key must be installed. Public keys are installed if they are present in the project or instance's metatada. + +For an instance to be ssable one of the following must happen: +1 - the project's metadata has an adequately built "sshKeys" entry and a corresponding private key is provided in GoogleComputeEngineTemplateOptions when createNodesInGroup is called. +2 - an instance of GoogleComputeEngineTemplateOptions with an adequate public and private key is provided. + +NOTE: if methods 2 is chosen the global project keys will not be installed in the instance. + +Please refer to Google's documentation on how to form valid project wide ssh keys metadata entries. + +FAQ: +-------- + +* Q. What is the identity for GCE? + +A. the identity is the developer email which can be obtained from the admin GUI. Its usually something in the form: <my account id>@developer.gserviceaccount.com + +* Q. What is the credential for GCE + +A. the credential is a private key, in pem format. It can be extracted from the p12 keystore that is obtained when creating a "Service Account" (in the GUI: Google apis console > Api Access > Create another client ID > "Service Account" + +* Q. How to convert a p12 keystore into a pem format jclouds Google Compute Engine can handle: + +A. + +1. Convert the p12 file into pem format (it will ask for the keystore password, which is usually "notasecret"): + openssl pkcs12 -in <my_keystore>.p12 -out <my_keystore>.pem -nodes + +2. Extract only the pk and remove passphrase + openssl rsa -in <my_keystore>.pem -out <my_key>.pem + +The last file (<my_key>.pem) should contain the pk that needs to be passed to `ContextBuilder.credential()` for the provider `google-compute-engine`. + + +Running the live tests: +-------- + +1. Place the following in your ~/.m2/settings.xml in a profile enabled when live: +``` + <test.google-compute-engine.identity>[email protected]</test.google-compute-engine.identity> + <test.google-compute-engine.credential>-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQRRbRqVDtJLN1MO/xJoKqZuphDeBh5jIKueW3aNIiWs1XFcct+h +-- this text is literally from your <my_key>.pem +aH7xmpHSTbbXmQkuuv+z8EKijigprd/FoJpTX1f5/R+4wQ== +-----END RSA PRIVATE KEY-----</test.google-compute-engine.credential> + </properties> +``` + +2. mvn clean install -Plive +
http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/pom.xml ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/pom.xml b/dependencies/jclouds/apis/gce/1.8.1-stratos/pom.xml new file mode 100644 index 0000000..c1a7b9d --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/pom.xml @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF 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> + <groupId>org.apache.jclouds.labs</groupId> + <artifactId>jclouds-labs-google</artifactId> + <version>1.8.1</version> + </parent> + + <!-- TODO: when out of labs, switch to org.jclouds.provider --> + <groupId>org.apache.stratos</groupId> + <artifactId>gce</artifactId> + <version>1.8.1-stratosv1</version> + <name>jclouds Google Compute Engine provider</name> + <description>jclouds components to access GoogleCompute</description> + <packaging>bundle</packaging> + + <properties> + <jclouds.version>1.8.1</jclouds.version> + <test.google-compute-engine.identity>Email associated with the Google API client_id + </test.google-compute-engine.identity> + <test.google-compute-engine.credential>Private key (PKCS12 file) associated with the Google API client_id + </test.google-compute-engine.credential> + <test.google-compute-engine.api-version>v1</test.google-compute-engine.api-version> + <test.google-compute-engine.build-version/> + <test.google-compute-engine.template>imageId=debian-7-wheezy-v20131120,locationId=us-central1-a,minRam=2048 + </test.google-compute-engine.template> + <jclouds.osgi.export>org.jclouds.googlecomputeengine*;version="${project.version}"</jclouds.osgi.export> + <jclouds.osgi.import> + org.jclouds.compute.internal;version="${jclouds.version}", + org.jclouds.rest.internal;version="${jclouds.version}", + org.jclouds*;version="${jclouds.version}", + * + </jclouds.osgi.import> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.jclouds</groupId> + <artifactId>jclouds-core</artifactId> + <version>${jclouds.version}</version> + </dependency> + <dependency> + <groupId>org.apache.jclouds</groupId> + <artifactId>jclouds-compute</artifactId> + <version>${jclouds.version}</version> + </dependency> + <dependency> + <groupId>org.apache.jclouds</groupId> + <artifactId>jclouds-compute</artifactId> + <version>${jclouds.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.jclouds</groupId> + <artifactId>jclouds-core</artifactId> + <version>${jclouds.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.jclouds.driver</groupId> + <artifactId>jclouds-slf4j</artifactId> + <version>${jclouds.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.jclouds.driver</groupId> + <artifactId>jclouds-sshj</artifactId> + <version>${jclouds.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <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>default-test</id> + <configuration> + <skipTests>true</skipTests> + </configuration> + </execution> + <execution> + <id>integration</id> + <phase>integration-test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration> + <systemPropertyVariables> + <test.google-compute-engine.identity>${test.google-compute-engine.identity} + </test.google-compute-engine.identity> + <test.google-compute-engine.credential> + ${test.google-compute-engine.credential} + </test.google-compute-engine.credential> + <test.google-compute-engine.api-version> + ${test.google-compute-engine.api-version} + </test.google-compute-engine.api-version> + <test.google-compute-engine.build-version> + ${test.google-compute-engine.build-version} + </test.google-compute-engine.build-version> + <test.google-compute-engine.template>${test.google-compute-engine.template} + </test.google-compute-engine.template> + </systemPropertyVariables> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java new file mode 100644 index 0000000..6440d91 --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.googlecomputeengine; + +import java.io.Closeable; + +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.jclouds.googlecomputeengine.features.AddressApi; +import org.jclouds.googlecomputeengine.features.DiskApi; +import org.jclouds.googlecomputeengine.features.FirewallApi; +import org.jclouds.googlecomputeengine.features.GlobalOperationApi; +import org.jclouds.googlecomputeengine.features.ImageApi; +import org.jclouds.googlecomputeengine.features.InstanceApi; +import org.jclouds.googlecomputeengine.features.MachineTypeApi; +import org.jclouds.googlecomputeengine.features.NetworkApi; +import org.jclouds.googlecomputeengine.features.ProjectApi; +import org.jclouds.googlecomputeengine.features.RegionApi; +import org.jclouds.googlecomputeengine.features.RegionOperationApi; +import org.jclouds.googlecomputeengine.features.RouteApi; +import org.jclouds.googlecomputeengine.features.SnapshotApi; +import org.jclouds.googlecomputeengine.features.ZoneApi; +import org.jclouds.googlecomputeengine.features.ZoneOperationApi; +import org.jclouds.rest.annotations.Delegate; + +import com.google.common.annotations.Beta; + + +/** + * Provides access to GoogleCompute. + * <p/> + * + * @see <a href="https://developers.google.com/compute/docs/reference/v1">api doc</a> + */ +@Beta +public interface GoogleComputeEngineApi extends Closeable { + + /** + * Provides access to Address features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + AddressApi getAddressApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Disk features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + DiskApi getDiskApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Firewall features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + FirewallApi getFirewallApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Global Operation features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + GlobalOperationApi getGlobalOperationApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Image features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + ImageApi getImageApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Instance features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + InstanceApi getInstanceApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to MachineType features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + MachineTypeApi getMachineTypeApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Network features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + NetworkApi getNetworkApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Project features + */ + @Delegate + ProjectApi getProjectApi(); + + /** + * Provides access to Region features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + RegionApi getRegionApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Region Operation features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + RegionOperationApi getRegionOperationApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Route features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + RouteApi getRouteApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Snapshot features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + SnapshotApi getSnapshotApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Zone features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + ZoneApi getZoneApiForProject(@PathParam("project") String projectName); + + /** + * Provides access to Zone Operation features + * + * @param projectName the name of the project + */ + @Delegate + @Path("/projects/{project}") + ZoneOperationApi getZoneOperationApiForProject(@PathParam("project") String projectName); + +} http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java new file mode 100644 index 0000000..544a851 --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApiMetadata.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.googlecomputeengine; + +import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; +import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GCE_PROVIDER_NAME; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_INTERVAL; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_TIMEOUT; +import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE; +import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM; +import static org.jclouds.reflect.Reflection2.typeToken; + +import java.net.URI; +import java.util.Properties; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.googlecomputeengine.compute.config.GoogleComputeEngineServiceContextModule; +import org.jclouds.googlecomputeengine.config.GoogleComputeEngineHttpApiModule; +import org.jclouds.googlecomputeengine.config.GoogleComputeEngineParserModule; +import org.jclouds.googlecomputeengine.config.OAuthModuleWithoutTypeAdapters; +import org.jclouds.oauth.v2.config.OAuthAuthenticationModule; +import org.jclouds.rest.internal.BaseHttpApiMetadata; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; + +/** + * Implementation of {@link ApiMetadata} for GoogleCompute v1 API + */ +public class GoogleComputeEngineApiMetadata extends BaseHttpApiMetadata<GoogleComputeEngineApi> { + + @Override + public Builder toBuilder() { + return new Builder().fromApiMetadata(this); + } + + public GoogleComputeEngineApiMetadata() { + this(new Builder()); + } + + protected GoogleComputeEngineApiMetadata(Builder builder) { + super(builder); + } + + public static Properties defaultProperties() { + Properties properties = BaseHttpApiMetadata.defaultProperties(); + properties.put("oauth.endpoint", "https://accounts.google.com/o/oauth2/token"); + properties.put(AUDIENCE, "https://accounts.google.com/o/oauth2/token"); + properties.put(SIGNATURE_OR_MAC_ALGORITHM, "RS256"); + properties.put(PROPERTY_SESSION_INTERVAL, 3600); + properties.setProperty(TEMPLATE, "osFamily=DEBIAN,osVersionMatches=7\\..*,locationId=us-central1-a,loginUser=jclouds"); + properties.put(OPERATION_COMPLETE_INTERVAL, 500); + properties.put(OPERATION_COMPLETE_TIMEOUT, 600000); + return properties; + } + + public static class Builder extends BaseHttpApiMetadata.Builder<GoogleComputeEngineApi, Builder> { + + protected Builder() { + id(GCE_PROVIDER_NAME) + .name("Google Compute Engine Api") + .identityName("Email associated with the Google API client_id") + .credentialName("Private key literal associated with the Google API client_id") + .documentation(URI.create("https://developers.google.com/compute/docs")) + .version("v1") + .defaultEndpoint("https://www.googleapis.com/compute/v1") + .defaultProperties(GoogleComputeEngineApiMetadata.defaultProperties()) + .view(typeToken(ComputeServiceContext.class)) + .defaultModules(ImmutableSet.<Class<? extends Module>>builder() + .add(GoogleComputeEngineHttpApiModule.class) + .add(GoogleComputeEngineParserModule.class) + .add(OAuthAuthenticationModule.class) + .add(OAuthModuleWithoutTypeAdapters.class) + .add(GoogleComputeEngineServiceContextModule.class) + .build()); + } + + @Override + public GoogleComputeEngineApiMetadata build() { + return new GoogleComputeEngineApiMetadata(this); + } + + @Override + protected Builder self() { + return this; + } + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java new file mode 100644 index 0000000..d20ff98 --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineConstants.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.googlecomputeengine; + +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; + +import com.google.common.annotations.Beta; + +public final class GoogleComputeEngineConstants { + + public static final String GCE_PROVIDER_NAME = "google-compute-engine"; + + /** + * The name of the project that keeps public resources. + */ + public static final String GOOGLE_PROJECT = "google"; + + public static final String CENTOS_PROJECT = "centos-cloud"; + + public static final String DEBIAN_PROJECT = "debian-cloud"; + + public static final String COMPUTE_SCOPE = "https://www.googleapis.com/auth/compute"; + + public static final String COMPUTE_READONLY_SCOPE = "https://www.googleapis.com/auth/compute.readonly"; + + public static final String STORAGE_READONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.read_only"; + + public static final String STORAGE_WRITEONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.write_only"; + + + /** + * The total time, in msecs, to wait for an operation to complete. + */ + @Beta + public static final String OPERATION_COMPLETE_TIMEOUT = "jclouds.google-compute-engine.operation-complete-timeout"; + + /** + * The interval, in msecs, between calls to check whether an operation has completed. + */ + @Beta + public static final String OPERATION_COMPLETE_INTERVAL = "jclouds.google-compute-engine.operation-complete-interval"; + + public static final Location GOOGLE_PROVIDER_LOCATION = new LocationBuilder().scope(LocationScope.PROVIDER).id + (GCE_PROVIDER_NAME).description(GCE_PROVIDER_NAME).build(); + + + /** + * The key we look for in instance metadata for the URI for the image the instance was created from. + */ + public static final String GCE_IMAGE_METADATA_KEY = "jclouds-image"; + + /** + * Metadata key to check for whether we should delete an instance's boot disk when we delete the instance. + */ + public static final String GCE_DELETE_BOOT_DISK_METADATA_KEY = "jclouds-delete-boot-disk"; + + /** + * The suffix we append to auto-created boot disk names. + */ + public static final String GCE_BOOT_DISK_SUFFIX = "boot-disk"; + + private GoogleComputeEngineConstants() { + throw new AssertionError("intentionally unimplemented"); + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineService.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineService.java b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineService.java new file mode 100644 index 0000000..3c140eb --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineService.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.googlecomputeengine.compute; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED; +import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_INTERVAL; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_TIMEOUT; +import static org.jclouds.util.Predicates2.retry; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; + +import org.jclouds.Constants; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.callables.RunScriptOnNode; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.internal.BaseComputeService; +import org.jclouds.compute.internal.PersistNodeCredentials; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; +import org.jclouds.compute.strategy.DestroyNodeStrategy; +import org.jclouds.compute.strategy.GetImageStrategy; +import org.jclouds.compute.strategy.GetNodeMetadataStrategy; +import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; +import org.jclouds.compute.strategy.ListNodesStrategy; +import org.jclouds.compute.strategy.RebootNodeStrategy; +import org.jclouds.compute.strategy.ResumeNodeStrategy; +import org.jclouds.compute.strategy.SuspendNodeStrategy; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.googlecomputeengine.GoogleComputeEngineApi; +import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions; +import org.jclouds.googlecomputeengine.config.UserProject; +import org.jclouds.googlecomputeengine.domain.Firewall; +import org.jclouds.googlecomputeengine.domain.Network; +import org.jclouds.googlecomputeengine.domain.Operation; +import org.jclouds.googlecomputeengine.features.FirewallApi; +import org.jclouds.http.HttpResponse; +import org.jclouds.scriptbuilder.functions.InitAdminAccess; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Atomics; +import com.google.common.util.concurrent.ListeningExecutorService; + +public class GoogleComputeEngineService extends BaseComputeService { + + private final Function<Set<? extends NodeMetadata>, Set<String>> findOrphanedGroups; + private final GroupNamingConvention.Factory namingConvention; + private final GoogleComputeEngineApi api; + private final Supplier<String> project; + private final Predicate<AtomicReference<Operation>> operationDonePredicate; + private final long operationCompleteCheckInterval; + private final long operationCompleteCheckTimeout; + + @Inject + protected GoogleComputeEngineService(ComputeServiceContext context, + Map<String, Credentials> credentialStore, + @Memoized Supplier<Set<? extends Image>> images, + @Memoized Supplier<Set<? extends Hardware>> hardwareProfiles, + @Memoized Supplier<Set<? extends Location>> locations, + ListNodesStrategy listNodesStrategy, + GetImageStrategy getImageStrategy, + GetNodeMetadataStrategy getNodeMetadataStrategy, + CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, + RebootNodeStrategy rebootNodeStrategy, + DestroyNodeStrategy destroyNodeStrategy, + ResumeNodeStrategy resumeNodeStrategy, + SuspendNodeStrategy suspendNodeStrategy, + Provider<TemplateBuilder> templateBuilderProvider, + @Named("DEFAULT") Provider<TemplateOptions> templateOptionsProvider, + @Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> + nodeRunning, + @Named(TIMEOUT_NODE_TERMINATED) Predicate<AtomicReference<NodeMetadata>> + nodeTerminated, + @Named(TIMEOUT_NODE_SUSPENDED) + Predicate<AtomicReference<NodeMetadata>> nodeSuspended, + InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, + InitAdminAccess initAdminAccess, + RunScriptOnNode.Factory runScriptOnNodeFactory, + PersistNodeCredentials persistNodeCredentials, + ComputeServiceConstants.Timeouts timeouts, + @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, + Optional<ImageExtension> imageExtension, + Optional<SecurityGroupExtension> securityGroupExtension, + Function<Set<? extends NodeMetadata>, Set<String>> findOrphanedGroups, + GroupNamingConvention.Factory namingConvention, + GoogleComputeEngineApi api, + @UserProject Supplier<String> project, + @Named("global") Predicate<AtomicReference<Operation>> operationDonePredicate, + @Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval, + @Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout) { + + super(context, credentialStore, images, hardwareProfiles, locations, listNodesStrategy, getImageStrategy, + getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, + resumeNodeStrategy, suspendNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning, + nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory, + persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension); + this.findOrphanedGroups = checkNotNull(findOrphanedGroups, "find orphaned groups function"); + this.namingConvention = checkNotNull(namingConvention, "naming convention factory"); + this.api = checkNotNull(api, "google compute api"); + this.project = checkNotNull(project, "user project name"); + this.operationDonePredicate = checkNotNull(operationDonePredicate, "operation completed predicate"); + this.operationCompleteCheckInterval = checkNotNull(operationCompleteCheckInterval, + "operation completed check interval"); + this.operationCompleteCheckTimeout = checkNotNull(operationCompleteCheckTimeout, + "operation completed check timeout"); + } + + @Override + protected synchronized void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) { + Set<String> orphanedGroups = findOrphanedGroups.apply(deadNodes); + for (String orphanedGroup : orphanedGroups) { + cleanUpNetworksAndFirewallsForGroup(orphanedGroup); + } + } + + + protected void cleanUpNetworksAndFirewallsForGroup(final String groupName) { + String resourceName = namingConvention.create().sharedNameForGroup(groupName); + final Network network = api.getNetworkApiForProject(project.get()).get(resourceName); + FirewallApi firewallApi = api.getFirewallApiForProject(project.get()); + Predicate<Firewall> firewallBelongsToNetwork = new Predicate<Firewall>() { + @Override + public boolean apply(Firewall input) { + return input != null && input.getNetwork().equals(network.getSelfLink()); + } + }; + + Set<AtomicReference<Operation>> operations = Sets.newHashSet(); + for (Firewall firewall : firewallApi.list().concat().filter(firewallBelongsToNetwork)) { + operations.add(new AtomicReference<Operation>(firewallApi.delete(firewall.getName()))); + } + + for (AtomicReference<Operation> operation : operations) { + retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval, + MILLISECONDS).apply(operation); + + if (operation.get().getHttpError().isPresent()) { + HttpResponse response = operation.get().getHttpError().get(); + logger.warn("delete orphaned firewall %s failed. Http Error Code: %d HttpError: %s", + operation.get().getTargetId(), response.getStatusCode(), response.getMessage()); + } + } + + AtomicReference<Operation> operation = Atomics.newReference(api.getNetworkApiForProject(project.get()).delete(resourceName)); + + retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval, + MILLISECONDS).apply(operation); + + if (operation.get().getHttpError().isPresent()) { + HttpResponse response = operation.get().getHttpError().get(); + logger.warn("delete orphaned network failed. Http Error Code: " + response.getStatusCode() + + " HttpError: " + response.getMessage()); + } + } + + + /** + * returns template options, except of type {@link org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions}. + */ + @Override + public GoogleComputeEngineTemplateOptions templateOptions() { + return GoogleComputeEngineTemplateOptions.class.cast(super.templateOptions()); + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java new file mode 100644 index 0000000..3a26d34 --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java @@ -0,0 +1,439 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.googlecomputeengine.compute; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.contains; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.tryFind; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.CENTOS_PROJECT; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.DEBIAN_PROJECT; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GCE_BOOT_DISK_SUFFIX; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GCE_DELETE_BOOT_DISK_METADATA_KEY; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.GCE_IMAGE_METADATA_KEY; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_INTERVAL; +import static org.jclouds.googlecomputeengine.GoogleComputeEngineConstants.OPERATION_COMPLETE_TIMEOUT; +import static org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig.Type; +import static org.jclouds.googlecomputeengine.predicates.InstancePredicates.isBootDisk; +import static org.jclouds.util.Predicates2.retry; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.Resource; +import javax.inject.Named; + +import org.jclouds.collect.Memoized; +import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.domain.Location; +import org.jclouds.domain.LoginCredentials; +import org.jclouds.googlecomputeengine.GoogleComputeEngineApi; +import org.jclouds.googlecomputeengine.compute.functions.FirewallTagNamingConvention; +import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions; +import org.jclouds.googlecomputeengine.config.UserProject; +import org.jclouds.googlecomputeengine.domain.Disk; +import org.jclouds.googlecomputeengine.domain.Image; +import org.jclouds.googlecomputeengine.domain.Instance; +import org.jclouds.googlecomputeengine.domain.Instance.AttachedDisk; +import org.jclouds.googlecomputeengine.domain.Instance.PersistentAttachedDisk; +import org.jclouds.googlecomputeengine.domain.InstanceInZone; +import org.jclouds.googlecomputeengine.domain.InstanceTemplate; +import org.jclouds.googlecomputeengine.domain.InstanceTemplate.PersistentDisk; +import org.jclouds.googlecomputeengine.domain.InstanceTemplate.PersistentDisk.Mode; +import org.jclouds.googlecomputeengine.domain.MachineType; +import org.jclouds.googlecomputeengine.domain.MachineTypeInZone; +import org.jclouds.googlecomputeengine.domain.Operation; +import org.jclouds.googlecomputeengine.domain.SlashEncodedIds; +import org.jclouds.googlecomputeengine.domain.Zone; +import org.jclouds.googlecomputeengine.features.InstanceApi; +import org.jclouds.http.HttpResponse; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.primitives.Ints; +import com.google.common.util.concurrent.Atomics; +import com.google.common.util.concurrent.UncheckedTimeoutException; +import com.google.inject.Inject; + +public class GoogleComputeEngineServiceAdapter implements ComputeServiceAdapter<InstanceInZone, MachineTypeInZone, Image, Zone> { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final GoogleComputeEngineApi api; + private final Supplier<String> userProject; + private final Supplier<Map<URI, ? extends Location>> zones; + private final Function<TemplateOptions, ImmutableMap.Builder<String, String>> metatadaFromTemplateOptions; + private final Predicate<AtomicReference<Operation>> retryOperationDonePredicate; + private final long operationCompleteCheckInterval; + private final long operationCompleteCheckTimeout; + private final FirewallTagNamingConvention.Factory firewallTagNamingConvention; + + @Inject + public GoogleComputeEngineServiceAdapter(GoogleComputeEngineApi api, + @UserProject Supplier<String> userProject, + Function<TemplateOptions, + ImmutableMap.Builder<String, String>> metatadaFromTemplateOptions, + @Named("zone") Predicate<AtomicReference<Operation>> operationDonePredicate, + @Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval, + @Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout, + @Memoized Supplier<Map<URI, ? extends Location>> zones, + FirewallTagNamingConvention.Factory firewallTagNamingConvention) { + this.api = checkNotNull(api, "google compute api"); + this.userProject = checkNotNull(userProject, "user project name"); + this.metatadaFromTemplateOptions = checkNotNull(metatadaFromTemplateOptions, + "metadata from template options function"); + this.operationCompleteCheckInterval = checkNotNull(operationCompleteCheckInterval, + "operation completed check interval"); + this.operationCompleteCheckTimeout = checkNotNull(operationCompleteCheckTimeout, + "operation completed check timeout"); + this.retryOperationDonePredicate = retry(operationDonePredicate, operationCompleteCheckTimeout, + operationCompleteCheckInterval, TimeUnit.MILLISECONDS); + this.zones = checkNotNull(zones, "zones"); + this.firewallTagNamingConvention = checkNotNull(firewallTagNamingConvention, "firewallTagNamingConvention"); + } + + @Override + public NodeAndInitialCredentials<InstanceInZone> createNodeWithGroupEncodedIntoName( + final String group, final String name, final Template template) { + + checkNotNull(template, "template"); + + GoogleComputeEngineTemplateOptions options = GoogleComputeEngineTemplateOptions.class.cast(template.getOptions()).clone(); + checkState(options.getNetwork().isPresent(), "network was not present in template options"); + Hardware hardware = checkNotNull(template.getHardware(), "hardware must be set"); + + checkNotNull(hardware.getUri(), "hardware must have a URI"); + checkNotNull(template.getImage().getUri(), "image URI is null"); + + // Note that the ordering is significant here - the first disk must be the boot disk. + List<PersistentDisk> disks = Lists.newArrayList(); + + if (!tryFind(options.getDisks(), isBootDisk()).isPresent()) { + Disk bootDisk = createBootDisk(template, name); + + disks.add(new PersistentDisk(Mode.READ_WRITE, + bootDisk.getSelfLink(), + null, + true, + true)); + } + + disks.addAll(options.getDisks()); + + InstanceTemplate instanceTemplate = InstanceTemplate.builder() + .forMachineType(hardware.getUri()); + + if (options.isEnableNat()) { + instanceTemplate.addNetworkInterface(options.getNetwork().get(), Type.ONE_TO_ONE_NAT); + } else { + instanceTemplate.addNetworkInterface(options.getNetwork().get()); + } + + instanceTemplate.disks(disks); + + LoginCredentials credentials = getFromImageAndOverrideIfRequired(template.getImage(), options); + + ImmutableMap.Builder<String, String> metadataBuilder = metatadaFromTemplateOptions.apply(options); + + metadataBuilder.put(GCE_IMAGE_METADATA_KEY, template.getImage().getUri().toString()); + + if (!options.shouldKeepBootDisk()) { + metadataBuilder.put(GCE_DELETE_BOOT_DISK_METADATA_KEY, Boolean.TRUE.toString()); + } + + instanceTemplate.metadata(metadataBuilder.build()); + instanceTemplate.serviceAccounts(options.getServiceAccounts()); + + final InstanceApi instanceApi = api.getInstanceApiForProject(userProject.get()); + final String zone = template.getLocation().getId(); + Operation operation = instanceApi.createInZone(name, zone, instanceTemplate); + + if (options.shouldBlockUntilRunning()) { + waitOperationDone(operation); + } + + // some times the newly created instances are not immediately returned + AtomicReference<Instance> instance = Atomics.newReference(); + + retry(new Predicate<AtomicReference<Instance>>() { + @Override + public boolean apply(AtomicReference<Instance> input) { + input.set(instanceApi.getInZone(zone, name)); + return input.get() != null; + } + }, operationCompleteCheckTimeout, operationCompleteCheckInterval, MILLISECONDS).apply(instance); + + if (!options.getTags().isEmpty()) { + Operation tagsOperation = instanceApi.setTagsInZone(zone, + name, options.getTags(), instance.get().getTags().getFingerprint()); + + waitOperationDone(tagsOperation); + + retry(new Predicate<AtomicReference<Instance>>() { + @Override + public boolean apply(AtomicReference<Instance> input) { + input.set(instanceApi.getInZone(zone, name)); + return input.get() != null; + } + }, operationCompleteCheckTimeout, operationCompleteCheckInterval, MILLISECONDS).apply(instance); + } + + // Add tags for security groups + final FirewallTagNamingConvention naming = firewallTagNamingConvention.get(group); + Set<String> tags = FluentIterable.from(Ints.asList(options.getInboundPorts())) + .transform(new Function<Integer, String>(){ + @Override + public String apply(Integer input) { + return input != null + ? naming.name(input) + : null; + } + }) + .toSet(); + instanceApi.setTagsInZone(zone, instance.get().getName(), tags, instance.get().getTags().getFingerprint()); + + InstanceInZone instanceInZone = new InstanceInZone(instance.get(), zone); + + return new NodeAndInitialCredentials<InstanceInZone>(instanceInZone, instanceInZone.slashEncode(), credentials); + } + + private Disk createBootDisk(Template template, String instanceName) { + URI imageUri = template.getImage().getUri(); + + GoogleComputeEngineTemplateOptions options = GoogleComputeEngineTemplateOptions.class.cast(template.getOptions()).clone(); + + int diskSize = options.getBootDiskSize().or(10l).intValue(); + + String diskName = instanceName + "-" + GCE_BOOT_DISK_SUFFIX; + + Operation diskOperation = api.getDiskApiForProject(userProject.get()) + .createFromImageWithSizeInZone(imageUri.toString(), + diskName, + diskSize, + template.getLocation().getId()); + + waitOperationDone(diskOperation); + + return api.getDiskApiForProject(userProject.get()).getInZone(template.getLocation().getId(), + diskName); + } + + @Override + public Iterable<MachineTypeInZone> listHardwareProfiles() { + ImmutableSet.Builder<MachineTypeInZone> builder = ImmutableSet.builder(); + + for (final Location zone : zones.get().values()) { + builder.addAll(api.getMachineTypeApiForProject(userProject.get()) + .listInZone(zone.getId()) + .concat() + .filter(new Predicate<MachineType>() { + @Override + public boolean apply(MachineType input) { + return !input.getDeprecated().isPresent(); + } + }) + .transform(new Function<MachineType, MachineTypeInZone>() { + + @Override + public MachineTypeInZone apply(MachineType arg0) { + return new MachineTypeInZone(arg0, arg0.getZone()); + } + })); + } + + return builder.build(); + } + + @Override + public Iterable<Image> listImages() { + return ImmutableSet.<Image>builder() + .addAll(api.getImageApiForProject(userProject.get()).list().concat()) + .addAll(api.getImageApiForProject(DEBIAN_PROJECT).list().concat()) + .addAll(api.getImageApiForProject(CENTOS_PROJECT).list().concat()) + .build(); + } + + @Override + public Image getImage(String id) { + return Objects.firstNonNull(api.getImageApiForProject(userProject.get()).get(id), + Objects.firstNonNull(api.getImageApiForProject(DEBIAN_PROJECT).get(id), + api.getImageApiForProject(CENTOS_PROJECT).get(id))); + + } + + @Override + public Iterable<Zone> listLocations() { + return api.getZoneApiForProject(userProject.get()).list().concat(); + } + + @Override + public InstanceInZone getNode(String name) { + SlashEncodedIds slashEncodedIds = SlashEncodedIds.fromSlashEncoded(name); + + Instance instance = api.getInstanceApiForProject(userProject.get()).getInZone(slashEncodedIds.getFirstId(), + slashEncodedIds.getSecondId()); + + return instance == null ? null : new InstanceInZone(instance, slashEncodedIds.getFirstId()); + } + + @Override + public Iterable<InstanceInZone> listNodes() { + return FluentIterable.from(zones.get().values()).transformAndConcat(new Function<Location, ImmutableSet<InstanceInZone>>() { + @Override + public ImmutableSet<InstanceInZone> apply(final Location input) { + return api.getInstanceApiForProject(userProject.get()).listInZone(input.getId()).concat() + .transform(new Function<Instance, InstanceInZone>() { + + @Override + public InstanceInZone apply(Instance arg0) { + return new InstanceInZone(arg0, input.getId()); + } + }).toSet(); + } + }).toSet(); + } + + @Override + public Iterable<InstanceInZone> listNodesByIds(final Iterable<String> ids) { + return filter(listNodes(), new Predicate<InstanceInZone>() { + + @Override + public boolean apply(InstanceInZone instanceInZone) { + return contains(ids, instanceInZone.getInstance().getName()); + } + }); + } + + @Override + public void destroyNode(final String name) { + SlashEncodedIds slashEncodedIds = SlashEncodedIds.fromSlashEncoded(name); + String diskName = null; + try { + Instance instance = api.getInstanceApiForProject(userProject.get()).getInZone(slashEncodedIds.getFirstId(), + slashEncodedIds.getSecondId()); + if (instance.getMetadata().getItems().get(GCE_DELETE_BOOT_DISK_METADATA_KEY).equals("true")) { + Optional<AttachedDisk> disk = tryFind(instance.getDisks(), new Predicate<AttachedDisk>() { + @Override + public boolean apply(AttachedDisk input) { + return PersistentAttachedDisk.class.isInstance(input) && + PersistentAttachedDisk.class.cast(input).isBoot(); + } + }); + if (disk.isPresent()) { + diskName = PersistentAttachedDisk.class.cast(disk.get()).getSourceDiskName(); + } + } + } catch (Exception e) { + // TODO: what exception actually gets thrown here if the instance doesn't really exist? + } + waitOperationDone(api.getInstanceApiForProject(userProject.get()).deleteInZone(slashEncodedIds.getFirstId(), + slashEncodedIds.getSecondId())); + + if (diskName != null) { + waitOperationDone(api.getDiskApiForProject(userProject.get()).deleteInZone(slashEncodedIds.getFirstId(), + diskName)); + } + + } + + @Override + public void rebootNode(final String name) { + SlashEncodedIds slashEncodedIds = SlashEncodedIds.fromSlashEncoded(name); + + waitOperationDone(api.getInstanceApiForProject(userProject.get()).resetInZone(slashEncodedIds.getFirstId(), + slashEncodedIds.getSecondId())); + } + + @Override + public void resumeNode(String name) { + throw new UnsupportedOperationException("resume is not supported by GCE"); + } + + @Override + public void suspendNode(String name) { + throw new UnsupportedOperationException("suspend is not supported by GCE"); + } + + private LoginCredentials getFromImageAndOverrideIfRequired(org.jclouds.compute.domain.Image image, + GoogleComputeEngineTemplateOptions options) { + LoginCredentials defaultCredentials = image.getDefaultCredentials(); + String[] keys = defaultCredentials.getPrivateKey().split(":"); + String publicKey = keys[0]; + String privateKey = keys[1]; + + LoginCredentials.Builder credentialsBuilder = defaultCredentials.toBuilder(); + credentialsBuilder.privateKey(privateKey); + + // LoginCredentials from image stores the public key along with the private key in the privateKey field + // @see GoogleComputePopulateDefaultLoginCredentialsForImageStrategy + // so if options doesn't have a public key set we set it from the default + if (options.getPublicKey() == null) { + options.authorizePublicKey(publicKey); + } + if (options.hasLoginPrivateKeyOption()) { + credentialsBuilder.privateKey(options.getPrivateKey()); + } + if (options.getLoginUser() != null) { + credentialsBuilder.identity(options.getLoginUser()); + } + if (options.hasLoginPasswordOption()) { + credentialsBuilder.password(options.getLoginPassword()); + } + if (options.shouldAuthenticateSudo() != null) { + credentialsBuilder.authenticateSudo(options.shouldAuthenticateSudo()); + } + LoginCredentials credentials = credentialsBuilder.build(); + options.overrideLoginCredentials(credentials); + return credentials; + } + + private void waitOperationDone(Operation operation) { + AtomicReference<Operation> operationRef = Atomics.newReference(operation); + + // wait for the operation to complete + if (!retryOperationDonePredicate.apply(operationRef)) { + throw new UncheckedTimeoutException("operation did not reach DONE state" + operationRef.get()); + } + + // check if the operation failed + if (operationRef.get().getHttpError().isPresent()) { + HttpResponse response = operationRef.get().getHttpError().get(); + throw new IllegalStateException("operation failed. Http Error Code: " + response.getStatusCode() + + " HttpError: " + response.getMessage()); + } + } + +} http://git-wip-us.apache.org/repos/asf/stratos/blob/b45ae00e/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java new file mode 100644 index 0000000..730c515 --- /dev/null +++ b/dependencies/jclouds/apis/gce/1.8.1-stratos/src/main/java/org/jclouds/googlecomputeengine/compute/config/GoogleComputeEngineServiceContextModule.java @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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.googlecomputeengine.compute.config; + +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Maps.uniqueIndex; +import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; + +import java.net.URI; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import javax.inject.Named; +import javax.inject.Singleton; + +import com.google.inject.Scopes; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.config.ComputeServiceAdapterContextModule; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.extensions.ImageExtension; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate; +import org.jclouds.domain.Location; +import org.jclouds.googlecomputeengine.GoogleComputeEngineApi; +import org.jclouds.googlecomputeengine.compute.GoogleComputeEngineService; +import org.jclouds.googlecomputeengine.compute.GoogleComputeEngineServiceAdapter; +import org.jclouds.googlecomputeengine.compute.extensions.GoogleComputeEngineSecurityGroupExtension; +import org.jclouds.googlecomputeengine.compute.functions.BuildInstanceMetadata; +import org.jclouds.googlecomputeengine.compute.functions.FirewallTagNamingConvention; +import org.jclouds.googlecomputeengine.compute.functions.FirewallToIpPermission; +import org.jclouds.googlecomputeengine.compute.functions.GoogleComputeEngineImageToImage; +import org.jclouds.googlecomputeengine.compute.functions.InstanceInZoneToNodeMetadata; +import org.jclouds.googlecomputeengine.compute.functions.MachineTypeInZoneToHardware; +import org.jclouds.googlecomputeengine.compute.functions.NetworkToSecurityGroup; +import org.jclouds.googlecomputeengine.compute.functions.OrphanedGroupsFromDeadNodes; +import org.jclouds.googlecomputeengine.compute.functions.RegionToLocation; +import org.jclouds.googlecomputeengine.compute.functions.ZoneToLocation; +import org.jclouds.googlecomputeengine.compute.loaders.FindNetworkOrCreate; +import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions; +import org.jclouds.googlecomputeengine.compute.predicates.AllNodesInGroupTerminated; +import org.jclouds.googlecomputeengine.compute.strategy.CreateNodesWithGroupEncodedIntoNameThenAddToSet; +import org.jclouds.googlecomputeengine.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy; +import org.jclouds.googlecomputeengine.compute.strategy.UseNodeCredentialsButOverrideFromTemplate; +import org.jclouds.googlecomputeengine.config.UserProject; +import org.jclouds.googlecomputeengine.domain.Firewall; +import org.jclouds.googlecomputeengine.domain.Image; +import org.jclouds.googlecomputeengine.domain.Instance; +import org.jclouds.googlecomputeengine.domain.InstanceInZone; +import org.jclouds.googlecomputeengine.domain.MachineTypeInZone; +import org.jclouds.googlecomputeengine.domain.Network; +import org.jclouds.googlecomputeengine.domain.Region; +import org.jclouds.googlecomputeengine.domain.Zone; +import org.jclouds.googlecomputeengine.domain.internal.NetworkAndAddressRange; +import org.jclouds.googlecomputeengine.functions.CreateNetworkIfNeeded; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.rest.AuthorizationException; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; +import com.google.inject.Injector; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; + +public class GoogleComputeEngineServiceContextModule + extends ComputeServiceAdapterContextModule<InstanceInZone, MachineTypeInZone, Image, Zone> { + + @Override + protected void configure() { + super.configure(); + + bind(ComputeService.class).to(GoogleComputeEngineService.class); + + bind(new TypeLiteral<ComputeServiceAdapter<InstanceInZone, MachineTypeInZone, Image, Zone>>() {}) + .to(GoogleComputeEngineServiceAdapter.class); + + bind(new TypeLiteral<Function<InstanceInZone, NodeMetadata>>() {}) + .to(InstanceInZoneToNodeMetadata.class); + + bind(new TypeLiteral<Function<MachineTypeInZone, Hardware>>() {}) + .to(MachineTypeInZoneToHardware.class); + + bind(new TypeLiteral<Function<Image, org.jclouds.compute.domain.Image>>() {}) + .to(GoogleComputeEngineImageToImage.class); + + bind(new TypeLiteral<Function<Region, Location>>() { + }) + .to(RegionToLocation.class); + + bind(new TypeLiteral<Function<Zone, Location>>() {}) + .to(ZoneToLocation.class); + + bind(new TypeLiteral<Function<Firewall, Iterable<IpPermission>>>() {}) + .to(FirewallToIpPermission.class); + + bind(new TypeLiteral<Function<Network, SecurityGroup>>() {}) + .to(NetworkToSecurityGroup.class); + + bind(new TypeLiteral<Function<TemplateOptions, ImmutableMap.Builder<String, String>>>() {}) + .to(BuildInstanceMetadata.class); + + bind(org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy.class) + .to(PopulateDefaultLoginCredentialsForImageStrategy.class); + + bind(org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to( + CreateNodesWithGroupEncodedIntoNameThenAddToSet.class); + + bind(TemplateOptions.class).to(GoogleComputeEngineTemplateOptions.class); + + bind(new TypeLiteral<Function<Set<? extends NodeMetadata>, Set<String>>>() {}) + .to(OrphanedGroupsFromDeadNodes.class); + + bind(new TypeLiteral<Predicate<String>>() {}).to(AllNodesInGroupTerminated.class); + + bind(new TypeLiteral<Function<NetworkAndAddressRange, Network>>() {}) + .to(CreateNetworkIfNeeded.class); + + bind(new TypeLiteral<CacheLoader<NetworkAndAddressRange, Network>>() {}) + .to(FindNetworkOrCreate.class); + + bind(new TypeLiteral<SecurityGroupExtension>() {}) + .to(GoogleComputeEngineSecurityGroupExtension.class); + + bind(PrioritizeCredentialsFromTemplate.class).to(UseNodeCredentialsButOverrideFromTemplate.class); + + install(new LocationsFromComputeServiceAdapterModule<InstanceInZone, MachineTypeInZone, Image, Zone>() {}); + + bind(FirewallTagNamingConvention.Factory.class).in(Scopes.SINGLETON); + } + + @Provides + @Singleton + @Memoized + public Supplier<Map<URI, ? extends org.jclouds.compute.domain.Image>> provideImagesMap( + AtomicReference<AuthorizationException> authException, + final Supplier<Set<? extends org.jclouds.compute.domain.Image>> images, + @Named(PROPERTY_SESSION_INTERVAL) long seconds) { + return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, + new Supplier<Map<URI, ? extends org.jclouds.compute.domain.Image>>() { + @Override + public Map<URI, ? extends org.jclouds.compute.domain.Image> get() { + return uniqueIndex(images.get(), new Function<org.jclouds.compute.domain.Image, URI>() { + @Override + public URI apply(org.jclouds.compute.domain.Image input) { + return input.getUri(); + } + }); + } + }, + seconds, TimeUnit.SECONDS); + } + + @Provides + @Singleton + @Memoized + public Supplier<Map<URI, ? extends Hardware>> provideHardwaresMap( + AtomicReference<AuthorizationException> authException, + final Supplier<Set<? extends Hardware>> hardwares, + @Named(PROPERTY_SESSION_INTERVAL) long seconds) { + return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, + new Supplier<Map<URI, ? extends Hardware>>() { + @Override + public Map<URI, ? extends Hardware> get() { + return uniqueIndex(hardwares.get(), new Function<Hardware, URI>() { + @Override + public URI apply(Hardware input) { + return input.getUri(); + } + }); + } + }, + seconds, TimeUnit.SECONDS); + } + + @Provides + @Singleton + @Memoized + public Supplier<Map<URI, ? extends Location>> provideZones( + AtomicReference<AuthorizationException> authException, + final GoogleComputeEngineApi api, final Function<Zone, Location> zoneToLocation, + @UserProject final Supplier<String> userProject, + @Named(PROPERTY_SESSION_INTERVAL) long seconds) { + return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, + new Supplier<Map<URI, ? extends Location>>() { + @Override + public Map<URI, ? extends Location> get() { + return uniqueIndex(transform(api.getZoneApiForProject(userProject.get()).list().concat(), zoneToLocation), + new Function<Location, URI>() { + @Override + public URI apply(Location input) { + return (URI) input.getMetadata().get("selfLink"); + } + }); + } + }, + seconds, TimeUnit.SECONDS); + } + + @Provides + @Singleton + @Memoized + public Supplier<Map<URI, Region>> provideRegions( + AtomicReference<AuthorizationException> authException, + final GoogleComputeEngineApi api, + @UserProject final Supplier<String> userProject, + @Named(PROPERTY_SESSION_INTERVAL) long seconds) { + return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, + new Supplier<Map<URI, Region>>() { + @Override + public Map<URI, Region> get() { + return uniqueIndex(api.getRegionApiForProject(userProject.get()).list().concat(), + new Function<Region, URI>() { + @Override + public URI apply(Region input) { + return input.getSelfLink(); + } + }); + } + }, + seconds, TimeUnit.SECONDS); + } + + @Provides + @Singleton + protected LoadingCache<NetworkAndAddressRange, Network> networkMap( + CacheLoader<NetworkAndAddressRange, Network> in) { + return CacheBuilder.newBuilder().build(in); + } + + @Override + protected Optional<ImageExtension> provideImageExtension(Injector i) { + return Optional.absent(); + } + + @Override + protected Optional<SecurityGroupExtension> provideSecurityGroupExtension(Injector i) { + return Optional.of(i.getInstance(SecurityGroupExtension.class)); + } + + @VisibleForTesting + public static final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus = + ImmutableMap.<Instance.Status, NodeMetadata.Status>builder() + .put(Instance.Status.PROVISIONING, NodeMetadata.Status.PENDING) + .put(Instance.Status.STAGING, NodeMetadata.Status.PENDING) + .put(Instance.Status.RUNNING, NodeMetadata.Status.RUNNING) + .put(Instance.Status.STOPPING, NodeMetadata.Status.PENDING) + .put(Instance.Status.STOPPED, NodeMetadata.Status.SUSPENDED) + .put(Instance.Status.TERMINATED, NodeMetadata.Status.TERMINATED).build(); + + @Singleton + @Provides + protected Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus() { + return toPortableNodeStatus; + } +}
