mirror of https://github.com/apache/jclouds.git
gce-compute -
- addressed reviewer changes - increased test coverage - addressed resource leak - updated README
This commit is contained in:
parent
d4a3c66e3c
commit
413d64f60f
|
@ -1,16 +1,54 @@
|
|||
Status:
|
||||
jclouds Google Compute Engine Provider
|
||||
======
|
||||
|
||||
All the private apis are implemented and tested.
|
||||
Snapshots are disabled because they are also disabled in GCE.
|
||||
Just missing jcloud.compute glue code (coming soon!)
|
||||
|
||||
How to run the live tests:
|
||||
Authenticating into the instances:
|
||||
--------
|
||||
|
||||
Pre-requisites:
|
||||
A Google Api account with Google Compute Engine enabled
|
||||
The access pk (provided by google in PKCS12 format) in pem format.
|
||||
User:
|
||||
If no user is provided in GoogleComputeTemplateOptions when launching an instance by defaul "jclouds" is used.
|
||||
|
||||
running all tests:
|
||||
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 GoogleComputeTemplateOptions when createNodesInGroup is called.
|
||||
2 - an instance of GoogleComputeTemplateOptions 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 crendential 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-gce 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 google-compute as a string literal property named "google-compute.credential".
|
||||
|
||||
|
||||
Running the live tests:
|
||||
--------
|
||||
|
||||
mvn clean install -Plive -Dtest.google-compute.identity=<my account>@developer.gserviceaccount.com -Dtest.google-compute.credential=<path to pk.pem>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -63,6 +63,13 @@
|
|||
<artifactId>jclouds-compute</artifactId>
|
||||
<version>${jclouds.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jclouds</groupId>
|
||||
<artifactId>jclouds-compute</artifactId>
|
||||
<version>${jclouds.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jclouds</groupId>
|
||||
<artifactId>jclouds-core</artifactId>
|
||||
|
@ -77,7 +84,13 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<groupId>org.jclouds.driver</groupId>
|
||||
<artifactId>jclouds-sshj</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -18,14 +18,12 @@
|
|||
*/
|
||||
package org.jclouds.googlecompute;
|
||||
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
|
||||
import static org.jclouds.oauth.v2.config.OAuthProperties.SIGNATURE_OR_MAC_ALGORITHM;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Module;
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.compute.ComputeServiceContext;
|
||||
import org.jclouds.googlecompute.compute.config.GoogleComputeServiceContextModule;
|
||||
import org.jclouds.googlecompute.config.GoogleComputeParserModule;
|
||||
import org.jclouds.googlecompute.config.GoogleComputeRestClientModule;
|
||||
import org.jclouds.googlecompute.config.OAuthModuleWithoutTypeAdapters;
|
||||
|
@ -33,9 +31,14 @@ import org.jclouds.oauth.v2.config.OAuthAuthenticationModule;
|
|||
import org.jclouds.rest.RestContext;
|
||||
import org.jclouds.rest.internal.BaseRestApiMetadata;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.Module;
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ApiMetadata} for GoogleCompute v1beta13 API
|
||||
|
@ -44,9 +47,8 @@ import com.google.inject.Module;
|
|||
*/
|
||||
public class GoogleComputeApiMetadata extends BaseRestApiMetadata {
|
||||
|
||||
public static final TypeToken<RestContext<GoogleComputeApi, GoogleComputeAsyncApi>> CONTEXT_TOKEN = new TypeToken<RestContext<GoogleComputeApi, GoogleComputeAsyncApi>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
};
|
||||
public static final TypeToken<RestContext<GoogleComputeApi, GoogleComputeAsyncApi>> CONTEXT_TOKEN = new
|
||||
TypeToken<RestContext<GoogleComputeApi, GoogleComputeAsyncApi>>() {};
|
||||
|
||||
@Override
|
||||
public Builder toBuilder() {
|
||||
|
@ -67,6 +69,10 @@ public class GoogleComputeApiMetadata extends BaseRestApiMetadata {
|
|||
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=GCEL,osVersionMatches=1[012].[01][04],locationId=us-central1-a," +
|
||||
"loginUser=jclouds");
|
||||
properties.put(GoogleComputeConstants.OPERATION_COMPLETE_INTERVAL, 500);
|
||||
properties.put(GoogleComputeConstants.OPERATION_COMPLETE_TIMEOUT, 600000);
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -82,11 +88,14 @@ public class GoogleComputeApiMetadata extends BaseRestApiMetadata {
|
|||
.version("v1beta13")
|
||||
.defaultEndpoint("https://www.googleapis.com/compute/v1beta13")
|
||||
.defaultProperties(GoogleComputeApiMetadata.defaultProperties())
|
||||
.view(typeToken(ComputeServiceContext.class))
|
||||
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
|
||||
.add(GoogleComputeRestClientModule.class)
|
||||
.add(GoogleComputeParserModule.class)
|
||||
.add(OAuthAuthenticationModule.class)
|
||||
.add(OAuthModuleWithoutTypeAdapters.class).build());
|
||||
.add(OAuthModuleWithoutTypeAdapters.class)
|
||||
.add(GoogleComputeServiceContextModule.class)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,21 +19,40 @@
|
|||
|
||||
package org.jclouds.googlecompute;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.domain.LocationBuilder;
|
||||
import org.jclouds.domain.LocationScope;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public interface GoogleComputeConstants {
|
||||
|
||||
public static final String GOOGLE_PROVIDER_NAME = "google-compute";
|
||||
|
||||
/**
|
||||
* The name of the project that keeps public resources.
|
||||
*/
|
||||
public static final String GOOGLE_PROJECT = "google";
|
||||
|
||||
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";
|
||||
|
||||
/**
|
||||
* TODO storage scopes should be moved to the GCS api
|
||||
* The total time, in msecs, to wait for an operation to complete.
|
||||
*/
|
||||
@Beta
|
||||
public static final String OPERATION_COMPLETE_TIMEOUT = "jclouds.google-compute.operation-complete-timeout";
|
||||
|
||||
public static final String STORAGE_WRITEONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.write_only";
|
||||
/**
|
||||
* The interval, in msecs, between calls to check whether an operation has completed.
|
||||
*/
|
||||
@Beta
|
||||
public static final String OPERATION_COMPLETE_INTERVAL = "jclouds.google-compute.operation-complete-interval";
|
||||
|
||||
public static final String STORAGE_READONLY_SCOPE = "https://www.googleapis.com/auth/devstorage.read_only";
|
||||
public static final Location GOOGLE_PROVIDER_LOCATION = new LocationBuilder().scope(LocationScope.PROVIDER).id
|
||||
(GOOGLE_PROVIDER_NAME).description(GOOGLE_PROVIDER_NAME).build();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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.googlecompute.compute;
|
||||
|
||||
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.util.concurrent.ListeningExecutorService;
|
||||
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.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.googlecompute.GoogleComputeApi;
|
||||
import org.jclouds.googlecompute.compute.options.GoogleComputeTemplateOptions;
|
||||
import org.jclouds.googlecompute.config.UserProject;
|
||||
import org.jclouds.googlecompute.domain.Operation;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
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.googlecompute.GoogleComputeConstants.OPERATION_COMPLETE_INTERVAL;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.OPERATION_COMPLETE_TIMEOUT;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public class GoogleComputeService extends BaseComputeService {
|
||||
|
||||
private final Function<Set<? extends NodeMetadata>, Set<String>> findOrphanedGroups;
|
||||
private final GroupNamingConvention.Factory namingConvention;
|
||||
private final GoogleComputeApi api;
|
||||
private final Supplier<String> project;
|
||||
private final Predicate<AtomicReference<Operation>> operationDonePredicate;
|
||||
private final long operationCompleteCheckInterval;
|
||||
private final long operationCompleteCheckTimeout;
|
||||
|
||||
@Inject
|
||||
protected GoogleComputeService(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,
|
||||
Function<Set<? extends NodeMetadata>, Set<String>> findOrphanedGroups,
|
||||
GroupNamingConvention.Factory namingConvention,
|
||||
GoogleComputeApi api,
|
||||
@UserProject Supplier<String> project,
|
||||
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);
|
||||
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(String groupName) {
|
||||
String resourceName = namingConvention.create().sharedNameForGroup(groupName);
|
||||
AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getFirewallApiForProject(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 firewall failed. Http Error Code: " + response.getStatusCode() +
|
||||
" HttpError: " + response.getMessage());
|
||||
}
|
||||
|
||||
operation = new AtomicReference<Operation>(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 GoogleComputeTemplateOptions}.
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions templateOptions() {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.templateOptions());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* 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.googlecompute.compute;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.UncheckedTimeoutException;
|
||||
import com.google.inject.Inject;
|
||||
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.LoginCredentials;
|
||||
import org.jclouds.googlecompute.GoogleComputeApi;
|
||||
import org.jclouds.googlecompute.compute.options.GoogleComputeTemplateOptions;
|
||||
import org.jclouds.googlecompute.config.UserProject;
|
||||
import org.jclouds.googlecompute.domain.Image;
|
||||
import org.jclouds.googlecompute.domain.Instance;
|
||||
import org.jclouds.googlecompute.domain.InstanceTemplate;
|
||||
import org.jclouds.googlecompute.domain.MachineType;
|
||||
import org.jclouds.googlecompute.domain.Operation;
|
||||
import org.jclouds.googlecompute.domain.Zone;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.logging.Logger;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Named;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.GOOGLE_PROJECT;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.OPERATION_COMPLETE_INTERVAL;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.OPERATION_COMPLETE_TIMEOUT;
|
||||
import static org.jclouds.googlecompute.domain.Instance.NetworkInterface.AccessConfig.Type;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public class GoogleComputeServiceAdapter implements ComputeServiceAdapter<Instance, MachineType, Image, Zone> {
|
||||
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
private final GoogleComputeApi api;
|
||||
private final Supplier<String> userProject;
|
||||
private final Function<TemplateOptions, ImmutableMap.Builder<String, String>> metatadaFromTemplateOptions;
|
||||
private final Predicate<AtomicReference<Operation>> retryOperationDonePredicate;
|
||||
private final long operationCompleteCheckInterval;
|
||||
private final long operationCompleteCheckTimeout;
|
||||
|
||||
@Inject
|
||||
public GoogleComputeServiceAdapter(GoogleComputeApi api,
|
||||
@UserProject Supplier<String> userProject,
|
||||
Function<TemplateOptions,
|
||||
ImmutableMap.Builder<String, String>> metatadaFromTemplateOptions,
|
||||
Predicate<AtomicReference<Operation>> operationDonePredicate,
|
||||
@Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval,
|
||||
@Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeAndInitialCredentials<Instance> createNodeWithGroupEncodedIntoName(
|
||||
final String group, final String name, Template template) {
|
||||
|
||||
checkNotNull(template, "template");
|
||||
|
||||
GoogleComputeTemplateOptions options = GoogleComputeTemplateOptions.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");
|
||||
URI machineType = checkNotNull(hardware.getUri(), "hardware uri must be set");
|
||||
|
||||
InstanceTemplate instanceTemplate = InstanceTemplate.builder()
|
||||
.forMachineType(machineType);
|
||||
|
||||
if (options.isEnableNat()) {
|
||||
instanceTemplate.addNetworkInterface(options.getNetwork().get(), Type.ONE_TO_ONE_NAT);
|
||||
} else {
|
||||
instanceTemplate.addNetworkInterface(options.getNetwork().get());
|
||||
}
|
||||
|
||||
LoginCredentials credentials = getFromImageAndOverrideIfRequired(template.getImage(), options);
|
||||
|
||||
ImmutableMap.Builder<String, String> metadataBuilder = metatadaFromTemplateOptions.apply(options);
|
||||
instanceTemplate.metadata(metadataBuilder.build());
|
||||
instanceTemplate.tags(options.getTags());
|
||||
instanceTemplate.serviceAccounts(options.getServiceAccounts());
|
||||
instanceTemplate.image(checkNotNull(template.getImage().getUri(), "image URI is null"));
|
||||
|
||||
Operation operation = api.getInstanceApiForProject(userProject.get())
|
||||
.createInZone(name, instanceTemplate, template.getLocation().getId());
|
||||
|
||||
if (options.shouldBlockUntilRunning()) {
|
||||
waitOperationDone(operation);
|
||||
}
|
||||
|
||||
// some times the newly created instances are not immediately returned
|
||||
AtomicReference<Instance> instance = new AtomicReference<Instance>();
|
||||
|
||||
retry(new Predicate<AtomicReference<Instance>>() {
|
||||
@Override
|
||||
public boolean apply(AtomicReference<Instance> input) {
|
||||
input.set(api.getInstanceApiForProject(userProject.get()).get(name));
|
||||
return input.get() != null;
|
||||
}
|
||||
}, operationCompleteCheckTimeout, operationCompleteCheckInterval, MILLISECONDS).apply(instance);
|
||||
|
||||
return new NodeAndInitialCredentials<Instance>(instance.get(), name, credentials);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Iterable<MachineType> listHardwareProfiles() {
|
||||
return api.getMachineTypeApiForProject(userProject.get()).list().concat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Image> listImages() {
|
||||
return ImmutableSet.<Image>builder()
|
||||
.addAll(api.getImageApiForProject(userProject.get()).list().concat())
|
||||
.addAll(api.getImageApiForProject(GOOGLE_PROJECT).list().concat())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getImage(String id) {
|
||||
return Objects.firstNonNull(api.getImageApiForProject(userProject.get()).get(id),
|
||||
api.getImageApiForProject(GOOGLE_PROJECT).get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Zone> listLocations() {
|
||||
return api.getZoneApiForProject(userProject.get()).list().concat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instance getNode(String name) {
|
||||
return api.getInstanceApiForProject(userProject.get()).get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Instance> listNodes() {
|
||||
return api.getInstanceApiForProject(userProject.get()).list().concat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyNode(final String name) {
|
||||
waitOperationDone(api.getInstanceApiForProject(userProject.get()).delete(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rebootNode(String name) {
|
||||
throw new UnsupportedOperationException("reboot is not supported by GCE");
|
||||
}
|
||||
|
||||
@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,
|
||||
GoogleComputeTemplateOptions 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 = new AtomicReference<Operation>(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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.config;
|
||||
|
||||
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.collect.ImmutableMap;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.TypeLiteral;
|
||||
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.extensions.ImageExtension;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
|
||||
import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
|
||||
import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.googlecompute.GoogleComputeApi;
|
||||
import org.jclouds.googlecompute.compute.GoogleComputeService;
|
||||
import org.jclouds.googlecompute.compute.GoogleComputeServiceAdapter;
|
||||
import org.jclouds.googlecompute.compute.functions.BuildInstanceMetadata;
|
||||
import org.jclouds.googlecompute.compute.functions.GoogleComputeImageToImage;
|
||||
import org.jclouds.googlecompute.compute.functions.InstanceToNodeMetadata;
|
||||
import org.jclouds.googlecompute.compute.functions.MachineTypeToHardware;
|
||||
import org.jclouds.googlecompute.compute.functions.OrphanedGroupsFromDeadNodes;
|
||||
import org.jclouds.googlecompute.compute.functions.ZoneToLocation;
|
||||
import org.jclouds.googlecompute.compute.options.GoogleComputeTemplateOptions;
|
||||
import org.jclouds.googlecompute.compute.predicates.AllNodesInGroupTerminated;
|
||||
import org.jclouds.googlecompute.compute.strategy.ApplyTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||
import org.jclouds.googlecompute.compute.strategy.GoogleComputePopulateDefaultLoginCredentialsForImageStrategy;
|
||||
import org.jclouds.googlecompute.compute.strategy.UseNodeCredentialsButOverrideFromTemplate;
|
||||
import org.jclouds.googlecompute.config.UserProject;
|
||||
import org.jclouds.googlecompute.domain.Image;
|
||||
import org.jclouds.googlecompute.domain.Instance;
|
||||
import org.jclouds.googlecompute.domain.MachineType;
|
||||
import org.jclouds.googlecompute.domain.Zone;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Maps.uniqueIndex;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public class GoogleComputeServiceContextModule
|
||||
extends ComputeServiceAdapterContextModule<Instance, MachineType, Image, Zone> {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
|
||||
bind(ComputeService.class).to(GoogleComputeService.class);
|
||||
|
||||
bind(new TypeLiteral<ComputeServiceAdapter<Instance, MachineType, Image, Zone>>() {})
|
||||
.to(GoogleComputeServiceAdapter.class);
|
||||
|
||||
bind(new TypeLiteral<Function<Instance, NodeMetadata>>() {})
|
||||
.to(InstanceToNodeMetadata.class);
|
||||
|
||||
bind(new TypeLiteral<Function<MachineType, Hardware>>() {})
|
||||
.to(MachineTypeToHardware.class);
|
||||
|
||||
bind(new TypeLiteral<Function<Image, org.jclouds.compute.domain.Image>>() {})
|
||||
.to(GoogleComputeImageToImage.class);
|
||||
|
||||
bind(new TypeLiteral<Function<Zone, Location>>() {})
|
||||
.to(ZoneToLocation.class);
|
||||
|
||||
bind(new TypeLiteral<Function<TemplateOptions, ImmutableMap.Builder<String, String>>>() {})
|
||||
.to(BuildInstanceMetadata.class);
|
||||
|
||||
bind(PopulateDefaultLoginCredentialsForImageStrategy.class)
|
||||
.to(GoogleComputePopulateDefaultLoginCredentialsForImageStrategy.class);
|
||||
|
||||
bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to(
|
||||
ApplyTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
|
||||
|
||||
bind(TemplateOptions.class).to(GoogleComputeTemplateOptions.class);
|
||||
|
||||
bind(new TypeLiteral<Function<Set<? extends NodeMetadata>, Set<String>>>() {})
|
||||
.to(OrphanedGroupsFromDeadNodes.class);
|
||||
|
||||
bind(new TypeLiteral<Predicate<String>>() {}).to(AllNodesInGroupTerminated.class);
|
||||
|
||||
bind(PrioritizeCredentialsFromTemplate.class).to(UseNodeCredentialsButOverrideFromTemplate.class);
|
||||
|
||||
install(new LocationsFromComputeServiceAdapterModule<Instance, MachineType, Image, Zone>() {});
|
||||
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Memoized
|
||||
public Supplier<Map<URI, ? extends org.jclouds.compute.domain.Image>> provideImagesMap(
|
||||
final Supplier<Set<? extends org.jclouds.compute.domain.Image>> images) {
|
||||
return 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Memoized
|
||||
public Supplier<Map<URI, ? extends Hardware>> provideHardwaresMap(
|
||||
final Supplier<Set<? extends Hardware>> hardwares) {
|
||||
return 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Memoized
|
||||
public Supplier<Map<URI, ? extends Location>> provideLocations(
|
||||
final GoogleComputeApi api, final Function<Zone, Location> zoneToLocation,
|
||||
final @UserProject Supplier<String> userProject) {
|
||||
return 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");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<ImageExtension> provideImageExtension(Injector i) {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.functions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Prepares metadata from the provided TemplateOptions
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class BuildInstanceMetadata implements Function<TemplateOptions, ImmutableMap.Builder<String, String>> {
|
||||
|
||||
@Override
|
||||
public ImmutableMap.Builder apply(TemplateOptions input) {
|
||||
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
if (input.getPublicKey() != null) {
|
||||
builder.put("sshKeys", format("%s:%s %s@localhost", checkNotNull(input.getLoginUser(),
|
||||
"loginUser cannot be null"), input.getPublicKey(), input.getLoginUser()));
|
||||
}
|
||||
builder.putAll(input.getUserMetadata());
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.functions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jclouds.compute.domain.ImageBuilder;
|
||||
import org.jclouds.compute.domain.OperatingSystem;
|
||||
import org.jclouds.compute.domain.OsFamily;
|
||||
import org.jclouds.googlecompute.GoogleComputeConstants;
|
||||
import org.jclouds.googlecompute.domain.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Joiner.on;
|
||||
import static com.google.common.collect.Iterables.getLast;
|
||||
import static com.google.common.collect.Iterables.limit;
|
||||
import static com.google.common.collect.Iterables.skip;
|
||||
import static org.jclouds.compute.domain.Image.Status;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.GOOGLE_PROVIDER_LOCATION;
|
||||
|
||||
/**
|
||||
* Transforms a google compute domain specific image to a generic Image object.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class GoogleComputeImageToImage implements Function<Image, org.jclouds.compute.domain.Image> {
|
||||
|
||||
|
||||
@Override
|
||||
public org.jclouds.compute.domain.Image apply(Image image) {
|
||||
ImageBuilder builder = new ImageBuilder()
|
||||
.id(image.getName())
|
||||
.name(image.getName())
|
||||
.providerId(image.getId())
|
||||
.description(image.getDescription().orNull())
|
||||
.status(Status.AVAILABLE)
|
||||
.location(GOOGLE_PROVIDER_LOCATION)
|
||||
.uri(image.getSelfLink());
|
||||
|
||||
List<String> splits = Lists.newArrayList(image.getName().split("-"));
|
||||
OperatingSystem.Builder osBuilder = defaultOperatingSystem(image);
|
||||
if (splits == null || splits.size() == 0 || splits.size() < 3) {
|
||||
return builder.operatingSystem(osBuilder.build()).build();
|
||||
}
|
||||
|
||||
OsFamily family = OsFamily.fromValue(splits.get(0));
|
||||
if (family != OsFamily.UNRECOGNIZED) {
|
||||
osBuilder.family(family);
|
||||
}
|
||||
|
||||
String version = on(".").join(limit(skip(splits, 1), splits.size() - 2));
|
||||
osBuilder.version(version);
|
||||
|
||||
builder.version(getLast(splits));
|
||||
return builder.operatingSystem(osBuilder.build()).build();
|
||||
}
|
||||
|
||||
private OperatingSystem.Builder defaultOperatingSystem(Image image) {
|
||||
return OperatingSystem.builder()
|
||||
.family(OsFamily.LINUX)
|
||||
.is64Bit(true)
|
||||
.description(image.getName());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.functions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.jclouds.collect.Memoized;
|
||||
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.functions.GroupNamingConvention;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.googlecompute.domain.Instance;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Transforms a google compute domain Instance into a generic NodeMetatada object.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class InstanceToNodeMetadata implements Function<Instance, NodeMetadata> {
|
||||
|
||||
private final Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus;
|
||||
private final GroupNamingConvention nodeNamingConvention;
|
||||
private final Supplier<Map<URI, ? extends Image>> images;
|
||||
private final Supplier<Map<URI, ? extends Hardware>> hardwares;
|
||||
private final Supplier<Map<URI, ? extends Location>> locations;
|
||||
|
||||
@Inject
|
||||
public InstanceToNodeMetadata(Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus,
|
||||
GroupNamingConvention.Factory namingConvention,
|
||||
@Memoized Supplier<Map<URI, ? extends Image>> images,
|
||||
@Memoized Supplier<Map<URI, ? extends Hardware>> hardwares,
|
||||
@Memoized Supplier<Map<URI, ? extends Location>> locations) {
|
||||
this.toPortableNodeStatus = toPortableNodeStatus;
|
||||
this.nodeNamingConvention = namingConvention.createWithoutPrefix();
|
||||
this.images = images;
|
||||
this.hardwares = hardwares;
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeMetadata apply(Instance input) {
|
||||
Map<URI, ? extends Image> imagesMap = images.get();
|
||||
Image image = checkNotNull(imagesMap.get(checkNotNull(input.getImage(), "image")),
|
||||
"no image for %s. images: %s", input.getImage(), imagesMap.values());
|
||||
|
||||
return new NodeMetadataBuilder()
|
||||
.id(input.getName())
|
||||
.name(input.getName())
|
||||
.providerId(input.getId())
|
||||
.hostname(input.getName())
|
||||
.imageId(image.getId())
|
||||
.location(checkNotNull(locations.get().get(input.getZone()), "location for %s", input.getZone()))
|
||||
.hardware(checkNotNull(hardwares.get().get(input.getMachineType()), "hardware type for %s",
|
||||
input.getMachineType().toString()))
|
||||
.operatingSystem(image.getOperatingSystem())
|
||||
.status(toPortableNodeStatus.get(input.getStatus()))
|
||||
.tags(input.getTags())
|
||||
.uri(input.getSelfLink())
|
||||
.userMetadata(input.getMetadata())
|
||||
.group(nodeNamingConvention.groupInUniqueNameOrNull(input.getName()))
|
||||
.privateAddresses(collectPrivateAddresses(input))
|
||||
.publicAddresses(collectPublicAddresses(input))
|
||||
.build();
|
||||
}
|
||||
|
||||
private Set<String> collectPrivateAddresses(Instance input) {
|
||||
ImmutableSet.Builder<String> privateAddressesBuilder = ImmutableSet.builder();
|
||||
for (Instance.NetworkInterface networkInterface : input.getNetworkInterfaces()) {
|
||||
if (networkInterface.getNetworkIP().isPresent()) {
|
||||
privateAddressesBuilder.add(networkInterface.getNetworkIP().get());
|
||||
}
|
||||
}
|
||||
return privateAddressesBuilder.build();
|
||||
}
|
||||
|
||||
private Set<String> collectPublicAddresses(Instance input) {
|
||||
ImmutableSet.Builder<String> publicAddressesBuilder = ImmutableSet.builder();
|
||||
for (Instance.NetworkInterface networkInterface : input.getNetworkInterfaces()) {
|
||||
for (Instance.NetworkInterface.AccessConfig accessConfig : networkInterface.getAccessConfigs()) {
|
||||
if (accessConfig.getNatIP().isPresent()) {
|
||||
publicAddressesBuilder.add(accessConfig.getNatIP().get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return publicAddressesBuilder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.functions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.jclouds.compute.domain.Hardware;
|
||||
import org.jclouds.compute.domain.HardwareBuilder;
|
||||
import org.jclouds.compute.domain.Processor;
|
||||
import org.jclouds.compute.domain.Volume;
|
||||
import org.jclouds.compute.domain.internal.VolumeImpl;
|
||||
import org.jclouds.googlecompute.domain.MachineType;
|
||||
|
||||
/**
|
||||
* Transforms a google compute domain specific machine type to a generic Hardware object.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class MachineTypeToHardware implements Function<MachineType, Hardware> {
|
||||
|
||||
@Override
|
||||
public Hardware apply(MachineType input) {
|
||||
return new HardwareBuilder()
|
||||
.id(input.getName())
|
||||
.name(input.getName())
|
||||
.hypervisor("kvm")
|
||||
.processor(new Processor(input.getGuestCpus(), 1.0))
|
||||
.providerId(input.getId())
|
||||
.ram(input.getMemoryMb())
|
||||
.uri(input.getSelfLink())
|
||||
.volumes(collectVolumes(input))
|
||||
.build();
|
||||
}
|
||||
|
||||
private Iterable<Volume> collectVolumes(MachineType input) {
|
||||
ImmutableSet.Builder<Volume> volumes = ImmutableSet.builder();
|
||||
for (MachineType.EphemeralDisk disk : input.getEphemeralDisks()) {
|
||||
volumes.add(new VolumeImpl(null, Volume.Type.LOCAL, new Integer(disk.getDiskGb()).floatValue(), null, true,
|
||||
false));
|
||||
}
|
||||
return volumes.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.functions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class OrphanedGroupsFromDeadNodes implements Function<Set<? extends NodeMetadata>, Set<String>> {
|
||||
|
||||
private final Predicate<String> isOrphanedGroupPredicate;
|
||||
|
||||
@Inject
|
||||
public OrphanedGroupsFromDeadNodes(Predicate<String> isOrphanedGroupPredicate) {
|
||||
this.isOrphanedGroupPredicate = isOrphanedGroupPredicate;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<String> apply(Set<? extends NodeMetadata> deadNodes) {
|
||||
Set<String> groups = Sets.newLinkedHashSet();
|
||||
for (NodeMetadata deadNode : deadNodes) {
|
||||
groups.add(deadNode.getGroup());
|
||||
}
|
||||
Set<String> orphanedGroups = Sets.newLinkedHashSet();
|
||||
for (String group : groups) {
|
||||
if (isOrphanedGroupPredicate.apply(group)) {
|
||||
orphanedGroups.add(group);
|
||||
}
|
||||
}
|
||||
return orphanedGroups;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.functions;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.domain.LocationBuilder;
|
||||
import org.jclouds.domain.LocationScope;
|
||||
import org.jclouds.googlecompute.domain.Zone;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.GOOGLE_PROVIDER_LOCATION;
|
||||
|
||||
/**
|
||||
* Transforms a google compute domain specific zone to a generic Zone object.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class ZoneToLocation implements Function<Zone, Location> {
|
||||
|
||||
@Override
|
||||
public Location apply(Zone input) {
|
||||
return new LocationBuilder()
|
||||
.description(input.getDescription().orNull())
|
||||
.metadata(ImmutableMap.of("selfLink", (Object) checkNotNull(input.getSelfLink(), "zone URI")))
|
||||
.id(input.getName())
|
||||
.scope(LocationScope.ZONE)
|
||||
.parent(GOOGLE_PROVIDER_LOCATION)
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
package org.jclouds.googlecompute.compute.options;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
import org.jclouds.googlecompute.domain.Instance;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Optional.fromNullable;
|
||||
import static org.jclouds.googlecompute.domain.Instance.ServiceAccount;
|
||||
|
||||
/**
|
||||
* Instance options specific to Google Compute Engine.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
public class GoogleComputeTemplateOptions extends TemplateOptions {
|
||||
|
||||
private Optional<URI> network = Optional.absent();
|
||||
private Optional<String> networkName = Optional.absent();
|
||||
private Set<Instance.ServiceAccount> serviceAccounts = Sets.newLinkedHashSet();
|
||||
private boolean enableNat = true;
|
||||
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions clone() {
|
||||
GoogleComputeTemplateOptions options = new GoogleComputeTemplateOptions();
|
||||
copyTo(options);
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(TemplateOptions to) {
|
||||
super.copyTo(to);
|
||||
if (to instanceof GoogleComputeTemplateOptions) {
|
||||
GoogleComputeTemplateOptions eTo = GoogleComputeTemplateOptions.class.cast(to);
|
||||
eTo.network(getNetwork().orNull());
|
||||
eTo.network(getNetworkName().orNull());
|
||||
eTo.serviceAccounts(getServiceAccounts());
|
||||
eTo.enableNat(isEnableNat());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getNetworkName()
|
||||
*/
|
||||
public GoogleComputeTemplateOptions network(String networkName) {
|
||||
this.networkName = fromNullable(networkName);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getNetwork()
|
||||
*/
|
||||
public GoogleComputeTemplateOptions network(URI network) {
|
||||
this.network = fromNullable(network);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getServiceAccounts()
|
||||
* @see ServiceAccount
|
||||
*/
|
||||
public GoogleComputeTemplateOptions addServiceAccount(ServiceAccount serviceAccout) {
|
||||
this.serviceAccounts.add(serviceAccout);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getServiceAccounts()
|
||||
* @see ServiceAccount
|
||||
*/
|
||||
public GoogleComputeTemplateOptions serviceAccounts(Set<ServiceAccount> serviceAccounts) {
|
||||
this.serviceAccounts = Sets.newLinkedHashSet(serviceAccounts);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #isEnableNat()
|
||||
*/
|
||||
public GoogleComputeTemplateOptions enableNat(boolean enableNat) {
|
||||
this.enableNat = enableNat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions blockOnPort(int port, int seconds) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.blockOnPort(port, seconds));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions inboundPorts(int... ports) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.inboundPorts(ports));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions authorizePublicKey(String publicKey) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.authorizePublicKey(publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions installPrivateKey(String privateKey) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.installPrivateKey(privateKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions blockUntilRunning(boolean blockUntilRunning) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.blockUntilRunning(blockUntilRunning));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions dontAuthorizePublicKey() {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.dontAuthorizePublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions nameTask(String name) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.nameTask(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions runAsRoot(boolean runAsRoot) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.runAsRoot(runAsRoot));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions runScript(Statement script) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.runScript(script));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions overrideLoginCredentials(LoginCredentials overridingCredentials) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.overrideLoginCredentials(overridingCredentials));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions overrideLoginPassword(String password) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.overrideLoginPassword(password));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions overrideLoginPrivateKey(String privateKey) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.overrideLoginPrivateKey(privateKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions overrideLoginUser(String loginUser) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.overrideLoginUser(loginUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions overrideAuthenticateSudo(boolean authenticateSudo) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.overrideAuthenticateSudo(authenticateSudo));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions userMetadata(Map<String, String> userMetadata) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.userMetadata(userMetadata));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions userMetadata(String key, String value) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.userMetadata(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions tags(Iterable<String> tags) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.tags(tags));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions wrapInInitScript(boolean wrapInInitScript) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.wrapInInitScript(wrapInInitScript));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions runScript(String script) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.runScript(script));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public GoogleComputeTemplateOptions blockOnComplete(boolean blockOnComplete) {
|
||||
return GoogleComputeTemplateOptions.class.cast(super.blockOnComplete(blockOnComplete));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ServiceAccounts to enable in the instances.
|
||||
*/
|
||||
public Set<Instance.ServiceAccount> getServiceAccounts() {
|
||||
return serviceAccounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the URI of an existing network the instances will be attached to. If no network URI or network name are
|
||||
* provided a new network will be created for the project.
|
||||
*/
|
||||
public Optional<URI> getNetwork() {
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of an existing network the instances will be attached to, the network is assumed to belong to
|
||||
* user's project. If no network URI network name are provided a new network will be created for the project.
|
||||
*/
|
||||
public Optional<String> getNetworkName() {
|
||||
return networkName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether an AccessConfig with Type ONE_TO_ONE_NAT should be enabled in the instances. When true
|
||||
* instances will have a NAT address that will be publicly accessible.
|
||||
*/
|
||||
public boolean isEnableNat() {
|
||||
return enableNat;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.predicates;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import org.jclouds.compute.ComputeService;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.all;
|
||||
import static com.google.common.collect.Sets.filter;
|
||||
import static org.jclouds.compute.predicates.NodePredicates.TERMINATED;
|
||||
import static org.jclouds.compute.predicates.NodePredicates.all;
|
||||
import static org.jclouds.compute.predicates.NodePredicates.inGroup;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class AllNodesInGroupTerminated implements Predicate<String> {
|
||||
|
||||
private final ComputeService computeService;
|
||||
|
||||
@Inject
|
||||
public AllNodesInGroupTerminated(ComputeService computeService) {
|
||||
this.computeService = checkNotNull(computeService, "compute service");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(String groupName) {
|
||||
return all(filter(computeService.listNodesDetailsMatching(all()), inGroup(groupName)), TERMINATED);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.strategy;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import org.jclouds.Constants;
|
||||
import org.jclouds.compute.config.CustomizationResponse;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.Template;
|
||||
import org.jclouds.compute.functions.GroupNamingConvention;
|
||||
import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
|
||||
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
|
||||
import org.jclouds.compute.strategy.ListNodesStrategy;
|
||||
import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||
import org.jclouds.googlecompute.GoogleComputeApi;
|
||||
import org.jclouds.googlecompute.compute.options.GoogleComputeTemplateOptions;
|
||||
import org.jclouds.googlecompute.config.UserProject;
|
||||
import org.jclouds.googlecompute.domain.Firewall;
|
||||
import org.jclouds.googlecompute.domain.Network;
|
||||
import org.jclouds.googlecompute.domain.Operation;
|
||||
import org.jclouds.googlecompute.options.FirewallOptions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.ImmutableSet.of;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.OPERATION_COMPLETE_INTERVAL;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.OPERATION_COMPLETE_TIMEOUT;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public class ApplyTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet extends
|
||||
CreateNodesWithGroupEncodedIntoNameThenAddToSet {
|
||||
|
||||
public static final String EXTERIOR_RANGE = "0.0.0.0/0";
|
||||
public static final String DEFAULT_INTERNAL_NETWORK_RANGE = "10.0.0.0/8";
|
||||
|
||||
private final GoogleComputeApi api;
|
||||
private final Supplier<String> userProject;
|
||||
private final Predicate<AtomicReference<Operation>> operationDonePredicate;
|
||||
private final long operationCompleteCheckInterval;
|
||||
private final long operationCompleteCheckTimeout;
|
||||
|
||||
@Inject
|
||||
protected ApplyTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet(
|
||||
CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy,
|
||||
ListNodesStrategy listNodesStrategy,
|
||||
GroupNamingConvention.Factory namingConvention,
|
||||
@Named(Constants.PROPERTY_USER_THREADS)
|
||||
ListeningExecutorService userExecutor,
|
||||
CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory
|
||||
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
|
||||
GoogleComputeApi api,
|
||||
@UserProject Supplier<String> userProject,
|
||||
Predicate<AtomicReference<Operation>> operationDonePredicate,
|
||||
@Named(OPERATION_COMPLETE_INTERVAL) Long operationCompleteCheckInterval,
|
||||
@Named(OPERATION_COMPLETE_TIMEOUT) Long operationCompleteCheckTimeout) {
|
||||
super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
|
||||
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
|
||||
|
||||
this.api = checkNotNull(api, "google compute api");
|
||||
this.userProject = checkNotNull(userProject, "user project name");
|
||||
this.operationCompleteCheckInterval = checkNotNull(operationCompleteCheckInterval,
|
||||
"operation completed check interval");
|
||||
this.operationCompleteCheckTimeout = checkNotNull(operationCompleteCheckTimeout,
|
||||
"operation completed check timeout");
|
||||
this.operationDonePredicate = operationDonePredicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template,
|
||||
Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
|
||||
Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
|
||||
|
||||
String sharedResourceName = namingConvention.create().sharedNameForGroup(group);
|
||||
Template mutableTemplate = template.clone();
|
||||
GoogleComputeTemplateOptions templateOptions = GoogleComputeTemplateOptions.class.cast(mutableTemplate
|
||||
.getOptions());
|
||||
assert template.getOptions().equals(templateOptions) : "options didn't clone properly";
|
||||
|
||||
// get or create the network and create a firewall with the users configuration
|
||||
Network network = getOrCreateNetwork(templateOptions, sharedResourceName);
|
||||
createFirewall(templateOptions, network, sharedResourceName);
|
||||
templateOptions.network(network.getSelfLink());
|
||||
|
||||
return super.execute(group, count, mutableTemplate, goodNodes, badNodes, customizationResponses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try and find a network either previously created by jclouds or user defined.
|
||||
*/
|
||||
private Network getOrCreateNetwork(GoogleComputeTemplateOptions templateOptions, String sharedResourceName) {
|
||||
|
||||
String networkName = templateOptions.getNetworkName().isPresent() ? templateOptions.getNetworkName().get() :
|
||||
sharedResourceName;
|
||||
|
||||
// check if the network was previously created (cache???)
|
||||
Network network = api.getNetworkApiForProject(userProject.get()).get(networkName);
|
||||
|
||||
if (network != null) {
|
||||
return network;
|
||||
}
|
||||
|
||||
if (network == null && templateOptions.getNetwork().isPresent()) {
|
||||
throw new IllegalStateException("user defined network does not exist: " + templateOptions.getNetwork().get());
|
||||
}
|
||||
|
||||
AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getNetworkApiForProject(userProject
|
||||
.get()).createInIPv4Range(sharedResourceName, DEFAULT_INTERNAL_NETWORK_RANGE));
|
||||
retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
|
||||
MILLISECONDS).apply(operation);
|
||||
if (operation.get().getHttpError().isPresent()) {
|
||||
throw new IllegalStateException("Could not create network, operation failed" + operation);
|
||||
}
|
||||
|
||||
return checkNotNull(api.getNetworkApiForProject(userProject.get()).get(sharedResourceName),
|
||||
"no network with name %s was found", sharedResourceName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find if a firewall already exists for this group, if not it creates one.
|
||||
*
|
||||
* @see org.jclouds.googlecompute.features.FirewallAsyncApi#patch(String, org.jclouds.googlecompute.options.FirewallOptions)
|
||||
*/
|
||||
private void createFirewall(GoogleComputeTemplateOptions templateOptions, Network network,
|
||||
String sharedResourceName) {
|
||||
|
||||
Firewall firewall = api.getFirewallApiForProject(userProject.get()).get(sharedResourceName);
|
||||
|
||||
if (firewall != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImmutableSet.Builder<Firewall.Rule> rules = ImmutableSet.builder();
|
||||
|
||||
Firewall.Rule.Builder tcpRule = Firewall.Rule.builder();
|
||||
tcpRule.IPProtocol(Firewall.Rule.IPProtocol.TCP);
|
||||
Firewall.Rule.Builder udpRule = Firewall.Rule.builder();
|
||||
udpRule.IPProtocol(Firewall.Rule.IPProtocol.UDP);
|
||||
for (Integer port : templateOptions.getInboundPorts()) {
|
||||
tcpRule.addPort(port);
|
||||
udpRule.addPort(port);
|
||||
}
|
||||
rules.add(tcpRule.build());
|
||||
rules.add(udpRule.build());
|
||||
|
||||
|
||||
FirewallOptions options = new FirewallOptions()
|
||||
.name(sharedResourceName)
|
||||
.network(network.getSelfLink())
|
||||
.sourceTags(templateOptions.getTags())
|
||||
.allowedRules(rules.build())
|
||||
.sourceRanges(of(DEFAULT_INTERNAL_NETWORK_RANGE, EXTERIOR_RANGE));
|
||||
|
||||
AtomicReference<Operation> operation = new AtomicReference<Operation>(api.getFirewallApiForProject(userProject
|
||||
.get()).createInNetwork(
|
||||
sharedResourceName,
|
||||
network.getSelfLink(),
|
||||
options));
|
||||
|
||||
retry(operationDonePredicate, operationCompleteCheckTimeout, operationCompleteCheckInterval,
|
||||
MILLISECONDS).apply(operation);
|
||||
|
||||
if (operation.get().getHttpError().isPresent()) {
|
||||
throw new IllegalStateException("Could not create firewall, operation failed" + operation.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.strategy;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import org.jclouds.compute.domain.TemplateBuilderSpec;
|
||||
import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrategy;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
import org.jclouds.ssh.internal.RsaSshKeyPairGenerator;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class GoogleComputePopulateDefaultLoginCredentialsForImageStrategy implements
|
||||
PopulateDefaultLoginCredentialsForImageStrategy {
|
||||
|
||||
private final TemplateBuilderSpec templateBuilder;
|
||||
private final RsaSshKeyPairGenerator keyPairGenerator;
|
||||
private String compoundKey;
|
||||
|
||||
@Inject
|
||||
GoogleComputePopulateDefaultLoginCredentialsForImageStrategy(@Named(TEMPLATE) String templateSpec,
|
||||
RsaSshKeyPairGenerator keyPairGenerator)
|
||||
throws NoSuchAlgorithmException {
|
||||
this.templateBuilder = TemplateBuilderSpec.parse(checkNotNull(templateSpec, "template builder spec"));
|
||||
checkNotNull(templateBuilder.getLoginUser(), "template builder spec must provide a loginUser");
|
||||
this.keyPairGenerator = checkNotNull(keyPairGenerator, "keypair generator");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void generateKeys() {
|
||||
Map<String, String> keys = keyPairGenerator.get();
|
||||
// as we need to store both the pubk and the pk, store them separated by : (base64 does not contain that char)
|
||||
compoundKey = String.format("%s:%s", checkNotNull(keys.get("public"), "public key cannot be null"),
|
||||
checkNotNull(keys.get("private"), "private key cannot be null"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginCredentials apply(Object image) {
|
||||
return LoginCredentials.builder()
|
||||
.authenticateSudo(templateBuilder.getAuthenticateSudo() != null ?
|
||||
templateBuilder.getAuthenticateSudo() : false)
|
||||
.privateKey(compoundKey)
|
||||
.user(templateBuilder.getLoginUser()).build();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.jclouds.googlecompute.compute.strategy;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import org.jclouds.compute.domain.Template;
|
||||
import org.jclouds.compute.options.RunScriptOptions;
|
||||
import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* GCE needs the credentials to create the node so the node credentials already take the Image credentials into account,
|
||||
* as such only overriding the TemplateOptions credentials is required.
|
||||
*
|
||||
* @author David Alves
|
||||
*/
|
||||
@Singleton
|
||||
public class UseNodeCredentialsButOverrideFromTemplate extends PrioritizeCredentialsFromTemplate {
|
||||
|
||||
|
||||
@Inject
|
||||
public UseNodeCredentialsButOverrideFromTemplate(
|
||||
Function<Template, LoginCredentials> credentialsFromImageOrTemplateOptions) {
|
||||
super(credentialsFromImageOrTemplateOptions);
|
||||
}
|
||||
|
||||
public LoginCredentials apply(Template template, LoginCredentials fromNode) {
|
||||
RunScriptOptions options = checkNotNull(template.getOptions(), "template options are required");
|
||||
LoginCredentials.Builder builder = LoginCredentials.builder(fromNode);
|
||||
if (options.getLoginUser() != null)
|
||||
builder.user(template.getOptions().getLoginUser());
|
||||
if (options.getLoginPassword() != null)
|
||||
builder.password(options.getLoginPassword());
|
||||
if (options.getLoginPrivateKey() != null)
|
||||
builder.privateKey(options.getLoginPrivateKey());
|
||||
if (options.shouldAuthenticateSudo() != null && options.shouldAuthenticateSudo())
|
||||
builder.authenticateSudo(true);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@ import org.jclouds.http.annotation.ServerError;
|
|||
import org.jclouds.json.config.GsonModule.DateAdapter;
|
||||
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
|
||||
import org.jclouds.location.Provider;
|
||||
import org.jclouds.location.config.LocationModule;
|
||||
import org.jclouds.rest.ConfiguresRestClient;
|
||||
import org.jclouds.rest.config.RestClientModule;
|
||||
|
||||
|
@ -164,4 +165,9 @@ public class GoogleComputeRestClientModule extends RestClientModule<GoogleComput
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void installLocations() {
|
||||
// install(new LocationModule());
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -555,7 +555,7 @@ public class Instance extends Resource {
|
|||
|
||||
private final String name;
|
||||
private final URI network;
|
||||
private final String networkIP;
|
||||
private final Optional<String> networkIP;
|
||||
private final Set<AccessConfig> accessConfigs;
|
||||
|
||||
@ConstructorProperties({
|
||||
|
@ -565,7 +565,7 @@ public class Instance extends Resource {
|
|||
Set<AccessConfig> accessConfigs) {
|
||||
this.name = checkNotNull(name, "name");
|
||||
this.network = checkNotNull(network, "network");
|
||||
this.networkIP = checkNotNull(networkIP, "networkIP");
|
||||
this.networkIP = fromNullable(networkIP);
|
||||
this.accessConfigs = accessConfigs == null ? ImmutableSet.<AccessConfig>of() : accessConfigs;
|
||||
}
|
||||
|
||||
|
@ -586,7 +586,7 @@ public class Instance extends Resource {
|
|||
/**
|
||||
* @return An IPV4 internal network address to assign to this instance.
|
||||
*/
|
||||
public String getNetworkIP() {
|
||||
public Optional<String> getNetworkIP() {
|
||||
return networkIP;
|
||||
}
|
||||
|
||||
|
@ -698,7 +698,7 @@ public class Instance extends Resource {
|
|||
|
||||
public Builder fromNetworkInterface(NetworkInterface in) {
|
||||
return this.network(in.getNetwork())
|
||||
.networkIP(in.getNetworkIP())
|
||||
.networkIP(in.getNetworkIP().orNull())
|
||||
.accessConfigs(in.getAccessConfigs());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* 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.googlecompute.compute;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.compute.RunNodesException;
|
||||
import org.jclouds.compute.domain.Template;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.googlecompute.compute.options.GoogleComputeTemplateOptions;
|
||||
import org.jclouds.googlecompute.domain.Instance;
|
||||
import org.jclouds.googlecompute.features.InstanceApiExpectTest;
|
||||
import org.jclouds.googlecompute.internal.BaseGoogleComputeServiceExpectTest;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_READONLY_SCOPE;
|
||||
import static org.jclouds.googlecompute.GoogleComputeConstants.COMPUTE_SCOPE;
|
||||
import static org.jclouds.googlecompute.features.FirewallApiExpectTest.GET_FIREWALL_REQUEST;
|
||||
import static org.jclouds.googlecompute.features.ImageApiExpectTest.LIST_PROJECT_IMAGES_REQUEST;
|
||||
import static org.jclouds.googlecompute.features.ImageApiExpectTest.LIST_PROJECT_IMAGES_RESPONSE;
|
||||
import static org.jclouds.googlecompute.features.InstanceApiExpectTest.LIST_INSTANCES_REQUEST;
|
||||
import static org.jclouds.googlecompute.features.InstanceApiExpectTest.LIST_INSTANCES_RESPONSE;
|
||||
import static org.jclouds.googlecompute.features.MachineTypeApiExpectTest.LIST_MACHINE_TYPES_REQUEST;
|
||||
import static org.jclouds.googlecompute.features.MachineTypeApiExpectTest.LIST_MACHINE_TYPES_RESPONSE;
|
||||
import static org.jclouds.googlecompute.features.NetworkApiExpectTest.GET_NETWORK_REQUEST;
|
||||
import static org.jclouds.googlecompute.features.OperationApiExpectTest.GET_OPERATION_REQUEST;
|
||||
import static org.jclouds.googlecompute.features.OperationApiExpectTest.GET_OPERATION_RESPONSE;
|
||||
import static org.jclouds.googlecompute.features.ZoneApiExpectTest.LIST_ZONES_REQ;
|
||||
import static org.jclouds.googlecompute.features.ZoneApiExpectTest.LIST_ZONES_RESPONSE;
|
||||
import static org.jclouds.util.Strings2.toStringAndClose;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class GoogleComputeServiceExpectTest extends BaseGoogleComputeServiceExpectTest {
|
||||
|
||||
public static final HttpRequest LIST_GOOGLE_IMAGES_REQUEST = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint("https://www.googleapis.com/compute/v1beta13/projects/google/images")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN).build();
|
||||
|
||||
public static final HttpResponse LIST_GOOGLE_IMAGES_RESPONSE = HttpResponse.builder().statusCode(200)
|
||||
.payload(staticPayloadFromResource("/image_list_single_page.json")).build();
|
||||
|
||||
private HttpRequest INSERT_NETWORK_REQUEST = HttpRequest
|
||||
.builder()
|
||||
.method("POST")
|
||||
.endpoint("https://www.googleapis.com/compute/v1beta13/projects/myproject/networks")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN)
|
||||
.payload(payloadFromStringWithContentType("{\"name\":\"jclouds-test\",\"IPv4Range\":\"10.0.0.0/8\"}",
|
||||
MediaType.APPLICATION_JSON))
|
||||
.build();
|
||||
|
||||
private HttpRequest INSERT_FIREWALL_REQUEST = HttpRequest
|
||||
.builder()
|
||||
.method("POST")
|
||||
.endpoint("https://www.googleapis.com/compute/v1beta13/projects/myproject/firewalls")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN)
|
||||
.payload(payloadFromStringWithContentType("{\"name\":\"jclouds-test\",\"network\":\"https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/networks/jclouds-test\"," +
|
||||
"\"sourceRanges\":[\"10.0.0.0/8\",\"0.0.0.0/0\"],\"allowed\":[{\"IPProtocol\":\"tcp\"," +
|
||||
"\"ports\":[\"22\"]}," +
|
||||
"{\"IPProtocol\":\"udp\",\"ports\":[\"22\"]}]}",
|
||||
MediaType.APPLICATION_JSON))
|
||||
.build();
|
||||
|
||||
private HttpResponse GET_NETWORK_RESPONSE = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromStringWithContentType("{\n" +
|
||||
" \"kind\": \"compute#network\",\n" +
|
||||
" \"id\": \"13024414170909937976\",\n" +
|
||||
" \"creationTimestamp\": \"2012-10-24T20:13:19.967\",\n" +
|
||||
" \"selfLink\": \"https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/networks/jclouds-test\",\n" +
|
||||
" \"name\": \"jclouds-test\",\n" +
|
||||
" \"description\": \"test network\",\n" +
|
||||
" \"IPv4Range\": \"10.0.0.0/8\",\n" +
|
||||
" \"gatewayIPv4\": \"10.0.0.1\"\n" +
|
||||
"}", MediaType.APPLICATION_JSON)).build();
|
||||
|
||||
private HttpResponse SUCESSFULL_OPERATION_RESPONSE = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/operation.json")).build();
|
||||
|
||||
|
||||
private HttpResponse getInstanceResponseForInstanceAndNetworkAndStatus(String instanceName, String networkName,
|
||||
String status) throws
|
||||
IOException {
|
||||
return HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromStringWithContentType(
|
||||
replaceInstanceNameNetworkAndStatusOnResource("/instance_get.json",
|
||||
instanceName, networkName, status),
|
||||
"application/json")).build();
|
||||
}
|
||||
|
||||
private HttpResponse getListInstancesResponseForSingleInstanceAndNetworkAndStatus(String instanceName,
|
||||
String networkName,
|
||||
String status) {
|
||||
return HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromStringWithContentType(
|
||||
replaceInstanceNameNetworkAndStatusOnResource("/instance_list.json",
|
||||
instanceName, networkName, status),
|
||||
"application/json")).build();
|
||||
}
|
||||
|
||||
private String replaceInstanceNameNetworkAndStatusOnResource(String resourceName, String instanceName,
|
||||
String networkName, String status) {
|
||||
try {
|
||||
return Strings2.toStringAndClose(this.getClass().getResourceAsStream(resourceName)).replace("test-0",
|
||||
instanceName).replace("default", networkName).replace("RUNNING", status);
|
||||
} catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpRequest createInstanceRequestForInstance(String instanceName, String networkName, String publicKey) {
|
||||
return HttpRequest
|
||||
.builder()
|
||||
.method("POST")
|
||||
.endpoint("https://www.googleapis.com/compute/v1beta13/projects/myproject/instances")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN)
|
||||
.payload(payloadFromStringWithContentType("{\"name\":\"" + instanceName + "\"," +
|
||||
"\"machineType\":\"https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/machineTypes/n1-standard-1\"," +
|
||||
"\"zone\":\"https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/zones/us-central1-a\"," +
|
||||
"\"image\":\"https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/google/images/gcel-12-04-v20121106\"," +
|
||||
"\"tags\":[],\"serviceAccounts\":[]," +
|
||||
"\"networkInterfaces\":[{\"network\":\"https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/networks/" + networkName + "\"," +
|
||||
"\"accessConfigs\":[{\"type\":\"ONE_TO_ONE_NAT\"}]}]," +
|
||||
"\"metadata\":{\"kind\":\"compute#metadata\",\"items\":[{\"key\":\"sshKeys\"," +
|
||||
"\"value\":\"jclouds:" +
|
||||
publicKey + " jclouds@localhost\"}]}}",
|
||||
MediaType.APPLICATION_JSON)).build();
|
||||
}
|
||||
|
||||
private HttpRequest getInstanceRequestForInstance(String instanceName) {
|
||||
return HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint("https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/instances/" + instanceName)
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN).build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
Properties overrides = super.setupProperties();
|
||||
overrides.put("google-compute.identity", "myproject");
|
||||
try {
|
||||
overrides.put("google-compute.credential", toStringAndClose(getClass().getResourceAsStream("/testpk.pem")));
|
||||
} catch (IOException e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
return overrides;
|
||||
}
|
||||
|
||||
@Test(enabled = false)
|
||||
public void testThrowsAuthorizationException() throws Exception {
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("oauth.identity", "MOMMA");
|
||||
properties.setProperty("oauth.credential", "MiA");
|
||||
|
||||
ComputeService client = requestsSendResponses(ImmutableMap.<HttpRequest, HttpResponse>of(), createModule(),
|
||||
properties);
|
||||
Template template = client.templateBuilder().build();
|
||||
Template toMatch = client.templateBuilder().imageId(template.getImage().getId()).build();
|
||||
assertEquals(toMatch.getImage(), template.getImage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTemplateMatch() throws Exception {
|
||||
ImmutableMap<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.
|
||||
<HttpRequest, HttpResponse>builder()
|
||||
.put(requestForScopes(COMPUTE_READONLY_SCOPE), TOKEN_RESPONSE)
|
||||
.put(LIST_ZONES_REQ, LIST_ZONES_RESPONSE)
|
||||
.put(LIST_PROJECT_IMAGES_REQUEST, LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.put(LIST_GOOGLE_IMAGES_REQUEST, LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.put(LIST_MACHINE_TYPES_REQUEST, LIST_MACHINE_TYPES_RESPONSE)
|
||||
.build();
|
||||
|
||||
ComputeService client = requestsSendResponses(requestResponseMap);
|
||||
Template template = client.templateBuilder().build();
|
||||
Template toMatch = client.templateBuilder().imageId(template.getImage().getId()).build();
|
||||
assertEquals(toMatch.getImage(), template.getImage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetworksAndFirewallDeletedWhenAllGroupNodesAreTerminated() throws IOException {
|
||||
|
||||
HttpRequest deleteNodeRequest = HttpRequest.builder()
|
||||
.method("DELETE")
|
||||
.endpoint("https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/instances/test-delete-networks")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN).build();
|
||||
|
||||
HttpRequest deleteFirewallRequest = HttpRequest.builder()
|
||||
.method("DELETE")
|
||||
.endpoint("https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/firewalls/jclouds-test-delete")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN).build();
|
||||
|
||||
HttpRequest deleteNetworkReqquest = HttpRequest.builder()
|
||||
.method("DELETE")
|
||||
.endpoint("https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/myproject/networks/jclouds-test-delete")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN).build();
|
||||
|
||||
List<HttpRequest> orderedRequests = ImmutableList.<HttpRequest>builder()
|
||||
.add(requestForScopes(COMPUTE_READONLY_SCOPE))
|
||||
.add(getInstanceRequestForInstance("test-delete-networks"))
|
||||
.add(LIST_PROJECT_IMAGES_REQUEST)
|
||||
.add(LIST_GOOGLE_IMAGES_REQUEST)
|
||||
.add(LIST_ZONES_REQ)
|
||||
.add(LIST_MACHINE_TYPES_REQUEST)
|
||||
.add(requestForScopes(COMPUTE_SCOPE))
|
||||
.add(deleteNodeRequest)
|
||||
.add(GET_OPERATION_REQUEST)
|
||||
.add(getInstanceRequestForInstance("test-delete-networks"))
|
||||
.add(LIST_PROJECT_IMAGES_REQUEST)
|
||||
.add(LIST_GOOGLE_IMAGES_REQUEST)
|
||||
.add(LIST_ZONES_REQ)
|
||||
.add(LIST_MACHINE_TYPES_REQUEST)
|
||||
.add(LIST_INSTANCES_REQUEST)
|
||||
.add(LIST_PROJECT_IMAGES_REQUEST)
|
||||
.add(LIST_GOOGLE_IMAGES_REQUEST)
|
||||
.add(LIST_ZONES_REQ)
|
||||
.add(LIST_MACHINE_TYPES_REQUEST)
|
||||
.add(deleteFirewallRequest)
|
||||
.add(GET_OPERATION_REQUEST)
|
||||
.add(deleteNetworkReqquest)
|
||||
.add(GET_OPERATION_REQUEST)
|
||||
.build();
|
||||
|
||||
|
||||
List<HttpResponse> orderedResponses = ImmutableList.<HttpResponse>builder()
|
||||
.add(TOKEN_RESPONSE)
|
||||
.add(getInstanceResponseForInstanceAndNetworkAndStatus("test-delete-networks", "test-network", Instance
|
||||
.Status.RUNNING.name()))
|
||||
.add(LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.add(LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.add(LIST_ZONES_RESPONSE)
|
||||
.add(LIST_MACHINE_TYPES_RESPONSE)
|
||||
.add(TOKEN_RESPONSE)
|
||||
.add(SUCESSFULL_OPERATION_RESPONSE)
|
||||
.add(GET_OPERATION_RESPONSE)
|
||||
.add(getInstanceResponseForInstanceAndNetworkAndStatus("test-delete-networks", "test-network", Instance
|
||||
.Status.TERMINATED.name()))
|
||||
.add(LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.add(LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.add(LIST_ZONES_RESPONSE)
|
||||
.add(LIST_MACHINE_TYPES_RESPONSE)
|
||||
.add(getListInstancesResponseForSingleInstanceAndNetworkAndStatus("test-delete-networks",
|
||||
"test-network", Instance
|
||||
.Status.TERMINATED.name()))
|
||||
.add(LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.add(LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.add(LIST_ZONES_RESPONSE)
|
||||
.add(LIST_MACHINE_TYPES_RESPONSE)
|
||||
.add(SUCESSFULL_OPERATION_RESPONSE)
|
||||
.add(GET_OPERATION_RESPONSE)
|
||||
.add(SUCESSFULL_OPERATION_RESPONSE)
|
||||
.add(GET_OPERATION_RESPONSE)
|
||||
.build();
|
||||
|
||||
ComputeService client = orderedRequestsSendResponses(orderedRequests, orderedResponses);
|
||||
client.destroyNode("test-delete-networks");
|
||||
|
||||
}
|
||||
|
||||
public void testListLocationsWhenResponseIs2xx() throws Exception {
|
||||
|
||||
ImmutableMap<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.
|
||||
<HttpRequest, HttpResponse>builder()
|
||||
.put(requestForScopes(COMPUTE_READONLY_SCOPE), TOKEN_RESPONSE)
|
||||
.put(LIST_ZONES_REQ, LIST_ZONES_RESPONSE)
|
||||
.put(LIST_INSTANCES_REQUEST, LIST_INSTANCES_RESPONSE)
|
||||
.put(LIST_PROJECT_IMAGES_REQUEST, LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.put(LIST_GOOGLE_IMAGES_REQUEST, LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.put(LIST_MACHINE_TYPES_REQUEST, LIST_MACHINE_TYPES_RESPONSE)
|
||||
.build();
|
||||
|
||||
ComputeService apiWhenServersExist = requestsSendResponses(requestResponseMap);
|
||||
|
||||
Set<? extends Location> locations = apiWhenServersExist.listAssignableLocations();
|
||||
|
||||
assertNotNull(locations);
|
||||
assertEquals(locations.size(), 2);
|
||||
assertEquals(locations.iterator().next().getId(), "us-central1-a");
|
||||
|
||||
assertNotNull(apiWhenServersExist.listNodes());
|
||||
assertEquals(apiWhenServersExist.listNodes().size(), 1);
|
||||
assertEquals(apiWhenServersExist.listNodes().iterator().next().getId(), "test-0");
|
||||
assertEquals(apiWhenServersExist.listNodes().iterator().next().getName(), "test-0");
|
||||
}
|
||||
|
||||
@Test(dependsOnMethods = "testListLocationsWhenResponseIs2xx")
|
||||
public void testCreateNodeWhenNetworkNorFirewallExistDoesNotExist() throws RunNodesException, IOException {
|
||||
|
||||
|
||||
String payload = Strings2.toStringAndClose(InstanceApiExpectTest.class.getResourceAsStream("/instance_get.json"));
|
||||
payload = payload.replace("test-0", "test-1");
|
||||
|
||||
HttpResponse getInstanceResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromStringWithContentType(payload, "application/json")).build();
|
||||
|
||||
List<HttpRequest> orderedRequests = ImmutableList.<HttpRequest>builder()
|
||||
.add(requestForScopes(COMPUTE_READONLY_SCOPE))
|
||||
.add(LIST_ZONES_REQ)
|
||||
.add(LIST_PROJECT_IMAGES_REQUEST)
|
||||
.add(LIST_GOOGLE_IMAGES_REQUEST)
|
||||
.add(LIST_MACHINE_TYPES_REQUEST)
|
||||
.add(GET_NETWORK_REQUEST)
|
||||
.add(requestForScopes(COMPUTE_SCOPE))
|
||||
.add(INSERT_NETWORK_REQUEST)
|
||||
.add(GET_OPERATION_REQUEST)
|
||||
.add(GET_NETWORK_REQUEST)
|
||||
.add(GET_FIREWALL_REQUEST)
|
||||
.add(INSERT_FIREWALL_REQUEST)
|
||||
.add(GET_OPERATION_REQUEST)
|
||||
.add(LIST_INSTANCES_REQUEST)
|
||||
.add(LIST_PROJECT_IMAGES_REQUEST)
|
||||
.add(LIST_GOOGLE_IMAGES_REQUEST)
|
||||
.add(LIST_ZONES_REQ)
|
||||
.add(LIST_MACHINE_TYPES_REQUEST)
|
||||
.add(createInstanceRequestForInstance("test-1", "jclouds-test", openSshKey))
|
||||
.add(GET_OPERATION_REQUEST)
|
||||
.add(getInstanceRequestForInstance("test-1"))
|
||||
.add(LIST_PROJECT_IMAGES_REQUEST)
|
||||
.add(LIST_GOOGLE_IMAGES_REQUEST)
|
||||
.add(LIST_ZONES_REQ)
|
||||
.add(LIST_MACHINE_TYPES_REQUEST)
|
||||
.build();
|
||||
|
||||
List<HttpResponse> orderedResponses = ImmutableList.<HttpResponse>builder()
|
||||
.add(TOKEN_RESPONSE)
|
||||
.add(LIST_ZONES_RESPONSE)
|
||||
.add(LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.add(LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.add(LIST_MACHINE_TYPES_RESPONSE)
|
||||
.add(HttpResponse.builder().statusCode(404).build())
|
||||
.add(TOKEN_RESPONSE)
|
||||
.add(SUCESSFULL_OPERATION_RESPONSE)
|
||||
.add(GET_OPERATION_RESPONSE)
|
||||
.add(GET_NETWORK_RESPONSE)
|
||||
.add(HttpResponse.builder().statusCode(404).build())
|
||||
.add(SUCESSFULL_OPERATION_RESPONSE)
|
||||
.add(GET_OPERATION_RESPONSE)
|
||||
.add(LIST_INSTANCES_RESPONSE)
|
||||
.add(LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.add(LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.add(LIST_ZONES_RESPONSE)
|
||||
.add(LIST_MACHINE_TYPES_RESPONSE)
|
||||
.add(SUCESSFULL_OPERATION_RESPONSE)
|
||||
.add(GET_OPERATION_RESPONSE)
|
||||
.add(getInstanceResponse)
|
||||
.add(LIST_PROJECT_IMAGES_RESPONSE)
|
||||
.add(LIST_GOOGLE_IMAGES_RESPONSE)
|
||||
.add(LIST_ZONES_RESPONSE)
|
||||
.add(LIST_MACHINE_TYPES_RESPONSE)
|
||||
.build();
|
||||
|
||||
|
||||
ComputeService computeService = orderedRequestsSendResponses(orderedRequests, orderedResponses);
|
||||
|
||||
GoogleComputeTemplateOptions options = computeService.templateOptions().as(GoogleComputeTemplateOptions.class);
|
||||
|
||||
getOnlyElement(computeService.createNodesInGroup("test", 1, options));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.googlecompute.compute;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.Module;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
|
||||
import org.jclouds.oauth.v2.OAuthTestUtils;
|
||||
import org.jclouds.sshj.config.SshjSshClientModule;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "live", singleThreaded = true)
|
||||
public class GoogleComputeServiceLiveTest extends BaseComputeServiceLiveTest {
|
||||
|
||||
public GoogleComputeServiceLiveTest() {
|
||||
provider = "google-compute";
|
||||
}
|
||||
|
||||
/**
|
||||
* Nodes may have additional metadata entries (particularly they may have an "sshKeys" entry)
|
||||
*/
|
||||
protected void checkUserMetadataInNodeEquals(NodeMetadata node, ImmutableMap<String, String> userMetadata) {
|
||||
assertTrue(node.getUserMetadata().keySet().containsAll(userMetadata.keySet()));
|
||||
}
|
||||
|
||||
// do not run until the auth exception problem is figured out.
|
||||
@Test(enabled = false)
|
||||
@Override
|
||||
public void testCorrectAuthException() throws Exception {
|
||||
}
|
||||
|
||||
// reboot is not supported by GCE
|
||||
@Test(enabled = true, dependsOnMethods = "testGet")
|
||||
public void testReboot() throws Exception {
|
||||
}
|
||||
|
||||
// suspend/Resume is not supported by GCE
|
||||
@Test(enabled = true, dependsOnMethods = "testReboot")
|
||||
public void testSuspendResume() throws Exception {
|
||||
}
|
||||
|
||||
@Test(enabled = true, dependsOnMethods = "testSuspendResume")
|
||||
@Override
|
||||
public void testGetNodesWithDetails() throws Exception {
|
||||
super.testGetNodesWithDetails();
|
||||
}
|
||||
|
||||
@Test(enabled = true, dependsOnMethods = "testSuspendResume")
|
||||
@Override
|
||||
public void testListNodes() throws Exception {
|
||||
super.testListNodes();
|
||||
}
|
||||
|
||||
@Test(enabled = true, dependsOnMethods = {"testListNodes", "testGetNodesWithDetails"})
|
||||
@Override
|
||||
public void testDestroyNodes() {
|
||||
super.testDestroyNodes();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Module getSshModule() {
|
||||
return new SshjSshClientModule();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties setupProperties() {
|
||||
Properties properties = super.setupProperties();
|
||||
OAuthTestUtils.setCredentialFromPemFile(properties, "google-compute.credential");
|
||||
return properties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.googlecompute.compute.functions;
|
||||
|
||||
import org.jclouds.compute.domain.OsFamily;
|
||||
import org.jclouds.googlecompute.domain.Image;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class GoogleComputeImageToImageTest {
|
||||
|
||||
Image.Builder imageBuilder = Image.builder()
|
||||
.id("1234")
|
||||
.selfLink(URI.create("http://test.com"))
|
||||
.sourceType("RAW")
|
||||
.description("")
|
||||
.rawDisk(Image.RawDisk.builder().source("").containerType("TAR").build());
|
||||
|
||||
public void testArbitratyImageName() {
|
||||
GoogleComputeImageToImage imageToImage = new GoogleComputeImageToImage();
|
||||
Image image = imageBuilder.name("arbitratyname").build();
|
||||
org.jclouds.compute.domain.Image transformed = imageToImage.apply(image);
|
||||
assertEquals(transformed.getName(), image.getName());
|
||||
assertEquals(transformed.getId(), image.getName());
|
||||
assertEquals(transformed.getProviderId(), image.getId());
|
||||
assertSame(transformed.getOperatingSystem().getFamily(), OsFamily.LINUX);
|
||||
}
|
||||
|
||||
public void testWellFormedImageName() {
|
||||
GoogleComputeImageToImage imageToImage = new GoogleComputeImageToImage();
|
||||
Image image = imageBuilder.name("ubuntu-12-04-v123123").build();
|
||||
org.jclouds.compute.domain.Image transformed = imageToImage.apply(image);
|
||||
assertEquals(transformed.getName(), image.getName());
|
||||
assertEquals(transformed.getId(), image.getName());
|
||||
assertEquals(transformed.getProviderId(), image.getId());
|
||||
assertSame(transformed.getOperatingSystem().getFamily(), OsFamily.UBUNTU);
|
||||
assertEquals(transformed.getOperatingSystem().getVersion(), "12.04");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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.googlecompute.compute.functions;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.easymock.EasyMock;
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.compute.domain.ComputeMetadata;
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
|
||||
import org.jclouds.googlecompute.compute.predicates.AllNodesInGroupTerminated;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.testng.Assert.assertSame;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public class OrphanedGroupsFromDeadNodesTest {
|
||||
|
||||
private static class IdAndGroupOnlyNodeMetadata extends NodeMetadataImpl {
|
||||
|
||||
public IdAndGroupOnlyNodeMetadata(String id, String group, Status status) {
|
||||
super(null, null, id, null, null, ImmutableMap.<String, String>of(), ImmutableSet.<String>of(), group, null,
|
||||
null, null, status, null, 0, ImmutableSet.<String>of(), ImmutableSet.<String>of(), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDetectsAllOrphanedGroupsWhenAllNodesTerminated() {
|
||||
|
||||
Set<? extends NodeMetadata> deadNodesGroup1 = (Set) ImmutableSet.builder()
|
||||
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.TERMINATED)).build();
|
||||
|
||||
Set<? extends NodeMetadata> deadNodesGroup2 = (Set) ImmutableSet.builder()
|
||||
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.TERMINATED)).build();
|
||||
|
||||
Set<? extends NodeMetadata> allDeadNodes = Sets.union(deadNodesGroup1, deadNodesGroup2);
|
||||
|
||||
ComputeService mock = createMock(ComputeService.class);
|
||||
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
|
||||
.andReturn((Set) deadNodesGroup1).once();
|
||||
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
|
||||
.andReturn((Set) deadNodesGroup2).once();
|
||||
|
||||
replay(mock);
|
||||
|
||||
OrphanedGroupsFromDeadNodes orphanedGroupsFromDeadNodes = new OrphanedGroupsFromDeadNodes(new
|
||||
AllNodesInGroupTerminated(mock));
|
||||
|
||||
Set<String> orphanedGroups = orphanedGroupsFromDeadNodes.apply(allDeadNodes);
|
||||
|
||||
assertSame(orphanedGroups.size(), 2);
|
||||
assertTrue(orphanedGroups.contains("1"));
|
||||
assertTrue(orphanedGroups.contains("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDetectsAllOrphanedGroupsWhenSomeNodesTerminatedAndOtherMissing() {
|
||||
|
||||
Set<? extends NodeMetadata> deadNodesGroup1 = (Set) ImmutableSet.builder()
|
||||
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.TERMINATED)).build();
|
||||
|
||||
Set<? extends NodeMetadata> deadNodesGroup2 = (Set) ImmutableSet.builder()
|
||||
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.TERMINATED)).build();
|
||||
|
||||
Set<? extends NodeMetadata> allDeadNodes = Sets.union(deadNodesGroup1, deadNodesGroup2);
|
||||
|
||||
ComputeService mock = createMock(ComputeService.class);
|
||||
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
|
||||
.andReturn((Set) deadNodesGroup1).once();
|
||||
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
|
||||
.andReturn((Set) ImmutableSet.of()).once();
|
||||
|
||||
replay(mock);
|
||||
|
||||
OrphanedGroupsFromDeadNodes orphanedGroupsFromDeadNodes = new OrphanedGroupsFromDeadNodes(new
|
||||
AllNodesInGroupTerminated(mock));
|
||||
|
||||
Set<String> orphanedGroups = orphanedGroupsFromDeadNodes.apply(allDeadNodes);
|
||||
|
||||
assertSame(orphanedGroups.size(), 2);
|
||||
assertTrue(orphanedGroups.contains("1"));
|
||||
assertTrue(orphanedGroups.contains("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDetectsAllOrphanedGroupsWhenSomeNodesAreAlive() {
|
||||
|
||||
Set<? extends NodeMetadata> deadNodesGroup1 = (Set) ImmutableSet.builder()
|
||||
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.TERMINATED)).build();
|
||||
|
||||
Set<? extends NodeMetadata> deadNodesGroup2 = (Set) ImmutableSet.builder()
|
||||
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.RUNNING)).build();
|
||||
|
||||
Set<? extends NodeMetadata> allDeadNodes = Sets.union(deadNodesGroup1, deadNodesGroup2);
|
||||
|
||||
ComputeService mock = createMock(ComputeService.class);
|
||||
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
|
||||
.andReturn((Set) deadNodesGroup1).once();
|
||||
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
|
||||
.andReturn((Set) deadNodesGroup2).once();
|
||||
|
||||
replay(mock);
|
||||
|
||||
OrphanedGroupsFromDeadNodes orphanedGroupsFromDeadNodes = new OrphanedGroupsFromDeadNodes(new
|
||||
AllNodesInGroupTerminated(mock));
|
||||
|
||||
Set<String> orphanedGroups = orphanedGroupsFromDeadNodes.apply(allDeadNodes);
|
||||
|
||||
assertSame(orphanedGroups.size(), 1);
|
||||
assertTrue(orphanedGroups.contains("1"));
|
||||
}
|
||||
}
|
|
@ -93,7 +93,7 @@ public class NetworkApiExpectTest extends BaseGoogleComputeApiExpectTest {
|
|||
TOKEN_RESPONSE, insert,
|
||||
insertNetworkResponse).getNetworkApiForProject("myproject");
|
||||
|
||||
assertEquals(api.createInIPv4Range("test-network", "10.0.1.0/8"), new ParseOperationTest().expected());
|
||||
assertEquals(api.createInIPv4Range("test-network", "10.0.0.0/8"), new ParseOperationTest().expected());
|
||||
}
|
||||
|
||||
public void testDeleteNetworkResponseIs2xx() {
|
||||
|
|
|
@ -37,12 +37,12 @@ import static org.testng.Assert.assertTrue;
|
|||
@Test(groups = "unit")
|
||||
public class ZoneApiExpectTest extends BaseGoogleComputeApiExpectTest {
|
||||
|
||||
public static final String ZONES_URL_PREFIX = "https://www.googleapis.com/compute/v1beta13/projects/google/zones";
|
||||
public static final String ZONES_URL_PREFIX = "https://www.googleapis.com/compute/v1beta13/projects/myproject/zones";
|
||||
|
||||
public static final HttpRequest GET_ZONE_REQ = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint(ZONES_URL_PREFIX + "/us-central2-a")
|
||||
.endpoint(ZONES_URL_PREFIX + "/us-central1-a")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("Authorization", "Bearer " + TOKEN).build();
|
||||
|
||||
|
@ -64,9 +64,9 @@ public class ZoneApiExpectTest extends BaseGoogleComputeApiExpectTest {
|
|||
.payload(payloadFromResource("/zone_get.json")).build();
|
||||
|
||||
ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE),
|
||||
TOKEN_RESPONSE, GET_ZONE_REQ, operationResponse).getZoneApiForProject("google");
|
||||
TOKEN_RESPONSE, GET_ZONE_REQ, operationResponse).getZoneApiForProject("myproject");
|
||||
|
||||
assertEquals(api.get("us-central2-a"),
|
||||
assertEquals(api.get("us-central1-a"),
|
||||
new ParseZoneTest().expected());
|
||||
}
|
||||
|
||||
|
@ -75,15 +75,15 @@ public class ZoneApiExpectTest extends BaseGoogleComputeApiExpectTest {
|
|||
HttpResponse operationResponse = HttpResponse.builder().statusCode(404).build();
|
||||
|
||||
ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE),
|
||||
TOKEN_RESPONSE, GET_ZONE_REQ, operationResponse).getZoneApiForProject("google");
|
||||
TOKEN_RESPONSE, GET_ZONE_REQ, operationResponse).getZoneApiForProject("myproject");
|
||||
|
||||
assertNull(api.get("us-central2-a"));
|
||||
assertNull(api.get("us-central1-a"));
|
||||
}
|
||||
|
||||
public void testListZoneNoOptionsResponseIs2xx() throws Exception {
|
||||
|
||||
ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE),
|
||||
TOKEN_RESPONSE, LIST_ZONES_REQ, LIST_ZONES_RESPONSE).getZoneApiForProject("google");
|
||||
TOKEN_RESPONSE, LIST_ZONES_REQ, LIST_ZONES_RESPONSE).getZoneApiForProject("myproject");
|
||||
|
||||
assertEquals(api.listFirstPage().toString(),
|
||||
new ParseZoneListTest().expected().toString());
|
||||
|
@ -94,7 +94,7 @@ public class ZoneApiExpectTest extends BaseGoogleComputeApiExpectTest {
|
|||
HttpResponse operationResponse = HttpResponse.builder().statusCode(404).build();
|
||||
|
||||
ZoneApi api = requestsSendResponses(requestForScopes(COMPUTE_READONLY_SCOPE),
|
||||
TOKEN_RESPONSE, LIST_ZONES_REQ, operationResponse).getZoneApiForProject("google");
|
||||
TOKEN_RESPONSE, LIST_ZONES_REQ, operationResponse).getZoneApiForProject("myproject");
|
||||
|
||||
assertTrue(api.list().concat().isEmpty());
|
||||
}
|
||||
|
|
|
@ -20,29 +20,49 @@ package org.jclouds.googlecompute.internal;
|
|||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.base.Ticker;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import org.jclouds.collect.PagedIterable;
|
||||
import org.jclouds.collect.PagedIterables;
|
||||
import org.jclouds.crypto.Crypto;
|
||||
import org.jclouds.googlecompute.domain.ListPage;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.oauth.v2.OAuthConstants;
|
||||
import org.jclouds.rest.internal.BaseRestApiExpectTest;
|
||||
import org.jclouds.ssh.SshKeys;
|
||||
import org.jclouds.util.Strings2;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.google.common.base.Charsets.UTF_8;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.io.BaseEncoding.base64Url;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.jclouds.crypto.Pems.privateKeySpec;
|
||||
import static org.jclouds.crypto.Pems.publicKeySpec;
|
||||
import static org.jclouds.crypto.PemsTest.PRIVATE_KEY;
|
||||
import static org.jclouds.crypto.PemsTest.PUBLIC_KEY;
|
||||
import static org.jclouds.io.Payloads.newStringPayload;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
|
@ -67,6 +87,8 @@ public class BaseGoogleComputeExpectTest<T> extends BaseRestApiExpectTest<T> {
|
|||
" \"expires_in\" : 3600\n" +
|
||||
"}")).build();
|
||||
|
||||
protected String openSshKey;
|
||||
|
||||
|
||||
public BaseGoogleComputeExpectTest() {
|
||||
provider = "google-compute";
|
||||
|
@ -74,16 +96,43 @@ public class BaseGoogleComputeExpectTest<T> extends BaseRestApiExpectTest<T> {
|
|||
|
||||
@Override
|
||||
protected Module createModule() {
|
||||
|
||||
|
||||
return new Module() {
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
// predictable time
|
||||
binder.bind(Ticker.class).toInstance(new Ticker() {
|
||||
@Override
|
||||
public long read() {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
// predictable node names
|
||||
try {
|
||||
KeyFactory keyfactory = KeyFactory.getInstance("RSA");
|
||||
PrivateKey privateKey = keyfactory.generatePrivate(privateKeySpec(newStringPayload
|
||||
(PRIVATE_KEY)));
|
||||
PublicKey publicKey = keyfactory.generatePublic(publicKeySpec(newStringPayload(PUBLIC_KEY)));
|
||||
KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||
openSshKey = SshKeys.encodeAsOpenSSH(RSAPublicKey.class.cast(publicKey));
|
||||
final Crypto crypto = createMock(Crypto.class);
|
||||
KeyPairGenerator rsaKeyPairGenerator = createMock(KeyPairGenerator.class);
|
||||
final SecureRandom secureRandom = createMock(SecureRandom.class);
|
||||
expect(crypto.rsaKeyPairGenerator()).andReturn(rsaKeyPairGenerator).anyTimes();
|
||||
rsaKeyPairGenerator.initialize(2048, secureRandom);
|
||||
expectLastCall().anyTimes();
|
||||
expect(rsaKeyPairGenerator.genKeyPair()).andReturn(keyPair).anyTimes();
|
||||
replay(crypto, rsaKeyPairGenerator, secureRandom);
|
||||
binder.bind(Crypto.class).toInstance(crypto);
|
||||
binder.bind(SecureRandom.class).toInstance(secureRandom);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
propagate(e);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
propagate(e);
|
||||
} catch (IOException e) {
|
||||
propagate(e);
|
||||
}
|
||||
// predictable node names
|
||||
final AtomicInteger suffix = new AtomicInteger();
|
||||
binder.bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(new Supplier<String>() {
|
||||
|
@ -134,7 +183,7 @@ public class BaseGoogleComputeExpectTest<T> extends BaseRestApiExpectTest<T> {
|
|||
return payloadFromString(Strings2.toStringAndClose(BaseGoogleComputeExpectTest.class.getResourceAsStream
|
||||
(resource)));
|
||||
} catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.googlecompute.internal;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.inject.Module;
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.compute.ComputeServiceContext;
|
||||
import org.jclouds.googlecompute.GoogleComputeApiMetadata;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public abstract class BaseGoogleComputeServiceContextExpectTest<T> extends BaseGoogleComputeExpectTest<T> implements
|
||||
Function<ComputeServiceContext, T> {
|
||||
|
||||
|
||||
@Override
|
||||
public T createClient(Function<HttpRequest, HttpResponse> fn, Module module, Properties props) {
|
||||
return apply(createComputeServiceContext(fn, module, props));
|
||||
}
|
||||
|
||||
private ComputeServiceContext createComputeServiceContext(Function<HttpRequest, HttpResponse> fn, Module module,
|
||||
Properties props) {
|
||||
return createInjector(fn, module, props).getInstance(ComputeServiceContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ApiMetadata createApiMetadata() {
|
||||
return new GoogleComputeApiMetadata();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.googlecompute.internal;
|
||||
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.compute.ComputeServiceContext;
|
||||
|
||||
/**
|
||||
* @author David Alves
|
||||
*/
|
||||
public class BaseGoogleComputeServiceExpectTest extends BaseGoogleComputeServiceContextExpectTest<ComputeService> {
|
||||
|
||||
@Override
|
||||
public ComputeService apply(ComputeServiceContext input) {
|
||||
return input.getComputeService();
|
||||
}
|
||||
}
|
|
@ -46,8 +46,8 @@ public class ParseNetworkTest extends BaseGoogleComputeParseTest<Network> {
|
|||
.selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/myproject/networks/jclouds-test"))
|
||||
.name("default")
|
||||
.description("Default network for the project")
|
||||
.IPv4Range("10.240.0.0/16")
|
||||
.gatewayIPv4("10.240.0.1")
|
||||
.IPv4Range("10.0.0.0/8")
|
||||
.gatewayIPv4("10.0.0.1")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,8 @@ public class ParseZoneListTest extends BaseGoogleComputeParseTest<ListPage<Zone>
|
|||
public ListPage<Zone> expected() {
|
||||
return ListPage.<Zone>builder()
|
||||
.kind(Resource.Kind.ZONE_LIST)
|
||||
.id("projects/google/zones")
|
||||
.selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/google/zones"))
|
||||
.id("projects/myproject/zones")
|
||||
.selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/myproject/zones"))
|
||||
.items(ImmutableSet.of(
|
||||
new ParseZoneTest().expected()
|
||||
, Zone.builder()
|
||||
|
@ -56,7 +56,7 @@ public class ParseZoneListTest extends BaseGoogleComputeParseTest<ListPage<Zone>
|
|||
.creationTimestamp(new SimpleDateFormatDateService().iso8601DateParse
|
||||
("2012-10-24T20:13:19.271"))
|
||||
.selfLink(URI.create("https://www.googleapis" +
|
||||
".com/compute/v1beta13/projects/google/zones/us-east1-a"))
|
||||
".com/compute/v1beta13/projects/myproject/zones/us-east1-a"))
|
||||
.name("us-east1-a")
|
||||
.description("us-east1-a")
|
||||
.status(Zone.Status.UP)
|
||||
|
|
|
@ -45,9 +45,9 @@ public class ParseZoneTest extends BaseGoogleComputeParseTest<Zone> {
|
|||
return Zone.builder()
|
||||
.id("13020128040171887099")
|
||||
.creationTimestamp(new SimpleDateFormatDateService().iso8601DateParse("2012-10-19T16:42:54.131"))
|
||||
.selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-central2-a"))
|
||||
.name("us-central2-a")
|
||||
.description("us-central2-a")
|
||||
.selfLink(URI.create("https://www.googleapis.com/compute/v1beta13/projects/myproject/zones/us-central1-a"))
|
||||
.name("us-central1-a")
|
||||
.description("us-central1-a")
|
||||
.status(Zone.Status.DOWN)
|
||||
.addMaintenanceWindow(Zone.MaintenanceWindow.builder()
|
||||
.name("2012-11-10-planned-outage")
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/networks/jclouds-test",
|
||||
"name": "default",
|
||||
"description": "Default network for the project",
|
||||
"IPv4Range": "10.240.0.0/16",
|
||||
"gatewayIPv4": "10.240.0.1"
|
||||
"IPv4Range": "10.0.0.0/8",
|
||||
"gatewayIPv4": "10.0.0.1"
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"name":"test-network","IPv4Range":"10.0.1.0/8"}
|
||||
{"name":"test-network","IPv4Range":"10.0.0.0/8"}
|
|
@ -11,8 +11,8 @@
|
|||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/networks/jclouds-test",
|
||||
"name": "default",
|
||||
"description": "Default network for the project",
|
||||
"IPv4Range": "10.240.0.0/16",
|
||||
"gatewayIPv4": "10.240.0.1"
|
||||
"IPv4Range": "10.0.0.0/8",
|
||||
"gatewayIPv4": "10.0.0.1"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -2,9 +2,9 @@
|
|||
"kind": "compute#zone",
|
||||
"id": "13020128040171887099",
|
||||
"creationTimestamp": "2012-10-19T16:42:54.131",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-central2-a",
|
||||
"name": "us-central2-a",
|
||||
"description": "us-central2-a",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/zones/us-central1-a",
|
||||
"name": "us-central1-a",
|
||||
"description": "us-central1-a",
|
||||
"status": "DOWN",
|
||||
"maintenanceWindows": [
|
||||
{
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"kind": "compute#zoneList",
|
||||
"id": "projects/google/zones",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones",
|
||||
"id": "projects/myproject/zones",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/zones",
|
||||
"items": [
|
||||
{
|
||||
"kind": "compute#zone",
|
||||
"id": "13020128040171887099",
|
||||
"creationTimestamp": "2012-10-19T16:42:54.131",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-central2-a",
|
||||
"name": "us-central2-a",
|
||||
"description": "us-central2-a",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/zones/us-central1-a",
|
||||
"name": "us-central1-a",
|
||||
"description": "us-central1-a",
|
||||
"status": "DOWN",
|
||||
"maintenanceWindows": [
|
||||
{
|
||||
|
@ -24,7 +24,7 @@
|
|||
"kind": "compute#zone",
|
||||
"id": "13024414164050619686",
|
||||
"creationTimestamp": "2012-10-24T20:13:19.271",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/google/zones/us-east1-a",
|
||||
"selfLink": "https://www.googleapis.com/compute/v1beta13/projects/myproject/zones/us-east1-a",
|
||||
"name": "us-east1-a",
|
||||
"description": "us-east1-a",
|
||||
"status": "UP",
|
||||
|
|
Loading…
Reference in New Issue