diff --git a/gcloud-java-compute/src/main/java/com/google/gcloud/compute/Compute.java b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/Compute.java
index 907f581fcca6..0512a1554ee0 100644
--- a/gcloud-java-compute/src/main/java/com/google/gcloud/compute/Compute.java
+++ b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/Compute.java
@@ -23,6 +23,8 @@
import com.google.common.collect.Sets;
import com.google.gcloud.Page;
import com.google.gcloud.Service;
+import com.google.gcloud.compute.AttachedDisk.PersistentDiskConfiguration;
+import com.google.gcloud.compute.NetworkInterface.AccessConfig;
import com.google.gcloud.compute.spi.ComputeRpc;
import java.io.Serializable;
@@ -520,6 +522,51 @@ static String selector(NetworkField... fields) {
}
}
+ /**
+ * Fields of a Compute Engine Instance resource.
+ *
+ * @see
+ * Network Resource
+ */
+ enum InstanceField {
+ CAN_IP_FORWARD("canIpForward"),
+ CPU_PLATFORM("cpuPlatform"),
+ CREATION_TIMESTAMP("creationTimestamp"),
+ DESCRIPTION("description"),
+ DISKS("disks"),
+ ID("id"),
+ MACHINE_TYPE("machineType"),
+ METADATA("metadata"),
+ NAME("name"),
+ NETWORK_INTERFACES("networkInterfaces"),
+ SCHEDULING("scheduling"),
+ SELF_LINK("selfLink"),
+ SERVICE_ACCOUNTS("serviceAccounts"),
+ STATUS("status"),
+ STATUS_MESSAGE("statusMessage"),
+ TAGS("tags"),
+ ZONE("zone");
+
+ private final String selector;
+
+ InstanceField(String selector) {
+ this.selector = selector;
+ }
+
+ public String selector() {
+ return selector;
+ }
+
+ static String selector(InstanceField... fields) {
+ Set fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1);
+ fieldStrings.add(SELF_LINK.selector());
+ for (InstanceField field : fields) {
+ fieldStrings.add(field.selector());
+ }
+ return Joiner.on(',').join(fieldStrings);
+ }
+ }
+
/**
* Base class for list filters.
*/
@@ -1045,6 +1092,54 @@ public static NetworkFilter notEquals(NetworkField field, boolean value) {
}
}
+ /**
+ * Class for filtering instance lists.
+ */
+ class InstanceFilter extends ListFilter {
+
+ private static final long serialVersionUID = 679008888882025686L;
+
+ private InstanceFilter(InstanceField field, ComparisonOperator operator, Object value) {
+ super(field.selector(), operator, value);
+ }
+
+ /**
+ * Returns an equals filter for the given field and string value. For string fields,
+ * {@code value} is interpreted as a regular expression using RE2 syntax. {@code value} must
+ * match the entire field.
+ *
+ * @see RE2
+ */
+ public static InstanceFilter equals(InstanceField field, String value) {
+ return new InstanceFilter(checkNotNull(field), ComparisonOperator.EQ, checkNotNull(value));
+ }
+
+ /**
+ * Returns a not-equals filter for the given field and string value. For string fields,
+ * {@code value} is interpreted as a regular expression using RE2 syntax. {@code value} must
+ * match the entire field.
+ *
+ * @see RE2
+ */
+ public static InstanceFilter notEquals(InstanceField field, String value) {
+ return new InstanceFilter(checkNotNull(field), ComparisonOperator.NE, checkNotNull(value));
+ }
+
+ /**
+ * Returns a equals filter for the given field and boolean value.
+ */
+ public static InstanceFilter equals(InstanceField field, boolean value) {
+ return new InstanceFilter(checkNotNull(field), ComparisonOperator.EQ, value);
+ }
+
+ /**
+ * Returns a not-equals filter for the given field and boolean value.
+ */
+ public static InstanceFilter notEquals(InstanceField field, boolean value) {
+ return new InstanceFilter(checkNotNull(field), ComparisonOperator.EQ, value);
+ }
+ }
+
/**
* Class for specifying disk type get options.
*/
@@ -1989,6 +2084,108 @@ public static NetworkListOption fields(NetworkField... fields) {
}
}
+ /**
+ * Class for specifying instance get options.
+ */
+ class InstanceOption extends Option {
+
+ private static final long serialVersionUID = -5277658025892081493L;
+
+ private InstanceOption(ComputeRpc.Option option, Object value) {
+ super(option, value);
+ }
+
+ /**
+ * Returns an option to specify the instance's fields to be returned by the RPC call. If this
+ * option is not provided, all instance's fields are returned. {@code InstanceOption.fields}
+ * can be used to specify only the fields of interest. {@link Instance#instanceId()} is always
+ * returned, even if not specified.
+ */
+ public static InstanceOption fields(InstanceField... fields) {
+ return new InstanceOption(ComputeRpc.Option.FIELDS, InstanceField.selector(fields));
+ }
+ }
+
+ /**
+ * Class for specifying instance list options.
+ */
+ class InstanceListOption extends Option {
+
+ private static final long serialVersionUID = -1096684312959047430L;
+
+ private InstanceListOption(ComputeRpc.Option option, Object value) {
+ super(option, value);
+ }
+
+ /**
+ * Returns an option to specify a filter on the instances being listed.
+ */
+ public static InstanceListOption filter(InstanceFilter filter) {
+ return new InstanceListOption(ComputeRpc.Option.FILTER, filter.toPb());
+ }
+
+ /**
+ * Returns an option to specify the maximum number of instances returned per page.
+ * {@code pageSize} must be between 0 and 500 (inclusive). If not specified 500 is used.
+ */
+ public static InstanceListOption pageSize(long pageSize) {
+ return new InstanceListOption(ComputeRpc.Option.MAX_RESULTS, pageSize);
+ }
+
+ /**
+ * Returns an option to specify the page token from which to start listing instances.
+ */
+ public static InstanceListOption pageToken(String pageToken) {
+ return new InstanceListOption(ComputeRpc.Option.PAGE_TOKEN, pageToken);
+ }
+
+ /**
+ * Returns an option to specify the instance's fields to be returned by the RPC call. If this
+ * option is not provided, all instance's fields are returned. {@code InstanceListOption.fields}
+ * can be used to specify only the fields of interest. {@link Instance#instanceId()} is always
+ * returned, even if not specified.
+ */
+ public static InstanceListOption fields(InstanceField... fields) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("items(").append(InstanceField.selector(fields)).append("),nextPageToken");
+ return new InstanceListOption(ComputeRpc.Option.FIELDS, builder.toString());
+ }
+ }
+
+ /**
+ * Class for specifying instance aggregated list options.
+ */
+ class InstanceAggregatedListOption extends Option {
+
+ private static final long serialVersionUID = -2020005298975967713L;
+
+ private InstanceAggregatedListOption(ComputeRpc.Option option, Object value) {
+ super(option, value);
+ }
+
+ /**
+ * Returns an option to specify a filter on the instances being listed.
+ */
+ public static InstanceAggregatedListOption filter(InstanceFilter filter) {
+ return new InstanceAggregatedListOption(ComputeRpc.Option.FILTER, filter.toPb());
+ }
+
+ /**
+ * Returns an option to specify the maximum number of instances returned per page.
+ * {@code pageSize} must be between 0 and 500 (inclusive). If not specified 500 is used.
+ */
+ public static InstanceAggregatedListOption pageSize(long pageSize) {
+ return new InstanceAggregatedListOption(ComputeRpc.Option.MAX_RESULTS, pageSize);
+ }
+
+ /**
+ * Returns an option to specify the page token from which to start listing instances.
+ */
+ public static InstanceAggregatedListOption pageToken(String pageToken) {
+ return new InstanceAggregatedListOption(ComputeRpc.Option.PAGE_TOKEN, pageToken);
+ }
+ }
+
/**
* Returns the requested disk type or {@code null} if not found.
*
@@ -2397,4 +2594,196 @@ Operation deprecate(ImageId image, DeprecationStatus deprecationStatus,
* @throws ComputeException upon failure
*/
Operation deleteNetwork(NetworkId network, OperationOption... options);
+
+ /**
+ * Creates a new instance.
+ *
+ * @return a zone operation for instance's creation
+ * @throws ComputeException upon failure
+ */
+ Operation create(InstanceInfo instance, OperationOption... options);
+
+ /**
+ * Returns the requested instance or {@code null} if not found.
+ *
+ * @throws ComputeException upon failure
+ */
+ Instance get(InstanceId instance, InstanceOption... options);
+
+ /**
+ * Lists instances for the provided zone.
+ *
+ * @throws ComputeException upon failure
+ */
+ Page listInstances(String zone, InstanceListOption... options);
+
+ /**
+ * Lists instances for all zones.
+ *
+ * @throws ComputeException upon failure
+ */
+ Page listInstances(InstanceAggregatedListOption... options);
+
+ /**
+ * Deletes the requested instance.
+ *
+ * @return a zone operation if the delete request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation delete(InstanceId instance, OperationOption... options);
+
+ /**
+ * Adds an access configuration to an instance's network interface.
+ *
+ * @return a zone operation if the add request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ Operation addAccessConfig(InstanceId instance, String networkInterface, AccessConfig accessConfig,
+ OperationOption... options);
+
+ /**
+ * Attaches a persistent disk to an instance given its configuration.
+ *
+ * @return a zone operation if the attach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation attachDisk(InstanceId instance, PersistentDiskConfiguration configuration,
+ OperationOption... options);
+
+ /**
+ * Attaches a persistent disk to an instance given the device name and its configuration.
+ *
+ * @return a zone operation if the attach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation attachDisk(InstanceId instance, String deviceName,
+ PersistentDiskConfiguration configuration, OperationOption... options);
+
+ /**
+ * Attaches a persistent disk to an instance given the device name, its configuration and the
+ * device index.
+ *
+ * @return a zone operation if the attach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation attachDisk(InstanceId instance, String deviceName,
+ PersistentDiskConfiguration configuration, int index, OperationOption... options);
+
+ /**
+ * Deletes an access configuration from an instance's network interface.
+ *
+ * @return a zone operation if the delete request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation deleteAccessConfig(InstanceId instance, String networkInterface, String accessConfig,
+ OperationOption... options);
+
+ /**
+ * Detaches a disk from an instance.
+ *
+ * @return a zone operation if the detach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation detachDisk(InstanceId instance, String deviceName, OperationOption... options);
+
+ /**
+ * Returns the serial port output for the provided instance and port number. {@code port} must be
+ * between 1 and 4 (inclusive).
+ *
+ * @return the serial port output or {@code null} if the instance was not found
+ * @throws ComputeException upon failure
+ */
+ String getSerialPortOutput(InstanceId instance, int port);
+
+ /**
+ * Returns the default serial port output for the provided instance. Default serial port
+ * corresponds to port number 1.
+ *
+ * @return the serial port output or {@code null} if the instance was not found
+ * @throws ComputeException upon failure
+ */
+ String getSerialPortOutput(InstanceId instance);
+
+ /**
+ * Resets the provided instance.
+ *
+ * @return a zone operation if the reset request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation reset(InstanceId instance, OperationOption... options);
+
+ /**
+ * Sets the auto-delete flag for a disk attached to the provided instance.
+ *
+ * @return a zone operation if the flag setting request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation setDiskAutoDelete(InstanceId instance, String deviceName, boolean autoDelete,
+ OperationOption... options);
+
+ /**
+ * Sets the machine type for the provided instance. Instance must be in
+ * {@link InstanceInfo.Status#TERMINATED} state to be able to set its machine type.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ Operation setMachineType(InstanceId instance, MachineTypeId machineType,
+ OperationOption... options);
+
+ /**
+ * Sets the metadata for the provided instance.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ Operation setMetadata(InstanceId instance, Metadata metadata, OperationOption... options);
+
+ /**
+ * Sets the scheduling options for the provided instance.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ Operation setSchedulingOptions(InstanceId instance, SchedulingOptions scheduling,
+ OperationOption... options);
+
+ /**
+ * Sets the tags for the provided instance.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ Operation setTags(InstanceId instance, Tags tags, OperationOption... options);
+
+ /**
+ * Starts the provided instance.
+ *
+ * @return a zone operation if the start request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ Operation start(InstanceId instance, OperationOption... options);
+
+ /**
+ * Stops the provided instance.
+ *
+ * @return a zone operation if the stop request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ Operation stop(InstanceId instance, OperationOption... options);
}
diff --git a/gcloud-java-compute/src/main/java/com/google/gcloud/compute/ComputeImpl.java b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/ComputeImpl.java
index d8ac3a840761..3a8f46a88265 100644
--- a/gcloud-java-compute/src/main/java/com/google/gcloud/compute/ComputeImpl.java
+++ b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/ComputeImpl.java
@@ -28,6 +28,8 @@
import com.google.gcloud.PageImpl;
import com.google.gcloud.PageImpl.NextPageFetcher;
import com.google.gcloud.RetryHelper;
+import com.google.gcloud.compute.AttachedDisk.PersistentDiskConfiguration;
+import com.google.gcloud.compute.NetworkInterface.AccessConfig;
import com.google.gcloud.compute.spi.ComputeRpc;
import java.util.Map;
@@ -412,6 +414,46 @@ public Page nextPage() {
}
}
+ private static class InstancePageFetcher implements NextPageFetcher {
+
+ private static final long serialVersionUID = 7563769742657453865L;
+ private final Map requestOptions;
+ private final ComputeOptions serviceOptions;
+ private final String zone;
+
+ InstancePageFetcher(String zone, ComputeOptions serviceOptions, String cursor,
+ Map optionMap) {
+ this.requestOptions =
+ PageImpl.nextRequestOptions(ComputeRpc.Option.PAGE_TOKEN, cursor, optionMap);
+ this.serviceOptions = serviceOptions;
+ this.zone = zone;
+ }
+
+ @Override
+ public Page nextPage() {
+ return listInstances(zone, serviceOptions, requestOptions);
+ }
+ }
+
+ private static class AggregatedInstancePageFetcher implements NextPageFetcher {
+
+ private static final long serialVersionUID = 1863059389783095681L;
+ private final Map requestOptions;
+ private final ComputeOptions serviceOptions;
+
+ AggregatedInstancePageFetcher(ComputeOptions serviceOptions, String cursor,
+ Map optionMap) {
+ this.requestOptions =
+ PageImpl.nextRequestOptions(ComputeRpc.Option.PAGE_TOKEN, cursor, optionMap);
+ this.serviceOptions = serviceOptions;
+ }
+
+ @Override
+ public Page nextPage() {
+ return listInstances(serviceOptions, requestOptions);
+ }
+ }
+
private final ComputeRpc computeRpc;
ComputeImpl(ComputeOptions options) {
@@ -1431,7 +1473,7 @@ public com.google.api.services.compute.model.Subnetwork call() {
}
private static Function
- subnetworkFromPb(final ComputeOptions serviceOptions) {
+ subnetworkFromPb(final ComputeOptions serviceOptions) {
return new Function() {
@Override
public Subnetwork apply(com.google.api.services.compute.model.Subnetwork subnetwork) {
@@ -1605,6 +1647,399 @@ public Operation deleteNetwork(String network, OperationOption... options) {
return deleteNetwork(NetworkId.of(network));
}
+ @Override
+ public Operation create(InstanceInfo instance, OperationOption... options) {
+ final InstanceInfo completeInstance = instance.setProjectId(options().projectId());
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.createInstance(completeInstance.instanceId().zone(),
+ completeInstance.toPb(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Instance get(final InstanceId instance, InstanceOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Instance answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Instance call() {
+ return computeRpc.getInstance(instance.zone(), instance.instance(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Instance.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ private static Function
+ instanceFromPb(final ComputeOptions serviceOptions) {
+ return new Function() {
+ @Override
+ public Instance apply(com.google.api.services.compute.model.Instance instance) {
+ return Instance.fromPb(serviceOptions.service(), instance);
+ }
+ };
+ }
+
+ @Override
+ public Page listInstances(String zone, InstanceListOption... options) {
+ return listInstances(zone, options(), optionMap(options));
+ }
+
+ private static Page listInstances(final String zone,
+ final ComputeOptions serviceOptions, final Map optionsMap) {
+ try {
+ ComputeRpc.Tuple> result =
+ runWithRetries(new Callable>>() {
+ @Override
+ public ComputeRpc.Tuple> call() {
+ return serviceOptions.rpc().listInstances(zone, optionsMap);
+ }
+ }, serviceOptions.retryParams(), EXCEPTION_HANDLER);
+ String cursor = result.x();
+ Iterable instances = Iterables.transform(
+ result.y() == null ? ImmutableList.of()
+ : result.y(), instanceFromPb(serviceOptions));
+ return new PageImpl<>(new InstancePageFetcher(zone, serviceOptions, cursor, optionsMap),
+ cursor, instances);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Page listInstances(InstanceAggregatedListOption... options) {
+ return listInstances(options(), optionMap(options));
+ }
+
+ private static Page listInstances(final ComputeOptions serviceOptions,
+ final Map optionsMap) {
+ try {
+ ComputeRpc.Tuple> result =
+ runWithRetries(new Callable>>() {
+ @Override
+ public ComputeRpc.Tuple> call() {
+ return serviceOptions.rpc().listInstances(optionsMap);
+ }
+ }, serviceOptions.retryParams(), EXCEPTION_HANDLER);
+ String cursor = result.x();
+ Iterable instances = Iterables.transform(
+ result.y() == null ? ImmutableList.of()
+ : result.y(), instanceFromPb(serviceOptions));
+ return new PageImpl<>(new AggregatedInstancePageFetcher(serviceOptions, cursor, optionsMap),
+ cursor, instances);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation delete(final InstanceId instance, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.deleteInstance(instance.zone(), instance.instance(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation addAccessConfig(final InstanceId instance, final String networkInterface,
+ final AccessConfig accessConfig, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.addAccessConfig(instance.zone(), instance.instance(),
+ networkInterface, accessConfig.toPb(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ private Operation attachDisk(final InstanceId instance, AttachedDisk diskToAttach,
+ OperationOption... options) {
+ final AttachedDisk completeDisk = diskToAttach.setProjectId(options().projectId());
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.attachDisk(instance.zone(), instance.instance(),
+ completeDisk.toPb(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation attachDisk(InstanceId instance, PersistentDiskConfiguration configuration,
+ OperationOption... options) {
+ return attachDisk(instance, AttachedDisk.of(configuration), options);
+ }
+
+ @Override
+ public Operation attachDisk(InstanceId instance, String deviceName,
+ PersistentDiskConfiguration configuration, OperationOption... options) {
+ return attachDisk(instance, AttachedDisk.of(deviceName, configuration), options);
+ }
+
+ @Override
+ public Operation attachDisk(InstanceId instance, String deviceName,
+ PersistentDiskConfiguration configuration, int index, OperationOption... options) {
+ AttachedDisk attachedDisk = AttachedDisk.builder(configuration)
+ .deviceName(deviceName)
+ .index(index)
+ .build();
+ return attachDisk(instance, attachedDisk, options);
+ }
+
+ @Override
+ public Operation deleteAccessConfig(final InstanceId instance, final String networkInterface,
+ final String accessConfig, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.deleteAccessConfig(instance.zone(), instance.instance(),
+ networkInterface, accessConfig, optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation detachDisk(final InstanceId instance, final String deviceName,
+ OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.detachDisk(instance.zone(), instance.instance(), deviceName,
+ optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public String getSerialPortOutput(final InstanceId instance, final int port) {
+ try {
+ return runWithRetries(new Callable() {
+ @Override
+ public String call() {
+ return computeRpc.getSerialPortOutput(instance.zone(), instance.instance(), port,
+ optionMap());
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public String getSerialPortOutput(final InstanceId instance) {
+ try {
+ return runWithRetries(new Callable() {
+ @Override
+ public String call() {
+ return computeRpc.getSerialPortOutput(instance.zone(), instance.instance(), null,
+ optionMap());
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation reset(final InstanceId instance, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.reset(instance.zone(), instance.instance(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation setDiskAutoDelete(final InstanceId instance, final String deviceName,
+ final boolean autoDelete, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.setDiskAutoDelete(instance.zone(), instance.instance(), deviceName,
+ autoDelete, optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation setMachineType(final InstanceId instance, final MachineTypeId machineType,
+ OperationOption... options) {
+ final String machineTypeUrl = machineType.setProjectId(options().projectId()).selfLink();
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.setMachineType(instance.zone(), instance.instance(), machineTypeUrl,
+ optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation setMetadata(final InstanceId instance, final Metadata metadata,
+ OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.setMetadata(instance.zone(), instance.instance(), metadata.toPb(),
+ optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation setSchedulingOptions(final InstanceId instance,
+ final SchedulingOptions schedulingOptions, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.setScheduling(instance.zone(), instance.instance(),
+ schedulingOptions.toPb(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation setTags(final InstanceId instance, final Tags tags, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.setTags(instance.zone(), instance.instance(), tags.toPb(),
+ optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation start(final InstanceId instance, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.start(instance.zone(), instance.instance(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
+ @Override
+ public Operation stop(final InstanceId instance, OperationOption... options) {
+ final Map optionsMap = optionMap(options);
+ try {
+ com.google.api.services.compute.model.Operation answer =
+ runWithRetries(new Callable() {
+ @Override
+ public com.google.api.services.compute.model.Operation call() {
+ return computeRpc.stop(instance.zone(), instance.instance(), optionsMap);
+ }
+ }, options().retryParams(), EXCEPTION_HANDLER);
+ return answer == null ? null : Operation.fromPb(this, answer);
+ } catch (RetryHelper.RetryHelperException e) {
+ throw ComputeException.translateAndThrow(e);
+ }
+ }
+
private Map optionMap(Option... options) {
Map optionMap = Maps.newEnumMap(ComputeRpc.Option.class);
for (Option option : options) {
diff --git a/gcloud-java-compute/src/main/java/com/google/gcloud/compute/Instance.java b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/Instance.java
new file mode 100644
index 000000000000..5f9aee0dd840
--- /dev/null
+++ b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/Instance.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed 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 com.google.gcloud.compute;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gcloud.compute.AttachedDisk.PersistentDiskConfiguration;
+import com.google.gcloud.compute.Compute.InstanceOption;
+import com.google.gcloud.compute.Compute.OperationOption;
+import com.google.gcloud.compute.NetworkInterface.AccessConfig;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A Google Compute Engine VM Instance. An instance is a virtual machine (VM) hosted on Google's
+ * infrastructure. Instances can run Linux and Windows Server images provided by Google, or any
+ * customized versions of these images. You can also build and run images of other operating
+ * systems. Objects of this class are immutable. To get an {@code Instance} object with the most
+ * recent information use {@link #reload}. {@code Instance} adds a layer of service-related
+ * functionality over {@link InstanceInfo}.
+ *
+ * @see Virtual Machine Instances
+ */
+public class Instance extends InstanceInfo {
+
+ private static final long serialVersionUID = 3072508155558980677L;
+
+ private final ComputeOptions options;
+ private transient Compute compute;
+
+ /**
+ * A builder for {@code Instance} objects.
+ */
+ public static class Builder extends InstanceInfo.Builder {
+
+ private final Compute compute;
+ private final InstanceInfo.BuilderImpl infoBuilder;
+
+ Builder(Compute compute, InstanceId instanceId, MachineTypeId machineType,
+ AttachedDisk attachedDisk, NetworkInterface networkInterface) {
+ this.compute = compute;
+ this.infoBuilder = new InstanceInfo.BuilderImpl(instanceId);
+ this.infoBuilder.machineType(machineType);
+ this.infoBuilder.attachedDisks(ImmutableList.of(attachedDisk));
+ this.infoBuilder.networkInterfaces(ImmutableList.of(networkInterface));
+ }
+
+ Builder(Instance instance) {
+ this.compute = instance.compute;
+ this.infoBuilder = new InstanceInfo.BuilderImpl(instance);
+ }
+
+ @Override
+ Builder id(String id) {
+ this.infoBuilder.id(id);
+ return this;
+ }
+
+ @Override
+ public Builder instanceId(InstanceId instanceId) {
+ this.infoBuilder.instanceId(instanceId);
+ return this;
+ }
+
+ @Override
+ Builder creationTimestamp(Long creationTimestamp) {
+ this.infoBuilder.creationTimestamp(creationTimestamp);
+ return this;
+ }
+
+ @Override
+ public Builder description(String description) {
+ this.infoBuilder.description(description);
+ return this;
+ }
+
+ @Override
+ Builder status(Status status) {
+ this.infoBuilder.status(status);
+ return this;
+ }
+
+ @Override
+ Builder statusMessage(String statusMessage) {
+ this.infoBuilder.statusMessage(statusMessage);
+ return this;
+ }
+
+ @Override
+ public Builder tags(Tags tags) {
+ this.infoBuilder.tags(tags);
+ return this;
+ }
+
+ @Override
+ public Builder machineType(MachineTypeId machineType) {
+ this.infoBuilder.machineType(machineType);
+ return this;
+ }
+
+ @Override
+ public Builder canIpForward(Boolean canIpForward) {
+ this.infoBuilder.canIpForward(canIpForward);
+ return this;
+ }
+
+ @Override
+ public Builder networkInterfaces(List networkInterfaces) {
+ this.infoBuilder.networkInterfaces(networkInterfaces);
+ return this;
+ }
+
+ @Override
+ public Builder networkInterfaces(NetworkInterface... networkInterfaces) {
+ this.infoBuilder.networkInterfaces(networkInterfaces);
+ return this;
+ }
+
+ @Override
+ public Builder attachedDisks(List attachedDisks) {
+ this.infoBuilder.attachedDisks(attachedDisks);
+ return this;
+ }
+
+ @Override
+ public Builder attachedDisks(AttachedDisk... attachedDisks) {
+ this.infoBuilder.attachedDisks(attachedDisks);
+ return this;
+ }
+
+ @Override
+ public Builder metadata(Metadata metadata) {
+ this.infoBuilder.metadata(metadata);
+ return this;
+ }
+
+ @Override
+ public Builder serviceAccounts(List serviceAccounts) {
+ this.infoBuilder.serviceAccounts(serviceAccounts);
+ return this;
+ }
+
+ @Override
+ public Builder schedulingOptions(SchedulingOptions schedulingOptions) {
+ this.infoBuilder.schedulingOptions(schedulingOptions);
+ return this;
+ }
+
+ @Override
+ Builder cpuPlatform(String cpuPlatform) {
+ this.infoBuilder.cpuPlatform(cpuPlatform);
+ return this;
+ }
+
+ @Override
+ public Instance build() {
+ return new Instance(compute, infoBuilder);
+ }
+ }
+
+ Instance(Compute compute, Instance.BuilderImpl infoBuilder) {
+ super(infoBuilder);
+ this.compute = checkNotNull(compute);
+ this.options = compute.options();
+ }
+
+ /**
+ * Checks if this instance exists.
+ *
+ * @return {@code true} if this instance exists, {@code false} otherwise
+ * @throws ComputeException upon failure
+ */
+ public boolean exists() {
+ return reload(InstanceOption.fields()) != null;
+ }
+
+ /**
+ * Fetches current instance's latest information. Returns {@code null} if the instance does not
+ * exist.
+ *
+ * @param options instance options
+ * @return a {@code Instance} object with latest information or {@code null} if not found
+ * @throws ComputeException upon failure
+ */
+ public Instance reload(InstanceOption... options) {
+ return compute.get(instanceId(), options);
+ }
+
+ /**
+ * Deletes this instance.
+ *
+ * @return a zone operation if delete request was successfully sent, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation delete(OperationOption... options) {
+ return compute.delete(instanceId(), options);
+ }
+
+ /**
+ * Adds an access configuration to the provided network interface for this instance.
+ *
+ * @return a zone operation if the add request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation addAccessConfig(String networkInterface, AccessConfig accessConfig,
+ OperationOption... options) {
+ return compute.addAccessConfig(instanceId(), networkInterface, accessConfig, options);
+ }
+
+ /**
+ * Attaches a persistent disk to this instance given its configuration.
+ *
+ * @return a zone operation if the attach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation attachDisk(PersistentDiskConfiguration configuration,
+ OperationOption... options) {
+ return compute.attachDisk(instanceId(), configuration, options);
+ }
+
+ /**
+ * Attaches a persistent disk to this instance given the device name and its configuration.
+ *
+ * @return a zone operation if the attach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation attachDisk(String deviceName, PersistentDiskConfiguration configuration,
+ OperationOption... options) {
+ return compute.attachDisk(instanceId(), deviceName, configuration, options);
+ }
+
+ /**
+ * Attaches a persistent disk to this instance given the device name, its configuration and the
+ * device index.
+ *
+ * @return a zone operation if the attach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation attachDisk(String deviceName, PersistentDiskConfiguration configuration,
+ int index, OperationOption... options) {
+ return compute.attachDisk(instanceId(), deviceName, configuration, index, options);
+ }
+
+ /**
+ * Deletes an access configuration from the provided network interface for this instance.
+ *
+ * @return a zone operation if the delete request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation deleteAccessConfig(String networkInterface, String accessConfig,
+ OperationOption... options) {
+ return compute.deleteAccessConfig(instanceId(), networkInterface, accessConfig, options);
+ }
+
+ /**
+ * Detaches a disk from this instance.
+ *
+ * @return a zone operation if the detach request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation detachDisk(String deviceName, OperationOption... options) {
+ return compute.detachDisk(instanceId(), deviceName, options);
+ }
+
+ /**
+ * Returns the serial port output for this instance and port number. {@code port} must be between
+ * 1 and 4 (inclusive).
+ *
+ * @return the serial port output or {@code null} if the instance was not found
+ * @throws ComputeException upon failure
+ */
+ public String getSerialPortOutput(int port) {
+ return compute.getSerialPortOutput(instanceId(), port);
+ }
+
+ /**
+ * Returns the default serial port output for this instance. Default serial port corresponds to
+ * port number 1.
+ *
+ * @return the serial port output or {@code null} if the instance was not found
+ * @throws ComputeException upon failure
+ */
+ public String getSerialPortOutput() {
+ return compute.getSerialPortOutput(instanceId());
+ }
+
+ /**
+ * Resets this instance.
+ *
+ * @return a zone operation if the reset request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation reset(OperationOption... options) {
+ return compute.reset(instanceId(), options);
+ }
+
+ /**
+ * Sets the auto-delete flag for a disk attached to this instance.
+ *
+ * @return a zone operation if the flag setting request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation setDiskAutoDelete(String deviceName, boolean autoDelete,
+ OperationOption... options) {
+ return compute.setDiskAutoDelete(instanceId(), deviceName, autoDelete, options);
+ }
+
+ /**
+ * Sets the machine type for this instance. The instance must be in
+ * {@link InstanceInfo.Status#TERMINATED} state to be able to set its machine type.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation setMachineType(MachineTypeId machineType, OperationOption... options) {
+ return compute.setMachineType(instanceId(), machineType, options);
+ }
+
+ /**
+ * Sets the metadata for this instance.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation setMetadata(Metadata metadata, OperationOption... options) {
+ return compute.setMetadata(instanceId(), metadata, options);
+ }
+
+ /**
+ * Sets the metadata for this instance, fingerprint value is taken from this instance's
+ * {@code tags().fingerprint()}.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation setMetadata(Map metadata, OperationOption... options) {
+ return setMetadata(metadata().toBuilder().values(metadata).build(), options);
+ }
+
+ /**
+ * Sets the scheduling options for this instance.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation setSchedulingOptions(SchedulingOptions scheduling, OperationOption... options) {
+ return compute.setSchedulingOptions(instanceId(), scheduling, options);
+ }
+
+ /**
+ * Sets the tags for this instance.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation setTags(Tags tags, OperationOption... options) {
+ return compute.setTags(instanceId(), tags, options);
+ }
+
+ /**
+ * Sets the tags for this instance, fingerprint value is taken from this instance's
+ * {@code tags().fingerprint()}.
+ *
+ * @return a zone operation if the set request was issued correctly, {@code null} if the instance
+ * was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation setTags(Iterable tags, OperationOption... options) {
+ return setTags(tags().toBuilder().values(tags).build(), options);
+ }
+
+ /**
+ * Starts this instance.
+ *
+ * @return a zone operation if the start request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation start(OperationOption... options) {
+ return compute.start(instanceId(), options);
+ }
+
+ /**
+ * Stops this instance.
+ *
+ * @return a zone operation if the stop request was issued correctly, {@code null} if the
+ * instance was not found
+ * @throws ComputeException upon failure
+ */
+ public Operation stop(OperationOption... options) {
+ return compute.stop(instanceId(), options);
+ }
+
+ /**
+ * Returns the snapshot's {@code Compute} object used to issue requests.
+ */
+ public Compute compute() {
+ return compute;
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || !obj.getClass().equals(Instance.class)) {
+ return false;
+ }
+ Instance other = (Instance) obj;
+ return Objects.equals(toPb(), other.toPb()) && Objects.equals(options, other.options);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(super.hashCode(), options);
+ }
+
+ private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
+ input.defaultReadObject();
+ this.compute = options.service();
+ }
+
+ static Instance fromPb(Compute compute,
+ com.google.api.services.compute.model.Instance instancePb) {
+ return new Instance(compute, new InstanceInfo.BuilderImpl(instancePb));
+ }
+}
diff --git a/gcloud-java-compute/src/main/java/com/google/gcloud/compute/spi/ComputeRpc.java b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/spi/ComputeRpc.java
index f3e51266aa50..4da5931d064c 100644
--- a/gcloud-java-compute/src/main/java/com/google/gcloud/compute/spi/ComputeRpc.java
+++ b/gcloud-java-compute/src/main/java/com/google/gcloud/compute/spi/ComputeRpc.java
@@ -16,18 +16,24 @@
package com.google.gcloud.compute.spi;
+import com.google.api.services.compute.model.AccessConfig;
import com.google.api.services.compute.model.Address;
+import com.google.api.services.compute.model.AttachedDisk;
import com.google.api.services.compute.model.DeprecationStatus;
import com.google.api.services.compute.model.Disk;
import com.google.api.services.compute.model.DiskType;
import com.google.api.services.compute.model.Image;
+import com.google.api.services.compute.model.Instance;
import com.google.api.services.compute.model.License;
import com.google.api.services.compute.model.MachineType;
+import com.google.api.services.compute.model.Metadata;
import com.google.api.services.compute.model.Network;
import com.google.api.services.compute.model.Operation;
import com.google.api.services.compute.model.Region;
+import com.google.api.services.compute.model.Scheduling;
import com.google.api.services.compute.model.Snapshot;
import com.google.api.services.compute.model.Subnetwork;
+import com.google.api.services.compute.model.Tags;
import com.google.api.services.compute.model.Zone;
import com.google.gcloud.compute.ComputeException;
@@ -496,4 +502,171 @@ Operation deprecateImage(String project, String image, DeprecationStatus depreca
* @throws ComputeException upon failure
*/
Operation deleteNetwork(String network, Map