From 2ec28df21b1e090831798ac08204f0918f47147a Mon Sep 17 00:00:00 2001 From: Nelson Araujo Date: Sat, 6 Aug 2016 23:33:29 -0700 Subject: [PATCH] JCLOUDS-1122: Support subnetworks definitions in Google Compute. --- .../GoogleComputeEngineApi.java | 11 +- .../GoogleComputeEngineServiceAdapter.java | 8 +- .../compute/functions/Resources.java | 5 + .../compute/loaders/SubnetworkLoader.java | 57 ++++++++ .../GoogleComputeEngineTemplateOptions.java | 25 ++++ .../googlecomputeengine/domain/Network.java | 29 +++- .../domain/NewInstance.java | 39 +++++- .../domain/Subnetwork.java | 62 +++++++++ .../features/AggregatedListApi.java | 50 +++++++ .../features/NetworkApi.java | 6 +- .../features/SubnetworkApi.java | 127 ++++++++++++++++++ .../options/SubnetworkCreationOptions.java | 52 +++++++ .../features/SubnetworkApiMockTest.java | 95 +++++++++++++ .../parse/ParseSubnetworkListTest.java | 51 +++++++ .../parse/ParseSubnetworkTest.java | 56 ++++++++ .../src/test/resources/subnetwork_get.json | 12 ++ .../src/test/resources/subnetwork_insert.json | 7 + .../src/test/resources/subnetwork_list.json | 19 +++ 18 files changed, 701 insertions(+), 10 deletions(-) create mode 100644 providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/SubnetworkLoader.java create mode 100644 providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Subnetwork.java create mode 100644 providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/SubnetworkApi.java create mode 100644 providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/options/SubnetworkCreationOptions.java create mode 100644 providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/SubnetworkApiMockTest.java create mode 100644 providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkListTest.java create mode 100644 providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkTest.java create mode 100644 providers/google-compute-engine/src/test/resources/subnetwork_get.json create mode 100644 providers/google-compute-engine/src/test/resources/subnetwork_insert.json create mode 100644 providers/google-compute-engine/src/test/resources/subnetwork_list.json diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java index 21c0764fdc..1e31890f23 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/GoogleComputeEngineApi.java @@ -17,11 +17,13 @@ package org.jclouds.googlecomputeengine; import java.io.Closeable; +import java.net.URI; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import org.jclouds.googlecloud.config.CurrentProject; +import org.jclouds.googlecomputeengine.domain.Subnetwork; import org.jclouds.googlecomputeengine.features.AddressApi; import org.jclouds.googlecomputeengine.features.AggregatedListApi; import org.jclouds.googlecomputeengine.features.BackendServiceApi; @@ -40,6 +42,8 @@ import org.jclouds.googlecomputeengine.features.ProjectApi; import org.jclouds.googlecomputeengine.features.RegionApi; import org.jclouds.googlecomputeengine.features.RouteApi; import org.jclouds.googlecomputeengine.features.SnapshotApi; +import org.jclouds.googlecomputeengine.features.SubnetworkApi; +import org.jclouds.googlecomputeengine.features.TargetHttpProxyApi; import org.jclouds.googlecomputeengine.features.TargetInstanceApi; import org.jclouds.googlecomputeengine.features.TargetPoolApi; import org.jclouds.googlecomputeengine.features.TargetHttpProxyApi; @@ -47,6 +51,7 @@ import org.jclouds.googlecomputeengine.features.UrlMapApi; import org.jclouds.googlecomputeengine.features.ZoneApi; import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Endpoint; +import org.jclouds.rest.annotations.EndpointParam; public interface GoogleComputeEngineApi extends Closeable { @@ -116,6 +121,11 @@ public interface GoogleComputeEngineApi extends Closeable { @Path("/global") NetworkApi networks(); + @Delegate + @Endpoint(CurrentProject.class) + @Path("/regions/{region}") + SubnetworkApi subnetworksInRegion(@PathParam("region") String region); + @Delegate OperationApi operations(); @@ -159,5 +169,4 @@ public interface GoogleComputeEngineApi extends Closeable { @Endpoint(CurrentProject.class) @Path("/global/urlMaps") UrlMapApi urlMaps(); - } diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java index fe54bdf04b..c2b180fcaf 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/GoogleComputeEngineServiceAdapter.java @@ -136,11 +136,17 @@ public final class GoogleComputeEngineServiceAdapter URI network = URI.create(networks.next()); assert !networks.hasNext() : "Error: Options should specify only one network"; + Iterator subnetworks = options.getSubnetworks().iterator(); + + URI subnetwork = subnetworks.hasNext() ? URI.create(subnetworks.next()) : null; + assert !subnetworks.hasNext() : "Error: Options should specify only one subnetwork"; + Scheduling scheduling = getScheduling(options); - NewInstance newInstance = new NewInstance.Builder( name, + NewInstance newInstance = new NewInstance.Builder(name, template.getHardware().getUri(), // machineType network, + subnetwork, disks) .description(group) .tags(Tags.create(null, ImmutableList.copyOf(options.getTags()))) diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/Resources.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/Resources.java index 3fa832e067..010093ccf8 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/Resources.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/functions/Resources.java @@ -32,6 +32,7 @@ import org.jclouds.googlecomputeengine.domain.Image; import org.jclouds.googlecomputeengine.domain.Instance; import org.jclouds.googlecomputeengine.domain.Network; import org.jclouds.googlecomputeengine.domain.Operation; +import org.jclouds.googlecomputeengine.domain.Subnetwork; import org.jclouds.javax.annotation.Nullable; import org.jclouds.oauth.v2.filters.OAuthFilter; import org.jclouds.rest.annotations.EndpointParam; @@ -82,6 +83,10 @@ public interface Resources { @Path("/stop") Operation stopInstance(@EndpointParam URI selfLink); + @Named("Subnetworks:get") + @GET + @Fallback(NullOnNotFoundOr404.class) @Nullable Subnetwork subnetwork(@EndpointParam URI selfLink); + /** Returns a disk by self-link or null if not found. */ @Named("Disks:get") @GET diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/SubnetworkLoader.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/SubnetworkLoader.java new file mode 100644 index 0000000000..6377a057fc --- /dev/null +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/loaders/SubnetworkLoader.java @@ -0,0 +1,57 @@ +/* + * 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.loaders; + +import java.net.URI; +import java.util.concurrent.ExecutionException; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.googlecomputeengine.compute.functions.Resources; +import org.jclouds.googlecomputeengine.domain.Subnetwork; +import org.jclouds.logging.Logger; + +import com.google.common.cache.CacheLoader; + + +@Singleton +public class SubnetworkLoader extends CacheLoader { + @Resource + protected Logger logger = Logger.NULL; + + private final Resources resources; + + @Inject + SubnetworkLoader(Resources resources) { + this.resources = resources; + } + + @Override + public Subnetwork load(URI key) throws ExecutionException { + try { + return resources.subnetwork(key); + } catch (Exception e) { + throw new ExecutionException(message(key, e), e); + } + } + + public static String message(URI key, Exception e) { + return String.format("could not find image for disk %s: %s", key.toString(), e.getMessage()); + } +} diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java index 1c4a227df1..987c2a8ba3 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/compute/options/GoogleComputeEngineTemplateOptions.java @@ -18,12 +18,15 @@ package org.jclouds.googlecomputeengine.compute.options; import java.util.List; import java.util.Map; +import java.util.Set; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.domain.LoginCredentials; import org.jclouds.googlecomputeengine.domain.Instance.ServiceAccount; import org.jclouds.scriptbuilder.domain.Statement; +import com.google.common.collect.ImmutableSet; + /** Instance options specific to Google Compute Engine. */ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions { @@ -32,6 +35,7 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions { private List serviceAccounts; private String bootDiskType; private boolean preemptible = false; + private Set subnetworks; @Override public GoogleComputeEngineTemplateOptions clone() { @@ -134,6 +138,11 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions { return serviceAccounts; } + public Set getSubnetworks() { + return subnetworks; + } + + /** * {@inheritDoc} */ @@ -286,6 +295,22 @@ public final class GoogleComputeEngineTemplateOptions extends TemplateOptions { return GoogleComputeEngineTemplateOptions.class.cast(super.networks(networks)); } + /** + * Assigns subnetworks to the machine. + */ + public GoogleComputeEngineTemplateOptions subnetworks(Iterable networks) { + this.subnetworks = ImmutableSet.copyOf(networks); + return this; + } + + /** + * Assigns subnetworks to the machine. + */ + public GoogleComputeEngineTemplateOptions subnetworks(String... networks) { + this.subnetworks = ImmutableSet.copyOf(networks); + return this; + } + /** * {@inheritDoc} */ diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Network.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Network.java index 3183b1df40..bf89e38aec 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Network.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Network.java @@ -18,11 +18,13 @@ package org.jclouds.googlecomputeengine.domain; import java.net.URI; import java.util.Date; +import java.util.List; import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; +import com.google.common.base.Strings; /** * Represents a network used to enable instance communication. @@ -30,6 +32,12 @@ import com.google.auto.value.AutoValue; @AutoValue public abstract class Network { + public enum NetworkType { + LegacyNetwork, + AutoSubnetwork, + CustomNetwork, + } + public abstract String id(); public abstract Date creationTimestamp(); @@ -38,6 +46,8 @@ public abstract class Network { public abstract String name(); + public abstract NetworkType type(); + @Nullable public abstract String description(); /** @@ -52,10 +62,23 @@ public abstract class Network { */ @Nullable public abstract String gatewayIPv4(); - @SerializedNames({ "id", "creationTimestamp", "selfLink", "name", "description", "IPv4Range", "gatewayIPv4" }) + @Nullable public abstract List subnetworks(); + public static Network create(String id, Date creationTimestamp, URI selfLink, String name, String description, String rangeIPv4, - String gatewayIPv4) { - return new AutoValue_Network(id, creationTimestamp, selfLink, name, description, rangeIPv4, gatewayIPv4); + String gatewayIPv4) { + return new AutoValue_Network(id, creationTimestamp, selfLink, name, NetworkType.LegacyNetwork, description, + rangeIPv4, gatewayIPv4, null); + } + + @SerializedNames({ "id", "creationTimestamp", "selfLink", "name", "description", "IPv4Range", "gatewayIPv4", "autoCreateSubnetworks", "subnetworks" }) + public static Network create(String id, Date creationTimestamp, URI selfLink, String name, String description, String rangeIPv4, + String gatewayIPv4, String autoCreateSubnetworks, List subnetworks) { + NetworkType type; + type = !Strings.isNullOrEmpty(rangeIPv4) ? NetworkType.LegacyNetwork + : (autoCreateSubnetworks.equals("true") ? NetworkType.AutoSubnetwork + : NetworkType.CustomNetwork); + return new AutoValue_Network(id, creationTimestamp, selfLink, name, type, description, rangeIPv4, gatewayIPv4, + subnetworks); } Network() { diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java index 2738646dde..d64d1ed44b 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/NewInstance.java @@ -39,6 +39,7 @@ public abstract class NewInstance { @AutoValue abstract static class NetworkInterface { abstract URI network(); + @Nullable abstract URI subnetwork(); abstract List accessConfigs(); @@ -46,9 +47,19 @@ public abstract class NewInstance { return create(network, Arrays.asList(AccessConfig.create(null, Type.ONE_TO_ONE_NAT, null))); } + static NetworkInterface create(URI network, URI subnetwork) { + return create(network, subnetwork, + Arrays.asList(AccessConfig.create(null, Type.ONE_TO_ONE_NAT, null))); + } + @SerializedNames({ "network", "accessConfigs" }) static NetworkInterface create(URI network, List accessConfigs) { - return new AutoValue_NewInstance_NetworkInterface(network, accessConfigs); + return new AutoValue_NewInstance_NetworkInterface(network, null, accessConfigs); + } + + @SerializedNames({ "network", "subnetwork", "accessConfigs" }) + static NetworkInterface create(URI network, URI subnetwork, List accessConfigs) { + return new AutoValue_NewInstance_NetworkInterface(network, subnetwork, accessConfigs); } NetworkInterface() { @@ -81,8 +92,18 @@ public abstract class NewInstance { return create(name, machineType, network, Arrays.asList(AttachDisk.newBootDisk(sourceImage)), null, null); } + public static NewInstance create(String name, URI machineType, URI network, URI subnetwork, URI sourceImage) { + return create(name, machineType, network, subnetwork, Arrays.asList(AttachDisk.newBootDisk(sourceImage)), null, + null); + } + public static NewInstance create(String name, URI machineType, URI network, List disks, @Nullable String description, @Nullable Tags tags) { + return create(name, machineType, network, null, disks, description, tags); + } + + public static NewInstance create(String name, URI machineType, URI network, @Nullable URI subnetwork, + List disks, @Nullable String description, @Nullable Tags tags) { checkArgument(disks.get(0).boot(), "disk 0 must be a boot disk! %s", disks); boolean foundBoot = false; for (AttachDisk disk : disks) { @@ -127,6 +148,14 @@ public abstract class NewInstance { this.disks = disks; } + public Builder(String name, URI machineType, URI network, URI subnetwork, List disks) { + checkNotNull(name, "NewInstance name cannot be null"); + this.name = name; + this.machineType = machineType; + this.networkInterfaces = ImmutableList.of(NetworkInterface.create(network, subnetwork)); + this.disks = disks; + } + public Builder(String name, URI machineType, URI network, URI sourceImage) { checkNotNull(name, "NewInstance name cannot be null"); this.name = name; @@ -135,6 +164,14 @@ public abstract class NewInstance { this.disks = Arrays.asList(AttachDisk.newBootDisk(sourceImage)); } + public Builder(String name, URI machineType, URI network, URI subnetwork, URI sourceImage) { + checkNotNull(name, "NewInstance name cannot be null"); + this.name = name; + this.machineType = machineType; + this.networkInterfaces = ImmutableList.of(NetworkInterface.create(network, subnetwork)); + this.disks = Arrays.asList(AttachDisk.newBootDisk(sourceImage)); + } + public Builder canIpForward(Boolean canIpForward){ this.canIpForward = canIpForward; return this; diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Subnetwork.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Subnetwork.java new file mode 100644 index 0000000000..a696a8c466 --- /dev/null +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/domain/Subnetwork.java @@ -0,0 +1,62 @@ +/* + * 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.domain; + +import java.net.URI; +import java.util.Date; + +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +/** + * Represents a network used to enable instance communication. + */ +@AutoValue +public abstract class Subnetwork { + + public abstract String id(); + + public abstract Date creationTimestamp(); + + public abstract URI selfLink(); + + public abstract String name(); + + @Nullable public abstract String description(); + + public abstract String gatewayAddress(); + + public abstract URI network(); + + public abstract String ipCidrRange(); + + public abstract URI region(); + + @SerializedNames({ "id", "creationTimestamp", "selfLink", "name", + "description", "gatewayAddress", "network", "ipCidrRange", "region" }) + public static Subnetwork create(String id, Date creationTimestamp, URI selfLink, String name, + String description, String gatewayAddress, URI network, + String ipCidrRange, URI region) { + return new AutoValue_Subnetwork(id, creationTimestamp, selfLink, name, description, + gatewayAddress, network, ipCidrRange, region); + } + + Subnetwork() { + } +} diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java index a663e420f8..f21d051d9b 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/AggregatedListApi.java @@ -36,6 +36,7 @@ import org.jclouds.googlecomputeengine.domain.ForwardingRule; import org.jclouds.googlecomputeengine.domain.Instance; import org.jclouds.googlecomputeengine.domain.MachineType; import org.jclouds.googlecomputeengine.domain.Operation; +import org.jclouds.googlecomputeengine.domain.Subnetwork; import org.jclouds.googlecomputeengine.domain.TargetInstance; import org.jclouds.googlecomputeengine.domain.TargetPool; import org.jclouds.googlecomputeengine.internal.BaseToIteratorOfListPage; @@ -498,4 +499,53 @@ public interface AggregatedListApi { }; } } + + /** + * Retrieves the list of instance resources available to the specified + * project. By default the list as a maximum size of 100, if no options are + * provided or ListOptions#getMaxResults() has not been set. + * + * @param pageToken + * marks the beginning of the next list page + * @param listOptions + * listing options + * @return a page of the list + */ + @Named("Subnetworks:aggregatedList") + @GET + @Path("/subnetworks") + ListPage pageOfSubnetworks(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions); + + /** @see #pageOfSubnetworks(String, ListOptions) */ + @Named("Subnetworks:aggregatedList") + @GET + @Path("/subnetworks") + @Transform(SubnetworksPages.class) + Iterator> subnetworks(); + + /** @see #pageOfSubnetworks(String, ListOptions) */ + @Named("Subnetworks:aggregatedList") + @GET + @Path("/subnetworks") + @Transform(SubnetworksPages.class) + Iterator> subnetworks(ListOptions options); + + static final class SubnetworksPages extends BaseToIteratorOfListPage { + private final GoogleComputeEngineApi api; + + @Inject + SubnetworksPages(GoogleComputeEngineApi api) { + this.api = api; + } + + @Override + protected Function> fetchNextPage(final ListOptions options) { + return new Function>() { + @Override + public ListPage apply(String pageToken) { + return api.aggregatedList().pageOfSubnetworks(pageToken, options); + } + }; + } + } } diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java index c9757849c4..c67b64c526 100644 --- a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/NetworkApi.java @@ -83,9 +83,7 @@ public interface NetworkApi { /** * Creates a persistent network resource in the specified project with the specified range and specified gateway. * - * @param networkName the network name - * @param IPv4Range the range of the network to be inserted. - * @param gatewayIPv4 the range of the network to be inserted. + * @param options the options to create the network. * @return an Operation resource. To check on the status of an operation, poll the Operations resource returned to * you, and look for the status field. */ @@ -126,7 +124,7 @@ public interface NetworkApi { @Transform(NetworkPages.class) Iterator> list(ListOptions options); - static final class NetworkPages extends BaseToIteratorOfListPage { + final class NetworkPages extends BaseToIteratorOfListPage { private final GoogleComputeEngineApi api; diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/SubnetworkApi.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/SubnetworkApi.java new file mode 100644 index 0000000000..1d52989327 --- /dev/null +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/features/SubnetworkApi.java @@ -0,0 +1,127 @@ +/* + * 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.features; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.net.URI; +import java.util.Iterator; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; + +import org.jclouds.Fallbacks.NullOnNotFoundOr404; +import org.jclouds.googlecloud.domain.ListPage; +import org.jclouds.googlecomputeengine.GoogleComputeEngineApi; +import org.jclouds.googlecomputeengine.domain.Operation; +import org.jclouds.googlecomputeengine.domain.Subnetwork; +import org.jclouds.googlecomputeengine.internal.BaseCallerArg0ToIteratorOfListPage; +import org.jclouds.googlecomputeengine.options.ListOptions; +import org.jclouds.googlecomputeengine.options.SubnetworkCreationOptions; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.oauth.v2.filters.OAuthFilter; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.PayloadParam; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SkipEncoding; +import org.jclouds.rest.annotations.Transform; +import org.jclouds.rest.binders.BindToJsonPayload; + +import com.google.common.base.Function; + +@SkipEncoding({'/', '='}) +@RequestFilters(OAuthFilter.class) +@Path("/subnetworks") +@Consumes(APPLICATION_JSON) +public interface SubnetworkApi { + + /** + * Returns a network by name or null if not found. + */ + @Named("Subnetworks:get") + @GET + @Path("/{subnetwork}") + @Fallback(NullOnNotFoundOr404.class) + Subnetwork get(@PathParam("subnetwork") String subnetworkName); + + /** + * Creates a persistent network resource in the specified project with the specified range and specified gateway. + * + * @param newSubnetwork definition of the subnetwork. + * @return an Operation resource. To check on the status of an operation, poll the Operations resource returned to + * you, and look for the status field. + */ + @Named("Subnetworks:insert") + @POST + @Produces(APPLICATION_JSON) + Operation createInNetwork(@BinderParam(BindToJsonPayload.class) SubnetworkCreationOptions newSubnetwork); + + /** Deletes a network by name and returns the operation in progress, or null if not found. */ + @Named("Subnetworks:delete") + @DELETE + @Path("/{subnetwork}") + @Fallback(NullOnNotFoundOr404.class) + Operation delete(@PathParam("subnetwork") String subnetworkName); + + /** + * Retrieves the list of network resources available to the specified project. + * By default the list as a maximum size of 100, if no options are provided or ListOptions#getMaxResults() has not + * been set. + * + * @param pageToken marks the beginning of the next list page + * @param listOptions listing options + * @return a page of the list + */ + @Named("Subnetworks:list") + @GET + ListPage listPage(@Nullable @QueryParam("pageToken") String pageToken, ListOptions listOptions); + + /** @see #listPage(String, ListOptions) */ + @Named("Subnetworks:list") + @GET + @Transform(SubnetworkPages.class) + Iterator> list(); + + static final class SubnetworkPages extends BaseCallerArg0ToIteratorOfListPage { + + private final GoogleComputeEngineApi api; + + @Inject SubnetworkPages(GoogleComputeEngineApi api) { + this.api = api; + } + + @Override protected Function> fetchNextPage(final String region, final ListOptions options) { + return new Function>() { + @Override public ListPage apply(String pageToken) { + return api.subnetworksInRegion(region).listPage(pageToken, options); + } + }; + } + } +} diff --git a/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/options/SubnetworkCreationOptions.java b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/options/SubnetworkCreationOptions.java new file mode 100644 index 0000000000..d90901ba3e --- /dev/null +++ b/providers/google-compute-engine/src/main/java/org/jclouds/googlecomputeengine/options/SubnetworkCreationOptions.java @@ -0,0 +1,52 @@ +/* + * 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.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; + +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +/** + * Represents a subnetwork used to enable instance communication. + */ +@AutoValue +public abstract class SubnetworkCreationOptions { + + public abstract String name(); + + @Nullable public abstract String description(); + + public abstract URI network(); + + public abstract String ipCidrRange(); + + public abstract URI region(); + + @SerializedNames({ "name", "description", "network", "ipCidrRange", "region" }) + public static SubnetworkCreationOptions create(String name, String description, URI network, + String ipCidrRange, URI region) { + return new AutoValue_SubnetworkCreationOptions(name, description, network, ipCidrRange, region); + } + + SubnetworkCreationOptions() { + } +} diff --git a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/SubnetworkApiMockTest.java b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/SubnetworkApiMockTest.java new file mode 100644 index 0000000000..f99f5d303a --- /dev/null +++ b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/features/SubnetworkApiMockTest.java @@ -0,0 +1,95 @@ +/* + * 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.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; + +import java.net.URI; + +import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineApiMockTest; +import org.jclouds.googlecomputeengine.options.SubnetworkCreationOptions; +import org.jclouds.googlecomputeengine.parse.ParseSubnetworkListTest; +import org.jclouds.googlecomputeengine.parse.ParseSubnetworkTest; +import org.jclouds.googlecomputeengine.parse.ParseOperationTest; +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "SubnetworkApiMockTest", singleThreaded = true) +public class SubnetworkApiMockTest extends BaseGoogleComputeEngineApiMockTest { + + public void get() throws Exception { + server.enqueue(jsonResponse("/subnetwork_get.json")); + + assertEquals(subnetworkApi().get("jclouds-test"), + new ParseSubnetworkTest().expected(url("/projects"))); + assertSent(server, "GET", "/projects/party/regions/someregion/subnetworks/jclouds-test"); + } + + public void get_4xx() throws Exception { + server.enqueue(response404()); + + assertNull(subnetworkApi().get("jclouds-test")); + assertSent(server, "GET", "/projects/party/regions/someregion/subnetworks/jclouds-test"); + } + + public void insert() throws Exception { + server.enqueue(jsonResponse("/operation.json")); + + assertEquals(subnetworkApi().createInNetwork(SubnetworkCreationOptions.create( + "jclouds-test", + "my subnetwork", + URI.create(url("/projects/party/global/networks/mynetwork")), + "10.0.0.0/24", + URI.create(url("/projects/party/regions/someregion")))), new ParseOperationTest().expected(url("/projects"))); + assertSent(server, "POST", "/projects/party/regions/someregion/subnetworks", + stringFromResource("/subnetwork_insert.json")); + } + + public void delete() throws Exception { + server.enqueue(jsonResponse("/operation.json")); + + assertEquals(subnetworkApi().delete("jclouds-test"), + new ParseOperationTest().expected(url("/projects"))); + assertSent(server, "DELETE", "/projects/party/regions/someregion/subnetworks/jclouds-test"); + } + + public void delete_4xx() throws Exception { + server.enqueue(response404()); + + assertNull(subnetworkApi().delete("jclouds-test")); + assertSent(server, "DELETE", "/projects/party/regions/someregion/subnetworks/jclouds-test"); + } + + public void list() throws Exception { + server.enqueue(jsonResponse("/subnetwork_list.json")); + + assertEquals(subnetworkApi().list().next(), new ParseSubnetworkListTest().expected(url("/projects"))); + assertSent(server, "GET", "/projects/party/regions/someregion/subnetworks"); + } + + public void list_empty() throws Exception { + server.enqueue(jsonResponse("/list_empty.json")); + + assertFalse(subnetworkApi().list().hasNext()); + assertSent(server, "GET", "/projects/party/regions/someregion/subnetworks"); + } + + SubnetworkApi subnetworkApi() { + return api().subnetworksInRegion("someregion"); + } +} diff --git a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkListTest.java b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkListTest.java new file mode 100644 index 0000000000..61fb6465d5 --- /dev/null +++ b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkListTest.java @@ -0,0 +1,51 @@ +/* + * 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.parse; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import javax.ws.rs.Consumes; + +import org.jclouds.googlecloud.domain.ForwardingListPage; +import org.jclouds.googlecloud.domain.ListPage; +import org.jclouds.googlecomputeengine.domain.Subnetwork; +import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineParseTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +@Test(groups = "unit", testName = "ParseSubnetworkListTest") +public class ParseSubnetworkListTest extends BaseGoogleComputeEngineParseTest> { + + @Override + public String resource() { + return "/network_list.json"; + } + + @Override @Consumes(APPLICATION_JSON) + public ListPage expected() { + return expected(BASE_URL); + } + + @Consumes(APPLICATION_JSON) + public ListPage expected(String baseUrl) { + return ForwardingListPage.create( // + ImmutableList.of(new ParseSubnetworkTest().expected(baseUrl)), // items + null // nextPageToken + ); + } +} diff --git a/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkTest.java b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkTest.java new file mode 100644 index 0000000000..08d17348e6 --- /dev/null +++ b/providers/google-compute-engine/src/test/java/org/jclouds/googlecomputeengine/parse/ParseSubnetworkTest.java @@ -0,0 +1,56 @@ +/* + * 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.parse; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.net.URI; + +import javax.ws.rs.Consumes; + +import org.jclouds.googlecomputeengine.domain.Subnetwork; +import org.jclouds.googlecomputeengine.internal.BaseGoogleComputeEngineParseTest; +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "ParseSubnetworkTest") +public class ParseSubnetworkTest extends BaseGoogleComputeEngineParseTest { + + @Override + public String resource() { + return "/network_get.json"; + } + + @Override @Consumes(APPLICATION_JSON) + public Subnetwork expected() { + return expected(BASE_URL); + } + + @Consumes(APPLICATION_JSON) + public Subnetwork expected(String baseUrl) { + return Subnetwork.create( // + "5850679262666457680", // id + parse("2016-06-07T14:29:35.476-07:00"), // creationTimestamp + URI.create(baseUrl + "/party/regions/someregion/subnetworks/jclouds-test"), // selfLink + "jclouds-subnetwork-test", // name + "A custom subnetwork for the project", // description + "10.128.0.1", + URI.create(baseUrl + "/party/global/networks/mynetwork"), // network + "10.128.0.0/20", // rangeIPv4 + URI.create(baseUrl + "/party/regions/someregion") // region + ); + } +} diff --git a/providers/google-compute-engine/src/test/resources/subnetwork_get.json b/providers/google-compute-engine/src/test/resources/subnetwork_get.json new file mode 100644 index 0000000000..0b44694cf9 --- /dev/null +++ b/providers/google-compute-engine/src/test/resources/subnetwork_get.json @@ -0,0 +1,12 @@ +{ + "kind": "compute#subnetwork", + "id": "5850679262666457680", + "creationTimestamp": "2016-06-07T14:29:35.476-07:00", + "gatewayAddress": "10.128.0.1", + "name": "jclouds-subnetwork-test", + "network": "https://www.googleapis.com/compute/v1/projects/party/global/networks/mynetwork", + "ipCidrRange": "10.128.0.0/20", + "region": "https://www.googleapis.com/compute/v1/projects/party/regions/someregion", + "selfLink": "https://www.googleapis.com/compute/v1/projects/party/regions/someregion/subnetworks/jclouds-test", + "description": "A custom subnetwork for the project" +} diff --git a/providers/google-compute-engine/src/test/resources/subnetwork_insert.json b/providers/google-compute-engine/src/test/resources/subnetwork_insert.json new file mode 100644 index 0000000000..927a125f82 --- /dev/null +++ b/providers/google-compute-engine/src/test/resources/subnetwork_insert.json @@ -0,0 +1,7 @@ +{ + "name": "jclouds-test", + "description": "my subnetwork", + "network": "https://www.googleapis.com/compute/v1/projects/party/global/networks/mynetwork", + "ipCidrRange": "10.0.0.0/24", + "region": "https://www.googleapis.com/compute/v1/projects/party/regions/someregion" +} diff --git a/providers/google-compute-engine/src/test/resources/subnetwork_list.json b/providers/google-compute-engine/src/test/resources/subnetwork_list.json new file mode 100644 index 0000000000..74353cd1d0 --- /dev/null +++ b/providers/google-compute-engine/src/test/resources/subnetwork_list.json @@ -0,0 +1,19 @@ +{ + "kind": "compute#networkList", + "id": "projects/party/networks", + "selfLink": "https://www.googleapis.com/compute/v1/projects/party/regions/someregion/subnetworks", + "items": [ + { + "kind": "compute#subnetwork", + "id": "5850679262666457680", + "creationTimestamp": "2016-06-07T14:29:35.476-07:00", + "gatewayAddress": "10.128.0.1", + "name": "jclouds-subnetwork-test", + "network": "https://www.googleapis.com/compute/v1/projects/party/global/networks/mynetwork", + "ipCidrRange": "10.128.0.0/20", + "region": "https://www.googleapis.com/compute/v1/projects/party/regions/someregion", + "selfLink": "https://www.googleapis.com/compute/v1/projects/party/regions/someregion/subnetworks/jclouds-test", + "description": "A custom subnetwork for the project" + } + ] +}