Add ACR Image Import Commands#6375
Conversation
|
View a preview at https://prompt.ws/r/Azure/azure-cli/6375 |
Codecov Report
@@ Coverage Diff @@
## dev #6375 +/- ##
===================================
Coverage 0% 0%
===================================
Files 11 11
Lines 133 133
Branches 9 9
===================================
Misses 133 133Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Can you also add this Case 3 to help -> examples?
There was a problem hiding this comment.
These two lines can be combined to elif not resource_id?
There was a problem hiding this comment.
Check if this API works with Classic registry, remove this validation if so.
5a81e23 to
8b24143
Compare
There was a problem hiding this comment.
Will there be additional commands within the acr image group? If not, this should be acr import-image or potentially acr import. If there will be additional commands in this group (even if its a future PR) then an image group is appropriate.
There was a problem hiding this comment.
How is this different from the repository group which says "Manage repositories (image names) for Azure Container Registries"?
There was a problem hiding this comment.
Is this basically copying from one registry to another or is it from any source to an ACR? It sounds like "copy" might be the more appropriate verb than "import". Import usually implies converting a file into resource, so there's a transformation. This sounds like just copying an image into the registry. But I could be misinterpreting.
There was a problem hiding this comment.
Import images from registries is the start. I guess we will add import from other places as docker hub etc.
There was a problem hiding this comment.
I'm a little confused by this argument. Why --image and not --tags?
There was a problem hiding this comment.
Is the container registry referred to here always another ACR resource? If so, this could just be called 'registry' or 'source_registry'. It should also support names or IDs. https://github.com/Azure/azure-cli/blob/dev/doc/authoring_command_modules/authoring_commands.md#supporting-name-or-id-parameters
There was a problem hiding this comment.
The resource id is used for talking with ARM when doing cross subscription importing. I believe customers have access to it.
There was a problem hiding this comment.
It still needs to support the name-or-id paradigm of the CLI. If it's a generic resource it's a bit more annoying than a strongly-typed resource like another ACR.
There was a problem hiding this comment.
It needs to enable our client side CLI to lookup resources cross subscription.
There was a problem hiding this comment.
To summarize the discussion we had with @djyou
The goal is to make --source a required parameter which can be a partial image name and have -r as a parameter which will be used to derive the source which can be a name or dns or fully fully qualified resource id.
There was a problem hiding this comment.
Why is this? Are there ever more than 1? If so, why return nothing in that case?
There was a problem hiding this comment.
I believe prompt already handles the "no TTY" case.
There was a problem hiding this comment.
It does in that it throws the NoTTYException, which you handle here.
There was a problem hiding this comment.
The CLI will already wrap this in a LRO. You can just return client.import_image(....)
There was a problem hiding this comment.
Since you are adding the first relevant item for this release, you can remove "Minor fix" above. Also, recommend:
* Added `acr image import` command.
troydai
left a comment
There was a problem hiding this comment.
Aside from minor comments, looks good to me.
There was a problem hiding this comment.
2.0.25
++++++
* Minor Fix
* Add 'acr import' command.
There was a problem hiding this comment.
Should this be a warning instead of a debug? Debug level logging is usually buried deeply. If this is a message impacting customer's decision process, it should be more visible.
There was a problem hiding this comment.
Unnecessary else since the raise will terminate the function.
7e8a2d3 to
2fb84f0
Compare
There was a problem hiding this comment.
can you just make it consistent to -n registry for all examples
There was a problem hiding this comment.
@djyou why are we using _ for parameter name seperators shouldn't this be --target-tags
Either way does not align with --image which is what build uses and I think we need to have the same convention here.
* Add support for image import * Enable image import commands * Enable Image Import Commands * Error handling * Rewrite acr_image_import + Help messages * Image import without support of docker hub and cross-subscription registires * Listify tags and repository arguments for image import * Support import image cross subscription * Help message for arguments
* Enable Image Import Commands (#148) * Add support for image import * Enable image import commands * Enable Image Import Commands * Error handling * Rewrite acr_image_import + Help messages * Image import without support of docker hub and cross-subscription registires * Listify tags and repository arguments for image import * Support import image cross subscription * Help message for arguments * Enable Image Import Commands with Prompt for resource_id * Add help message * . * Move tty to _utils * Enable Image Import Commands with Prompt for resource_id * Add help message * . * Move tty to _utils * Move prompt out of _util * . * Validate using login server * . * .
* Make util method more generic, update help * Improve examples
* Enable Image Import Commands (#148) * Add support for image import * Enable image import commands * Enable Image Import Commands * Error handling * Rewrite acr_image_import + Help messages * Image import without support of docker hub and cross-subscription registires * Listify tags and repository arguments for image import * Support import image cross subscription * Help message for arguments * Test cases for image import * Test cases for image import * Naming
| examples: | ||
| - name: Import an image to the target registry and inherits repository:tag from source. | ||
| text: > | ||
| az acr import -n MyRegistry --source SourceRegistry.azurecr.io/SourceRepository:SourceTag |
There was a problem hiding this comment.
At least lower case repository name as it can only be lower case. I would recommend lower case login server and tag also. Tag is case sensitive.
| with self.argument_context('acr import') as c: | ||
| c.argument('source', help="A source identifier, either fully qualified (e.g. 'registry.azurecr.io/repository:tag') or partially qualified (e.g. 'repository@sha').") | ||
| c.argument('source_registry', options_list=['--registry', '-r'], help='The container registry of the source. It can be name, canonical name (login server) or resource ID of the source registry.') | ||
| c.argument('target_tags', options_list=['--target-tags', '-t'], help="The repository and optionally a tag in the 'repository:tag' format for target images.", action='append') |
There was a problem hiding this comment.
- It is shortened as '-t'
- As the variable itself implies "target_tags"
| c.argument('no_logs', help="Do not show logs after successfully queuing the build.", action='store_true') | ||
|
|
||
| with self.argument_context('acr import') as c: | ||
| c.argument('source', help="A source identifier, either fully qualified (e.g. 'registry.azurecr.io/repository:tag') or partially qualified (e.g. 'repository@sha').") |
| ) | ||
|
|
||
| SOURCE_REGISTRY_MISING = "Please specify the source container registry: " | ||
| IMPORT_NOT_SUPPORTED = "Imports are only supported for managed registries." |
There was a problem hiding this comment.
The word "Imports" is paired with "Managed registries"
|
|
||
| SOURCE_REGISTRY_MISING = "Please specify the source container registry: " | ||
| IMPORT_NOT_SUPPORTED = "Imports are only supported for managed registries." | ||
| INVALID_SOURCE_IMAGE = "Please specify source image in the form of '[registry.azurecr.io/]repository:tag' or 'repository@sha'." |
There was a problem hiding this comment.
[registry.azurecr.io/]repository[:tag] or [registry.azurecr.io/]repository@sha?
There was a problem hiding this comment.
Lines too long and also I have included the example for import-by-sha in the az acr import -h
| except NoTTYException: | ||
| raise CLIError(NO_TTY_ERROR) | ||
| registry_by_registry_name = get_registry_from_name(cmd.cli_ctx, source_registry) | ||
| registry_by_login_server = get_registry_from_name(cmd.cli_ctx, source_registry) |
There was a problem hiding this comment.
Can you not call get_registry_from_name twice?
| except NoTTYException: | ||
| raise CLIError(NO_TTY_ERROR) | ||
|
|
||
| source_image = source[slash + 1:] |
There was a problem hiding this comment.
Also move this validation earlier.
|
|
||
| with self.argument_context('acr import') as c: | ||
| c.argument('source', help="A source identifier, either fully qualified (e.g., 'registry.azurecr.io/repository:tag') or partially qualified (e.g., 'repository@sha').") | ||
| c.argument('source_registry', options_list=['--registry', '-r'], help='The container registry of the source. It can be name, canonical name (login server) or resource ID of the source registry.') |
There was a problem hiding this comment.
Are we introducing canonical name to CLI now? /cc @sajayantony
|
@tjprescott this is for this sprint, please take another look. |
| c.argument('no_logs', help="Do not show logs after successfully queuing the build.", action='store_true') | ||
|
|
||
| with self.argument_context('acr import') as c: | ||
| c.argument('source', help="The source identifier in the format '[registry.azurecr.io/]repository:tag' or '[registry.azurecr.io/]repository@digest'.") |
There was a problem hiding this comment.
'[registry.azurecr.io/]repository[:tag]'
There was a problem hiding this comment.
I do not think tag is optional for source...unless you want to import a collection of images rather than a single one, do you?
There was a problem hiding this comment.
Could you please validate? Think doesn't help here ...
There was a problem hiding this comment.
What if somebody is so unlucky that there is no latest tag in that repository he/she specifies?
There was a problem hiding this comment.
It is possible right? Like all the images either built or imported into the repository were not defaulted to latest when they were born in that repository.
There was a problem hiding this comment.
How do you think users might expect under such situation?
|
|
||
| helps['acr import'] = """ | ||
| type: command | ||
| short-summary: Imports to the container registry from source. |
|
|
||
| with self.argument_context('acr import') as c: | ||
| c.argument('source', help="The source identifier in the format '[registry.azurecr.io/]repository:tag' or '[registry.azurecr.io/]repository@digest'.") | ||
| c.argument('source_registry', options_list=['--registry', '-r'], help='The container registry of the source. It can be name, login server or resource ID of the source registry.') |
There was a problem hiding this comment.
NIT: The source container registry.
|
|
||
| if registry_name: | ||
| elements = [item for item in registry_list if | ||
| item.login_server.lower() == login_server.lower() or item.name.lower() == registry_name.lower()] |
There was a problem hiding this comment.
login_server will not be None guaranteed by the caller
There was a problem hiding this comment.
How does the caller even know from the method signature without reading its implementation?
There was a problem hiding this comment.
Line 53:
It promises a none empty value for source_registry because the value will be either from the command directly or from the interactivity by prompt. Again, as the client side, we cannot validate it however we can guarantee a nonempty value.
Similar for line 61 but the reason differs a little. If the source_registry_login_server is empty or none, an CLIError has already be thrown before reaching the call.
There was a problem hiding this comment.
But you are shipping an open source project with a util method that can possibly be used by anyone and it's clearly broken if called otherwise.
Does caller have to handle the exception if login_server is None?
There was a problem hiding this comment.
I have done validation on the parameter for registry_name
There was a problem hiding this comment.
For the login server, yes, caller is the layer to handle exceptions before the actual call.
There was a problem hiding this comment.
I think this method should be generic enough to accept either name or login_server (i.e., both should be default to None). Just use the non-empty ones to compare and filter the results.
I am hoping this method can further be reused to support scenarios such as acr login -n registry.azurecr.io.
There was a problem hiding this comment.
For the login server, yes, caller is the layer to handle exceptions before the actual call.
It makes no sense to me that your method takes a None value and doesn't handle it but throws a null reference exception out and wishes the caller can handle it.
There was a problem hiding this comment.
I mean the caller throws an exception when the login server is empty before the method actually gets called. Please see Line 59 for now. Btw I will combine the two errors together as you suggested :-)
| if not elements: | ||
| return None | ||
| elif len(elements) > 1: | ||
| logger.warning("More than one registry is found by %s.", login_server) |
|
|
||
| SOURCE_REGISTRY_MISING = "Please specify the source container registry name, login server or resource ID: " | ||
| IMPORT_NOT_SUPPORTED = "Imports are only supported for managed registries." | ||
| INVALID_SOURCE_IMAGE = "Please specify source image in the form of '[registry.azurecr.io/]repository:tag' or " \ |
There was a problem hiding this comment.
'[registry.azurecr.io/]repository[:tag]', also the source image?
Could you check if tag is optional or not?
There was a problem hiding this comment.
Tag for the target image is optional. For the source, I do not think so.
There was a problem hiding this comment.
I recall the server side attaches a latest if tag is empty, could you validate?
There was a problem hiding this comment.
Why is IMPORT_NOT_SUPPORTED a client side check. I think we should move this to the server and throw if called on a classic registry. - @nathana1
There was a problem hiding this comment.
Wow really? I am so curious. Can you explain more on what you meant by moving to the server? As I have checked all the modules in the commands including build-task, custom, repository and web hook. All of them seem to use the validate_managed_registries and have defined a static constant to be XXX_NOT_SUPPORTED
| raise CLIError(INVALID_SOURCE_IMAGE) | ||
| source_registry_login_server = source[:slash] | ||
| if not source_registry_login_server: | ||
| raise CLIError(INVALID_SOURCE_IMAGE) |
There was a problem hiding this comment.
source_registry_login_server = source[:slash]
source_image = source[slash + 1:]
if not source_registry_login_server or not source_image:
raise CLIError(INVALID_SOURCE_IMAGE)
| source_registry = prompt(SOURCE_REGISTRY_MISING) | ||
| except NoTTYException: | ||
| raise CLIError(NO_TTY_ERROR) | ||
| registry_from_name = get_registry_from_name(cmd.cli_ctx, source_registry, source_registry) |
There was a problem hiding this comment.
NIT: I think you can just call this registry since it's an registry object and isn't necessarily obtained from name (can be login server too).
| registry_from_name = get_registry_from_name(cmd.cli_ctx, source_registry_login_server) | ||
|
|
||
| if registry_from_name: | ||
| if source_registry and registry_from_name.id != source_registry: |
There was a problem hiding this comment.
source_registry can be specified as a name and this validation will fail. I think you need to use the is_valid_resource_id method to differentiate the two cases anyway.
There was a problem hiding this comment.
But thanks for pointing this out as you helped me recall the reason why I need the two lines:
if registry_from_name:
source_registry = registry_from_name.id
There was a problem hiding this comment.
They are necessary because it ensures that when the control flow reaches Line 64, source_registry can only be none or resource id
…n, or none of them should. (inconsistent-return-statements)
tjprescott
left a comment
There was a problem hiding this comment.
LGTM. I still don't get the --image/-t pairing, but I'll leave that up to you.
| with self.argument_context('acr import') as c: | ||
| c.argument('source', help="The source identifier in the format '[registry.azurecr.io/]repository[:tag]' or '[registry.azurecr.io/]repository@digest'.") | ||
| c.argument('source_registry', options_list=['--registry', '-r'], help='The source container registry can be name, login server or resource ID of the source registry.') | ||
| c.argument('target_tags', options_list=['--image', '-t'], help="The repository and optionally a tag in the 'repository:tag' format for target images.", action='append') |
There was a problem hiding this comment.
I'm still confused by this aliasing. Why --image and -t? Why not -i?
There was a problem hiding this comment.
This is to align with docker's convention as well as build-task.
| get_registry_from_name | ||
| ) | ||
|
|
||
| SOURCE_REGISTRY_MISING = "Please specify the source container registry name, login server or resource ID: " |
| raise CLIError(INVALID_SOURCE_IMAGE) | ||
| registry = get_registry_from_name(cmd.cli_ctx, source_registry_login_server) | ||
| if source_registry: | ||
| registry_from_source = get_registry_from_name(cmd.cli_ctx, source_registry, source_registry) |
There was a problem hiding this comment.
Please avoid calling this list operation again, it's an expensive operation that fans out to all regions. You can call it once but filter twice.
| source_registry = prompt(SOURCE_REGISTRY_MISING) | ||
| except NoTTYException: | ||
| raise CLIError(NO_TTY_ERROR) | ||
| registry_from_source = get_registry_from_name(cmd.cli_ctx, source_registry, source_registry) |
There was a problem hiding this comment.
If source_registry is already a resource ID, this request would be a waste since it must return None, or you need to support get registry from resource ID.
|
Please fix the CI before the end of the day so we can merge this in time for this release. |
| raise CLIError(NO_TTY_ERROR) | ||
| registry_from_source = get_registry_from_name_or_login_server(cmd.cli_ctx, source_registry, source_registry) | ||
| if registry_from_source: | ||
| source_registry = registry_from_source.id |
There was a problem hiding this comment.
Add a condition to execute the above 3 lines only if source_registry is not a valid resource id.
This checklist is used to make sure that common guidelines for a pull request are followed.
The PR has modified HISTORY.rst describing any customer-facing, functional changes. Note that this does not include changes only to help content. (see Modifying change log).
I adhere to the Command Guidelines.