Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions src/containerapp/azext_containerapp/_client_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def _polish_bad_errors(ex):
return _polish_bad_errors


# TODO update for SDK
def handle_raw_exception(e):
import json

Expand Down Expand Up @@ -110,3 +111,7 @@ def cf_namespaces(cli_ctx, *_):

def cf_storages(cli_ctx, *_):
return app_client_factory(cli_ctx).managed_environments_storages


def cf_source_controls(cli_ctx, *_):
return app_client_factory(cli_ctx).container_apps_source_controls
2 changes: 2 additions & 0 deletions src/containerapp/azext_containerapp/_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ def get_replica(cls, cmd, resource_group_name, container_app_name, revision_name
r = send_raw_request(cmd.cli_ctx, "GET", request_url)
return r.json()


# TODO can't remove until SDK support added for authtoken generation
@classmethod
def get_auth_token(cls, cmd, resource_group_name, name):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
Expand Down
4 changes: 2 additions & 2 deletions src/containerapp/azext_containerapp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def trigger_workflow(token, repo, name, branch):


# pylint:disable=unused-argument
def await_github_action(cmd, token, repo, branch, name, resource_group_name, timeout_secs=1200):
def await_github_action(cmd, client, token, repo, branch, name, resource_group_name, timeout_secs=1200):
from .custom import show_github_action
from ._clients import PollingAnimation

Expand All @@ -177,7 +177,7 @@ def await_github_action(cmd, token, repo, branch, name, resource_group_name, tim
while gh_action_status == "InProgress":
time.sleep(SHORT_POLLING_INTERVAL_SECS)
animation.tick()
gh_action_status = safe_get(show_github_action(cmd, name, resource_group_name), "properties", "operationState")
gh_action_status = show_github_action(cmd, client, name, resource_group_name).operation_state
if (datetime.utcnow() - start).seconds >= timeout_secs:
raise CLIInternalError("Timed out while waiting for the Github action to be created.")
animation.flush()
Expand Down
14 changes: 11 additions & 3 deletions src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
# pylint: disable=line-too-long, too-many-statements, bare-except
# from azure.cli.core.commands import CliCommandType
# from msrestazure.tools import is_valid_resource_id, parse_resource_id
from azext_containerapp._client_factory import ex_handler_factory, cf_containerapps, cf_managedenvs, cf_revisions, cf_replicas, cf_dapr_components, cf_certificates, cf_storages
from azext_containerapp._client_factory import (ex_handler_factory,
cf_containerapps,
cf_managedenvs,
cf_revisions,
cf_replicas,
cf_dapr_components,
cf_certificates,
cf_storages,
cf_source_controls)
from ._validators import validate_ssh


Expand Down Expand Up @@ -89,7 +97,7 @@ def load_command_table(self, _):
g.custom_command('remove', 'remove_managed_identity', supports_no_wait=True, exception_handler=ex_handler_factory())
g.custom_show_command('show', 'show_managed_identity')

with self.command_group('containerapp github-action') as g: # TODO: Silas
with self.command_group('containerapp github-action', client_factory=cf_source_controls) as g:
g.custom_command('add', 'create_or_update_github_action', exception_handler=ex_handler_factory())
g.custom_show_command('show', 'show_github_action', exception_handler=ex_handler_factory())
g.custom_command('delete', 'delete_github_action', exception_handler=ex_handler_factory())
Expand All @@ -103,7 +111,7 @@ def load_command_table(self, _):
g.custom_command('copy', 'copy_revision', client_factory=cf_containerapps, supports_no_wait=True, exception_handler=ex_handler_factory())
g.custom_command('set-mode', 'set_revision_mode', client_factory=cf_containerapps, supports_no_wait=True, exception_handler=ex_handler_factory())

with self.command_group('containerapp revision label', client_factory=cf_containerapps) as g: # Tests
with self.command_group('containerapp revision label', client_factory=cf_containerapps) as g: # Tests
g.custom_command('add', 'add_revision_label', supports_no_wait=True)
g.custom_command('remove', 'remove_revision_label')
g.custom_command('swap', 'swap_revision_label')
Expand Down
80 changes: 45 additions & 35 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
MutuallyExclusiveArgumentError)
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.core.commands import LongRunningOperation
from azure.cli.core.util import open_page_in_browser
from azure.cli.core.util import open_page_in_browser, sdk_no_wait
from azure.cli.command_modules.appservice.utils import _normalize_location
from knack.log import get_logger
from knack.prompting import prompt_y_n
Expand Down Expand Up @@ -232,7 +232,7 @@ def create_containerapp(cmd,
for r in assign_user_identities:
r = _ensure_identity_resource_id(subscription_id, resource_group_name, r)
valid_user_ids[r] = UserAssignedIdentity()

identity_def.user_assigned_identities = valid_user_ids

scale_def = None
Expand Down Expand Up @@ -899,7 +899,9 @@ def _validate_github(repo, branch, token):
return branch


# TODO test
def create_or_update_github_action(cmd,
client,
name,
resource_group_name,
repo_url,
Expand All @@ -915,6 +917,8 @@ def create_or_update_github_action(cmd,
service_principal_client_secret=None,
service_principal_tenant_id=None,
no_wait=False):
from azure.mgmt.appcontainers.models import SourceControl, GithubActionConfiguration, AzureCredentials, RegistryInfo

if not token and not login_with_github:
raise_missing_token_suggestion()
elif not token:
Expand All @@ -928,27 +932,26 @@ def create_or_update_github_action(cmd,

branch = _validate_github(repo, branch, token)

source_control_info = None
source_control_info = SourceControl()

try:
source_control_info = GitHubActionClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)

source_control_info = show_github_action(cmd, client, name, resource_group_name)
except Exception as ex:
if not service_principal_client_id or not service_principal_client_secret or not service_principal_tenant_id:
raise RequiredArgumentMissingError('Service principal client ID, secret and tenant ID are required to add github actions for the first time. Please create one using the command \"az ad sp create-for-rbac --name {{name}} --role contributor --scopes /subscriptions/{{subscription}}/resourceGroups/{{resourceGroup}} --sdk-auth\"') from ex
source_control_info = SourceControlModel
source_control_info = SourceControl()

source_control_info["properties"]["repoUrl"] = repo_url
source_control_info["properties"]["branch"] = branch
source_control_info.repo_url = repo_url
source_control_info.branch = branch

azure_credentials = None

if service_principal_client_id or service_principal_client_secret or service_principal_tenant_id:
azure_credentials = AzureCredentialsModel
azure_credentials["clientId"] = service_principal_client_id
azure_credentials["clientSecret"] = service_principal_client_secret
azure_credentials["tenantId"] = service_principal_tenant_id
azure_credentials["subscriptionId"] = get_subscription_id(cmd.cli_ctx)
azure_credentials = AzureCredentials()
azure_credentials.client_id = service_principal_client_id
azure_credentials.client_secret = service_principal_client_secret
azure_credentials.tenant_id = service_principal_tenant_id
azure_credentials.subscription_id = get_subscription_id(cmd.cli_ctx)

# Registry
if registry_username is None or registry_password is None:
Expand All @@ -964,54 +967,58 @@ def create_or_update_github_action(cmd,
except Exception as ex:
raise RequiredArgumentMissingError('Failed to retrieve credentials for container registry. Please provide the registry username and password') from ex

registry_info = RegistryInfoModel
registry_info["registryUrl"] = registry_url
registry_info["registryUserName"] = registry_username
registry_info["registryPassword"] = registry_password
registry_info = RegistryInfo()
registry_info.registry_url = registry_url
registry_info.registry_user_name = registry_username
registry_info.registry_password = registry_password

github_action_configuration = GitHubActionConfiguration
github_action_configuration["registryInfo"] = registry_info
github_action_configuration["azureCredentials"] = azure_credentials
github_action_configuration["contextPath"] = context_path
github_action_configuration["image"] = image
github_action_configuration = GithubActionConfiguration()
github_action_configuration.registry_info = registry_info
github_action_configuration.azure_credentials = azure_credentials
github_action_configuration.context_path = context_path
github_action_configuration.image = image

source_control_info["properties"]["githubActionConfiguration"] = github_action_configuration
source_control_info.github_action_configuration = github_action_configuration

headers = ["x-ms-github-auxiliary={}".format(token)]
headers = {"x-ms-github-auxiliary": token}

try:
logger.warning("Creating Github action...")
r = GitHubActionClient.create_or_update(cmd=cmd, resource_group_name=resource_group_name, name=name, github_action_envelope=source_control_info, headers=headers, no_wait=no_wait)
r = client.begin_create_or_update(resource_group_name=resource_group_name, container_app_name=name,
source_control_envelope=source_control_info, source_control_name="current", headers=headers)
if not no_wait:
await_github_action(cmd, token, repo, branch, name, resource_group_name)
await_github_action(cmd, client, token, repo, branch, name, resource_group_name)
return r
except Exception as e:
handle_raw_exception(e)


def show_github_action(cmd, name, resource_group_name):
# TODO this output is slightly different. We should label this a breaking change
def show_github_action(cmd, client, name, resource_group_name):
try:
return GitHubActionClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)
return client.get(resource_group_name=resource_group_name,
container_app_name=name,
source_control_name="current")
except Exception as e:
handle_raw_exception(e)


def delete_github_action(cmd, name, resource_group_name, token=None, login_with_github=False):
def delete_github_action(cmd, client, name, resource_group_name, token=None, login_with_github=False):
# Check if there is an existing source control to delete
try:
github_action_config = GitHubActionClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)
github_action_config = show_github_action(cmd, client, name, resource_group_name)
except Exception as e:
handle_raw_exception(e)

repo_url = github_action_config["properties"]["repoUrl"]
repo_url = github_action_config.repo_url

if not token and not login_with_github:
raise_missing_token_suggestion()
elif not token:
scopes = ["admin:repo_hook", "repo", "workflow"]
token = get_github_access_token(cmd, scopes)
elif token and login_with_github:
logger.warning("Both token and --login-with-github flag are provided. Will use provided token")
logger.warning("Both --token and --login-with-github flag are provided. Will use provided token")

# Check if PAT can access repo
try:
Expand Down Expand Up @@ -1046,10 +1053,11 @@ def delete_github_action(cmd, name, resource_group_name, token=None, login_with_
# If exception due to github package missing, etc just continue without validating the repo and rely on api validation
pass

headers = ["x-ms-github-auxiliary={}".format(token)]
headers = {"x-ms-github-auxiliary": token}

try:
return GitHubActionClient.delete(cmd=cmd, resource_group_name=resource_group_name, name=name, headers=headers)
return client.begin_delete(resource_group_name=resource_group_name,
container_app_name=name, source_control_name="current", headers=headers)
except Exception as e:
handle_raw_exception(e)

Expand Down Expand Up @@ -1952,6 +1960,7 @@ def get_replica(cmd, client, resource_group_name, name, replica, revision=None):
return client.get_replica(container_app_name=name, resource_group_name=resource_group_name, revision_name=revision, replica_name=replica)


# TODO use SDK when authtoken support is added
def containerapp_ssh(cmd, resource_group_name, name, container=None, revision=None, replica=None, startup_command="sh"):
if isinstance(startup_command, list):
startup_command = startup_command[0] # CLI seems a little buggy when calling a param "--command"
Expand Down Expand Up @@ -1985,6 +1994,7 @@ def stream_containerapp_logs(cmd, resource_group_name, name, container=None, rev
raise ValidationError("--tail must be between 0 and 300.")

sub = get_subscription_id(cmd.cli_ctx)
# TODO use SDK when authtoken support is added
token_response = ContainerAppClient.get_auth_token(cmd, resource_group_name, name)
token = token_response["properties"]["token"]
logstream_endpoint = token_response["properties"]["logStreamEndpoint"]
Expand Down Expand Up @@ -2135,7 +2145,7 @@ def both_match(c):
else:
certificate_name = certificate
try:

r = client.get(resource_group_name=resource_group_name, environment_name=name, certificate_name=certificate_name)
return [r] if both_match(r) else []
except Exception as e:
Expand Down