Azure / azure-libraries-for-net

Azure libraries for .Net
MIT License
379 stars 192 forks source link

[BUG] Fluent SDK: GenericResource: DeleteById blows up with 'parentResourcePath' cannot be null #1045

Open pbstrein opened 4 years ago

pbstrein commented 4 years ago

Describe the bug When I have a generic resource, and I run .DeleteById(), I get an error, and the deletion cannot occur.

Exception or Stack Trace Microsoft.Rest.ValidationException: 'parentResourcePath' cannot be null. at Microsoft.Azure.Management.ResourceManager.Fluent.ResourcesOperations.BeginDeleteWithHttpMessagesAsync(String resourceGroupName, String resourceProviderNamespace, String parentResourcePath, String resourceType, String resourceName, String apiVersion, Dictionary2 customHeaders, CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.ResourcesOperations.DeleteWithHttpMessagesAsync(String resourceGroupName, String resourceProviderNamespace, String parentResourcePath, String resourceType, String resourceName, String apiVersion, Dictionary2 customHeaders, CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.ResourcesOperationsExtensions.DeleteAsync(IResourcesOperations operations, String resourceGroupName, String resourceProviderNamespace, String parentResourcePath, String resourceType, String resourceName, String apiVersion, CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.GenericResourcesImpl.DeleteAsync(String resourceGroupName, String resourceProviderNamespace, String parentResourcePath, String resourceType, String resourceName, String apiVersion, CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.GenericResourcesImpl.DeleteByIdAsync(String id, String apiVersion, CancellationToken cancellationToken) at Microsoft.Azure.Management.ResourceManager.Fluent.Core.Extensions.Synchronize(Func1 function) at Microsoft.Azure.Management.ResourceManager.Fluent.GenericResourcesImpl.DeleteById(String id, String apiVersion) at Epic.Core.Forge.Provisioning.Providers.Az.Resources.AzRestWindowsVM.Delete(AzClients azClients, String id) in C:\EpicSource\Cloud\Forge\Epic.Core.Forge.Provisioning\Providers\Az\Resources\AzRestWindowsVM.cs:line 97 at Epic.Core.Forge.Provisioning.Providers.Az.AzProvider.DeleteWindowsVM(AzAuth auth, String id) in C:\EpicSource\Cloud\Forge\Epic.Core.Forge.Provisioning\Providers\Az\AzProvider.cs:line 33 at Epic.Core.Forge.Web.Controllers.AzureController.DeleteAzWindowsVM(DeleteAzureWindowsVMDTO deleteDTO) in C:\EpicSource\Cloud\Forge\Epic.Core.Forge.Web\Controllers\AzureController.cs:line 86 at lambda_method(Closure , Object ) at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult() at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.gAwaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

To Reproduce

Code Snippet

IResourceManager resourceManager;

<setup resource manager> ...

 var results = resourceManager.GenericResources.ListByTag(virtualMachine.ResourceGroupName, <key>, <value>);

            foreach (var resource in results)
            {
                resourceManager.GenericResources.DeleteById(resource.Id);
            }

Expected behavior The generic resource should be deleted since I gave it the resource id.

Setup (please complete the following information):

Additional context Similar to #42

For a temporary workaround, I can use the Delete function and put in an empty string for the parentResourcePath. However, this should only be a workaround. I would rather use the DeleteById method. I found this workaround by looking at #912 and doing something similar.

The code below shows the workaround that works.

IResourceManager resourceManager;

 resourceManager = ResourceManager
                .Configure()
                .WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
                .Authenticate(<credentials>)
                .WithSubscription(<subscriptionId>);

 var results = resourceManager.GenericResources.ListByTag(virtualMachine.ResourceGroupName, <key>, <value>);

            foreach (var resource in results)
            {
                   resourceManager.GenericResources.Delete(virtualMachine.ResourceGroupName, resource.ResourceProviderNamespace, "", resource.ResourceType, resource.Name);
            }

Information Checklist Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report

pbstrein commented 4 years ago

After further investigation, it appears that resource.ParentResourceId is null, and if I pass null into .Delete() for the parentResourcePath, I get the same error as DeleteById. There may be an underlying issue that parentResourceIds are not getting set, causing this issue.

yungezz commented 4 years ago

hi @xccc-msft could you pls have a look? thanks

xseeseesee commented 4 years ago

@pbstrein This is the validation rule set for parentResourcePath.

For you workaround, it might be better to check parentResourcePath

foreach (var resource in results)
{
    var parentResourcePath = ResourceUtils.ParentRelativePathFromResourceId(res.Id);
    resourceManager.GenericResources.Delete(virtualMachine.ResourceGroupName, resource.ResourceProviderNamespace, parentResourcePath , resource.ResourceType, resource.Name);
}