Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
887ecf6
Add --source and --repo to containerapp create
snehapar9 Jun 30, 2023
011439a
Refactored code for containerapp create
snehapar9 Jul 6, 2023
e9fd59b
Merge branch 'main' into snehapar/Add-source-and-repo-to-create
snehapar9 Jul 6, 2023
89241ca
Merge branch 'snehapar/add-source-and-repo-to-containerapp-create' in…
snehapar9 Jul 6, 2023
916259c
Removed extra source param
snehapar9 Jul 6, 2023
1439120
Addressed PR comments
snehapar9 Jul 6, 2023
fb7f4fe
Removed new line
snehapar9 Jul 6, 2023
d18d6c7
Fixed linting errors
snehapar9 Jul 6, 2023
78181aa
Remove new line
snehapar9 Jul 6, 2023
d646bd1
Minor fixes for GitHub repo params
snehapar9 Jul 6, 2023
56e68d4
Addressed PR comments
snehapar9 Jul 8, 2023
1b70cf5
Merge branch 'main' into snehapar/Add-source-and-repo-to-create
snehapar9 Jul 25, 2023
3a809ca
Add source back
snehapar9 Jul 25, 2023
07e1be5
Remove return statement
snehapar9 Jul 25, 2023
25862fb
Addressed PR comments
snehapar9 Jul 26, 2023
5845255
Add source flag to update
snehapar9 Jul 26, 2023
0b2098b
Refactor validation
snehapar9 Jul 26, 2023
3fd9fde
Revert validation change
snehapar9 Jul 26, 2023
557f05e
Add live_only to tests
snehapar9 Jul 26, 2023
6c3a58e
Refactored update
snehapar9 Jul 27, 2023
d1631be
Merge branch 'snehapar/Add-source-and-repo-to-create' into snehapar/A…
snehapar9 Jul 27, 2023
ab167d0
Add tests for update
snehapar9 Jul 27, 2023
2af684f
Update integration test
snehapar9 Jul 28, 2023
a1afa52
Fix help text
snehapar9 Jul 28, 2023
8db50ef
Fix linting
snehapar9 Jul 28, 2023
7afbb53
Fix linting
snehapar9 Jul 28, 2023
2b21611
Addressed PR comments
snehapar9 Jul 31, 2023
5b3b66d
Fix lint errors
snehapar9 Jul 31, 2023
d78c952
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 17, 2023
6341e8a
Fix ci failure
snehapar9 Aug 17, 2023
608438e
Add new line
snehapar9 Aug 19, 2023
e852926
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 28, 2023
07c199b
Address PR comments
snehapar9 Aug 28, 2023
6dc0b8c
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 28, 2023
620e0c0
Performed refactoring
snehapar9 Aug 28, 2023
79dd20d
Refactor and re-run tests
snehapar9 Aug 28, 2023
358b9bb
Fix linting
snehapar9 Aug 29, 2023
82547a3
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 29, 2023
20a266e
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 29, 2023
bdba046
Remove service connectors check
snehapar9 Aug 29, 2023
14d648e
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 29, 2023
69e7d44
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 29, 2023
ee0d465
Address PR comments and re-run tests
snehapar9 Aug 30, 2023
8df8f34
Addressed PR comments
snehapar9 Aug 30, 2023
30b571c
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Aug 30, 2023
b0f79a5
Delete recording
snehapar9 Aug 30, 2023
97eeb45
Add recording for source and repo test
snehapar9 Aug 31, 2023
fb5181d
Fix build failure
snehapar9 Aug 31, 2023
49e1c6c
Address PR comments
snehapar9 Sep 5, 2023
1b1ac8f
Re-run test
snehapar9 Sep 5, 2023
36a8059
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Sep 5, 2023
236bbe1
Update src/containerapp/azext_containerapp/_help.py
snehapar9 Sep 5, 2023
8688b0d
Addressed PR comments
snehapar9 Sep 5, 2023
83e7404
Update comments
snehapar9 Sep 5, 2023
9e8d76e
Addressed PR comments
snehapar9 Sep 6, 2023
dd6abb3
Address PR comments
snehapar9 Sep 6, 2023
3ea22fe
Re-run test
snehapar9 Sep 6, 2023
f23eda9
Addressed PR comments
snehapar9 Sep 7, 2023
091beb0
Addressed PR comments
snehapar9 Sep 7, 2023
9918a9a
Fix linting
snehapar9 Sep 7, 2023
64912b0
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Sep 7, 2023
47ea3d7
Delete recording
snehapar9 Sep 7, 2023
5139b33
Addressed PR comments
snehapar9 Sep 8, 2023
f1ac303
Addressed PR comments
snehapar9 Sep 8, 2023
648090d
Addressed PR comments and performed refactoring
snehapar9 Sep 10, 2023
3aaea4f
Fix linting
snehapar9 Sep 10, 2023
1200a8c
Add env deletion at the end of test
snehapar9 Sep 11, 2023
a2888b1
Re-run test
snehapar9 Sep 11, 2023
85c4302
Address PR comments and re-run tests
snehapar9 Sep 11, 2023
47f563c
Refactor and re-run tests
snehapar9 Sep 12, 2023
ab09b8c
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Sep 12, 2023
b77faac
Merge branch 'main' into snehapar/Add-source-to-containerapp-update
snehapar9 Sep 12, 2023
04a738c
Added comment for live only tests
snehapar9 Sep 13, 2023
0badf02
Add ACR registry validation
snehapar9 Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/containerapp/azext_containerapp/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@
--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 new GitHub Actions workflow in the provided GitHub repository
Comment thread
snehapar9 marked this conversation as resolved.
Outdated
text: |
az containerapp create -n MyContainerapp -g MyResourceGroup \\
--environment MyContainerappEnv --registry-server MyRegistryServer \\
--registry-user MyRegistryUser --registry-pass MyRegistryPass \\
--repo https://github.com/myAccount/myRepo
- name: Create a Container App from the provided application source
text: |
az containerapp create -n MyContainerapp -g MyResourceGroup \\
--environment MyContainerappEnv --registry-server MyRegistryServer \\
--registry-user MyRegistryUser --registry-pass MyRegistryPass \\
--source .
"""

helps['containerapp update'] = """
Expand Down Expand Up @@ -88,6 +100,9 @@
--scale-rule-type my-custom-type \\
--scale-rule-metadata key=value key2=value2 \\
--scale-rule-auth triggerparam=secretref triggerparam=secretref
- name: Update a Container App from the provided application source
text: |
az containerapp update -n MyContainerapp -g MyResourceGroup --source . \\
Comment thread
snehapar9 marked this conversation as resolved.
Outdated
"""

helps['containerapp delete'] = """
Expand Down
14 changes: 14 additions & 0 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ def load_arguments(self, _):
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.")
Comment thread
snehapar9 marked this conversation as resolved.
Outdated

with self.argument_context('containerapp create', arg_group='Identity') as c:
c.argument('user_assigned', nargs='+', help="Space-separated user identities to be assigned.")
Expand All @@ -135,9 +136,22 @@ def load_arguments(self, _):
c.argument('service_type', help="The service information for dev services.")
c.ignore('service_type')

with self.argument_context('containerapp create', arg_group='GitHub Repository') as c:
c.argument('repo', help='Create an app via GitHub Actions in the format: https://github.com/<owner>/<repository-name> or <owner>/<repository-name>')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to The repo used to create app via Github Actions. Typically in the format of https://github.com/<owner>/<repository-name> or <owner>/<repository-name> ?

@snehapar9 snehapar9 Aug 30, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Juliehzl re-used this from repo's help text in the up command here

c.argument('token', help='A Personal Access Token with write access to the specified repository. For more information: https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line. If not provided or not found in the cache (and using --repo), a browser page will be opened to authenticate with Github.')
c.argument('branch', options_list=['--branch', '-b'], help='Branch in the provided GitHub repository. Assumed to be the GitHub repository\'s default branch if not specified.')
c.argument('context_path', help='Path in the repository to run docker build. Defaults to "./". Dockerfile is assumed to be named "Dockerfile" and in this directory.')
Comment thread
snehapar9 marked this conversation as resolved.
c.argument('service_principal_client_id', help='The service principal client ID. Used by GitHub Actions to authenticate with Azure.', options_list=["--service-principal-client-id", "--sp-cid"])
c.argument('service_principal_client_secret', help='The service principal client secret. Used by GitHub Actions to authenticate with Azure.', options_list=["--service-principal-client-secret", "--sp-sec"])
c.argument('service_principal_tenant_id', help='The service principal tenant ID. Used by GitHub Actions to authenticate with Azure.', options_list=["--service-principal-tenant-id", "--sp-tid"])

with self.argument_context('containerapp show') as c:
c.argument('show_secrets', help="Show Containerapp secrets.", action='store_true')

# Source
with self.argument_context('containerapp update') as c:
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.")
Comment thread
snehapar9 marked this conversation as resolved.
Outdated

with self.argument_context('containerapp update', arg_group='Container') as c:
c.argument('image', options_list=['--image', '-i'], help="Container image, e.g. publisher/image-name:tag.")
c.argument('workload_profile_name', options_list=['--workload-profile-name', '-w'], help='The friendly name for the workload profile', is_preview=True)
Expand Down
1 change: 0 additions & 1 deletion src/containerapp/azext_containerapp/_up_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
LOG_ANALYTICS_RP,
CONTAINER_APPS_RP,
ACR_IMAGE_SUFFIX,
MAXIMUM_CONTAINER_APP_NAME_LENGTH,
ACR_TASK_TEMPLATE,
DEFAULT_PORT)

Expand Down
18 changes: 15 additions & 3 deletions src/containerapp/azext_containerapp/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,33 @@

import re
from azure.cli.core.azclierror import (ValidationError, ResourceNotFoundError, InvalidArgumentValueError,
MutuallyExclusiveArgumentError)
MutuallyExclusiveArgumentError, RequiredArgumentMissingError)
from msrestazure.tools import is_valid_resource_id
from knack.log import get_logger

from ._clients import ContainerAppClient
from ._ssh_utils import ping_container_app
from ._utils import safe_get, is_registry_msi_system
from ._constants import ACR_IMAGE_SUFFIX, LOG_TYPE_SYSTEM
from ._constants import ACR_IMAGE_SUFFIX, LOG_TYPE_SYSTEM, MAXIMUM_SECRET_LENGTH
from urllib.parse import urlparse


logger = get_logger(__name__)


# called directly from custom method bc otherwise it disrupts the --environment auto RID functionality
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")
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 repo and registry_server and "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} "
Comment thread
snehapar9 marked this conversation as resolved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise ValidationError(f"--registry-server ACR name must be less than {MAXIMUM_SECRET_LENGTH} "
raise InvalidArgumentValueError(f"--registry-server ACR name must be less than {MAXIMUM_SECRET_LENGTH} "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, ValidationError is acceptable, but personally, I think InvalidArgumentValueError is a more accurate usage.
If you want to maintain consistency with _validate_up_args, then I think it's also OK. Or modify them all to InvalidArgumentValueError

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for taking a look! Just going to keep it as-is so we are consistent with this validation in the up command.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

"characters when using --repo")
if registry_identity and (registry_pass or registry_user):
raise MutuallyExclusiveArgumentError("Cannot provide both registry identity and username/password")
if is_registry_msi_system(registry_identity) and no_wait:
Expand Down
84 changes: 83 additions & 1 deletion src/containerapp/azext_containerapp/containerapp_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
from ._constants import (CONTAINER_APPS_RP,
HELLO_WORLD_IMAGE)

from ._github_oauth import cache_github_token

logger = get_logger(__name__)


Expand Down Expand Up @@ -168,6 +170,30 @@ def get_argument_no_wait(self):
def get_argument_yaml(self):
return self.get_param("yaml")

def get_argument_source(self):
return self.get_param("source")

def get_argument_repo(self):
return self.get_param("repo")

def get_argument_branch(self):
return self.get_param("branch")

def get_argument_token(self):
return self.get_param("token")

def get_argument_context_path(self):
return self.get_param("context_path")

def get_argument_service_principal_client_id(self):
return self.get_param("service_principal_client_id")

def get_argument_service_principal_client_secret(self):
return self.get_param("service_principal_client_secret")

def get_argument_service_principal_tenant_id(self):
return self.get_param("service_principal_tenant_id")

def get_argument_image(self):
return self.get_param("image")

Expand Down Expand Up @@ -331,7 +357,7 @@ def __init__(

def validate_arguments(self):
validate_container_app_name(self.get_argument_name(), AppType.ContainerApp.name)
validate_create(self.get_argument_registry_identity(), self.get_argument_registry_pass(), self.get_argument_registry_user(), self.get_argument_registry_server(), self.get_argument_no_wait())
validate_create(self.get_argument_registry_identity(), self.get_argument_registry_pass(), self.get_argument_registry_user(), self.get_argument_registry_server(), self.get_argument_no_wait(), self.get_argument_source(), self.get_argument_repo())
validate_revision_suffix(self.get_argument_revision_suffix())

def construct_containerapp(self):
Expand Down Expand Up @@ -541,6 +567,9 @@ def construct_containerapp(self):
else:
set_managed_identity(self.cmd, self.get_argument_resource_group_name(), self.containerapp_def, user_assigned=[self.get_argument_registry_identity()])

if self.get_argument_source():
Comment thread
snehapar9 marked this conversation as resolved.
Outdated
self.set_up_create_containerapp_if_source_or_repo()

def create_containerapp(self):
try:
r = self.client.create_or_update(
Expand Down Expand Up @@ -594,6 +623,59 @@ def post_process_containerapp(self, r):
linker_client.linker.begin_create_or_update(resource_uri=r["id"],
parameters=item["parameters"],
linker_name=item["linker_name"]).result()

if self.get_argument_repo():
app = self.set_up_create_containerapp_if_source_or_repo()
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):
from ._up_utils import (ContainerApp, ResourceGroup, ContainerAppEnvironment, _reformat_image, get_token, _has_dockerfile, _get_dockerfile_content, _get_ingress_and_target_port, _get_registry_details)
ingress = self.get_argument_ingress()
target_port = self.get_argument_target_port()
dockerfile = "Dockerfile"
token = get_token(self.cmd, self.get_argument_repo(), self.get_argument_token())
# Parse resource group name and managed env name
env_id = self.containerapp_def["properties"]['environmentId']
parsed_managed_env = parse_resource_id(env_id)
env_name = parsed_managed_env['name']
env_rg = parsed_managed_env['resource_group']

# Parse location
env_info = self.get_environment_client().show(cmd=self.cmd, resource_group_name=env_rg, name=env_name)
location = env_info['location']

# Set image to None if it was previously set to the default image (case where image was not provided by the user) else reformat it
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())
Comment thread
snehapar9 marked this conversation as resolved.
Outdated

if self.get_argument_source() and not _has_dockerfile(self.get_argument_source(), dockerfile):
pass
Comment thread
snehapar9 marked this conversation as resolved.
Outdated
else:
dockerfile_content = _get_dockerfile_content(self.get_argument_repo(), self.get_argument_branch(), token, self.get_argument_source(), self.get_argument_context_path(), dockerfile)
ingress, target_port = _get_ingress_and_target_port(self.get_argument_ingress(), self.get_argument_target_port(), dockerfile_content)
Comment thread
snehapar9 marked this conversation as resolved.
Outdated
# Construct ContainerApp
resource_group = ResourceGroup(self.cmd, env_rg, location=location)
env = ContainerAppEnvironment(self.cmd, env_name, resource_group, location=location)
app = ContainerApp(self.cmd, self.get_argument_name(), resource_group, None, image, env, target_port, self.get_argument_registry_server(), self.get_argument_registry_user(), self.get_argument_registry_pass(), self.get_argument_env_vars(), self.get_argument_workload_profile_name(), ingress)

# Fetch registry credentials
_get_registry_details(self.cmd, app, self.get_argument_source()) # fetch ACR creds from arguments registry arguments

if self.get_argument_source():
# Uses buildpacks to generate image if Dockerfile was not provided by the user
app.run_acr_build(dockerfile, self.get_argument_source(), quiet=False, build_from_source=not _has_dockerfile(self.get_argument_source(), dockerfile))
# Update image
self.containerapp_def["properties"]["template"]["containers"][0]["image"] = HELLO_WORLD_IMAGE if app.image is None else app.image
Comment thread
snehapar9 marked this conversation as resolved.
Outdated
return app

def set_up_create_containerapp_repo(self, app, r, env, env_rg):
from ._up_utils import (_create_github_action, get_token)
# Get GitHub access token
token = get_token(self.cmd, self.get_argument_repo(), self.get_argument_token())
_create_github_action(app, env, self.get_argument_service_principal_client_id(), self.get_argument_service_principal_client_secret(),
self.get_argument_service_principal_tenant_id(), self.get_argument_branch(), token, self.get_argument_repo(), self.get_argument_context_path())
cache_github_token(self.cmd, token, self.get_argument_repo())
r = self.client.show(cmd=self.cmd, resource_group_name=env_rg, name=app.name)
return r

def set_up_create_containerapp_yaml(self, name, file_name):
Expand Down
Loading