-
Notifications
You must be signed in to change notification settings - Fork 1.5k
containerapp - Add --source and --repo to containerapp create #6463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
containerapp - Add --source and --repo to containerapp create #6463
Conversation
|
Hi @snehapar9, |
|
Hi @snehapar9, |
|
Thank you for your contribution! We will review the pull request and get back to you soon. |
cormacpayne
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few initial comments to take a look at
| --image my-app:v1.0 --environment MyContainerappEnv \\ | ||
| --secrets mysecret=secretvalue1 anothersecret="secret value 2" \\ | ||
| --secret-volume-mount "mnt/secrets" | ||
| - name: Create a container app from a dockerfile in a GitHub repo (setting up github actions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "github actions" --> "GitHub Actions"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
| --image my-app:v1.0 --environment MyContainerappEnv \\ | ||
| --secrets mysecret=secretvalue1 anothersecret="secret value 2" \\ | ||
| --secret-volume-mount "mnt/secrets" | ||
| - name: Create a container app from a dockerfile in a GitHub repo (setting up github actions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove the "Dockerfile" part of this since our GitHub Action doesn't require the application source to have one. It may be worth thinking about rephrasing this snippet as follows:
"Create a Container App from a new GitHub Actions workflow in the provided repository."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
| --environment MyContainerappEnv --registry-server MyRegistryServer \\ | ||
| --registry-user MyRegistryUser --registry-pass MyRegistryPass \\ | ||
| --repo https://github.com/myAccount/myRepo | ||
| - name: Create a container app from a dockerfile in a local directory (or autogenerate a container if no dockerfile is found) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment as above: I would recommend removing the mention of "Dockerfile" and think about rephrasing this snippet as follows:
"Create a Container App from the provided application source."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
| if source and repo: | ||
| raise MutuallyExclusiveArgumentError( | ||
| "Cannot use --source and --repo togther. " | ||
| "Can either deploy from a local directory or a Github repo" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "Github repo" --> "GitHub repository"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
| if not image: | ||
| imageNotProvided = True | ||
| image = HELLO_WORLD_IMAGE | ||
| token = get_token(cmd, repo, token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we move this line down to where it's used first? Preferably around the if-statement on 716
| dockerfile_content = _get_dockerfile_content(repo, branch, token, source, context_path, dockerfile) | ||
| ingress, target_port = _get_ingress_and_target_port(ingress, target_port, dockerfile_content) | ||
|
|
||
| app.create_acr_if_needed() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we able to remove this? Even if it doesn't create the ACR instance in any of the flows provided by this function, if the implementation changes under-the-hood, it could start causing some unexpected behavior when we don't want the ACR instance to be created in any circumstance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this line. Since it is now required to set registry-server, registry-user and registry-pass args, the method create_acr_if_needed() is not longer required
| else: | ||
| set_managed_identity(cmd, resource_group_name, containerapp_def, user_assigned=[registry_identity]) | ||
| try: | ||
| image = None if imageNotProvided else _reformat_image(source,repo,image) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the relationship between imageNotProvided and image if we set them to True and HELLO_WORLD_IMAGE, respectively, at the beginning of this function, but then revert image back to None if imageNotProvided is True. Would you mind elaborating on the purpose of this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed this line to image = None if self.get_argument_image().__eq__(HELLO_WORLD_IMAGE) else _reformat_image(self.get_argument_source(), self.get_argument_repo(), self.get_argument_image())
Since image is defaulted to mcr.microsoft.com/k8se/quickstart:latest if it was not provided by the user, we need to reassign it to None so the final image pushed to the registry is in the format <registry-server>/<containerapp-name>:tag
| validate_container_app_name(name, AppType.ContainerApp.name) | ||
| validate_create(registry_identity, registry_pass, registry_user, registry_server, no_wait) | ||
| validate_revision_suffix(revision_suffix) | ||
| _validate_create_args(cmd, source, repo, registry_server, registry_user, registry_pass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rebase newest code and move following validate logic to ContainerAppCreateDecorator.validate_arguments
_validate_create_args(cmd, source, repo, registry_server, registry_user, registry_pass)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merged latest changes from main
cormacpayne
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nits, but the core changes look good to me.
For testing:
(1) We should be able to replicate the --source test that we have for up with this new create flow
(2) Let's confirm that we've tested the --repo flow locally since we're not able to create a test for it (due to GitHub interactions)
(3) Let's confirm that the existing create flows continue to work as expected (either by re-running any create tests that exist today or running the flow locally)
| LOG_ANALYTICS_RP, | ||
| CONTAINER_APPS_RP, | ||
| ACR_IMAGE_SUFFIX, | ||
| MAXIMUM_CONTAINER_APP_NAME_LENGTH, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: intended change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, MAXIMUM_CONTAINER_APP_NAME_LENGTH is not used anywhere in this file.
| def validate_create(registry_identity, registry_pass, registry_user, registry_server, no_wait): | ||
| def validate_create(registry_identity, registry_pass, registry_user, registry_server, no_wait, source=None, repo=None): | ||
| if source and repo: | ||
| raise MutuallyExclusiveArgumentError("Cannot use --source and --repo together. ""Can either deploy from a local directory or a Github repository") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: intended "" in the middle of the error message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch! Not intended.
| registries_def["identity"] = self.get_argument_registry_identity() | ||
| safe_set(containerapp_def, "properties", "configuration", "registries", value=[registries_def]) | ||
| return containerapp_def | ||
| return containerapp_def |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: stylistically this looks correct, but just want to ensure this change was intended
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change was not intended.
| registries_def["identity"] = self.get_argument_registry_identity() | ||
| safe_set(containerapp_def, "properties", "configuration", "registries", value=[registries_def]) | ||
| return containerapp_def | ||
| return containerapp_def |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with this change, there is no return out of if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think it would matter functionality wise since we are always returning containerapp_def on line 586.(This is how it currently is in main. I agree stylistically, it is better to return out of the if statement instead.
| TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..')) | ||
|
|
||
| class ContainerAppCreateTest(ScenarioTest): | ||
| @live_only() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
avoid live_only if the tests are replayable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test needs to be a live test because build packs requires docker to be running.
I'm not sure how to determine if the other tests can be replayed or not. Without live-only I had to first delete the yaml files to be able to re-run them successfully.
| c.argument('workload_profile_name', options_list=['--workload-profile-name', '-w'], help="Name of the workload profile to run the app on.", is_preview=True) | ||
| c.argument('secret_volume_mount', help="Path to mount all secrets e.g. mnt/secrets", is_preview=True) | ||
| c.argument('termination_grace_period', type=int, options_list=['--termination-grace-period', '--tgp'], help="Duration in seconds a replica is given to gracefully shut down before it is forcefully terminated. (Default: 30)", is_preview=True) | ||
| c.argument('source', help="Local directory path containing the application source and Dockerfile for building the container image. Preview: If no Dockerfile is present, a container image is generated using buildpacks. If Docker is not running or buildpacks cannot be used, Oryx will be used to generate the image. See the supported Oryx runtimes here: https://github.com/microsoft/Oryx/blob/main/doc/supportedRuntimeVersions.md.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the argument is in preview, add is_preview=True
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These flags are not in preview.
| app = self.set_up_create_containerapp_if_source_or_repo(containerapp_def=containerapp_def) | ||
| containerapp_def = self.set_up_create_containerapp_source(app=app, containerapp_def=containerapp_def) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need two steps to construct container app payload? Is app required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
app is an instance of ContainerApp and is required in the set_up_create_containerapp_source method to properly set up source and repo for containerapp create (similar to containerapp up)
|
| r = self.set_up_create_containerapp_repo(app=app, r=r, env=app.env, env_rg=app.resource_group.name) | ||
| return r | ||
|
|
||
| def set_up_create_containerapp_if_source_or_repo(self, containerapp_def): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, I have refactored the containerapp decorator with this pr: containerapp refactor decorator by Greedygre · Pull Request #6511 · Azure/azure-cli-extensions (github.com)
You can use self.containerapp_def to set and get current payload for containerapp.
Thanks for using this.
|
|
||
| # Update image | ||
| containerapp_def["properties"]["template"]["containers"][0]["image"] = HELLO_WORLD_IMAGE if app.image is None else app.image | ||
| return containerapp_def |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with newest code, use self.containerapp_def to construct payload, no need to return.
|
| rule | cmd_name | rule_message | suggest_message |
|---|---|---|---|
| containerapp create | cmd containerapp create added parameter branch |
||
| containerapp create | cmd containerapp create added parameter context_path |
||
| containerapp create | cmd containerapp create added parameter repo |
||
| containerapp create | cmd containerapp create added parameter service_principal_client_id |
||
| containerapp create | cmd containerapp create added parameter service_principal_client_secret |
||
| containerapp create | cmd containerapp create added parameter service_principal_tenant_id |
||
| containerapp create | cmd containerapp create added parameter source |
||
| containerapp create | cmd containerapp create added parameter token |
|
|
||
| if self.get_argument_source(): | ||
| app = self.set_up_create_containerapp_if_source_or_repo() | ||
| self.containerapp_def = self.set_up_create_containerapp_source(app=app) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As set_up_create_containerapp_source no need and has not return containerapp, here need to remove.
| self.containerapp_def = self.set_up_create_containerapp_source(app=app) | |
| self.set_up_create_containerapp_source(app=app) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
| if not registry_server or not registry_user or not registry_pass: | ||
| raise RequiredArgumentMissingError('Usage error: --registry-server, --registry-username and --registry-password are required while using --source or --repo.') | ||
| if repo and registry_server and "azurecr.io" in registry_server: | ||
| parsed = urlparse(registry_server) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cormacpayne we should check this condition for source also?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@snehapar9 we should since we have the condition above to validate that registry_server is provided when source is provided -- we should be able to indent this entire block and remove the repo and registry_server checks to make it straightforward:
if source or repo:
if not registry_server or not registry_user or not registry_pass:
raise RequiredArgumentMissingError('Usage error: --registry-server, --registry-username and --registry-password are required while using --source or --repo.')
if "azurecr.io" in registry_server:
parsed = urlparse(registry_server)
registry_name = (parsed.netloc if parsed.scheme else parsed.path).split(".")[0]
if registry_name and len(registry_name) > MAXIMUM_SECRET_LENGTH:
raise ValidationError(f"--registry-server ACR name must be less than {MAXIMUM_SECRET_LENGTH} "
"characters when using --repo")```|
Closing this PR. Changes in this PR were merged into this PR |
This checklist is used to make sure that common guidelines for a pull request are followed.
Related command
General Guidelines
azdev style <YOUR_EXT>locally? (pip install azdevrequired)python scripts/ci/test_index.py -qlocally?For new extensions:
About Extension Publish
There is a pipeline to automatically build, upload and publish extension wheels.
Once your pull request is merged into main branch, a new pull request will be created to update
src/index.jsonautomatically.You only need to update the version information in file setup.py and historical information in file HISTORY.rst in your PR but do not modify
src/index.json.