azure-sdk-for-python: Empty repository in container registry causes unexpected SDK issue when trying to list tags

  • Package Name: azure-containerregistry
  • Package Version: 1.0.0
  • Operating System: macOS 13.1
  • Python Version: 3.9.6

Describe the bug When trying to list the tags on an empty container registry repository (all tags have been removed), the SDK produces the unexpected error TypeError: 'NoneType' object is not iterable. The error is thrown when trying to iterate on a non-none object of type azure.core.paging.ItemPaged, which is what is unexpected.

To Reproduce Steps to reproduce the behavior:

  1. Delete all tags for a container registry’s repository.

  2. Try and use the SDK to list tags for the empty repository. The issue will come when try to iterate on the response (even if a check for None is done prior):

    client = ContainerRegistryClient(endpoint=registry_endpoint, credential=credentials, audience="https://management.azure.com")
    response = client.list_tag_properties(repository=repository_name)
    if response is not None:
        for tag in response: # <--- error occurs here - TypeError: 'NoneType' object is not iterable
                print("\t" + tag.name)
    

I have created a script which reproduces the issue, which also includes setup of a registry/repository for testing. See below for more detail.

Expected behavior The SDK should either not throw an issue when trying to iterate on the tag response (ItemPaged type) OR return a NoneType response so that appropriate checks can be added by client applications.

Screenshots

Below is a screenshot in Azure portal of a repository which has had all tags removed:

image

Using the SDK to list tags for “my-test-repository” causes the above issue to occur.

Additional context

I’ve created a script which completely initializes a container registry and and puts it in the desired state, and then reproduces the error.

  1. Install required dependencies:

    azure-containerregistry==1.0.0
    azure-identity==1.12.0
    azure-mgmt-containerregistry==10.0.0
    
  2. Create environment variables for test script, and populate with environment-specific values.

    export AZURE_SUBSCRIPTION_ID=xxx
    export AZURE_CLIENT_ID=xxx
    export AZURE_CLIENT_SECRET=xxx
    export AZURE_TENANT_ID=xxx
    export AZURE_RESOURCE_GROUP=xxx
    export AZURE_REGISTRY_NAME=xxx
    
  3. Run the following script to recreate the issue. The script will autocreate the container registry (and repository) if it doesn’t exist. The script will then try and list tags for the empty repository.

    #!/usr/bin/python
    
    import os
    from azure.identity._credentials import client_secret
    from azure.containerregistry import ContainerRegistryClient
    from azure.mgmt.containerregistry import ContainerRegistryManagementClient
    from azure.core.exceptions import ResourceNotFoundError
    from azure.mgmt.containerregistry.models import (RegistryNameCheckRequest, Registry, Sku, ImportImageParameters, ImportSource)
    
    
    subscription_id = os.environ.get('AZURE_SUBSCRIPTION_ID', 'xxx')
    credentials = client_secret.ClientSecretCredential(
        client_id=os.environ.get('AZURE_CLIENT_ID', 'xxx'),
        client_secret=os.environ.get('AZURE_CLIENT_SECRET', 'xxx'),
        tenant_id=os.environ.get('AZURE_TENANT_ID', 'xxx'),
    )
    
    registry_name = os.environ.get('AZURE_REGISTRY_NAME', 'xxx')
    registry_endpoint = registry_name + ".azurecr.io"
    repository_name = "my-test-repository"
    
    def list_tags():
        print(f"listing tags for repository {repository_name} in registry {registry_name}")
        client = ContainerRegistryClient(endpoint=registry_endpoint, credential=credentials, audience="https://management.azure.com")
        response = client.list_tag_properties(repository=repository_name)
        if response is not None:
            for tag in response:
                    print("\t" + tag.name)
        else:
            print('response is none')
    
    
    def initialize_registry():
        resource_group = os.environ.get('AZURE_RESOURCE_GROUP', 'xxx')
        mgmt_client = ContainerRegistryManagementClient(credential=credentials, subscription_id=subscription_id)
        print(f"checking for registry '{registry_name}' in resource group '{resource_group}'")
        try:
            registry = mgmt_client.registries.get(resource_group_name=resource_group, registry_name=registry_name)
        except ResourceNotFoundError:
            registry = None
    
        if registry is None:
            print(f"registry '{registry_name}' does not exist; checking for name availability")
            status = mgmt_client.registries.check_name_availability(registry_name_check_request=RegistryNameCheckRequest(name=registry_name))
            if status.name_available:
                print(f"creating registry '{registry_name}'")
                poller = mgmt_client.registries.begin_create(resource_group_name=resource_group, registry_name=registry_name, registry=
                    Registry(location="eastus", sku=Sku(name="Basic"))
                )
                while not poller.done():
                    poller.wait(timeout=5)
                registry = poller.result()
                print(f"registry '{registry_name}' created!")
            else:
                print(f"name '{registry_name}' is unavailable, please use a different one!")
                exit(1)
    
        print(f"registry '{registry_name}' exists, checking for repository '{repository_name}'")
        client = ContainerRegistryClient(endpoint=registry_endpoint, credential=credentials, audience="https://management.azure.com")
        try:
            repository = client.get_repository_properties(repository=repository_name)
        except ResourceNotFoundError:
            repository = None
        
        test_image = "hello-world:latest"
        if repository is None:
            print(f"creating repository '{repository_name}' by importing test image '{test_image}'")
            params = ImportImageParameters(
                target_tags=[repository_name + ":latest"],
                source=ImportSource(
                    registry_uri="docker.io",
                    source_image="library/" + test_image,
                )
            )
            poller = mgmt_client.registries.begin_import_image(resource_group_name=resource_group, registry_name=registry_name, parameters=params)
            while not poller.done():
                poller.wait(timeout=5)
            print(f"test image {test_image} successfully imported to '{repository_name}'")
    
        print(f"ensuring test image '{test_image}' is removed from repository '{repository_name}'")
        client.delete_tag(repository=repository_name, tag="latest")
    
        print(f"repository '{repository_name}' is empty and ready for recreating the issue!\n")
    
    
    initialize_registry()
    list_tags()
    

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 15 (6 by maintainers)

Most upvoted comments

Sorry I didn’t respond sooner, but I have tested the beta version and can confirm it is working! Thank you!!

When will a stable release occur with the same?

/unresolve

Hi @l3ender , we are working on it. You can expect it get fix in our next preview release.