Skip to content
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

Eject feature final draft #143

Open
wants to merge 9 commits into
base: containerapp-0.3.10
Choose a base branch
from
18 changes: 18 additions & 0 deletions src/containerapp/azext_containerapp/_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,24 @@ def list(cls, cmd, resource_group_name, environment_name, formatter=lambda x: x)

return app_list

@classmethod
def list_secrets(cls, cmd, resource_group_name, name, component_name):

management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
api_version = STABLE_API_VERSION
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/managedEnvironments/{}/daprComponents/{}/listSecrets?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
component_name,
api_version
)

r = send_raw_request(cmd.cli_ctx, "POST", request_url, body=None)
return r.json()

class StorageClient():
@classmethod
Expand Down
7 changes: 7 additions & 0 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,10 @@ def load_arguments(self, _):

with self.argument_context('containerapp hostname delete') as c:
c.argument('hostname', help='The custom domain name.')

with self.argument_context('containerapp eject') as c:
c.argument('ejected_subscription', help='The name of the subscription to eject into')
c.argument('ejected_resource_group', help='The name of the resource group to eject into')
c.argument('ejected_cluster', help='The name of some existing cluster to eject into')
c.argument('deploy', options_list=['--deploy'], help='Set this flag to deploy your apps in this environment', action='store_true')

6 changes: 5 additions & 1 deletion src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def transform_revision_output(rev):
def transform_revision_list_output(revs):
return [transform_revision_output(r) for r in revs]


def load_command_table(self, _):
with self.command_group('containerapp') as g:
g.custom_show_command('show', 'show_containerapp', table_transformer=transform_containerapp_output)
Expand Down Expand Up @@ -174,3 +173,8 @@ def load_command_table(self, _):
g.custom_command('bind', 'bind_hostname', exception_handler=ex_handler_factory())
g.custom_command('list', 'list_hostname')
g.custom_command('delete', 'delete_hostname', confirmation=True, exception_handler=ex_handler_factory())

with self.command_group('containerapp eject') as g:
g.custom_command('environment', 'eject_environment')
g.custom_command('app', 'eject_app')

106 changes: 104 additions & 2 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# --------------------------------------------------------------------------------------------
# pylint: disable=line-too-long, consider-using-f-string, logging-format-interpolation, inconsistent-return-statements, broad-except, bare-except, too-many-statements, too-many-locals, too-many-boolean-expressions, too-many-branches, too-many-nested-blocks, pointless-statement, expression-not-assigned, unbalanced-tuple-unpacking

import json
import threading
import sys
import time
Expand All @@ -27,6 +28,7 @@

from msrestazure.tools import parse_resource_id, is_valid_resource_id
from msrest.exceptions import DeserializationError
import yaml

from ._client_factory import handle_raw_exception
from ._clients import ManagedEnvironmentClient, ContainerAppClient, GitHubActionClient, DaprComponentClient, StorageClient, AuthClient
Expand Down Expand Up @@ -73,6 +75,8 @@
GOOGLE_SECRET_SETTING_NAME, TWITTER_SECRET_SETTING_NAME, APPLE_SECRET_SETTING_NAME, CONTAINER_APPS_RP,
NAME_INVALID, NAME_ALREADY_EXISTS, ACR_IMAGE_SUFFIX)

from .eject import (_meet_system_requirements, _convert_deploy_app, _configure_aks_cluster, _convert_deploy_dapr_component)

logger = get_logger(__name__)


Expand Down Expand Up @@ -821,10 +825,8 @@ def show_containerapp(cmd, name, resource_group_name):
except CLIError as e:
handle_raw_exception(e)


def list_containerapp(cmd, resource_group_name=None, managed_env=None):
_validate_subscription_registered(cmd, CONTAINER_APPS_RP)

try:
containerapps = []
if resource_group_name is None:
Expand Down Expand Up @@ -3387,3 +3389,103 @@ def show_auth_config(cmd, resource_group_name, name):
except:
pass
return auth_settings

# name is the name of the container app environment
def eject_environment(cmd, resource_group_name, name, ejected_subscription=None, ejected_resource_group=None, ejected_cluster=None, deploy=False):
print(f"Ejecting the environment {name}")

if not _meet_system_requirements():
return

dapr_components = list_dapr_components(cmd, resource_group_name, name)
dapr_component_secrets_pairs = []
for component in dapr_components:
try:
secrets = DaprComponentClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name, component_name=component["name"])["value"]
except Exception:
secrets = []
dapr_component_secrets_pairs.append((component, secrets))

apps_json = list_containerapp(cmd, resource_group_name=resource_group_name, managed_env=name)
app_secrets_pairs = []
for app in apps_json:
app_name = app["name"]
app_secrets_pairs.append((app, list_secrets(cmd, app_name, resource_group_name, True)))

current_sub_id = get_subscription_id(cmd.cli_ctx)
if ejected_subscription is not None:
print("Setting the current subscription to the subscription to be ejected into")
cmd.cli_ctx.data['subscription_id'] = ejected_subscription

# eject into a new AKS cluster named [NAME]-copy into the same resource group
configured = _configure_aks_cluster(
cmd,
ejected_resource_group if ejected_resource_group else resource_group_name,
ejected_cluster if ejected_cluster else name+"-copy",
ejected_cluster==None,
)
if not configured:
print("Cluster wasn't configured properly")
return

for component, secrets in dapr_component_secrets_pairs:
_convert_deploy_dapr_component(component, secrets, deploy)

for app, secrets in app_secrets_pairs:
_convert_deploy_app(
cmd,
app,
app["name"],
secrets,
deploy,
)

cmd.cli_ctx.data['subscription_id'] = current_sub_id

def eject_app(cmd, resource_group_name, name, ejected_subscription=None, ejected_resource_group=None, ejected_cluster=None, deploy=False):
print(f"Ejecting the app {name}")

if _meet_system_requirements() == False:
return False

app_json = show_containerapp(cmd, resource_group_name=resource_group_name, name=name)
app_secrets = list_secrets(cmd, name, resource_group_name, True)

env_name = app_json["properties"]["managedEnvironmentId"].split("/")[-1]

dapr_components = list_dapr_components(cmd, resource_group_name, env_name)
dapr_component_secrets_pairs = []
for component in dapr_components:
if "scopes" not in component["properties"] or name in component["properties"]["scopes"]:
try:
secrets = DaprComponentClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name, component_name=component["name"])["value"]
except Exception:
secrets = []
dapr_component_secrets_pairs.append((component, secrets))

current_sub_id = get_subscription_id(cmd.cli_ctx)
if ejected_subscription is not None:
print("Setting the current subscription to the subscription to be ejected into")
cmd.cli_ctx.data['subscription_id'] = ejected_subscription

configured = _configure_aks_cluster(
cmd,
ejected_resource_group if ejected_resource_group else resource_group_name,
ejected_cluster if ejected_cluster else name+"-copy",
ejected_cluster==None,
)
if not configured:
return

for component, secret in dapr_component_secrets_pairs:
_convert_deploy_dapr_component(component, secret, deploy)

_convert_deploy_app(
cmd,
app_json,
name,
app_secrets,
deploy,
)

cmd.cli_ctx.data['subscription_id'] = current_sub_id
Loading