You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2017/01/26 21:40:11 UTC

[3/4] jclouds-labs git commit: Cleanup legacy code and introduce the resource group cache

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6fcea4dc/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
index 524eb69..378030f 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/strategy/CreateResourceGroupThenCreateNodes.java
@@ -21,7 +21,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static org.jclouds.azurecompute.arm.compute.functions.VMImageToImage.decodeFieldsFromUniqueId;
-import static org.jclouds.util.Predicates2.retry;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_SUBNET_ADDRESS_PREFIX;
+import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.DEFAULT_VNET_ADDRESS_SPACE_PREFIX;
 
 import java.net.URI;
 import java.util.Arrays;
@@ -36,9 +37,7 @@ import javax.inject.Singleton;
 
 import org.jclouds.Constants;
 import org.jclouds.azurecompute.arm.AzureComputeApi;
-import org.jclouds.azurecompute.arm.compute.config.AzureComputeServiceContextModule;
 import org.jclouds.azurecompute.arm.compute.domain.RegionAndIdAndIngressRules;
-import org.jclouds.azurecompute.arm.compute.functions.LocationToResourceGroupName;
 import org.jclouds.azurecompute.arm.compute.options.AzureTemplateOptions;
 import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
 import org.jclouds.azurecompute.arm.domain.RegionAndId;
@@ -47,10 +46,8 @@ import org.jclouds.azurecompute.arm.domain.StorageService;
 import org.jclouds.azurecompute.arm.domain.Subnet;
 import org.jclouds.azurecompute.arm.domain.VMImage;
 import org.jclouds.azurecompute.arm.domain.VirtualNetwork;
-import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
 import org.jclouds.azurecompute.arm.features.SubnetApi;
 import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
-import org.jclouds.azurecompute.arm.functions.ParseJobStatus;
 import org.jclouds.compute.config.CustomizationResponse;
 import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
@@ -65,6 +62,7 @@ import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThen
 import org.jclouds.domain.Location;
 import org.jclouds.logging.Logger;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
 import com.google.common.cache.LoadingCache;
@@ -81,9 +79,11 @@ public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEnco
    protected Logger logger = Logger.NULL;
 
    private final AzureComputeApi api;
-   private final AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants;
-   private final LocationToResourceGroupName locationToResourceGroupName;
    private final LoadingCache<RegionAndIdAndIngressRules, String> securityGroupMap;
+   private final LoadingCache<String, ResourceGroup> resourceGroupMap;
+   private final String defaultVnetAddressPrefix;
+   private final String defaultSubnetAddressPrefix;
+   private final Predicate<URI> storageAccountCreated;
 
    @Inject
    protected CreateResourceGroupThenCreateNodes(
@@ -92,22 +92,28 @@ public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEnco
          GroupNamingConvention.Factory namingConvention,
          @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
          CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
-         AzureComputeApi api, AzureComputeServiceContextModule.AzureComputeConstants azureComputeConstants,
-         LocationToResourceGroupName locationToResourceGroupName,
-         LoadingCache<RegionAndIdAndIngressRules, String> securityGroupMap) {
+         AzureComputeApi api, @Named(DEFAULT_VNET_ADDRESS_SPACE_PREFIX) String defaultVnetAddressPrefix,
+         @Named(DEFAULT_SUBNET_ADDRESS_PREFIX) String defaultSubnetAddressPrefix,
+         LoadingCache<RegionAndIdAndIngressRules, String> securityGroupMap,
+         LoadingCache<String, ResourceGroup> resourceGroupMap, @Named("STORAGE") Predicate<URI> storageAccountCreated) {
       super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
             customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
       this.api = checkNotNull(api, "api cannot be null");
       checkNotNull(userExecutor, "userExecutor cannot be null");
-      this.azureComputeConstants = azureComputeConstants;
-      this.locationToResourceGroupName = locationToResourceGroupName;
       this.securityGroupMap = securityGroupMap;
+      this.resourceGroupMap = resourceGroupMap;
+      this.defaultVnetAddressPrefix = defaultVnetAddressPrefix;
+      this.defaultSubnetAddressPrefix = defaultSubnetAddressPrefix;
+      this.storageAccountCreated = storageAccountCreated;
    }
 
    @Override
    public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template,
          Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
          Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
+
+      AzureTemplateOptions options = template.getOptions().as(AzureTemplateOptions.class);
+
       // If there is a script to be run on the node and public key
       // authentication has been configured, warn users if the private key
       // is not present
@@ -115,56 +121,39 @@ public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEnco
          logger.warn(">> a runScript was configured but no SSH key has been provided. "
                + "Authentication will delegate to the ssh-agent");
       }
-      String azureGroupName = locationToResourceGroupName.apply(template.getLocation().getId());
-
-      AzureTemplateOptions options = template.getOptions().as(AzureTemplateOptions.class);
-      // create resource group for jclouds group if it does not already exist
-      ResourceGroupApi resourceGroupApi = api.getResourceGroupApi();
-      ResourceGroup resourceGroup = resourceGroupApi.get(azureGroupName);
-      final String location = template.getLocation().getId();
-
-      if (resourceGroup == null) {
-         final Map<String, String> tags = ImmutableMap.of("description", "jclouds managed VMs");
-         resourceGroupApi.create(azureGroupName, location, tags).name();
-      }
 
-      String vnetName = azureGroupName + "virtualnetwork";
-      String subnetName = azureGroupName + "subnet";
+      // This sill create the resource group if it does not exist
+      String location = template.getLocation().getId();
+      ResourceGroup resourceGroup = resourceGroupMap.getUnchecked(location);
+      String azureGroupName = resourceGroup.name();
 
-      if (options.getVirtualNetworkName() != null) {
-         vnetName = options.getVirtualNetworkName();
-      }
-
-      this.getOrCreateVirtualNetworkWithSubnet(vnetName, subnetName, location, options, azureGroupName);
+      getOrCreateVirtualNetworkWithSubnet(location, options, azureGroupName);
       configureSecurityGroupForOptions(group, azureGroupName, template.getLocation(), options);
 
       StorageService storageService = getOrCreateStorageService(group, azureGroupName, location, template.getImage());
-      String blob = storageService.storageServiceProperties().primaryEndpoints().get("blob");
-      options.blob(blob);
-
-      Map<?, ListenableFuture<Void>> responses = super.execute(group, count, template, goodNodes, badNodes,
-            customizationResponses);
+      options.blob(storageService.storageServiceProperties().primaryEndpoints().get("blob"));
 
-      return responses;
+      return super.execute(group, count, template, goodNodes, badNodes, customizationResponses);
    }
 
-   protected synchronized void getOrCreateVirtualNetworkWithSubnet(final String virtualNetworkName,
-         final String subnetName, final String location, AzureTemplateOptions options, final String azureGroupName) {
+   protected synchronized void getOrCreateVirtualNetworkWithSubnet(final String location, AzureTemplateOptions options,
+         final String azureGroupName) {
+      String virtualNetworkName = Optional.fromNullable(options.getVirtualNetworkName()).or(
+            azureGroupName + "virtualnetwork");
+      String subnetName = azureGroupName + "subnet";
 
       // Subnets belong to a virtual network so that needs to be created first
       VirtualNetworkApi vnApi = api.getVirtualNetworkApi(azureGroupName);
       VirtualNetwork vn = vnApi.get(virtualNetworkName);
 
       if (vn == null) {
+         Subnet subnet = Subnet.create(subnetName, null, null,
+               Subnet.SubnetProperties.builder().addressPrefix(defaultSubnetAddressPrefix).build());
+
          VirtualNetwork.VirtualNetworkProperties virtualNetworkProperties = VirtualNetwork.VirtualNetworkProperties
-               .builder()
-               .addressSpace(
-                     VirtualNetwork.AddressSpace.create(Arrays.asList(this.azureComputeConstants
-                           .azureDefaultVnetAddressPrefixProperty())))
-               .subnets(
-                     Arrays.asList(Subnet.create(subnetName, null, null, Subnet.SubnetProperties.builder()
-                           .addressPrefix(this.azureComputeConstants.azureDefaultSubnetAddressPrefixProperty()).build())))
-               .build();
+               .builder().addressSpace(VirtualNetwork.AddressSpace.create(Arrays.asList(defaultVnetAddressPrefix)))
+               .subnets(Arrays.asList(subnet)).build();
+
          vn = vnApi.createOrUpdate(virtualNetworkName, location, virtualNetworkProperties);
       }
 
@@ -199,15 +188,10 @@ public class CreateResourceGroupThenCreateNodes extends CreateNodesWithGroupEnco
       URI uri = api.getStorageAccountApi(resourceGroupName).create(storageAccountName, locationName,
             ImmutableMap.of("jclouds", name),
             ImmutableMap.of("accountType", StorageService.AccountType.Standard_LRS.toString()));
-      boolean starageAccountCreated = retry(new Predicate<URI>() {
-         @Override
-         public boolean apply(URI uri) {
-            return ParseJobStatus.JobStatus.DONE == api.getJobApi().jobStatus(uri);
-         }
-      }, 60 * 2 * 1000 /* 2 minutes timeout */).apply(uri);
-      // TODO check provisioning state of the primary
-      checkState(starageAccountCreated, "Storage account %s was not created in the configured timeout",
+
+      checkState(storageAccountCreated.apply(uri), "Storage account %s was not created in the configured timeout",
             storageAccountName);
+
       return api.getStorageAccountApi(resourceGroupName).get(storageAccountName);
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6fcea4dc/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
index 2a15e4b..4ac5eaa 100644
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
+++ b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/config/AzureComputeProperties.java
@@ -22,30 +22,16 @@ package org.jclouds.azurecompute.arm.config;
  */
 public class AzureComputeProperties {
 
-   public static final String STORAGE_API_VERSION = "2015-06-15";
-
    public static final String OPERATION_TIMEOUT = "jclouds.azurecompute.arm.operation.timeout";
 
-   public static final String OPERATION_POLL_INITIAL_PERIOD = "jclouds.azurecompute.arm.operation.poll.initial.period";
-
-   public static final String OPERATION_POLL_MAX_PERIOD = "jclouds.azurecompute.arm.operation.poll.max.period";
-
-   public static final String TCP_RULE_FORMAT = "jclouds.azurecompute.arm.tcp.rule.format";
-
-   public static final String TCP_RULE_REGEXP = "jclouds.azurecompute.arm.tcp.rule.regexp";
-
    public static final String IMAGE_PUBLISHERS = "jclouds.azurecompute.arm.publishers";
 
-   public static final String DEFAULT_IMAGE_LOGIN = "jclouds.azurecompute.arm.defaultimagelogin";
-
    public static final String TIMEOUT_RESOURCE_DELETED = "jclouds.azurecompute.arm.timeout.resourcedeleted";
 
    public static final String DEFAULT_VNET_ADDRESS_SPACE_PREFIX = "jclouds.azurecompute.arm.vnet.addressprefix";
 
    public static final String DEFAULT_SUBNET_ADDRESS_PREFIX = "jclouds.azurecompute.arm.subnet.addressprefix";
 
-   public static final String DEFAULT_DATADISKSIZE = "jclouds.azurecompute.arm.datadisksize";
-
    public static final String API_VERSION_PREFIX = "jclouds.azurecompute.arm.apiversion.";
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6fcea4dc/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java b/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
deleted file mode 100644
index 9d2a1c1..0000000
--- a/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/functions/CleanupResources.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.functions;
-
-import static com.google.common.base.Predicates.notNull;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.transform;
-import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
-import static org.jclouds.util.Closeables2.closeQuietly;
-
-import java.net.URI;
-import java.util.List;
-
-import javax.annotation.Resource;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import org.jclouds.azurecompute.arm.AzureComputeApi;
-import org.jclouds.azurecompute.arm.compute.functions.LocationToResourceGroupName;
-import org.jclouds.azurecompute.arm.domain.IdReference;
-import org.jclouds.azurecompute.arm.domain.IpConfiguration;
-import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
-import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
-import org.jclouds.azurecompute.arm.domain.RegionAndId;
-import org.jclouds.azurecompute.arm.domain.StorageServiceKeys;
-import org.jclouds.azurecompute.arm.domain.VirtualMachine;
-import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
-import org.jclouds.azurecompute.arm.util.BlobHelper;
-import org.jclouds.compute.functions.GroupNamingConvention;
-import org.jclouds.compute.reference.ComputeServiceConstants;
-import org.jclouds.logging.Logger;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
-@Singleton
-public class CleanupResources {
-
-   @Resource
-   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
-   protected Logger logger = Logger.NULL;
-
-   protected final AzureComputeApi api;
-   private final Predicate<URI> resourceDeleted;
-   private final StorageProfileToStorageAccountName storageProfileToStorageAccountName;
-   private final LocationToResourceGroupName locationToResourceGroupName;
-   private final GroupNamingConvention.Factory namingConvention;
-
-   @Inject
-   CleanupResources(AzureComputeApi azureComputeApi, @Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> resourceDeleted,
-         StorageProfileToStorageAccountName storageProfileToStorageAccountName,
-         LocationToResourceGroupName locationToResourceGroupName, GroupNamingConvention.Factory namingConvention) {
-      this.api = azureComputeApi;
-      this.resourceDeleted = resourceDeleted;
-      this.storageProfileToStorageAccountName = storageProfileToStorageAccountName;
-      this.locationToResourceGroupName = locationToResourceGroupName;
-      this.namingConvention = namingConvention;
-   }
-
-   public boolean cleanupNode(final String id) {
-      RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
-      String group = locationToResourceGroupName.apply(regionAndId.region());
-
-      VirtualMachine virtualMachine = api.getVirtualMachineApi(group).get(regionAndId.id());
-      if (virtualMachine == null) {
-         return true;
-      }
-
-      logger.debug(">> destroying %s ...", regionAndId.slashEncode());
-      boolean vmDeleted = deleteVirtualMachine(group, virtualMachine);
-
-      // We don't delete the network here, as it is global to the resource
-      // group. It will be deleted when the resource group is deleted
-
-      cleanupVirtualMachineNICs(group, virtualMachine);
-      cleanupVirtualMachineStorage(group, virtualMachine);
-
-      return vmDeleted;
-   }
-
-   public void cleanupVirtualMachineNICs(String group, VirtualMachine virtualMachine) {
-      for (String nicName : getNetworkCardInterfaceNames(virtualMachine)) {
-         NetworkInterfaceCard nic = api.getNetworkInterfaceCardApi(group).get(nicName);
-         Iterable<String> publicIps = getPublicIps(group, nic);
-
-         logger.debug(">> destroying nic %s...", nicName);
-         URI nicDeletionURI = api.getNetworkInterfaceCardApi(group).delete(nicName);
-         resourceDeleted.apply(nicDeletionURI);
-
-         for (String publicIp : publicIps) {
-            logger.debug(">> deleting public ip nic %s...", publicIp);
-            api.getPublicIPAddressApi(group).delete(publicIp);
-         }
-      }
-   }
-
-   public void cleanupVirtualMachineStorage(String group, VirtualMachine virtualMachine) {
-      String storageAccountName = storageProfileToStorageAccountName
-            .apply(virtualMachine.properties().storageProfile());
-      StorageServiceKeys keys = api.getStorageAccountApi(group).getKeys(storageAccountName);
-
-      // Remove the virtual machine files
-      logger.debug(">> deleting virtual machine disk storage...");
-      BlobHelper blobHelper = new BlobHelper(storageAccountName, keys.key1());
-      try {
-         blobHelper.deleteContainerIfExists("vhds");
-
-         if (!blobHelper.customImageExists()) {
-            logger.debug(">> deleting storage account %s...", storageAccountName);
-            api.getStorageAccountApi(group).delete(storageAccountName);
-         } else {
-            logger.debug(">> the storage account contains custom images. Will not delete it!");
-         }
-      } finally {
-         closeQuietly(blobHelper);
-      }
-   }
-
-   public boolean cleanupSecurityGroupIfOrphaned(String resourceGroup, String group) {
-      String name = namingConvention.create().sharedNameForGroup(group);
-      NetworkSecurityGroupApi sgapi = api.getNetworkSecurityGroupApi(resourceGroup);
-
-      boolean deleted = false;
-
-      try {
-         NetworkSecurityGroup securityGroup = sgapi.get(name);
-         if (securityGroup != null) {
-            List<NetworkInterfaceCard> nics = securityGroup.properties().networkInterfaces();
-            if (nics == null || nics.isEmpty()) {
-               logger.debug(">> deleting orphaned security group %s from %s...", name, resourceGroup);
-               try {
-                  deleted = resourceDeleted.apply(sgapi.delete(name));
-               } catch (Exception ex) {
-                  logger.warn(ex, ">> error deleting orphaned security group %s from %s...", name, resourceGroup);
-               }
-            }
-         }
-      } catch (Exception ex) {
-         logger.warn(ex, "Error deleting security groups for %s and group %s", resourceGroup, group);
-      }
-
-      return deleted;
-   }
-
-   public boolean deleteResourceGroupIfEmpty(String group) {
-      boolean deleted = false;
-      if (api.getVirtualMachineApi(group).list().isEmpty() && api.getStorageAccountApi(group).list().isEmpty()
-            && api.getNetworkInterfaceCardApi(group).list().isEmpty()
-            && api.getPublicIPAddressApi(group).list().isEmpty()
-            && api.getNetworkSecurityGroupApi(group).list().isEmpty()) {
-         logger.debug(">> the resource group %s is empty. Deleting...", group);
-         deleted = resourceDeleted.apply(api.getResourceGroupApi().delete(group));
-      }
-      return deleted;
-   }
-
-   private Iterable<String> getPublicIps(String group, NetworkInterfaceCard nic) {
-      return transform(
-            filter(transform(nic.properties().ipConfigurations(), new Function<IpConfiguration, IdReference>() {
-               @Override
-               public IdReference apply(IpConfiguration input) {
-                  return input.properties().publicIPAddress();
-               }
-            }), notNull()), new Function<IdReference, String>() {
-               @Override
-               public String apply(IdReference input) {
-                  return Iterables.getLast(Splitter.on("/").split(input.id()));
-               }
-            });
-   }
-
-   private List<String> getNetworkCardInterfaceNames(VirtualMachine virtualMachine) {
-      List<String> nics = Lists.newArrayList();
-      for (IdReference idReference : virtualMachine.properties().networkProfile().networkInterfaces()) {
-         nics.add(Iterables.getLast(Splitter.on("/").split(idReference.id())));
-      }
-      return nics;
-   }
-
-   private boolean deleteVirtualMachine(String group, VirtualMachine virtualMachine) {
-      return resourceDeleted.apply(api.getVirtualMachineApi(group).delete(virtualMachine.name()));
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/6fcea4dc/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtensionLiveTest.java
----------------------------------------------------------------------
diff --git a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtensionLiveTest.java b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtensionLiveTest.java
index 02fe52e..cb13c7b 100644
--- a/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtensionLiveTest.java
+++ b/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/compute/extensions/AzureComputeSecurityGroupExtensionLiveTest.java
@@ -18,7 +18,6 @@ package org.jclouds.azurecompute.arm.compute.extensions;
 
 import static com.google.common.collect.Iterables.get;
 import static com.google.common.collect.Iterables.getOnlyElement;
-import static java.util.logging.Logger.getAnonymousLogger;
 import static org.jclouds.compute.options.TemplateOptions.Builder.inboundPorts;
 import static org.jclouds.compute.options.TemplateOptions.Builder.securityGroups;
 import static org.jclouds.compute.predicates.NodePredicates.inGroup;
@@ -26,14 +25,12 @@ import static org.jclouds.net.domain.IpProtocol.TCP;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
-import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
-import org.jclouds.azurecompute.arm.AzureComputeApi;
 import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
-import org.jclouds.azurecompute.arm.compute.functions.LocationToResourceGroupName;
+import org.jclouds.azurecompute.arm.compute.strategy.CleanupResources;
 import org.jclouds.azurecompute.arm.domain.ResourceGroup;
 import org.jclouds.azurecompute.arm.internal.AzureLiveTestUtils;
 import org.jclouds.compute.ComputeService;
@@ -50,7 +47,9 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.cache.LoadingCache;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
 
 /**
  * Live test for AzureCompute
@@ -59,10 +58,9 @@ import com.google.common.collect.ImmutableMap;
 @Test(groups = "live", singleThreaded = true, testName = "AzureComputeSecurityGroupExtensionLiveTest")
 public class AzureComputeSecurityGroupExtensionLiveTest extends BaseSecurityGroupExtensionLiveTest {
 
-   private AzureComputeApi api;
-   private LocationToResourceGroupName locationToResourceGroupName;
-   private String resourceGroupName;
+   private LoadingCache<String, ResourceGroup> resourceGroupMap;
    private ResourceGroup testResourceGroup;
+   private CleanupResources cleanupResources;
 
    public AzureComputeSecurityGroupExtensionLiveTest() {
       provider = "azurecompute-arm";
@@ -71,8 +69,10 @@ public class AzureComputeSecurityGroupExtensionLiveTest extends BaseSecurityGrou
    @BeforeClass(groups = { "integration", "live" })
    public void setupContext() {
       super.setupContext();
-      api = context.utils().injector().getInstance(AzureComputeApi.class);
-      locationToResourceGroupName = context.utils().injector().getInstance(LocationToResourceGroupName.class);
+      resourceGroupMap = context.utils().injector()
+            .getInstance(Key.get(new TypeLiteral<LoadingCache<String, ResourceGroup>>() {
+            }));
+      cleanupResources = context.utils().injector().getInstance(CleanupResources.class);
       createResourceGroupIfMissing();
    }
 
@@ -119,12 +119,7 @@ public class AzureComputeSecurityGroupExtensionLiveTest extends BaseSecurityGrou
    @Override
    protected void tearDownContext() {
       super.tearDownContext();
-      if (testResourceGroup != null) {
-         // Cleanup the resource group we created for the tests
-         getAnonymousLogger().info(
-               "deleting resource group " + testResourceGroup.name() + " for the security group live tests...");
-         api.getResourceGroupApi().delete(testResourceGroup.name());
-      }
+      cleanupResources.deleteResourceGroupIfEmpty(testResourceGroup.name());
    }
 
    @Override
@@ -142,13 +137,6 @@ public class AzureComputeSecurityGroupExtensionLiveTest extends BaseSecurityGrou
 
    private void createResourceGroupIfMissing() {
       Location location = getNodeTemplate().getLocation();
-      resourceGroupName = locationToResourceGroupName.apply(location.getId());
-      ResourceGroup resourceGroupInLocation = api.getResourceGroupApi().get(resourceGroupName);
-      if (resourceGroupInLocation == null) {
-         getAnonymousLogger().info(
-               "creating resource group " + resourceGroupName + " for the security group live tests...");
-         final Map<String, String> tags = ImmutableMap.of("description", "AzureComputeSecurityGroupExtensionLiveTest");
-         testResourceGroup = api.getResourceGroupApi().create(resourceGroupName, location.getId(), tags);
-      }
+      testResourceGroup = resourceGroupMap.getUnchecked(location.getId());
    }
 }