JCLOUDS-934. Add support for specifying boot disk type in compute service

This commit is contained in:
Andrew Bayer 2015-06-15 15:55:03 -07:00
parent c1b1cfbbea
commit f3555cba1b
7 changed files with 195 additions and 20 deletions

View File

@ -22,9 +22,11 @@ import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static org.jclouds.googlecloud.internal.ListPages.concat;
import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.IMAGE_PROJECTS;
import static org.jclouds.googlecomputeengine.compute.strategy.CreateNodesWithGroupEncodedIntoNameThenAddToSet.simplifyPorts;
import static org.jclouds.googlecomputeengine.config.GoogleComputeEngineProperties.IMAGE_PROJECTS;
import javax.inject.Inject;
import javax.inject.Named;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
@ -32,9 +34,14 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
import javax.inject.Named;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.UncheckedTimeoutException;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.NodeMetadata;
@ -49,6 +56,7 @@ import org.jclouds.googlecomputeengine.compute.functions.FirewallTagNamingConven
import org.jclouds.googlecomputeengine.compute.functions.Resources;
import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
import org.jclouds.googlecomputeengine.domain.AttachDisk;
import org.jclouds.googlecomputeengine.domain.DiskType;
import org.jclouds.googlecomputeengine.domain.Image;
import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.Instance.Scheduling;
@ -62,15 +70,6 @@ import org.jclouds.googlecomputeengine.domain.Zone;
import org.jclouds.googlecomputeengine.features.InstanceApi;
import org.jclouds.location.suppliers.all.JustProvider;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.UncheckedTimeoutException;
/**
* This implementation maps the following:
* <ul>
@ -120,8 +119,10 @@ public final class GoogleComputeEngineServiceAdapter
checkNotNull(template.getHardware().getUri(), "hardware must have a URI");
checkNotNull(template.getImage().getUri(), "image URI is null");
String zone = template.getLocation().getId();
List<AttachDisk> disks = Lists.newArrayList();
disks.add(AttachDisk.newBootDisk(template.getImage().getUri()));
disks.add(AttachDisk.newBootDisk(template.getImage().getUri(), getDiskTypeArgument(options, zone)));
Iterator<String> networks = options.getNetworks().iterator();
@ -156,7 +157,6 @@ public final class GoogleComputeEngineServiceAdapter
format("%s:%s %s@localhost", credentials.getUser(), options.getPublicKey(), credentials.getUser()));
}
String zone = template.getLocation().getId();
InstanceApi instanceApi = api.instancesInZone(zone);
Operation create = instanceApi.create(newInstance);
@ -303,4 +303,15 @@ public final class GoogleComputeEngineServiceAdapter
String path = link.getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
private URI getDiskTypeArgument(GoogleComputeEngineTemplateOptions options, String zone) {
if (options.bootDiskType() != null) {
DiskType diskType = api.diskTypesInZone(zone).get(options.bootDiskType());
if (diskType != null) {
return diskType.selfLink();
}
}
return null;
}
}

View File

@ -21,14 +21,15 @@ import java.util.Map;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.googlecomputeengine.domain.Instance.ServiceAccount;
import org.jclouds.scriptbuilder.domain.Statement;
/** Instance options specific to Google Compute Engine. */
public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
private boolean autoCreateKeyPair = true;
private List<ServiceAccount> serviceAccounts;
private String bootDiskType;
@Override
public GoogleComputeEngineTemplateOptions clone() {
@ -44,9 +45,25 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
GoogleComputeEngineTemplateOptions eTo = GoogleComputeEngineTemplateOptions.class.cast(to);
eTo.autoCreateKeyPair(autoCreateKeyPair());
eTo.serviceAccounts(serviceAccounts());
eTo.bootDiskType(bootDiskType());
}
}
/**
* Sets the boot disk type.
*/
public GoogleComputeEngineTemplateOptions bootDiskType(String diskType) {
this.bootDiskType = diskType;
return this;
}
/**
* Gets the boot disk type.
*/
public String bootDiskType() {
return bootDiskType;
}
/**
* Sets whether an SSH key pair should be created automatically.
*/

View File

@ -43,6 +43,10 @@ public abstract class AttachDisk {
return create(null, null, sourceImage, null);
}
static InitializeParams create(URI sourceImage, URI diskType) {
return create(null, null, sourceImage, diskType);
}
@SerializedNames({ "diskName", "diskSizeGb", "sourceImage", "diskType" })
public static InitializeParams create(String diskName, Long diskSizeGb, URI sourceImage, URI diskType) {
return new AutoValue_AttachDisk_InitializeParams(diskName, diskSizeGb, sourceImage, diskType);
@ -103,6 +107,10 @@ public abstract class AttachDisk {
return create(Type.PERSISTENT, null, InitializeParams.create(sourceImage), true, true);
}
public static AttachDisk newBootDisk(URI sourceImage, URI diskType) {
return create(Type.PERSISTENT, null, InitializeParams.create(sourceImage, diskType), true, true);
}
public static AttachDisk existingDisk(URI existingDisk) {
return create(Type.PERSISTENT, existingDisk, null, false, false);
}

View File

@ -18,27 +18,34 @@ 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.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.net.URI;
import java.util.Properties;
import java.util.Set;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Module;
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.compute.options.TemplateOptions;
import org.jclouds.compute.predicates.NodePredicates;
import org.jclouds.googlecloud.internal.TestProperties;
import org.jclouds.googlecomputeengine.GoogleComputeEngineApi;
import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions;
import org.jclouds.googlecomputeengine.domain.Disk;
import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.MachineType;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
@Test(groups = "live", singleThreaded = true)
public class GoogleComputeEngineServiceLiveTest extends BaseComputeServiceLiveTest {
@ -67,6 +74,29 @@ public class GoogleComputeEngineServiceLiveTest extends BaseComputeServiceLiveTe
}
}
public void testCreateNodeWithSsd() throws Exception {
String group = this.group + "ssd";
try {
TemplateOptions options = client.templateOptions();
options.as(GoogleComputeEngineTemplateOptions.class).bootDiskType("pd-ssd");
// create a node
Set<? extends NodeMetadata> nodes =
client.createNodesInGroup(group, 1, options);
assertEquals(nodes.size(), 1, "One node should have been created");
// Verify the disk on the instance is an ssd.
NodeMetadata node = Iterables.get(nodes, 0);
GoogleComputeEngineApi api = client.getContext().unwrapApi(GoogleComputeEngineApi.class);
Instance instance = api.instancesInZone(node.getLocation().getId()).get(node.getName());
Disk disk = api.disksInZone(node.getLocation().getId()).get(toName(instance.disks().get(0).source()));
assertTrue(disk.type().toString().endsWith("pd-ssd"));
} finally {
client.destroyNodesMatching(NodePredicates.inGroup(group));
}
}
/**
* Nodes may have additional metadata entries (particularly they may have an "sshKeys" entry)
*/
@ -110,4 +140,10 @@ public class GoogleComputeEngineServiceLiveTest extends BaseComputeServiceLiveTe
assert nodeTags.contains(tag) : String.format("node tags did not match %s %s node:", tags, nodeTags, node);
}
}
private static String toName(URI link) {
String path = link.getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
}

View File

@ -184,12 +184,64 @@ public class GoogleComputeEngineServiceMockTest extends BaseGoogleComputeEngineA
assertSent(server, "GET", "/projects/party/zones/us-central1-a/instances/test-1");
}
public void createNodeWithSpecificDiskType() throws Exception {
server.enqueue(singleRegionSingleZoneResponse());
server.enqueue(jsonResponse("/image_list.json"));
server.enqueue(jsonResponse("/image_list_debian.json")); // per IMAGE_PROJECTS = "debian-cloud"
server.enqueue(jsonResponse("/aggregated_machinetype_list.json"));
server.enqueue(jsonResponse("/network_get_default.json"));
server.enqueue(new MockResponse().setResponseCode(404)); // Get Firewall
server.enqueue(jsonResponse("/operation.json")); // Create Firewall
server.enqueue(jsonResponse("/zone_operation.json"));
server.enqueue(aggregatedListWithInstanceNetworkAndStatus("test-0", "test-network", RUNNING));
server.enqueue(jsonResponse("/disktype_ssd.json"));
server.enqueue(jsonResponse("/operation.json")); // Create Instance
server.enqueue(instanceWithNetworkAndStatusAndSsd("test-1", "test-network", RUNNING));
ComputeService computeService = computeService();
GoogleComputeEngineTemplateOptions options = computeService.templateOptions()
.as(GoogleComputeEngineTemplateOptions.class).autoCreateKeyPair(false)
.tags(ImmutableSet.of("aTag")).blockUntilRunning(false)
.bootDiskType("pd-ssd");
Template template = computeService.templateBuilder().options(options).build();
NodeMetadata node = getOnlyElement(computeService.createNodesInGroup("test", 1, template));
// prove our caching works.
assertEquals(node.getImageId(), template.getImage().getId());
assertEquals(node.getLocation().getId(), template.getLocation().getId());
assertSent(server, "GET", "/projects/party/regions");
assertSent(server, "GET", "/projects/party/global/images");
assertSent(server, "GET", "/projects/debian-cloud/global/images");
assertSent(server, "GET", "/projects/party/aggregated/machineTypes");
assertSent(server, "GET", "/projects/party/global/networks/default");
assertSent(server, "GET", "/projects/party/global/firewalls/jclouds-test-65f"); // Get Firewall
assertSent(server, "POST", "/projects/party/global/firewalls", // Create Firewall
stringFromResource("/firewall_insert_2.json"));
assertSent(server, "GET", "/projects/party/zones/us-central1-a/operations/operation-1354084865060");
assertSent(server, "GET", "/projects/party/aggregated/instances");
assertSent(server, "GET", "/projects/party/zones/us-central1-a/diskTypes/pd-ssd");
assertSent(server, "POST", "/projects/party/zones/us-central1-a/instances",
String.format(stringFromResource("/instance_insert_ssd.json"), template.getHardware().getId(), template.getImage().getId()));
assertSent(server, "GET", "/projects/party/zones/us-central1-a/instances/test-1");
}
private MockResponse instanceWithNetworkAndStatus(String instanceName, String networkName, Instance.Status status) {
return new MockResponse().setBody(
stringFromResource("/instance_get.json").replace("test-0", instanceName).replace("default", networkName)
.replace("RUNNING", status.toString()));
}
private MockResponse instanceWithNetworkAndStatusAndSsd(String instanceName, String networkName, Instance.Status status) {
return new MockResponse().setBody(
stringFromResource("/instance_get.json").replace("test-0", instanceName).replace("default", networkName)
.replace("RUNNING", status.toString()).replace("pd-standard", "pd-ssd"));
}
private MockResponse aggregatedListWithInstanceNetworkAndStatus(String instanceName, String networkName,
Instance.Status status) {
return new MockResponse().setBody(

View File

@ -0,0 +1,11 @@
{
"kind": "compute#diskType",
"creationTimestamp": "2014-06-02T11:07:28.529-07:00",
"name": "pd-ssd",
"description": "SSD Persistent Disk",
"validDiskSize": "10GB-1TB",
"zone": "https://content.googleapis.com/compute/v1/projects/party/zones/us-central1-a",
"selfLink": "https://content.googleapis.com/compute/v1/projects/party/zones/us-central1-a/diskTypes/pd-ssd",
"defaultDiskSizeGb": "100"
}

View File

@ -0,0 +1,40 @@
{
"machineType": "%s",
"name": "test-1",
"networkInterfaces": [
{
"network": "https://www.googleapis.com/compute/v1/projects/party/networks/default",
"accessConfigs": [
{
"type": "ONE_TO_ONE_NAT"
}
]
}
],
"disks": [
{
"type": "PERSISTENT",
"initializeParams": {
"sourceImage": "%s",
"diskType": "https://content.googleapis.com/compute/v1/projects/party/zones/us-central1-a/diskTypes/pd-ssd"
},
"boot": true,
"autoDelete": true
}
],
"description": "test",
"tags": {
"items": [
"aTag",
"jclouds-test-65f"
]
},
"metadata": {
"items": [
{
"key": "jclouds-group",
"value": "test"
}
]
}
}