Skip to content
Permalink
Browse files
[JCLOUDS-1430] - add region and zone API
  • Loading branch information
andreaturli committed Jul 6, 2018
1 parent 5bd2a80 commit f38f804537c2c7a3b76ba355ab113475ef862fc1
Showing 12 changed files with 949 additions and 29 deletions.
@@ -17,6 +17,7 @@
package org.jclouds.aliyun.ecs;

import org.jclouds.aliyun.ecs.features.ImageApi;
import org.jclouds.aliyun.ecs.features.RegionAndZoneApi;
import org.jclouds.rest.annotations.Delegate;

import java.io.Closeable;
@@ -26,4 +27,7 @@ public interface ECSComputeServiceApi extends Closeable {
@Delegate
ImageApi imageApi();

@Delegate
RegionAndZoneApi regionAndZoneApi();

}
@@ -57,7 +57,7 @@ protected Builder() {
.homepage(URI.create("https://www.alibabacloud.com"))
.console(URI.create("https://ecs.console.aliyun.com"))
.endpoint("https://ecs.aliyuncs.com")
.iso3166Codes("US-CA", "US-VA", "DE", "JP", "ID-JK", "SG", "IN", "AU-NSW", "MY", "CN-HE", "CN-SH", "CN-ZJ", "CN-GD", "HK", "AE-DU") // TODO
.iso3166Codes("US-CA", "US-VA", "DE", "JP", "ID-JK", "SG", "IN", "AU-NSW", "MY", "CN-HE", "CN-SH", "CN-ZJ", "CN-GD", "HK", "AE-DU")
.defaultProperties(ECSServiceApiMetadata.defaultProperties());
}

@@ -0,0 +1,36 @@
/*
* 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.aliyun.ecs.domain;

import com.google.auto.value.AutoValue;
import org.jclouds.json.SerializedNames;

@AutoValue
public abstract class Region {

Region() {}

@SerializedNames({ "RegionId", "LocalName" })
public static Region create(String regionId, String localName) {
return new AutoValue_Region(regionId, localName);
}

public abstract String regionId();

public abstract String localName();

}
@@ -0,0 +1,66 @@
/*
* 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.aliyun.ecs.domain;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import org.jclouds.json.SerializedNames;

import java.util.List;
import java.util.Map;

@AutoValue
public abstract class ResourceInfo {

ResourceInfo() {
}

@SerializedNames(
{ "IoOptimized", "SystemDiskCategories", "InstanceTypes", "InstanceTypeFamilies", "DataDiskCategories",
"InstanceGenerations", "NetworkTypes" })
public static ResourceInfo create(boolean ioOptimized, Map<String, List<String>> systemDiskCategories,
Map<String, List<String>> instanceTypes, Map<String, List<String>> instanceTypeFamilies,
Map<String, List<String>> dataDiskCategories, Map<String, List<String>> instanceGenerations,
Map<String, List<String>> networkTypes) {
return new AutoValue_ResourceInfo(ioOptimized, systemDiskCategories == null ?
ImmutableMap.<String, List<String>>of() :
ImmutableMap.copyOf(systemDiskCategories),
instanceTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(instanceTypes),
instanceTypeFamilies == null ?
ImmutableMap.<String, List<String>>of() :
ImmutableMap.copyOf(instanceTypeFamilies), dataDiskCategories == null ?
ImmutableMap.<String, List<String>>of() :
ImmutableMap.copyOf(dataDiskCategories), instanceGenerations == null ?
ImmutableMap.<String, List<String>>of() :
ImmutableMap.copyOf(instanceGenerations),
networkTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(networkTypes));
}

public abstract boolean ioOptimized();

public abstract Map<String, List<String>> systemDiskCategories();

public abstract Map<String, List<String>> instanceTypes();

public abstract Map<String, List<String>> instanceTypeFamilies();

public abstract Map<String, List<String>> dataDiskCategories();

public abstract Map<String, List<String>> instanceGenerations();

public abstract Map<String, List<String>> networkTypes();
}
@@ -0,0 +1,71 @@
/*
* 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.aliyun.ecs.domain;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import org.jclouds.json.SerializedNames;

import java.util.List;
import java.util.Map;

@AutoValue
public abstract class Zone {

Zone() {}

@SerializedNames({ "ZoneId", "LocalName", "DedicatedHostGenerations", "AvailableResourceCreation",
"AvailableDedicatedHostTypes", "AvailableResources", "AvailableInstanceTypes",
"AvailableVolumeCategories", "AvailableDiskCategories" })
public static Zone create(String zoneId, String localName,
Map<String, List<Object>> dedicatedHostGenerations, // FIXME neither doc nor example showed the type in the list
Map<String, List<String>> availableResourceCreation,
Map<String, List<String>> availableDedicatedHostTypes,
Map<String, List<ResourceInfo>> availableResources,
Map<String, List<String>> availableInstanceTypes,
Map<String, List<String>> availableVolumeCategories,
Map<String, List<String>> availableDiskCategories) {
return new AutoValue_Zone(zoneId, localName,
dedicatedHostGenerations == null ? ImmutableMap.<String, List<Object>>of() : ImmutableMap.copyOf(dedicatedHostGenerations),
availableResourceCreation == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableResourceCreation),
availableDedicatedHostTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableDedicatedHostTypes),
availableResources == null ? ImmutableMap.<String, List<ResourceInfo>>of() : ImmutableMap.copyOf(availableResources),
availableInstanceTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableInstanceTypes),
availableVolumeCategories == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableVolumeCategories),
availableDiskCategories == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableDiskCategories)
);
}

public abstract String zoneId();

public abstract String localName();

public abstract Map<String, List<Object>> dedicatedHostGenerations();

public abstract Map<String, List<String>> availableResourceCreation();

public abstract Map<String, List<String>> availableDedicatedHostTypes();

public abstract Map<String, List<ResourceInfo>> availableResources();

public abstract Map<String, List<String>> availableInstanceTypes();

public abstract Map<String, List<String>> availableVolumeCategories();

public abstract Map<String, List<String>> availableDiskCategories();

}
@@ -0,0 +1,58 @@
/*
* 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.aliyun.ecs.features;

import org.jclouds.Constants;
import org.jclouds.Fallbacks;
import org.jclouds.aliyun.ecs.domain.Region;
import org.jclouds.aliyun.ecs.domain.Zone;
import org.jclouds.aliyun.ecs.filters.FormSign;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;

import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.List;

/**
* https://www.alibabacloud.com/help/doc-detail/25609.htm?spm=a2c63.p38356.a1.4.7dd43c1aeoTmzO
*/
@Consumes(MediaType.APPLICATION_JSON)
@RequestFilters(FormSign.class)
@QueryParams(keys = { "Version", "Format", "SignatureVersion", "ServiceCode", "SignatureMethod" },
values = {"{" + Constants.PROPERTY_API_VERSION + "}", "JSON", "1.0", "ecs", "HMAC-SHA1"})
public interface RegionAndZoneApi {

@Named("region:list")
@GET
@SelectJson("Region")
@QueryParams(keys = "Action", values = "DescribeRegions")
@Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
List<Region> describeRegions();

@Named("zone:list")
@GET
@SelectJson("Zone")
@QueryParams(keys = "Action", values = "DescribeZones")
@Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
List<Zone> describeZones(@QueryParam("RegionId") String region);
}
@@ -16,16 +16,17 @@
*/
package org.jclouds.aliyun.ecs.compute.features;

import com.google.common.collect.ImmutableMap;
import org.jclouds.aliyun.ecs.compute.internal.BaseECSComputeServiceApiMockTest;
import org.jclouds.aliyun.ecs.domain.Image;
import org.jclouds.aliyun.ecs.domain.Regions;
import org.jclouds.aliyun.ecs.domain.options.ListImagesOptions;
import org.jclouds.aliyun.ecs.domain.options.PaginationOptions;
import org.jclouds.collect.IterableWithMarker;
import org.testng.annotations.Test;

import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.jclouds.aliyun.ecs.domain.options.ListImagesOptions.Builder.paginationOptions;
import static org.jclouds.aliyun.ecs.domain.options.PaginationOptions.Builder.pageNumber;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

@@ -36,13 +37,12 @@ public void testListImages() throws InterruptedException {
server.enqueue(jsonResponse("/images-first.json"));
server.enqueue(jsonResponse("/images-second.json"));
server.enqueue(jsonResponse("/images-last.json"));

Iterable<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName()).concat();
assertEquals(size(images), 28); // Force the PagedIterable to advance
assertEquals(server.getRequestCount(), 3);
assertSent(server, "GET", "DescribeImages");
assertSent(server, "GET", "DescribeImages", 2);
assertSent(server, "GET", "DescribeImages", 3);
assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()));
assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 2);
assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 3);
}

public void testListImagesReturns404() {
@@ -54,26 +54,18 @@ public void testListImagesReturns404() {

public void testListImagesWithOptions() throws InterruptedException {
server.enqueue(jsonResponse("/images-first.json"));

IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), ListImagesOptions.Builder
.paginationOptions(PaginationOptions.Builder.pageNumber(1)));

IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), paginationOptions(pageNumber(1)));
assertEquals(size(images), 10);
assertEquals(server.getRequestCount(), 1);

assertSent(server, "GET", "DescribeImages", 1);
assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 1);
}

public void testListImagesWithOptionsReturns404() throws InterruptedException {
server.enqueue(response404());

IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), ListImagesOptions.Builder
.paginationOptions(PaginationOptions.Builder.pageNumber(2)));

IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), paginationOptions(pageNumber(2)));
assertTrue(isEmpty(images));

assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "DescribeImages", 2);
assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 2);
}

}
@@ -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.aliyun.ecs.compute.features;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jclouds.aliyun.ecs.compute.internal.BaseECSComputeServiceApiLiveTest;
import org.jclouds.aliyun.ecs.domain.Region;
import org.jclouds.aliyun.ecs.domain.Zone;
import org.jclouds.aliyun.ecs.features.RegionAndZoneApi;
import org.testng.annotations.Test;

import java.util.concurrent.atomic.AtomicInteger;

import static org.testng.Assert.assertTrue;
import static org.testng.util.Strings.isNullOrEmpty;

@Test(groups = "live", testName = "RegionAndZoneApiLiveTest")
public class RegionAndZoneApiLiveTest extends BaseECSComputeServiceApiLiveTest {

public void testListRegions() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().describeRegions(), new Predicate<Region>() {
@Override
public boolean apply(Region input) {
found.incrementAndGet();
return !isNullOrEmpty(input.regionId());
}
}), "All regions must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some region to be returned");
}

public void testListZones() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().describeZones("eu-central-1"), new Predicate<Zone>() {
@Override
public boolean apply(Zone input) {
found.incrementAndGet();
return !isNullOrEmpty(input.zoneId());
}
}), "All zones must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some zone to be returned");
}

private RegionAndZoneApi api() {
return api.regionAndZoneApi();
}
}

0 comments on commit f38f804

Please sign in to comment.