Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7525036
Tests and recordings + remove live_only (Ingress, Dapr, Env, Traffic)…
runefa May 9, 2022
73b00f2
cache github token for up with --repo
StrawnSC May 6, 2022
e29e75e
use core CLI cred persistence library
StrawnSC May 9, 2022
ff8dd51
use Github repo's default branch for up and github actions
StrawnSC May 6, 2022
f92ce6c
Merge pull request #98 from StrawnSC/containerapp-up-default-branch-2
StrawnSC May 9, 2022
083ed7b
handle workflow rename
StrawnSC May 10, 2022
302bbb5
Merge pull request #97 from StrawnSC/cache-gh-creds-2
StrawnSC May 10, 2022
4f92886
fix style
StrawnSC May 10, 2022
207bde5
bump version, add to history / help text
StrawnSC May 10, 2022
b475d7d
Update api's to use stable version (#99)
calvinsID May 12, 2022
914a32b
Added label support (#101)
runefa May 12, 2022
0d69221
Added FileShare commands.
May 3, 2022
aca1f9b
Updated params to match spec. Added param help.
May 3, 2022
28f8624
Added help. Removed --ids support.
May 3, 2022
6f35fd4
Removed automatic import statements.
May 3, 2022
d87ba28
Added back type in help.
May 3, 2022
0dbb781
add fileshare tests; fix merge conflicts
May 3, 2022
6f61c9f
Updated param names to better reflect AzureFile dependency.
May 3, 2022
a730b1b
Added validation to ensure share name and account name are longer tha…
May 3, 2022
63f28aa
Added warning message if user is updating existing storage.
May 4, 2022
3b47ea4
only encrypt on windows
StrawnSC May 12, 2022
fc24268
Removed live_only from storage test.
May 12, 2022
7bacfac
Merge pull request #103 from StrawnSC/fix-gh-cred-caching
StrawnSC May 12, 2022
db50513
Added credscan suppression.
May 12, 2022
60efdf3
fix tests / CI issues
StrawnSC May 12, 2022
669d001
Merge branch 'may-11-2022-containerapp-release' of github.com:calvins…
StrawnSC May 12, 2022
e784096
Merge branch 'may-11-2022-containerapp-release' into add_back_fileshares
StrawnSC May 12, 2022
c6787dd
record test
StrawnSC May 12, 2022
dd164bb
Merge pull request #102 from StrawnSC/add_back_fileshares
StrawnSC May 12, 2022
7deb02d
Update HISTORY.rst
StrawnSC May 12, 2022
d88abbd
fix linter
StrawnSC May 12, 2022
12c7fc1
Merge branch 'add_back_fileshares' into may-11-2022-containerapp-release
StrawnSC May 12, 2022
4bddaa9
allow using --traffic-weight for --revision-weight to prevent breakin…
StrawnSC May 13, 2022
5a2884c
add breaking change warning
StrawnSC May 13, 2022
32ff71f
deprecate param to prevent breaking change
StrawnSC May 13, 2022
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
19 changes: 19 additions & 0 deletions scripts/ci/credscan/CredScanSuppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,25 @@
"src\\aks-preview\\azext_aks_preview\\tests\\latest\\data\\setup_proxy.sh"
],
"_justification": "Dummy self-signed certificate + private key used for testing only."
},
{
"file": [
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_container_acr.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_e2e.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_env_dapr_components.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_env_e2e.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_env_storage.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_identity_e2e.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_identity_system.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_identity_user.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_ingress_e2e.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_ingress_traffic_e2e.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_logstream.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_update.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_dapr_e2e.yaml",
"src\\containerapp\\azext_containerapp\\tests\\latest\\recordings\\test_containerapp_up_image_e2e.yaml"
],
"_justification": "Dummy resources' keys left during testing Microsoft.App (required for log-analytics to create managedEnvironments)"
}
]
}
8 changes: 7 additions & 1 deletion src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
Release History
===============

0.3.4
++++++
* BREAKING CHANGE: 'az containerapp up' and 'az containerapp github-action add' now use the github repo's default branch instead of "main"
* 'az containerapp up' now caches Github credentials so the user won't be prompted to sign in if using the same repo
* Fixed bug with 'az containerapp up --repo' where it hangs after creating github action
* Added 'az containerapp env storage' to manage Container App environment file shares

0.3.3
++++++
* Improved 'az containerapp up' handling of environment locations
* Added 'az containerapp env storage' to manage Container App environment file shares

0.3.2
++++++
Expand Down
160 changes: 138 additions & 22 deletions src/containerapp/azext_containerapp/_clients.py

Large diffs are not rendered by default.

48 changes: 47 additions & 1 deletion src/containerapp/azext_containerapp/_github_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
# --------------------------------------------------------------------------------------------
# pylint: disable=consider-using-f-string

import os
import sys
from datetime import datetime

from knack.log import get_logger
from azure.cli.core.util import open_page_in_browser
from azure.cli.core.auth.persistence import SecretStore, build_persistence
from azure.cli.core.azclierror import (ValidationError, CLIInternalError, UnclassifiedUserFault)
from knack.log import get_logger

from ._utils import repo_url_to_name

logger = get_logger(__name__)

Expand All @@ -25,6 +32,45 @@
]


def _get_github_token_secret_store(cmd):
location = os.path.join(cmd.cli_ctx.config.config_dir, "github_token_cache")
# TODO use core CLI util to take care of this once it's merged and released
encrypt = sys.platform.startswith('win32') # encryption not supported on non-windows platforms
file_persistence = build_persistence(location, encrypt)
return SecretStore(file_persistence)


def cache_github_token(cmd, token, repo):
repo = repo_url_to_name(repo)
secret_store = _get_github_token_secret_store(cmd)
cache = secret_store.load()

for entry in cache:
if isinstance(entry, dict) and entry.get("value") == token:
if repo not in entry.get("repos", []):
entry["repos"] = [*entry.get("repos", []), repo]
entry["last_modified_timestamp"] = datetime.utcnow().timestamp()
break
else:
cache_entry = {"last_modified_timestamp": datetime.utcnow().timestamp(), "value": token, "repos": [repo]}
cache = [cache_entry, *cache]

secret_store.save(cache)


def load_github_token_from_cache(cmd, repo):
repo = repo_url_to_name(repo)
secret_store = _get_github_token_secret_store(cmd)
cache = secret_store.load()

if isinstance(cache, list):
for entry in cache:
if isinstance(entry, dict) and repo in entry.get("repos", []):
return entry.get("value")

return None


def get_github_access_token(cmd, scope_list=None, token=None): # pylint: disable=unused-argument
if token:
return token
Expand Down
80 changes: 77 additions & 3 deletions src/containerapp/azext_containerapp/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,32 @@
az containerapp revision copy -n MyContainerapp -g MyResourceGroup --cpu 0.75 --memory 1.5Gi
"""

helps['containerapp revision label'] = """
type: group
short-summary: Manage revision labels assigned to traffic weights.
"""

helps['containerapp revision label add'] = """
type: command
short-summary: Set a revision label to a revision with an associated traffic weight.
examples:
- name: Add a label to the latest revision.
text: |
az containerapp revision label add -n MyContainerapp -g MyResourceGroup --label myLabel --revision latest
- name: Add a label to a previous revision.
text: |
az containerapp revision label add -g MyResourceGroup --label myLabel --revision revisionName
"""

helps['containerapp revision label remove'] = """
type: command
short-summary: Remove a revision label from a revision with an associated traffic weight.
examples:
- name: Remove a label.
text: |
az containerapp revision label remove -n MyContainerapp -g MyResourceGroup --label myLabel
"""

# Environment Commands
helps['containerapp env'] = """
type: group
Expand Down Expand Up @@ -356,6 +382,48 @@
az containerapp env dapr-component remove -g MyResourceGroup --dapr-component-name MyDaprComponentName --name MyEnvironment
"""

helps['containerapp env storage'] = """
type: group
short-summary: Commands to manage storage for the Container Apps environment.
"""

helps['containerapp env storage list'] = """
type: command
short-summary: List the storages for an environment.
examples:
- name: List the storages for an environment.
text: |
az containerapp env storage list -g MyResourceGroup -n MyEnvironment
"""

helps['containerapp env storage show'] = """
type: command
short-summary: Show the details of a storage.
examples:
- name: Show the details of a storage.
text: |
az containerapp env storage show -g MyResourceGroup --storage-name MyStorageName -n MyEnvironment
"""

helps['containerapp env storage set'] = """
type: command
short-summary: Create or update a storage.
examples:
- name: Create a storage.
text: |
az containerapp env storage set -g MyResourceGroup -n MyEnv --storage-name MyStorageName --access-mode ReadOnly --azure-file-account-key MyAccountKey --azure-file-account-name MyAccountName --azure-file-share-name MyShareName
"""

helps['containerapp env storage remove'] = """
type: command
short-summary: Remove a storage from an environment.
examples:
- name: Remove a storage from a Container Apps environment.
text: |
az containerapp env storage remove -g MyResourceGroup --storage-name MyStorageName -n MyEnvironment
"""


# Identity Commands
helps['containerapp identity'] = """
type: group
Expand Down Expand Up @@ -459,12 +527,18 @@
type: command
short-summary: Configure traffic-splitting for a container app.
examples:
- name: Route 100%% of a container app's traffic to its latest revision.
- name: Route 100% of a container app's traffic to its latest revision.
text: |
az containerapp ingress traffic set -n MyContainerapp -g MyResourceGroup --traffic-weight latest=100
az containerapp ingress traffic set -n MyContainerapp -g MyResourceGroup --revision-weight latest=100
- name: Split a container app's traffic between two revisions.
text: |
az containerapp ingress traffic set -n MyContainerapp -g MyResourceGroup --traffic-weight latest=80 MyRevisionName=20
az containerapp ingress traffic set -n MyContainerapp -g MyResourceGroup --revision-weight latest=80 MyRevisionName=20
- name: Split a container app's traffic between two labels.
text: |
az containerapp ingress traffic set -n MyContainerapp -g MyResourceGroup --label-weight myLabel=80 myLabel2=20
- name: Split a container app's traffic between a label and a revision.
text: |
az containerapp ingress traffic set -n MyContainerapp -g MyResourceGroup --revision-weight latest=80 --label-weight myLabel=20
"""

helps['containerapp ingress traffic show'] = """
Expand Down
7 changes: 7 additions & 0 deletions src/containerapp/azext_containerapp/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,10 @@
"tenantId": None, # str
"subscriptionId": None # str
}

AzureFileProperties = {
"accountName": None,
"accountKey": None,
"accessMode": None,
"shareName": None
}
24 changes: 19 additions & 5 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def load_arguments(self, _):
c.argument('docker_bridge_cidr', options_list=['--docker-bridge-cidr'], help='CIDR notation IP range assigned to the Docker bridge. It must not overlap with any Subnet IP ranges or the IP range defined in Platform Reserved CIDR, if defined')
c.argument('platform_reserved_cidr', options_list=['--platform-reserved-cidr'], help='IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other Subnet IP ranges')
c.argument('platform_reserved_dns_ip', options_list=['--platform-reserved-dns-ip'], help='An IP address from the IP range defined by Platform Reserved CIDR that will be reserved for the internal DNS server.')
c.argument('internal_only', arg_type=get_three_state_flag(), options_list=['--internal-only'], help='Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource, therefore must provide infrastructureSubnetResourceId and appSubnetResourceId if enabling this property')
c.argument('internal_only', arg_type=get_three_state_flag(), options_list=['--internal-only'], help='Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource, therefore must provide infrastructureSubnetResourceId if enabling this property')

with self.argument_context('containerapp env update') as c:
c.argument('name', name_type, help='Name of the Container Apps environment.')
Expand All @@ -147,6 +147,14 @@ def load_arguments(self, _):
with self.argument_context('containerapp env show') as c:
c.argument('name', name_type, help='Name of the Container Apps Environment.')

with self.argument_context('containerapp env storage') as c:
c.argument('name', id_part=None)
c.argument('storage_name', help="Name of the storage.")
c.argument('access_mode', id_part=None, arg_type=get_enum_type(["ReadWrite", "ReadOnly"]), help="Access mode for the AzureFile storage.")
c.argument('azure_file_account_key', options_list=["--azure-file-account-key", "--storage-account-key", "-k"], help="Key of the AzureFile storage account.")
c.argument('azure_file_share_name', options_list=["--azure-file-share-name", "--file-share", "-f"], help="Name of the share on the AzureFile storage.")
c.argument('azure_file_account_name', options_list=["--azure-file-account-name", "--account-name", "-a"], help="Name of the AzureFile storage account.")

with self.argument_context('containerapp identity') as c:
c.argument('user_assigned', nargs='+', help="Space-separated user identities.")
c.argument('system_assigned', help="Boolean indicating whether to assign system-assigned identity.")
Expand All @@ -157,7 +165,7 @@ def load_arguments(self, _):
with self.argument_context('containerapp github-action add') as c:
c.argument('repo_url', help='The GitHub repository to which the workflow file will be added. In the format: https://github.com/<owner>/<repository-name>')
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')
c.argument('branch', options_list=['--branch', '-b'], help='The branch of the GitHub repo. Defaults to "main" if not specified.')
c.argument('branch', options_list=['--branch', '-b'], help='The branch of the Github repo. Assumed to be the Github repo\'s default branch if not specified.')
c.argument('login_with_github', help='Interactively log in with Github to retrieve the Personal Access Token')
c.argument('registry_url', help='The container registry server, e.g. myregistry.azurecr.io')
c.argument('registry_username', help='The username of the registry. If using Azure Container Registry, we will try to infer the credentials if not supplied')
Expand All @@ -179,14 +187,20 @@ def load_arguments(self, _):
c.argument('from_revision', help='Revision to copy from. Default: latest revision.')
c.argument('image', options_list=['--image', '-i'], help="Container image, e.g. publisher/image-name:tag.")

with self.argument_context('containerapp revision label') as c:
c.argument('name', id_part=None)
c.argument('revision', help='Name of the revision.')
c.argument('label', help='Name of the label.')

with self.argument_context('containerapp ingress') as c:
c.argument('allow_insecure', help='Allow insecure connections for ingress traffic.')
c.argument('type', validator=validate_ingress, arg_type=get_enum_type(['internal', 'external']), help="The ingress type.")
c.argument('transport', arg_type=get_enum_type(['auto', 'http', 'http2']), help="The transport protocol used for ingress traffic.")
c.argument('target_port', type=int, validator=validate_target_port, help="The application port used for ingress traffic.")

with self.argument_context('containerapp ingress traffic') as c:
c.argument('traffic_weights', nargs='*', options_list=['--traffic-weight'], help="A list of revision weight(s) for the container app. Space-separated values in 'revision_name=weight' format. For latest revision, use 'latest=weight'")
c.argument('revision_weights', nargs='+', options_list=['--revision-weight', c.deprecate(target='--traffic-weight', redirect='--revision-weight')], help="A list of revision weight(s) for the container app. Space-separated values in 'revision_name=weight' format. For latest revision, use 'latest=weight'")
c.argument('label_weights', nargs='+', options_list=['--label-weight'], help="A list of label weight(s) for the container app. Space-separated values in 'label_name=weight' format.")

with self.argument_context('containerapp secret') as c:
c.argument('secrets', nargs='+', options_list=['--secrets', '-s'], help="A list of secret(s) for the container app. Space-separated values in 'key=value' format (where 'key' cannot be longer than 20 characters).")
Expand Down Expand Up @@ -235,8 +249,8 @@ def load_arguments(self, _):

with self.argument_context('containerapp up', arg_group='Github Repo') 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>')
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 missing (and using --repo), a browser page will be opened to authenticate with Github.')
c.argument('branch', options_list=['--branch', '-b'], help='The branch of the GitHub repo. Defaults to "main"')
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='The branch of the Github repo. Assumed to be the Github repo\'s default branch if not specified.')
c.argument('context_path', help='Path in the repo from which to run the docker build. Defaults to "./". Dockerfile is assumed to be named "Dockerfile" and in this directory.')
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"])
Expand Down
3 changes: 1 addition & 2 deletions src/containerapp/azext_containerapp/_ssh_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
# pylint: disable=logging-fstring-interpolation

import os
import sys
Expand Down Expand Up @@ -50,8 +51,6 @@

class WebSocketConnection:
def __init__(self, cmd, resource_group_name, name, revision, replica, container, startup_command):
from websocket._exceptions import WebSocketBadStatusException

token_response = ContainerAppClient.get_auth_token(cmd, resource_group_name, name)
self._token = token_response["properties"]["token"]
self._logstream_endpoint = token_response["properties"]["logStreamEndpoint"]
Expand Down
Loading