Configure the Graph RBAC API and allow mocking service endpoints
Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/6472341a Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/6472341a Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/6472341a Branch: refs/heads/master Commit: 6472341adcb1a3df0acb0bfc5923dfad80be4dff Parents: 86fc88f Author: Ignasi Barrera <[email protected]> Authored: Mon Dec 4 10:06:21 2017 +0100 Committer: Ignasi Barrera <[email protected]> Committed: Mon Dec 4 10:12:16 2017 +0100 ---------------------------------------------------------------------- .../azurecompute/arm/AzureComputeApi.java | 10 +++ .../arm/AzureComputeProviderMetadata.java | 4 +- .../arm/AzureManagementApiMetadata.java | 10 ++- .../arm/config/AzureComputeHttpApiModule.java | 52 ++++---------- .../azurecompute/arm/config/GraphRBAC.java | 19 +++++ .../azurecompute/arm/features/GraphRBACApi.java | 50 ++++++++++++++ .../arm/config/ParseTenantIdTest.java | 50 ++++++++++++++ .../arm/features/GraphRBACApiMockTest.java | 39 +++++++++++ .../VirtualMachineScaleSetApiLiveTest.java | 32 --------- .../internal/BaseAzureComputeApiMockTest.java | 73 ++++++++++++++++---- .../src/test/resources/serviceprincipals.json | 53 ++++++++++++++ 11 files changed, 302 insertions(+), 90 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java index afc8413..1f6b726 100644 --- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java +++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeApi.java @@ -24,6 +24,7 @@ import org.jclouds.azurecompute.arm.domain.ServicePrincipal; import org.jclouds.azurecompute.arm.features.AvailabilitySetApi; import org.jclouds.azurecompute.arm.features.DeploymentApi; import org.jclouds.azurecompute.arm.features.DiskApi; +import org.jclouds.azurecompute.arm.features.GraphRBACApi; import org.jclouds.azurecompute.arm.features.ImageApi; import org.jclouds.azurecompute.arm.features.JobApi; import org.jclouds.azurecompute.arm.features.LoadBalancerApi; @@ -243,6 +244,15 @@ public interface AzureComputeApi extends Closeable { MetricDefinitionsApi getMetricsDefinitionsApi(@PathParam("resourceid") String resourceid); /** + * The Azure Active Directory Graph API provides programmatic access to Azure + * AD through REST API endpoints. + * + * @see <a href="https://docs.microsoft.com/en-us/rest/api/graphrbac/">docs</a> + */ + @Delegate + GraphRBACApi getGraphRBACApi(); + + /** * Returns the information about the current service principal. */ @Provides http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java index 6848167..335de98 100644 --- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java +++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureComputeProviderMetadata.java @@ -37,11 +37,11 @@ import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE; import java.net.URI; import java.util.Properties; -import org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule.CurrentServicePrincipal; import org.jclouds.azurecompute.arm.domain.Region; import org.jclouds.azurecompute.arm.features.AvailabilitySetApi; import org.jclouds.azurecompute.arm.features.DeploymentApi; import org.jclouds.azurecompute.arm.features.DiskApi; +import org.jclouds.azurecompute.arm.features.GraphRBACApi; import org.jclouds.azurecompute.arm.features.ImageApi; import org.jclouds.azurecompute.arm.features.LoadBalancerApi; import org.jclouds.azurecompute.arm.features.LocationApi; @@ -125,7 +125,7 @@ public class AzureComputeProviderMetadata extends BaseProviderMetadata { properties.put(API_VERSION_PREFIX + MetricDefinitionsApi.class.getSimpleName(), "2017-05-01-preview"); properties.put(API_VERSION_PREFIX + MetricsApi.class.getSimpleName(), "2016-09-01"); properties.put(API_VERSION_PREFIX + VirtualMachineScaleSetApi.class.getSimpleName(), "2017-03-30"); - properties.put(API_VERSION_PREFIX + CurrentServicePrincipal.class.getSimpleName(), "1.6"); + properties.put(API_VERSION_PREFIX + GraphRBACApi.class.getSimpleName(), "1.6"); return properties; } http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java index d0c3e21..9c73e99 100644 --- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java +++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/AzureManagementApiMetadata.java @@ -16,6 +16,8 @@ */ package org.jclouds.azurecompute.arm; +import static org.jclouds.reflect.Reflection2.typeToken; + import java.net.URI; import java.util.Properties; @@ -32,8 +34,6 @@ import org.jclouds.rest.internal.BaseHttpApiMetadata; import com.google.common.collect.ImmutableSet; import com.google.inject.Module; -import static org.jclouds.reflect.Reflection2.typeToken; - /** * Implementation of {@link ApiMetadata} for Microsoft Azure Resource Manager REST API */ @@ -43,9 +43,13 @@ public class AzureManagementApiMetadata extends BaseHttpApiMetadata<AzureCompute public Builder toBuilder() { return new Builder().fromApiMetadata(this); } + + public static Builder builder() { + return new Builder(); + } public AzureManagementApiMetadata() { - this(new Builder()); + this(builder()); } protected AzureManagementApiMetadata(final Builder builder) { http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeHttpApiModule.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeHttpApiModule.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeHttpApiModule.java index 8d417ca..7ba23db 100644 --- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeHttpApiModule.java +++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeHttpApiModule.java @@ -17,7 +17,6 @@ package org.jclouds.azurecompute.arm.config; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; -import static org.jclouds.rest.config.BinderUtils.bindHttpApi; import java.net.URI; import java.util.concurrent.TimeUnit; @@ -26,14 +25,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.inject.Singleton; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.core.MediaType; import org.jclouds.azurecompute.arm.AzureComputeApi; +import org.jclouds.azurecompute.arm.config.GraphRBAC.GraphRBACForTenant; import org.jclouds.azurecompute.arm.domain.ServicePrincipal; -import org.jclouds.azurecompute.arm.filters.ApiVersionFilter; import org.jclouds.azurecompute.arm.handlers.AzureComputeErrorHandler; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; @@ -43,28 +38,22 @@ import org.jclouds.location.suppliers.ImplicitLocationSupplier; import org.jclouds.location.suppliers.implicit.FirstRegion; import org.jclouds.oauth.v2.config.OAuthConfigFactory; import org.jclouds.oauth.v2.config.OAuthScopes; -import org.jclouds.oauth.v2.filters.OAuthFilter; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.ConfiguresHttpApi; -import org.jclouds.rest.annotations.Endpoint; -import org.jclouds.rest.annotations.OnlyElement; -import org.jclouds.rest.annotations.QueryParams; -import org.jclouds.rest.annotations.RequestFilters; -import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.config.HttpApiModule; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.inject.Provides; import com.google.inject.Scopes; +import com.google.inject.TypeLiteral; import com.google.inject.name.Named; @ConfiguresHttpApi public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> { private static final Pattern OAUTH_TENANT_PATTERN = Pattern - .compile("https://login.microsoftonline.com/([^/]+)/oauth2/token"); + .compile("https://login.microsoft(?:online)?.com/([^/]+)/oauth2/token"); @Override protected void bindErrorHandlers() { @@ -82,15 +71,20 @@ public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> { @Override protected void configure() { super.configure(); - bindHttpApi(binder(), CurrentServicePrincipal.class); bind(OAuthScopes.class).toInstance(OAuthScopes.NoScopes.create()); bind(OAuthConfigFactory.class).to(AzureOAuthConfigFactory.class).in(Scopes.SINGLETON); + bindServiceEndpoints(); + } + + protected void bindServiceEndpoints() { + bind(new TypeLiteral<Supplier<URI>>() { + }).annotatedWith(GraphRBAC.class).to(GraphRBACForTenant.class).in(Scopes.SINGLETON); } @Provides @Singleton @Tenant - protected String provideTenant(@Named("oauth.endpoint") final String oauthEndpoint) { + protected final String provideTenant(@Named("oauth.endpoint") final String oauthEndpoint) { Matcher m = OAUTH_TENANT_PATTERN.matcher(oauthEndpoint); if (!m.matches()) { throw new IllegalArgumentException("Could not parse tenantId from: " + oauthEndpoint); @@ -100,37 +94,15 @@ public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> { @Provides @Singleton - @GraphRBAC - protected Supplier<URI> graphRBACEndpoint(@Tenant String tenantId) { - return Suppliers.ofInstance(URI.create(GraphRBAC.ENDPOINT + tenantId)); - } - - @Provides - @Singleton - protected Supplier<ServicePrincipal> provideServicePrincipal(final CurrentServicePrincipal currentServicePrincipal, + protected final Supplier<ServicePrincipal> provideServicePrincipal(final AzureComputeApi api, AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds) { // This supplier must be defensive against any auth exception. return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException, new Supplier<ServicePrincipal>() { @Override public ServicePrincipal get() { - return currentServicePrincipal.get(); + return api.getGraphRBACApi().getCurrentServicePrincipal(); } }, seconds, TimeUnit.SECONDS); } - - @RequestFilters({ OAuthFilter.class, ApiVersionFilter.class }) - @Consumes(MediaType.APPLICATION_JSON) - @Endpoint(GraphRBAC.class) - @OAuthResource(GraphRBAC.ENDPOINT) - public interface CurrentServicePrincipal { - - @Named("servicePrincipal:get") - @GET - @Path("/servicePrincipals") - @QueryParams(keys = "$filter", values = "appId eq '{jclouds.identity}'") - @SelectJson("value") - @OnlyElement - ServicePrincipal get(); - } } http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/GraphRBAC.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/GraphRBAC.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/GraphRBAC.java index 6853782..a7f8b4f 100644 --- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/GraphRBAC.java +++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/GraphRBAC.java @@ -20,9 +20,13 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.net.URI; +import javax.inject.Inject; import javax.inject.Qualifier; +import com.google.common.base.Supplier; + /** * Provides the Graph RBAC API endpoint for the current tenant. */ @@ -32,4 +36,19 @@ import javax.inject.Qualifier; public @interface GraphRBAC { String ENDPOINT = "https://graph.windows.net/"; + + static class GraphRBACForTenant implements Supplier<URI> { + private final String tenantId; + + @Inject + GraphRBACForTenant(@Tenant String tenantId) { + this.tenantId = tenantId; + } + + @Override + public URI get() { + return URI.create(GraphRBAC.ENDPOINT + tenantId); + } + + } } http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/GraphRBACApi.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/GraphRBACApi.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/GraphRBACApi.java new file mode 100644 index 0000000..fe2bccc --- /dev/null +++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/features/GraphRBACApi.java @@ -0,0 +1,50 @@ +/* + * 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.azurecompute.arm.features; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; + +import org.jclouds.azurecompute.arm.config.GraphRBAC; +import org.jclouds.azurecompute.arm.config.OAuthResource; +import org.jclouds.azurecompute.arm.domain.ServicePrincipal; +import org.jclouds.azurecompute.arm.filters.ApiVersionFilter; +import org.jclouds.oauth.v2.filters.OAuthFilter; +import org.jclouds.rest.annotations.Endpoint; +import org.jclouds.rest.annotations.OnlyElement; +import org.jclouds.rest.annotations.QueryParams; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.SelectJson; + +import com.google.inject.name.Named; + +@RequestFilters({ OAuthFilter.class, ApiVersionFilter.class }) +@Consumes(MediaType.APPLICATION_JSON) +@Endpoint(GraphRBAC.class) +@OAuthResource(GraphRBAC.ENDPOINT) +public interface GraphRBACApi { + + @Named("servicePrincipal:get") + @GET + @Path("/servicePrincipals") + @QueryParams(keys = "$filter", values = "appId eq '{jclouds.identity}'") + @SelectJson("value") + @OnlyElement + ServicePrincipal getCurrentServicePrincipal(); +} http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/config/ParseTenantIdTest.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/config/ParseTenantIdTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/config/ParseTenantIdTest.java new file mode 100644 index 0000000..5894505 --- /dev/null +++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/config/ParseTenantIdTest.java @@ -0,0 +1,50 @@ +/* + * 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.azurecompute.arm.config; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "ParseTenantIdTest") +public class ParseTenantIdTest { + + @Test + public void testParseTenantId() { + AzureComputeHttpApiModule module = new AzureComputeHttpApiModule(); + + assertEquals(module.provideTenant("https://login.microsoftonline.com/tenantId/oauth2/token"), "tenantId"); + assertEquals(module.provideTenant("https://login.microsoft.com/tenant2/oauth2/token"), "tenant2"); + + assertInvalid(module, "https://login.microsoftonline.com/a/b/c/oauth2/token"); + assertInvalid(module, "https://login.microsoft.com/a/b/c/oauth2/token"); + assertInvalid(module, "https://login.microsoftonline.com//oauth2/token"); + assertInvalid(module, "https://login.microsoft.com//oauth2/token"); + assertInvalid(module, "https://login.microsoftabc.com/tenant/oauth2/token"); + } + + private static void assertInvalid(AzureComputeHttpApiModule module, String endpoint) { + try { + module.provideTenant(endpoint); + fail("Expected an IllegalArgumentException for endpoint: " + endpoint); + } catch (IllegalArgumentException ex) { + assertEquals(ex.getMessage(), "Could not parse tenantId from: " + endpoint); + } + } + +} http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/GraphRBACApiMockTest.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/GraphRBACApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/GraphRBACApiMockTest.java new file mode 100644 index 0000000..20b95e2 --- /dev/null +++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/GraphRBACApiMockTest.java @@ -0,0 +1,39 @@ +/* + * 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.azurecompute.arm.features; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; + +import org.jclouds.azurecompute.arm.domain.ServicePrincipal; +import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiMockTest; +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "GraphRBACApiMockTest", singleThreaded = true) +public class GraphRBACApiMockTest extends BaseAzureComputeApiMockTest { + + public void testGetCurrentServicePrincipal() throws IOException, InterruptedException { + server.enqueue(jsonResponse("/serviceprincipals.json")); + + ServicePrincipal sp = api.getGraphRBACApi().getCurrentServicePrincipal(); + + assertEquals(sp.appId(), "applicationId"); + assertSent(server, "GET", "/graphrbac/tenant-id/servicePrincipals?$filter=appId%20eq%20%27mock%27&api-version=1.6"); + } + +} http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineScaleSetApiLiveTest.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineScaleSetApiLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineScaleSetApiLiveTest.java index d827d70..ff93998 100644 --- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineScaleSetApiLiveTest.java +++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VirtualMachineScaleSetApiLiveTest.java @@ -17,7 +17,6 @@ package org.jclouds.azurecompute.arm.features; import com.google.common.collect.ImmutableMap; -import org.jclouds.azurecompute.arm.domain.DataDisk; import org.jclouds.azurecompute.arm.domain.Extension; import org.jclouds.azurecompute.arm.domain.ExtensionProfile; import org.jclouds.azurecompute.arm.domain.ExtensionProfileSettings; @@ -69,8 +68,6 @@ public class VirtualMachineScaleSetApiLiveTest extends BaseAzureComputeApiLiveTe private String subscriptionid; private String vmssName; - private VirtualMachineScaleSetSKU SKU; - private String nicName; private String virtualNetworkName; private String subnetId; private Subnet subnet; @@ -151,25 +148,6 @@ public class VirtualMachineScaleSetApiLiveTest extends BaseAzureComputeApiLiveTe return VirtualMachineScaleSetUpgradePolicy.create("Manual"); } - private List<DataDisk> getDataDisks() { - List<DataDisk> datadisks = new ArrayList<DataDisk>(); - - datadisks.add(DataDisk.create(null, "10", 1, null, - null, "FromImage", - "None", getManagedDiskParameters(), - null)); - - return datadisks; - } - - private StorageProfile getStorageProfile() { - return StorageProfile.create(getWindowsImageReference(), getWindowsOSDisk(), getDataDisks()); - } - - private StorageProfile getWindowsStorageProfile_Default() { - return StorageProfile.create(getWindowsImageReference(), getWindowsOSDisk(), null); - } - private StorageProfile getLinuxStorageProfile_Default() { return StorageProfile.create(getLinuxImageReference(), getLinuxOSDisk(), null); } @@ -178,21 +156,11 @@ public class VirtualMachineScaleSetApiLiveTest extends BaseAzureComputeApiLiveTe return ManagedDiskParameters.create(null, "Standard_LRS"); } - private OSDisk getWindowsOSDisk() { - return OSDisk.create("Windows", null, null, null, "FromImage", - null, getManagedDiskParameters(), null); - } - private OSDisk getLinuxOSDisk() { return OSDisk.create("Linux", null, null, null, "FromImage", null, getManagedDiskParameters(), null); } - private ImageReference getWindowsImageReference() { - return ImageReference.create(null, "Microsoft.Windows", "Windows2016", - "Enterprise", "latest"); - } - private ImageReference getLinuxImageReference() { return ImageReference.create(null, "Canonical", "UbuntuServer", "16.04-LTS", "latest"); http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiMockTest.java ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiMockTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiMockTest.java index 1a83407..9d5eab0 100644 --- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiMockTest.java +++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/internal/BaseAzureComputeApiMockTest.java @@ -15,30 +15,44 @@ * limitations under the License. */ package org.jclouds.azurecompute.arm.internal; + +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterables.filter; import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static org.assertj.core.util.Sets.newHashSet; import static org.jclouds.oauth.v2.config.CredentialType.BEARER_TOKEN_CREDENTIALS; import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE; import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.net.URI; import java.util.Properties; import java.util.Set; import org.jclouds.ContextBuilder; import org.jclouds.azurecompute.arm.AzureComputeApi; import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata; +import org.jclouds.azurecompute.arm.AzureManagementApiMetadata; +import org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule; +import org.jclouds.azurecompute.arm.config.GraphRBAC; import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.date.DateService; +import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.ApiContext; +import org.jclouds.rest.ConfiguresHttpApi; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import com.google.common.base.Charsets; +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableSet; import com.google.common.io.Resources; import com.google.gson.JsonParser; import com.google.inject.Module; +import com.google.inject.TypeLiteral; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; import com.squareup.okhttp.mockwebserver.RecordedRequest; @@ -48,8 +62,6 @@ public class BaseAzureComputeApiMockTest { private static final String MOCK_BEARER_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9"; private static final String DEFAULT_ENDPOINT = new AzureComputeProviderMetadata().getEndpoint(); - private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor())); - protected MockWebServer server; protected AzureComputeApi api; protected ApiContext<AzureComputeApi> context; @@ -62,25 +74,42 @@ public class BaseAzureComputeApiMockTest { public void start() throws IOException { server = new MockWebServer(); server.play(); - AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build(); - context = ContextBuilder.newBuilder(pm) - .credentials("", MOCK_BEARER_TOKEN) + + context = ContextBuilder.newBuilder(testProviderMetadata()) + .credentials("mock", MOCK_BEARER_TOKEN) .endpoint(server.getUrl("/").toString() + "subscriptions/SUBSCRIPTIONID") - .modules(modules) + .modules(setupModules()) .overrides(setupProperties()) .build(); api = context.getApi(); dateService = context.utils().injector().getInstance(DateService.class); } + protected ProviderMetadata testProviderMetadata() { + // Omit the default HTTP API modules to allow overriding + Set<Class<? extends Module>> defaultModules = newHashSet(filter( + new AzureManagementApiMetadata().getDefaultModules(), + not(Predicates.<Class<? extends Module>> equalTo(AzureComputeHttpApiModule.class)))); + return AzureComputeProviderMetadata.builder() + .apiMetadata(AzureManagementApiMetadata.builder().defaultModules(defaultModules).build()).build(); + } + protected Properties setupProperties() { Properties properties = new Properties(); - properties.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString()); properties.put("oauth.endpoint", "https://login.microsoftonline.com/tenant-id/oauth2/token"); return properties; } + protected Set<Module> setupModules() { + ImmutableSet.Builder<Module> modules = ImmutableSet.builder(); + modules.add(new ExecutorServiceModule(sameThreadExecutor())); + // Override the default HTTP module to accomodate custom bindings for the + // hardcoded endpoints such as the Graph RBAC API one. + modules.add(new TestAzureComputeHttpApiModule(server)); + return modules.build(); + } + @AfterMethod(alwaysRun = true) public void stop() throws IOException { server.shutdown(); @@ -107,21 +136,22 @@ public class BaseAzureComputeApiMockTest { return new MockResponse().setStatus("HTTP/1.1 202 Accepted"); } - protected MockResponse response204() { return new MockResponse().setStatus("HTTP/1.1 204 No Content"); } protected MockResponse response202WithHeader() { return new MockResponse() - .setStatus("HTTP/1.1 202 Accepted") - .addHeader("Location", "https://management.azure.com/subscriptions/SUBSCRIPTIONID/operationresults/eyJqb2JJZCI6IlJFU09VUkNFR1JPVVBERUxFVElPTkpPQi1SVEVTVC1DRU5UUkFMVVMiLCJqb2JMb2NhdGlvbiI6ImNlbnRyYWx1cyJ9?api-version=2014-04-01"); + .setStatus("HTTP/1.1 202 Accepted") + .addHeader( + "Location", + "https://management.azure.com/subscriptions/SUBSCRIPTIONID/operationresults/eyJqb2JJZCI6IlJFU09VUkNFR1JPVVBERUxFVElPTkpPQi1SVEVTVC1DRU5UUkFMVVMiLCJqb2JMb2NhdGlvbiI6ImNlbnRyYWx1cyJ9?api-version=2014-04-01"); } protected String stringFromResource(String resourceName) { try { - return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8) - .replace(DEFAULT_ENDPOINT, url("")); + return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8).replace(DEFAULT_ENDPOINT, + url("")); } catch (IOException e) { throw Throwables.propagate(e); } @@ -137,10 +167,27 @@ public class BaseAzureComputeApiMockTest { } protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json) - throws InterruptedException { + throws InterruptedException { RecordedRequest request = assertSent(server, method, path); assertEquals(request.getHeader("Content-Type"), "application/json"); assertEquals(parser.parse(new String(request.getBody(), Charsets.UTF_8)), parser.parse(json)); return request; } + + @ConfiguresHttpApi + private static class TestAzureComputeHttpApiModule extends AzureComputeHttpApiModule { + private final MockWebServer server; + + public TestAzureComputeHttpApiModule(MockWebServer server) { + this.server = server; + } + + @Override + protected void bindServiceEndpoints() { + // Override the hardcoded service URIs to allow mocking service endpoints + bind(new TypeLiteral<Supplier<URI>>() { + }).annotatedWith(GraphRBAC.class).toInstance( + Suppliers.ofInstance(URI.create(server.getUrl("/graphrbac").toString() + "/tenant-id"))); + } + } } http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6472341a/azurecompute-arm/src/test/resources/serviceprincipals.json ---------------------------------------------------------------------- diff --git a/azurecompute-arm/src/test/resources/serviceprincipals.json b/azurecompute-arm/src/test/resources/serviceprincipals.json new file mode 100644 index 0000000..cdeefa6 --- /dev/null +++ b/azurecompute-arm/src/test/resources/serviceprincipals.json @@ -0,0 +1,53 @@ +{ + "odata.metadata": "https://graph.windows.net/tenantId/$metadata#directoryObjects/Microsoft.DirectoryServices.ServicePrincipal", + "value": [ + { + "odata.type": "Microsoft.DirectoryServices.ServicePrincipal", + "objectType": "ServicePrincipal", + "objectId": "objectId", + "deletionTimestamp": null, + "accountEnabled": true, + "addIns": [], + "alternativeNames": [], + "appDisplayName": "jclouds", + "appId": "applicationId", + "appOwnerTenantId": "tenantId", + "appRoleAssignmentRequired": false, + "appRoles": [], + "displayName": "jclouds", + "errorUrl": null, + "homepage": "https://jclouds.apache.org", + "keyCredentials": [], + "logoutUrl": null, + "oauth2Permissions": [ + { + "adminConsentDescription": "Allow the application to access jclouds on behalf of the signed-in user.", + "adminConsentDisplayName": "Access jclouds", + "id": "id", + "isEnabled": true, + "type": "User", + "userConsentDescription": "Allow the application to access jclouds on your behalf.", + "userConsentDisplayName": "Access jclouds", + "value": "user_impersonation" + } + ], + "passwordCredentials": [], + "preferredTokenSigningKeyThumbprint": null, + "publisherName": "Default Directory", + "replyUrls": [ + "https://jclouds.apache.org" + ], + "samlMetadataUrl": null, + "servicePrincipalNames": [ + "https://jclouds.onmicrosoft.com/jcloudsid", + "applicationId" + ], + "servicePrincipalType": "Application", + "tags": [ + "WindowsAzureActiveDirectoryIntegratedApp" + ], + "tokenEncryptionKeyId": null + } + ] +} +
