nacx commented on this pull request.

Thanks, @nakomis! And many thanks for taking care of adding new tests. It's 
highly appreciated!

> @@ -98,7 +109,8 @@ protected VirtualMachineInStatePredicateFactory 
> provideVirtualMachineRunningPred
    @Named(TIMEOUT_RESOURCE_DELETED)
    protected Predicate<URI> provideResourceDeletedPredicate(final 
AzureComputeApi api,
          final ComputeServiceConstants.Timeouts timeouts, final PollPeriod 
pollPeriod) {
-      return retry(new ActionDonePredicate(api), timeouts.nodeTerminated, 
pollPeriod.pollInitialPeriod,
+      long timeout = timeouts.nodeTerminated;

Undo this tiny change.

> @@ -121,7 +131,17 @@ public boolean cleanupVirtualMachineNICs(VirtualMachine 
> virtualMachine) {
             PublicIPAddress ip = 
api.getPublicIPAddressApi(publicIpResourceGroup).get(publicIpName);
             if (ip.tags() != null && 
Boolean.parseBoolean(ip.tags().get(AUTOGENERATED_IP_KEY))) {
                logger.debug(">> deleting public ip %s...", publicIpName);
-               deleted &= 
api.getPublicIPAddressApi(publicIpResourceGroup).delete(publicIpName);
+               try {
+                  boolean ipDeleted = 
api.getPublicIPAddressApi(publicIpResourceGroup).delete(publicIpName);
+                  if (!ipDeleted) {
+                     logger.warn(">> ip not deleted %s...", ip);
+                  }
+                  deleted &= ipDeleted;
+               } catch (Exception ex) {
+                  logger.warn(">> Error deleting ip %s", ip);

Mark `deleted = false` too?

> @@ -110,6 +122,27 @@ protected VirtualMachineInStatePredicateFactory 
> provideNodeSuspendedPredicate(fi
             timeouts.nodeTerminated, pollPeriod.pollInitialPeriod, 
pollPeriod.pollMaxPeriod);
    }
 
+   @Provides
+   @Named(TIMEOUT_RESOURCE_REMOVED)
+   protected Predicate<IdReference> provideResourceRemovedPredicate(final 
AzureComputeApi api, final ComputeServiceConstants.Timeouts timeouts,

The name of this method is quite confusing. I'd suggest renaming to 
`InResourceGroup` or similar (even if you have to change its semantics and 
return presence instead of absence).

> +      // Wait for 404 on the disk api
+      Predicate<IdReference> diskDeleted = new Predicate<IdReference>() {
+         @Override
+         public boolean apply(IdReference input) {
+            return api.getDiskApi(input.resourceGroup()).get(input.name()) == 
null;
+         }
+      };
+
+      Predicate<IdReference> filter = retry(diskDeleted, 1200, 5, 15, SECONDS);
+      Set<IdReference> nonDeleted = Sets.filter(deleteDisks, filter);
+
+      if (!nonDeleted.isEmpty()) {
+         logger.warn(">> disks not deleted: %s", 
Joiner.on(',').join(nonDeleted));
+      }
+
+      Set<IdReference> disksNotInRg = Sets.filter(deleteDisks, 
resourceRemoved);

This predicate can be expensive and is just used to print a log. Put these 
lines inside a `log.IsWarnEnabed` guard to save these calls if logging is 
disabled.

> @@ -171,9 +216,12 @@ public boolean cleanupSecurityGroupIfOrphaned(String 
> resourceGroup, String group
                logger.debug(">> deleting orphaned security group %s from 
%s...", name, resourceGroup);
                try {
                   deleted = resourceDeleted.apply(sgapi.delete(name));
+                  
resourceRemoved.apply(IdReference.create(securityGroup.id()));

Do we really need to wait for this? Getting all resources in an RG looks like a 
expensive call? Is there any better call we could make instead?

>           }
       }
 
       return deleted;
    }
 
+   private void cleanupVirtualNetworks(String resourceGroupName) {
+      for (VirtualNetwork virtualNetwork : 
api.getVirtualNetworkApi(resourceGroupName).list()) {
+         if (virtualNetwork.tags() != null && 
virtualNetwork.tags().containsKey("jclouds")) {
+            try {
+               // Virtual networks cannot be deleted if there are any 
resources attached. It does not seem to be possible
+               // to list devices connected to a virtual network
+               // 
https://docs.microsoft.com/en-us/azure/virtual-network/manage-virtual-network#delete-a-virtual-network
+               // We also check the tags to ensure that it's jclouds-created
+               
api.getVirtualNetworkApi(resourceGroupName).delete(virtualNetwork.name());
+               resourceRemoved.apply(IdReference.create(virtualNetwork.id()));

Same here. Better use the `get` virtual network call instead of the resource 
group one.

>           }
       }
 
       return deleted;
    }
 
+   private void cleanupVirtualNetworks(String resourceGroupName) {
+      for (VirtualNetwork virtualNetwork : 
api.getVirtualNetworkApi(resourceGroupName).list()) {
+         if (virtualNetwork.tags() != null && 
virtualNetwork.tags().containsKey("jclouds")) {
+            try {
+               // Virtual networks cannot be deleted if there are any 
resources attached. It does not seem to be possible
+               // to list devices connected to a virtual network
+               // 
https://docs.microsoft.com/en-us/azure/virtual-network/manage-virtual-network#delete-a-virtual-network
+               // We also check the tags to ensure that it's jclouds-created
+               
api.getVirtualNetworkApi(resourceGroupName).delete(virtualNetwork.name());
+               resourceRemoved.apply(IdReference.create(virtualNetwork.id()));
+            } catch (IllegalArgumentException e) {
+               if (e.getMessage().contains("InUseSubnetCannotBeDeleted")) {
+                  // Can be ignored as virtualNetwork is in use
+                  logger.debug("Cannot delete virtual network %s as it is in 
use", virtualNetwork);

Better change to `warn`.

> @@ -231,7 +309,10 @@ private static boolean 
> isOrphanedJcloudsAvailabilitySet(AvailabilitySet availabi
    }
 
    private boolean deleteVirtualMachine(String group, VirtualMachine 
virtualMachine) {
-      return 
resourceDeleted.apply(api.getVirtualMachineApi(group).delete(virtualMachine.name()));
+      URI uri = api.getVirtualMachineApi(group).delete(virtualMachine.name());
+      boolean deleted = uri == null || resourceDeleted.apply(uri);
+      resourceRemoved.apply(IdReference.create(virtualMachine.id()));

Same here. Prefer the VM api get method.

> @@ -65,6 +72,20 @@ public void initializeContext() {
       super.initializeContext();
       resourceDeleted = context.utils().injector().getInstance(Key.get(new 
TypeLiteral<Predicate<URI>>() {
       }, Names.named(TIMEOUT_RESOURCE_DELETED)));
+      resourceRemoved = context.utils().injector().getInstance(Key.get(new 
TypeLiteral<Predicate<IdReference>>() {
+      }, Names.named(TIMEOUT_RESOURCE_REMOVED)));
+   }
+
+   @Test(groups = "live", enabled = true)
+   public void testResourceGroupDeletedOnDestroy() throws Exception {
+      template = buildTemplate(templateBuilder());
+      nodes = newTreeSet(client.createNodesInGroup(group, 1, template));
+      NodeMetadata node = nodes.first();

nit: `NodeMetadata node = 
Iterables.getOnlyElement(client.createNodesInGroup(group, 1, template));`

> @@ -65,6 +72,20 @@ public void initializeContext() {
       super.initializeContext();
       resourceDeleted = context.utils().injector().getInstance(Key.get(new 
TypeLiteral<Predicate<URI>>() {
       }, Names.named(TIMEOUT_RESOURCE_DELETED)));
+      resourceRemoved = context.utils().injector().getInstance(Key.get(new 
TypeLiteral<Predicate<IdReference>>() {
+      }, Names.named(TIMEOUT_RESOURCE_REMOVED)));
+   }
+
+   @Test(groups = "live", enabled = true)
+   public void testResourceGroupDeletedOnDestroy() throws Exception {
+      template = buildTemplate(templateBuilder());
+      nodes = newTreeSet(client.createNodesInGroup(group, 1, template));
+      NodeMetadata node = nodes.first();
+      client.destroyNode(node.getId());
+      List<Resource> resources = 
view.unwrapApi(AzureComputeApi.class).getResourceGroupApi().resources(resourceGroupName);
+      if (!resources.isEmpty()) {
+         Assert.fail("Resource group was not empty, contained " + resources);

nit: static import

> +
+        
expect(diskApi.delete("myOsDisk")).andReturn(URI.create("http://myDeletionUri";));
+        
expect(diskApi.delete("myDataDisk")).andReturn(URI.create("http://myDeletionUri";));
+        // Simulate a delay in disk deletion
+        expect(diskApi.get("myDataDisk")).andReturn(disk).times(3);
+        expect(diskApi.get("myDataDisk")).andReturn(null).times(2); // An 
additional call is made when filtering the set
+        expect(diskApi.get("myOsDisk")).andReturn(null);
+
+        replay(api, diskApi);
+
+        // Do call
+        cleanupResources(api).cleanupManagedDisks(vm);
+
+        // Verify deleted resource group
+        verify(api, diskApi);
+    }

Now that you're adding this (thanks!) add a method to verify that the virtual 
networks are deleted too.

Also add a test to verify that the resources that _don't_ have the jclouds tags 
(availability sets, networks, etc), are not deleted.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/jclouds/jclouds/pull/1240#pullrequestreview-156433617

Reply via email to