JCLOUDS-925: Add support to start and stop instances in the ComputeService

This commit is contained in:
Ignasi Barrera 2015-05-27 10:58:26 +02:00
parent 55348c0ddb
commit 2e7ca20f1c
13 changed files with 288 additions and 83 deletions

View File

@ -16,9 +16,12 @@
*/
package org.jclouds.googlecomputeengine.compute;
import static autovalue.shaded.com.google.common.common.collect.Sets.newHashSet;
import static com.google.common.collect.Iterables.filter;
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.compute.predicates.NodePredicates.all;
import static org.jclouds.googlecloud.internal.ListPages.concat;
import java.util.Map;
@ -62,6 +65,8 @@ import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.googlecomputeengine.features.FirewallApi;
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
import autovalue.shaded.com.google.common.common.collect.ImmutableSet;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
@ -119,6 +124,31 @@ public final class GoogleComputeEngineService extends BaseComputeService {
this.api = api;
this.operationDone = operationDone;
}
@Override
public void destroyNode(String id) {
// GCE does not return TERMINATED nodes, so in practice no node will never reach the TERMINATED
// state, and the deleted nodes will never be returned.
// In order to be able to clean up the resources associated to the deleted nodes, we have to retrieve
// the details of the nodes before deleting them.
NodeMetadata node = getNodeMetadata(id);
super.destroyNode(id);
cleanUpIncidentalResourcesOfDeadNodes(ImmutableSet.of(node));
}
@Override
public Set<? extends NodeMetadata> destroyNodesMatching(Predicate<NodeMetadata> filter) {
// GCE does not return TERMINATED nodes, so in practice no node will never reach the TERMINATED
// state, and the deleted nodes will never be returned.
// In order to be able to clean up the resources associated to the deleted nodes, we have to retrieve
// the details of the nodes before deleting them.
Set<? extends NodeMetadata> nodes = newHashSet(filter(listNodesDetailsMatching(all()), filter));
super.destroyNodesMatching(filter); // This returns an empty list (a list of null elements) in GCE, as the api does not return deleted nodes
cleanUpIncidentalResourcesOfDeadNodes(nodes);
return nodes;
}
@Override
protected synchronized void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) {

View File

@ -257,12 +257,12 @@ public final class GoogleComputeEngineServiceAdapter
waitOperationDone(resources.resetInstance(URI.create(checkNotNull(selfLink, "id"))));
}
@Override public void resumeNode(String name) {
throw new UnsupportedOperationException("resume is not supported by GCE");
@Override public void resumeNode(String selfLink) {
waitOperationDone(resources.startInstance(URI.create(checkNotNull(selfLink, "id"))));
}
@Override public void suspendNode(String name) {
throw new UnsupportedOperationException("suspend is not supported by GCE");
@Override public void suspendNode(String selfLink) {
waitOperationDone(resources.stopInstance(URI.create(checkNotNull(selfLink, "id"))));
}
private void waitOperationDone(Operation operation) {

View File

@ -52,7 +52,7 @@ import org.jclouds.googlecomputeengine.compute.functions.MachineTypeToHardware;
import org.jclouds.googlecomputeengine.compute.functions.OrphanedGroupsFromDeadNodes;
import org.jclouds.googlecomputeengine.compute.functions.Resources;
import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
import org.jclouds.googlecomputeengine.compute.predicates.AllNodesInGroupTerminated;
import org.jclouds.googlecomputeengine.compute.predicates.GroupIsEmpty;
import org.jclouds.googlecomputeengine.compute.predicates.AtomicInstanceVisible;
import org.jclouds.googlecomputeengine.compute.predicates.AtomicOperationDone;
import org.jclouds.googlecomputeengine.compute.strategy.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
@ -112,7 +112,7 @@ public final class GoogleComputeEngineServiceContextModule
}).to(OrphanedGroupsFromDeadNodes.class);
bind(new TypeLiteral<Predicate<String>>() {
}).to(AllNodesInGroupTerminated.class);
}).to(GroupIsEmpty.class);
bind(FirewallTagNamingConvention.Factory.class).in(Scopes.SINGLETON);
bindHttpApi(binder(), Resources.class);
@ -175,7 +175,7 @@ public final class GoogleComputeEngineServiceContextModule
.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();
.put(Instance.Status.TERMINATED, NodeMetadata.Status.SUSPENDED).build();
@Provides Map<Instance.Status, NodeMetadata.Status> toPortableNodeStatus() {
return toPortableNodeStatus;

View File

@ -63,9 +63,21 @@ public interface Resources {
@DELETE
@Fallback(NullOnNotFoundOr404.class) @Nullable Operation delete(@EndpointParam URI selfLink);
/** Hard-resets the instance by self-link and returns the operation in progres */
/** Hard-resets the instance by self-link and returns the operation in progress */
@Named("Instances:reset")
@POST
@Path("/reset")
Operation resetInstance(@EndpointParam URI selfLink);
/** Starts the instance by self-link and returns the operation in progress */
@Named("Instances:start")
@POST
@Path("/start")
Operation startInstance(@EndpointParam URI selfLink);
/** Stops the instance by self-link and returns the operation in progress */
@Named("Instances:stop")
@POST
@Path("/stop")
Operation stopInstance(@EndpointParam URI selfLink);
}

View File

@ -16,9 +16,8 @@
*/
package org.jclouds.googlecomputeengine.compute.predicates;
import static com.google.common.collect.Iterables.all;
import static autovalue.shaded.com.google.common.common.collect.Iterables.isEmpty;
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;
@ -28,16 +27,16 @@ import org.jclouds.compute.ComputeService;
import com.google.common.base.Predicate;
public final class AllNodesInGroupTerminated implements Predicate<String> {
public final class GroupIsEmpty implements Predicate<String> {
private final ComputeService computeService;
@Inject AllNodesInGroupTerminated(ComputeService computeService) {
@Inject GroupIsEmpty(ComputeService computeService) {
this.computeService = computeService;
}
@Override public boolean apply(String groupName) {
return all(filter(computeService.listNodesDetailsMatching(all()), inGroup(groupName)), TERMINATED);
return isEmpty(filter(computeService.listNodesDetailsMatching(all()), inGroup(groupName)));
}
}

View File

@ -17,18 +17,21 @@
package org.jclouds.googlecomputeengine.compute;
import static com.google.common.collect.Iterables.contains;
import static org.jclouds.util.Strings2.toStringAndClose;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.Properties;
import java.util.Set;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
import org.jclouds.googlecloud.internal.TestProperties;
import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
import org.jclouds.googlecomputeengine.domain.MachineType;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test;
@ -71,43 +74,28 @@ public class GoogleComputeEngineServiceLiveTest extends BaseComputeServiceLiveTe
assertTrue(node.getUserMetadata().keySet().containsAll(userMetadata.keySet()));
}
// do not run until the auth exception problem is figured out.
@Test(enabled = false)
@Test(expectedExceptions = AuthorizationException.class)
@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")
public void testListNodesByIds() throws Exception {
super.testGetNodesWithDetails();
}
@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", "testListNodesByIds"})
@Override
public void testDestroyNodes() {
super.testDestroyNodes();
ComputeServiceContext context = null;
try {
String credential = toStringAndClose(getClass().getResourceAsStream("/test"));
Properties overrides = setupProperties();
overrides.setProperty(provider + ".identity", "000000000000@developer.gserviceaccount.com");
overrides.setProperty(provider + ".credential", credential);
context = newBuilder()
.modules(ImmutableSet.of(getLoggingModule(), credentialStoreModule))
.overrides(overrides).build(ComputeServiceContext.class);
context.getComputeService().listNodes();
} catch (AuthorizationException e) {
throw e;
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
} finally {
if (context != null)
context.close();
}
}
@Override

View File

@ -18,7 +18,6 @@ package org.jclouds.googlecomputeengine.compute;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.jclouds.googlecomputeengine.domain.Instance.Status.RUNNING;
import static org.jclouds.googlecomputeengine.domain.Instance.Status.TERMINATED;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
@ -77,11 +76,12 @@ public class GoogleComputeEngineServiceMockTest extends BaseGoogleComputeEngineA
server.enqueue(instanceWithNetworkAndStatus("test-delete-1", "default", RUNNING));
server.enqueue(singleRegionSingleZoneResponse());
server.enqueue(jsonResponse("/aggregated_machinetype_list.json")); // Why are we getting machineTypes to delete an instance?
server.enqueue(instanceWithNetworkAndStatus("test-delete-1", "default", RUNNING));
server.enqueue(jsonResponse("/operation.json")); // instance delete
server.enqueue(jsonResponse("/zone_operation.json"));
server.enqueue(instanceWithNetworkAndStatus("test-delete-1", "default", TERMINATED));
server.enqueue(aggregatedListWithInstanceNetworkAndStatus("test-delete-1", "default", TERMINATED));
server.enqueue(jsonResponse("/GoogleComputeEngineServiceExpectTest/firewall_list.json"));
server.enqueue(response404()); // deleted instance no longer exists
server.enqueue(aggregatedListInstanceEmpty());
server.enqueue(jsonResponse("/firewall_list_compute.json"));
server.enqueue(jsonResponse("/operation.json"));
server.enqueue(jsonResponse("/zone_operation.json"));
@ -91,6 +91,7 @@ public class GoogleComputeEngineServiceMockTest extends BaseGoogleComputeEngineA
assertSent(server, "GET", "/jclouds/zones/us-central1-a/instances/test-delete-1");
assertSent(server, "GET", "/projects/party/regions");
assertSent(server, "GET", "/projects/party/aggregated/machineTypes"); // Why are we getting machineTypes to delete an instance?
assertSent(server, "GET", "/jclouds/zones/us-central1-a/instances/test-delete-1");
assertSent(server, "DELETE", "/jclouds/zones/us-central1-a/instances/test-delete-1"); // instance delete
assertSent(server, "GET", "/projects/party/zones/us-central1-a/operations/operation-1354084865060");
assertSent(server, "GET", "/projects/party/zones/us-central1-a/instances/test-delete-1"); // get instance
@ -195,5 +196,9 @@ public class GoogleComputeEngineServiceMockTest extends BaseGoogleComputeEngineA
stringFromResource("/aggregated_instance_list.json").replace("test-0", instanceName)
.replace("default", networkName).replace("RUNNING", status.toString()));
}
private MockResponse aggregatedListInstanceEmpty() {
return new MockResponse().setBody(stringFromResource("/aggregated_instance_list_empty.json"));
}
}

View File

@ -29,7 +29,7 @@ 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.googlecomputeengine.compute.predicates.AllNodesInGroupTerminated;
import org.jclouds.googlecomputeengine.compute.predicates.GroupIsEmpty;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
@ -51,13 +51,13 @@ public class OrphanedGroupsFromDeadNodesTest {
@Test
public void testDetectsAllOrphanedGroupsWhenAllNodesTerminated() {
public void testDetectsNoOrphanedGroupsWhenAllNodesArePresentAndTerminated() {
Set<NodeMetadata> deadNodesGroup1 = (Set) ImmutableSet.builder()
Set<NodeMetadata> deadNodesGroup1 = ImmutableSet.<NodeMetadata>builder()
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.TERMINATED)).build();
Set<NodeMetadata> deadNodesGroup2 = (Set) ImmutableSet.builder()
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.TERMINATED)).build();
Set<NodeMetadata> deadNodesGroup2 = ImmutableSet.<NodeMetadata> builder()
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.SUSPENDED)).build();
Set<NodeMetadata> allDeadNodes = Sets.union(deadNodesGroup1, deadNodesGroup2);
@ -74,18 +74,16 @@ public class OrphanedGroupsFromDeadNodesTest {
Set<String> orphanedGroups = orphanedGroupsFromDeadNodes.apply(allDeadNodes);
assertSame(orphanedGroups.size(), 2);
assertTrue(orphanedGroups.contains("1"));
assertTrue(orphanedGroups.contains("2"));
assertTrue(orphanedGroups.isEmpty());
}
@Test
public void testDetectsAllOrphanedGroupsWhenSomeNodesTerminatedAndOtherMissing() {
public void testDetectsOneOrphanedGroupWhenSomeNodesTerminatedAndOtherMissing() {
Set<NodeMetadata> deadNodesGroup1 = (Set) ImmutableSet.builder()
Set<NodeMetadata> deadNodesGroup1 = ImmutableSet.<NodeMetadata> builder()
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.TERMINATED)).build();
Set<NodeMetadata> deadNodesGroup2 = (Set) ImmutableSet.builder()
Set<NodeMetadata> deadNodesGroup2 = ImmutableSet.<NodeMetadata> builder()
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.TERMINATED)).build();
Set<NodeMetadata> allDeadNodes = Sets.union(deadNodesGroup1, deadNodesGroup2);
@ -103,19 +101,18 @@ public class OrphanedGroupsFromDeadNodesTest {
Set<String> orphanedGroups = orphanedGroupsFromDeadNodes.apply(allDeadNodes);
assertSame(orphanedGroups.size(), 2);
assertTrue(orphanedGroups.contains("1"));
assertSame(orphanedGroups.size(), 1);
assertTrue(orphanedGroups.contains("2"));
}
@Test
public void testDetectsAllOrphanedGroupsWhenSomeNodesAreAlive() {
public void testDetectsOneOrphanedGroupWhenSomeNodesAreAliveAndOtherMissing() {
Set<NodeMetadata> deadNodesGroup1 = (Set) ImmutableSet.builder()
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.TERMINATED)).build();
Set<NodeMetadata> deadNodesGroup1 = ImmutableSet.<NodeMetadata> builder()
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.RUNNING)).build();
Set<NodeMetadata> deadNodesGroup2 = (Set) ImmutableSet.builder()
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.RUNNING)).build();
Set<NodeMetadata> deadNodesGroup2 = ImmutableSet.<NodeMetadata> builder()
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.TERMINATED)).build();
Set<NodeMetadata> allDeadNodes = Sets.union(deadNodesGroup1, deadNodesGroup2);
@ -123,7 +120,7 @@ public class OrphanedGroupsFromDeadNodesTest {
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
.andReturn((Set) deadNodesGroup1).once();
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
.andReturn((Set) deadNodesGroup2).once();
.andReturn((Set) ImmutableSet.of()).once();
replay(mock);
@ -133,7 +130,36 @@ public class OrphanedGroupsFromDeadNodesTest {
Set<String> orphanedGroups = orphanedGroupsFromDeadNodes.apply(allDeadNodes);
assertSame(orphanedGroups.size(), 1);
assertTrue(orphanedGroups.contains("2"));
}
@Test
public void testDetectsAllOrphanedGroupsWhenAllNodesArerMissing() {
Set<NodeMetadata> deadNodesGroup1 = ImmutableSet.<NodeMetadata> builder()
.add(new IdAndGroupOnlyNodeMetadata("a", "1", NodeMetadata.Status.RUNNING)).build();
Set<NodeMetadata> deadNodesGroup2 = ImmutableSet.<NodeMetadata> builder()
.add(new IdAndGroupOnlyNodeMetadata("b", "2", NodeMetadata.Status.TERMINATED)).build();
Set<NodeMetadata> allDeadNodes = Sets.union(deadNodesGroup1, deadNodesGroup2);
ComputeService mock = createMock(ComputeService.class);
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
.andReturn((Set) ImmutableSet.of()).once();
expect(mock.listNodesDetailsMatching(EasyMock.<Predicate<ComputeMetadata>>anyObject()))
.andReturn((Set) ImmutableSet.of()).once();
replay(mock);
OrphanedGroupsFromDeadNodes orphanedGroupsFromDeadNodes = new OrphanedGroupsFromDeadNodes(
allNodesInGroupTerminated(mock));
Set<String> orphanedGroups = orphanedGroupsFromDeadNodes.apply(allDeadNodes);
assertSame(orphanedGroups.size(), 2);
assertTrue(orphanedGroups.contains("1"));
assertTrue(orphanedGroups.contains("2"));
}
private Predicate<String> allNodesInGroupTerminated(final ComputeService mock) {
@ -141,6 +167,6 @@ public class OrphanedGroupsFromDeadNodesTest {
@Override protected void configure() {
bind(ComputeService.class).toInstance(mock);
}
}).getInstance(AllNodesInGroupTerminated.class); // rather than opening ctor.
}).getInstance(GroupIsEmpty.class); // rather than opening ctor.
}
}

View File

@ -0,0 +1,126 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jclouds.googlecomputeengine.compute.functions;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.Network;
import org.jclouds.googlecomputeengine.domain.Operation;
import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineApiMockTest;
import org.jclouds.googlecomputeengine.parse.ParseInstanceTest;
import org.jclouds.googlecomputeengine.parse.ParseNetworkTest;
import org.jclouds.googlecomputeengine.parse.ParseOperationTest;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "ResourcesMockTest", singleThreaded = true)
public class ResourcesMockTest extends BaseGoogleComputeEngineApiMockTest {
public void testInstance() throws Exception {
server.enqueue(jsonResponse("/instance_get.json"));
Instance instance = resourceApi().instance(server.getUrl("/foo/bar").toURI());
assertEquals(instance, new ParseInstanceTest().expected(url("/projects")));
assertSent(server, "GET", "/foo/bar");
}
public void testInstanceReturns404() throws Exception {
server.enqueue(response404());
Instance instance = resourceApi().instance(server.getUrl("/foo/bar").toURI());
assertNull(instance);
assertSent(server, "GET", "/foo/bar");
}
public void testNetwork() throws Exception {
server.enqueue(jsonResponse("/network_get.json"));
Network network = resourceApi().network(server.getUrl("/foo/bar").toURI());
assertEquals(network, new ParseNetworkTest().expected(url("/projects")));
assertSent(server, "GET", "/foo/bar");
}
public void testNetworkReturns404() throws Exception {
server.enqueue(response404());
Network network = resourceApi().network(server.getUrl("/foo/bar").toURI());
assertNull(network);
assertSent(server, "GET", "/foo/bar");
}
public void testOperation() throws Exception {
server.enqueue(jsonResponse("/operation.json"));
Operation operation = resourceApi().operation(server.getUrl("/foo/bar").toURI());
assertEquals(operation, new ParseOperationTest().expected(url("/projects")));
assertSent(server, "GET", "/foo/bar");
}
public void testOperationReturns404() throws Exception {
server.enqueue(response404());
Operation operation = resourceApi().operation(server.getUrl("/foo/bar").toURI());
assertNull(operation);
assertSent(server, "GET", "/foo/bar");
}
public void testDelete() throws Exception {
server.enqueue(jsonResponse("/operation.json"));
Operation operation = resourceApi().delete(server.getUrl("/foo/bar").toURI());
assertEquals(operation, new ParseOperationTest().expected(url("/projects")));
assertSent(server, "DELETE", "/foo/bar");
}
public void testDeleteReturns404() throws Exception {
server.enqueue(response404());
Operation operation = resourceApi().delete(server.getUrl("/foo/bar").toURI());
assertNull(operation);
assertSent(server, "DELETE", "/foo/bar");
}
public void testResetInstance() throws Exception {
server.enqueue(jsonResponse("/operation.json"));
Operation operation = resourceApi().resetInstance(server.getUrl("/foo/bar").toURI());
assertEquals(operation, new ParseOperationTest().expected(url("/projects")));
assertSent(server, "POST", "/foo/bar/reset");
}
public void testStopInstance() throws Exception {
server.enqueue(jsonResponse("/operation.json"));
Operation operation = resourceApi().stopInstance(server.getUrl("/foo/bar").toURI());
assertEquals(operation, new ParseOperationTest().expected(url("/projects")));
assertSent(server, "POST", "/foo/bar/stop");
}
public void testStartInstance() throws Exception {
server.enqueue(jsonResponse("/operation.json"));
Operation operation = resourceApi().startInstance(server.getUrl("/foo/bar").toURI());
assertEquals(operation, new ParseOperationTest().expected(url("/projects")));
assertSent(server, "POST", "/foo/bar/start");
}
private Resources resourceApi() {
return builder().build().utils().injector().getInstance(Resources.class);
}
}

View File

@ -20,6 +20,7 @@ import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
import static org.jclouds.googlecloud.config.GoogleCloudProperties.CREDENTIAL_TYPE;
import static org.jclouds.googlecloud.config.GoogleCloudProperties.PROJECT_NAME;
import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.IMAGE_PROJECTS;
@ -36,9 +37,9 @@ import org.jclouds.ContextBuilder;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule;
import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
import org.jclouds.googlecomputeengine.GoogleComputeEngineProviderMetadata;
import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
@ -72,6 +73,7 @@ public class BaseGoogleComputeEngineApiMockTest {
Properties overrides = new Properties();
overrides.put(PROJECT_NAME, "party");
overrides.put(IMAGE_PROJECTS, "debian-cloud");
overrides.put(TIMEOUT_NODE_TERMINATED, "0"); // Avoid retry & polling in mock tests
overrides.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString());
return ContextBuilder.newBuilder(new GoogleComputeEngineProviderMetadata())
.credentials(identity, credential)

View File

@ -1,10 +0,0 @@
{
"kind": "compute#network",
"id": "13024414170909937976",
"creationTimestamp": "2012-10-24T20:13:19.967",
"selfLink": "https://www.googleapis.com/compute/v1/projects/party/global/networks/jclouds-test-delete",
"name": "jclouds-test-delete",
"description": "Default network for the project",
"IPv4Range": "10.0.0.0/8",
"gatewayIPv4": "10.0.0.1"
}

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnJvA40x4OK+9nVYTS0N916VMjC6/qYe/IuDUdy6hdW1wz9IO
MTS3CPxlE0KuoNO1/M7O7yFso6IragTxUkNqJ2mUOqV0Bf0CEkUIKzeYRGfpx+QM
PorHbLQXjjFinWKwibZuv6lqtvqwcsrjW7bpWsz9x+0qqKM0o0UhjUMhTRqZoYxo
E2zUH7WA8JRatE/bQkjv/nWBfI+/WzSDhJn7AjIql0Nd4Q+bxohIJEZu8yDw1H6T
pd7mw83m6UYBk4eZH79r3d2euuQMUKIunyLbw7vNJJ8qYTJQuNYIiJuWKnzzjxuJ
UfumhdOqfjSobznhAjTLUbA/btZCiQ/TasV4cQIDAQABAoIBAEeOn1b8ZN455qDS
aKR2JTT4cX6ICckznnEYW9xNMTcPl4FN0HBJTuzLLn/bcyFHOxtVf5YiJpqqCb46
ne1hokp54mHdoaLu1Rh19GKS139CH77XA4U8Mh0IOM8e35lcM5/o/LeUeI89Aoyh
CbupWvzDN543TsuZLv7/InKCXt/0dXhAQpq3UiBT63EITQbyom5fSPnMzqM3F8jD
E9ZqkX4JsnTPC7FQDIpPCaKjG9YCZqoljz+1ssli3mN66V/JKefcCiVoubalmmT2
dpvmRtFaKvhAmkWYakYybYg8aDi3YygAHSU1bzxlY4TNiQgPdnTTDAPyeqqVrE1D
Chi+18UCgYEAzlk7c+tFwxZ3ryycOe0loFudUNE5rviHhPgbOHoSTooXh0Hq1Vrb
2ic+4FbRpoPHLpcLM9LX+arezUdeFBZ8qunjUG6MbUhAeAm/3cfMk+nZg3Skpg8+
C1D3hxGX4qdhURHvc2QUH7VIUWbucvPgtL8pt1z5Su/EE1Cb2XVsvu8CgYEAwkqZ
4vTZxI0XqJo6BfUnKGJEDC8xeWr10ELPdXLTCuNDpLSYNedQAxZi9XzvbnbWZ/MF
Z7IWkzzyAjsX0gpI56cxxtas/chxUboBlUo6ZW8QcPDcU2sKJi318wzElqqvRMNM
InfLf8nuPC9hyhe49/lFBBSZJeIo396DuqnTPp8CgYBO4NVVLm5wcLo3gDoH+psT
fXHZXuFJ/T7wmVbuc9tjom30CkKWZDD+Z1olr4pcuKr/KEXj/YkJq0OX/Nv9mcr2
GooGSPvtGl1qhW+Oe728HPxEv+XghJsXAFBelV8WCR2uO8jotyzqIgYO9+XWk1sm
PJzZtvSkrJqrN3kb20NCiQKBgDDVP0hj8jgMnl2qJdtJesYTrLbDRdQWpiHqKOqE
Kbca1+2V1ov1z453GfhJpoRFKi6GTl15zWLEdq9I2vvXyesvgrtPSbufnZvE/JDh
TzwfZip832O4C5z9AExOcTrNO7A0xfYD1goQXuiRoCqDO+JXrJkR9EwpQ8zAyKsp
9AZRAoGAGq3TYpmlI5oucEURHKsHOrIBirHFD+RaXMynxzgwkRnt6Z5Mg10I7Ddr
LiGK8/IrF8bg1F7weLVmj93zjvhQTh5yvb1jwVdFGXM2rbR7/P7F6n2f7xM4+lmv
Tq7E9Sv8UVuraAwJihlKCuBtpZM1t2JhcuNjXAZngj7R9j5HIZg=
-----END RSA PRIVATE KEY-----