JCLOUDS-906: Add ServiceAccounts to GoogleComputeEngineTemplateOptions

This commit is contained in:
Daniel Broudy 2015-05-29 10:13:31 -07:00 committed by Ignasi Barrera
parent aa33619c92
commit 55348c0ddb
7 changed files with 219 additions and 24 deletions

View File

@ -138,15 +138,14 @@ public final class GoogleComputeEngineServiceAdapter
tags.add(naming.name(ports)); tags.add(naming.name(ports));
} }
NewInstance newInstance = NewInstance.create( NewInstance newInstance = new NewInstance.Builder( name,
name, // name
template.getHardware().getUri(), // machineType template.getHardware().getUri(), // machineType
network, // network network,
disks, // disks disks)
group, // description .description(group)
Tags.create(null, ImmutableList.copyOf(tags)) // tags .tags(Tags.create(null, ImmutableList.copyOf(tags)))
); .serviceAccounts(options.serviceAccounts())
.build();
// Add metadata from template and for ssh key and image id // Add metadata from template and for ssh key and image id
newInstance.metadata().putAll(options.getUserMetadata()); newInstance.metadata().putAll(options.getUserMetadata());
@ -177,7 +176,7 @@ public final class GoogleComputeEngineServiceAdapter
null, // networkInterfaces null, // networkInterfaces
null, // disks null, // disks
newInstance.metadata(), // metadata newInstance.metadata(), // metadata
null, // serviceAccounts newInstance.serviceAccounts(), // serviceAccounts
Scheduling.create(OnHostMaintenance.MIGRATE, true) // scheduling Scheduling.create(OnHostMaintenance.MIGRATE, true) // scheduling
)); ));
checkState(instanceVisible.apply(instance), "instance %s is not api visible!", instance.get()); checkState(instanceVisible.apply(instance), "instance %s is not api visible!", instance.get());

View File

@ -16,16 +16,19 @@
*/ */
package org.jclouds.googlecomputeengine.compute.options; package org.jclouds.googlecomputeengine.compute.options;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.googlecomputeengine.domain.Instance.ServiceAccount;
/** Instance options specific to Google Compute Engine. */ /** Instance options specific to Google Compute Engine. */
public final class GoogleComputeEngineTemplateOptions extends TemplateOptions { public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
private boolean autoCreateKeyPair = true; private boolean autoCreateKeyPair = true;
private List<ServiceAccount> serviceAccounts;
@Override @Override
public GoogleComputeEngineTemplateOptions clone() { public GoogleComputeEngineTemplateOptions clone() {
@ -40,6 +43,7 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
if (to instanceof GoogleComputeEngineTemplateOptions) { if (to instanceof GoogleComputeEngineTemplateOptions) {
GoogleComputeEngineTemplateOptions eTo = GoogleComputeEngineTemplateOptions.class.cast(to); GoogleComputeEngineTemplateOptions eTo = GoogleComputeEngineTemplateOptions.class.cast(to);
eTo.autoCreateKeyPair(autoCreateKeyPair()); eTo.autoCreateKeyPair(autoCreateKeyPair());
eTo.serviceAccounts(serviceAccounts());
} }
} }
@ -52,12 +56,30 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions {
} }
/** /**
* Sets whether an SSH key pair should be created automatically. * Gets whether an SSH key pair should be created automatically.
*/ */
public boolean autoCreateKeyPair() { public boolean autoCreateKeyPair() {
return autoCreateKeyPair; return autoCreateKeyPair;
} }
/**
* Sets a list of service accounts, with their specified scopes, to authorize on created instance.
* For example, to give a node the 'compute' scope you would add a service account with the email 'default'
* and the scope 'https://www.googleapis.com/auth/compute'
* These scopes will be given to all nodes created with these template options.
*/
public GoogleComputeEngineTemplateOptions serviceAccounts(List<ServiceAccount> serviceAccounts){
this.serviceAccounts = serviceAccounts;
return this;
}
/**
* Gets the list of service accounts, with their specified scopes, that will be authorize on created instances.
*/
public List<ServiceAccount> serviceAccounts(){
return serviceAccounts;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -17,6 +17,7 @@
package org.jclouds.googlecomputeengine.domain; package org.jclouds.googlecomputeengine.domain;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI; import java.net.URI;
import java.util.Arrays; import java.util.Arrays;
@ -80,7 +81,8 @@ public abstract class NewInstance {
return create(name, machineType, network, Arrays.asList(AttachDisk.newBootDisk(sourceImage)), null, null); return create(name, machineType, network, Arrays.asList(AttachDisk.newBootDisk(sourceImage)), null, null);
} }
public static NewInstance create(String name, URI machineType, URI network, List<AttachDisk> disks, @Nullable String description, @Nullable Tags tags) { public static NewInstance create(String name, URI machineType, URI network, List<AttachDisk> disks,
@Nullable String description, @Nullable Tags tags) {
checkArgument(disks.get(0).boot(), "disk 0 must be a boot disk! %s", disks); checkArgument(disks.get(0).boot(), "disk 0 must be a boot disk! %s", disks);
boolean foundBoot = false; boolean foundBoot = false;
for (AttachDisk disk : disks) { for (AttachDisk disk : disks) {
@ -93,13 +95,86 @@ public abstract class NewInstance {
description, tags != null ? tags : Tags.create(), Metadata.create(), null, null); description, tags != null ? tags : Tags.create(), Metadata.create(), null, null);
} }
@SerializedNames({ "name", "machineType", "canIpForward", "networkInterfaces", "disks", "description", "tags", "metadata", @SerializedNames({ "name", "machineType", "canIpForward", "networkInterfaces", "disks", "description",
"serviceAccounts", "scheduling" }) "tags", "metadata", "serviceAccounts", "scheduling" })
static NewInstance create(String name, URI machineType, Boolean canIpForward, List<NetworkInterface> networkInterfaces, static NewInstance create(String name, URI machineType, Boolean canIpForward,
List<AttachDisk> disks, String description, Tags tags, Metadata metadata, List<ServiceAccount> serviceAccounts, Scheduling scheduling) { List<NetworkInterface> networkInterfaces, List<AttachDisk> disks, String description,
return new AutoValue_NewInstance(name, machineType, canIpForward, networkInterfaces, disks, description, tags, metadata, serviceAccounts, scheduling); Tags tags, Metadata metadata, List<ServiceAccount> serviceAccounts, Scheduling scheduling) {
return new AutoValue_NewInstance(name, machineType, canIpForward, networkInterfaces, disks, description,
tags, metadata, serviceAccounts, scheduling);
} }
NewInstance() { NewInstance() {
} }
public static class Builder {
private String name;
private URI machineType;
private Boolean canIpForward;
private List<NetworkInterface> networkInterfaces;
private List<AttachDisk> disks;
private String description;
private Tags tags;
private Metadata metadata;
private List<ServiceAccount> serviceAccounts;
private Scheduling scheduling;
public Builder(String name, URI machineType, URI network, List<AttachDisk> disks) {
checkNotNull(name, "NewInstance name cannot be null");
this.name = name;
this.machineType = machineType;
this.networkInterfaces = ImmutableList.of(NetworkInterface.create(network));
this.disks = disks;
}
public Builder(String name, URI machineType, URI network, URI sourceImage) {
checkNotNull(name, "NewInstance name cannot be null");
this.name = name;
this.machineType = machineType;
this.networkInterfaces = ImmutableList.of(NetworkInterface.create(network));
this.disks = Arrays.asList(AttachDisk.newBootDisk(sourceImage));
}
public Builder canIpForward(Boolean canIpForward){
this.canIpForward = canIpForward;
return this;
}
public Builder description(String description){
this.description = description;
return this;
}
public Builder tags(Tags tags){
this.tags = tags;
return this;
}
public Builder metadata(Metadata metadata){
this.metadata = metadata;
return this;
}
/**
* A list of service accounts, with their specified scopes, authorized for this instance.
* Service accounts generate access tokens that can be accessed through the metadata server
* and used to authenticate applications on the instance.
* Note: to add scopes to the default service account on the VM you can use 'default' as
* a keyword for email.
*/
public Builder serviceAccounts(List<ServiceAccount> serviceAccounts){
this.serviceAccounts = serviceAccounts;
return this;
}
public Builder scheduling(Scheduling scheduling){
this.scheduling = scheduling;
return this;
}
public NewInstance build() {
return create(name, machineType, canIpForward, networkInterfaces, disks, description, tags != null ? tags : Tags.create(),
metadata != null ? metadata : Metadata.create(), serviceAccounts, scheduling);
}
}
} }

View File

@ -40,10 +40,6 @@ public abstract class Tags implements Cloneable {
return Tags.create(null, null); return Tags.create(null, null);
} }
public static Tags create(String fingerprint) {
return Tags.create(fingerprint, null);
}
@SerializedNames({ "fingerprint", "items" }) @SerializedNames({ "fingerprint", "items" })
public static Tags create(String fingerprint, ImmutableList<String> items) { // Dictates the type when created from json! public static Tags create(String fingerprint, ImmutableList<String> items) { // Dictates the type when created from json!
ImmutableList<String> empty = ImmutableList.of(); ImmutableList<String> empty = ImmutableList.of();

View File

@ -34,7 +34,9 @@ import org.jclouds.googlecomputeengine.domain.Instance;
import org.jclouds.googlecomputeengine.domain.Instance.AttachedDisk; import org.jclouds.googlecomputeengine.domain.Instance.AttachedDisk;
import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig.Type; import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig.Type;
import org.jclouds.googlecomputeengine.domain.Instance.Scheduling; import org.jclouds.googlecomputeengine.domain.Instance.Scheduling;
import org.jclouds.googlecomputeengine.domain.Instance.Scheduling.OnHostMaintenance;
import org.jclouds.googlecomputeengine.domain.Instance.SerialPortOutput; import org.jclouds.googlecomputeengine.domain.Instance.SerialPortOutput;
import org.jclouds.googlecomputeengine.domain.Instance.ServiceAccount;
import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig; import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig;
import org.jclouds.googlecomputeengine.domain.Metadata; import org.jclouds.googlecomputeengine.domain.Metadata;
import org.jclouds.googlecomputeengine.domain.NewInstance; import org.jclouds.googlecomputeengine.domain.NewInstance;
@ -99,11 +101,16 @@ public class InstanceApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
); );
instance.metadata().put("mykey", "myvalue"); instance.metadata().put("mykey", "myvalue");
instance2 = NewInstance.create( instance2 = new NewInstance.Builder(INSTANCE_NAME2, // name
INSTANCE_NAME2, // name
getDefaultMachineTypeUrl(), // machineType getDefaultMachineTypeUrl(), // machineType
getNetworkUrl(INSTANCE_NETWORK_NAME), // network getNetworkUrl(INSTANCE_NETWORK_NAME), // network
imageUri); // sourceImage imageUri) // sourceImage
.canIpForward(true)
.description("description")
.tags(Tags.create(null, ImmutableList.of("tag1")))
.serviceAccounts(ImmutableList.of(ServiceAccount.create("default", ImmutableList.of("https://www.googleapis.com/auth/compute"))))
.scheduling(Scheduling.create(OnHostMaintenance.MIGRATE, true))
.build();
return api; return api;
} }
@ -116,6 +123,18 @@ public class InstanceApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
return api.disksInZone(DEFAULT_ZONE_NAME); return api.disksInZone(DEFAULT_ZONE_NAME);
} }
@Test(groups = "live", dependsOnMethods = "testInsertInstance")
public void testGetInstance2() {
Instance instance = api().get(INSTANCE_NAME2);
assertNotNull(instance);
assertInstanceEquals(instance, this.instance2);
assertTrue(instance.canIpForward());
assertEquals(instance.description(), "description");
assertEquals(instance.serviceAccounts().get(0).scopes(), ImmutableList.of("https://www.googleapis.com/auth/compute"));
assertTrue(instance.scheduling().automaticRestart());
assertEquals(instance.scheduling().onHostMaintenance(), OnHostMaintenance.MIGRATE);
}
@Test(groups = "live") @Test(groups = "live")
public void testInsertInstance() { public void testInsertInstance() {
// need to insert the network first // need to insert the network first
@ -294,7 +313,7 @@ public class InstanceApiLiveTest extends BaseGoogleComputeEngineApiLiveTest {
assertOperationDoneSuccessfully(api().reset(INSTANCE_NAME)); assertOperationDoneSuccessfully(api().reset(INSTANCE_NAME));
} }
@Test(groups = "live", dependsOnMethods = "testInsertInstance") @Test(groups = "live", dependsOnMethods = "testGetInstance2")
public void testStopInstance() { public void testStopInstance() {
Instance originalInstance = api().get(INSTANCE_NAME2); Instance originalInstance = api().get(INSTANCE_NAME2);
assertEquals(originalInstance.status(), Instance.Status.RUNNING); assertEquals(originalInstance.status(), Instance.Status.RUNNING);

View File

@ -27,6 +27,8 @@ import org.jclouds.googlecomputeengine.domain.AttachDisk;
import org.jclouds.googlecomputeengine.domain.AttachDisk.DiskInterface; import org.jclouds.googlecomputeengine.domain.AttachDisk.DiskInterface;
import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig; import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig;
import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig.Type; import org.jclouds.googlecomputeengine.domain.Instance.NetworkInterface.AccessConfig.Type;
import org.jclouds.googlecomputeengine.domain.Instance.Scheduling;
import org.jclouds.googlecomputeengine.domain.Instance.ServiceAccount;
import org.jclouds.googlecomputeengine.domain.Metadata; import org.jclouds.googlecomputeengine.domain.Metadata;
import org.jclouds.googlecomputeengine.domain.NewInstance; import org.jclouds.googlecomputeengine.domain.NewInstance;
import org.jclouds.googlecomputeengine.domain.Instance.Scheduling.OnHostMaintenance; import org.jclouds.googlecomputeengine.domain.Instance.Scheduling.OnHostMaintenance;
@ -250,6 +252,41 @@ public class InstanceApiMockTest extends BaseGoogleComputeEngineApiMockTest {
assertSent(server, "POST", "/projects/party/zones/us-central1-a/instances/test-1/stop"); assertSent(server, "POST", "/projects/party/zones/us-central1-a/instances/test-1/stop");
} }
public void builderTest() throws Exception {
server.enqueue(jsonResponse("/zone_operation.json"));
NewInstance newInstance = new NewInstance.Builder("test-1", // name
URI.create(url("/projects/party/zones/us-central1-a/machineTypes/n1-standard-1")), // machineType
URI.create(url("/projects/party/global/networks/default")), // network
URI.create(url("/projects/party/global/images/centos-6-2-v20120326"))).build(); // sourceImage)
assertEquals(instanceApi().create(newInstance), new ParseZoneOperationTest().expected(url("/projects")));
assertSent(server, "POST", "/projects/party/zones/us-central1-a/instances",
stringFromResource("/instance_insert_simple.json"));
}
public void insert_builder_allOptions() throws Exception {
server.enqueue(jsonResponse("/zone_operation.json"));
NewInstance newInstance = new NewInstance.Builder(
"test-1", // name
URI.create(url("/projects/party/zones/us-central1-a/machineTypes/n1-standard-1")), // machineType
URI.create(url("/projects/party/global/networks/default")), // network
Arrays.asList(AttachDisk.existingBootDisk(URI.create(url("/projects/party/zones/us-central1-a/disks/test")))))
.canIpForward(true)
.description("desc")
.tags(null)
.metadata(Metadata.create().put("aKey", "aValue"))
.serviceAccounts(ImmutableList.of(ServiceAccount.create("default",
ImmutableList.of("https://www.googleapis.com/auth/compute"))))
.scheduling(Scheduling.create(OnHostMaintenance.MIGRATE, true))
.build();
assertEquals(instanceApi().create(newInstance), new ParseZoneOperationTest().expected(url("/projects")));
assertSent(server, "POST", "/projects/party/zones/us-central1-a/instances",
stringFromResource("/instance_insert_full.json"));
}
InstanceApi instanceApi(){ InstanceApi instanceApi(){
return api().instancesInZone("us-central1-a"); return api().instancesInZone("us-central1-a");
} }

View File

@ -0,0 +1,47 @@
{
"name": "test-1",
"machineType": "https://www.googleapis.com/compute/v1/projects/party/zones/us-central1-a/machineTypes/n1-standard-1",
"canIpForward": true,
"networkInterfaces": [
{
"network": "https://www.googleapis.com/compute/v1/projects/party/global/networks/default",
"accessConfigs": [
{
"type": "ONE_TO_ONE_NAT"
}
]
}
],
"disks": [
{
"type": "PERSISTENT",
"source": "https://www.googleapis.com/compute/v1/projects/party/zones/us-central1-a/disks/test",
"boot": true,
"autoDelete": false
}
],
"description": "desc",
"tags": {
"items": []
},
"metadata": {
"items": [
{
"key": "aKey",
"value": "aValue"
}
]
},
"serviceAccounts": [
{
"email": "default",
"scopes": [
"https://www.googleapis.com/auth/compute"
]
}
],
"scheduling": {
"onHostMaintenance": "MIGRATE",
"automaticRestart": true
}
}