Skip to content

Commit

Permalink
add containerapp support for service connector (Azure#22290)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChenTanyi authored May 7, 2022
1 parent 4223f08 commit cfeaead
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ def add_source_resource_block(context, source, enable_id=True, validate_source_i
context.argument('enable_csi', options_list=['--enable-csi'], arg_type=get_three_state_flag(),
help="Use keyvault as a secrets store via a CSI volume. "
"If specified, AuthType Arguments are not needed.")
elif source == RESOURCE.ContainerApp:
context.argument('scope', options_list=['-c', '--container'], type=str,
help="The container where the connection information "
"will be saved (as environment variables).")
context.ignore('enable_csi')
else:
context.ignore('scope')
context.ignore('enable_csi')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class RESOURCE(Enum):
WebApp = 'webapp'
SpringCloud = 'spring-cloud'
KubernetesCluster = 'aks'
ContainerApp = 'containerapp'
CosmosCassandra = 'cosmos-cassandra'
CosmosGremlin = 'cosmos-gremlin'
CosmosMongo = 'cosmos-mongo'
Expand Down Expand Up @@ -69,7 +70,7 @@ class CLIENT_TYPE(Enum):


# The source resources released as CLI extensions
SOURCE_RESOURCES_IN_EXTENSION = [RESOURCE.SpringCloud]
SOURCE_RESOURCES_IN_EXTENSION = [RESOURCE.SpringCloud, RESOURCE.ContainerApp]

# The source resources using user token
SOURCE_RESOURCES_USERTOKEN = [RESOURCE.KubernetesCluster]
Expand All @@ -82,6 +83,7 @@ class CLIENT_TYPE(Enum):
RESOURCE.WebApp: '/subscriptions/{subscription}/resourceGroups/{source_resource_group}/providers/Microsoft.Web/sites/{site}',
RESOURCE.SpringCloud: '/subscriptions/{subscription}/resourceGroups/{source_resource_group}/providers/Microsoft.AppPlatform/Spring/{spring}/apps/{app}/deployments/{deployment}',
# RESOURCE.KubernetesCluster: '/subscriptions/{subscription}/resourceGroups/{source_resource_group}/providers/Microsoft.ContainerService/managedClusters/{cluster}',
RESOURCE.ContainerApp: '/subscriptions/{subscription}/resourceGroups/{source_resource_group}/providers/Microsoft.App/containerApps/{app}'
}


Expand Down Expand Up @@ -165,6 +167,18 @@ class CLIENT_TYPE(Enum):
'help': 'Name of the managed cluster',
'placeholder': 'MyCluster'
}
},
RESOURCE.ContainerApp: {
'source_resource_group': {
'options': ['--resource-group', '-g'],
'help': 'The resource group which contains the container app',
'placeholder': 'ContainerAppRG'
},
'app': {
'options': ['--name', '-n'],
'help': 'Name of the container app',
'placeholder': 'MyContainerApp'
}
}
}

Expand Down Expand Up @@ -651,6 +665,34 @@ class CLIENT_TYPE(Enum):
RESOURCE.WebPubSub: [AUTH_TYPE.SecretAuto, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.ConfluentKafka: [AUTH_TYPE.Secret],
},
RESOURCE.ContainerApp: {
RESOURCE.Postgres: [AUTH_TYPE.Secret],
RESOURCE.PostgresFlexible: [AUTH_TYPE.Secret],
RESOURCE.Mysql: [AUTH_TYPE.Secret],
RESOURCE.MysqlFlexible: [AUTH_TYPE.Secret],
RESOURCE.Sql: [AUTH_TYPE.Secret],
RESOURCE.Redis: [AUTH_TYPE.SecretAuto],
RESOURCE.RedisEnterprise: [AUTH_TYPE.SecretAuto],

RESOURCE.CosmosCassandra: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.CosmosGremlin: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.CosmosMongo: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.CosmosTable: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.CosmosSql: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],

RESOURCE.StorageBlob: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.StorageQueue: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.StorageFile: [AUTH_TYPE.SecretAuto],
RESOURCE.StorageTable: [AUTH_TYPE.SecretAuto],

RESOURCE.KeyVault: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.AppConfig: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.EventHub: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.ServiceBus: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.SignalR: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.WebPubSub: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
RESOURCE.ConfluentKafka: [AUTH_TYPE.Secret],
},
}


Expand Down Expand Up @@ -988,3 +1030,4 @@ class CLIENT_TYPE(Enum):
}
}
SUPPORTED_CLIENT_TYPE[RESOURCE.KubernetesCluster] = SUPPORTED_CLIENT_TYPE[RESOURCE.WebApp]
SUPPORTED_CLIENT_TYPE[RESOURCE.ContainerApp] = SUPPORTED_CLIENT_TYPE[RESOURCE.WebApp]
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# --------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

import unittest
from azure.cli.core.commands.client_factory import get_subscription_id
from azure.cli.testsdk import (
ScenarioTest,
record_only
)
from azure.cli.command_modules.serviceconnector._resource_config import (
RESOURCE,
SOURCE_RESOURCES,
TARGET_RESOURCES
)
from ._test_utils import CredentialReplacer


@unittest.skip('Need containerapp extension installed')
class ContainerAppConnectionScenarioTest(ScenarioTest):
default_container_name = 'simple-hello-world-container'

def __init__(self, method_name):
super(ContainerAppConnectionScenarioTest, self).__init__(
method_name,
recording_processors=[CredentialReplacer()]
)


# @record_only()
def test_containerapp_mysql_e2e(self):
self.kwargs.update({
'subscription': get_subscription_id(self.cli_ctx),
'source_resource_group': 'servicelinker-test-linux-group',
'target_resource_group': 'servicelinker-test-linux-group',
'app': 'servicelinker-mysql-aca',
'server': 'servicelinker-mysql',
'database': 'mysqlDB'
})

# prepare password
user = 'servicelinker'
password = self.cmd('keyvault secret show --vault-name cupertino-kv-test -n TestDbPassword')\
.get_output_in_json().get('value')

# prepare params
name = 'testconn'
source_id = SOURCE_RESOURCES.get(RESOURCE.ContainerApp).format(**self.kwargs)
target_id = TARGET_RESOURCES.get(RESOURCE.Mysql).format(**self.kwargs)

# create connection, test clientType=None
self.cmd('containerapp connection create mysql --connection {} --source-id {} --target-id {} '
'--secret name={} secret={} --client-type none -c {}'.format(name, source_id, target_id, user, password, self.default_container_name))

# list connection
connections = self.cmd(
'containerapp connection list --source-id {}'.format(source_id),
checks = [
self.check('length(@)', 1),
self.check('[0].authInfo.authType', 'secret'),
self.check('[0].clientType', 'none')
]
).get_output_in_json()
connection_id = connections[0].get('id')

# update connection
self.cmd('containerapp connection update mysql --id {} --client-type dotnet '
'--secret name={} secret={}'.format(connection_id, user, password),
checks = [ self.check('clientType', 'dotnet') ])

# list configuration
self.cmd('containerapp connection list-configuration --id {}'.format(connection_id))

# validate connection
self.cmd('containerapp connection validate --id {}'.format(connection_id))

# show connection
self.cmd('containerapp connection show --id {}'.format(connection_id))

# delete connection
self.cmd('containerapp connection delete --id {} --yes'.format(connection_id))


# @record_only()
def test_containerapp_storageblob_e2e(self):
self.kwargs.update({
'subscription': get_subscription_id(self.cli_ctx),
'source_resource_group': 'servicelinker-test-linux-group',
'target_resource_group': 'servicelinker-test-linux-group',
'app': 'servicelinker-storage-aca',
'account': 'servicelinkerstorage'
})

# prepare params
name = 'testconn'
source_id = SOURCE_RESOURCES.get(RESOURCE.ContainerApp).format(**self.kwargs)
target_id = TARGET_RESOURCES.get(RESOURCE.StorageBlob).format(**self.kwargs)

# create connection
self.cmd('containerapp connection create storage-blob --connection {} --source-id {} --target-id {} '
'--system-identity --client-type python -c {}'.format(name, source_id, target_id, self.default_container_name))

# list connection
connections = self.cmd(
'containerapp connection list --source-id {}'.format(source_id),
checks = [
self.check('length(@)', 1),
self.check('[0].authInfo.authType', 'systemAssignedIdentity'),
self.check('[0].clientType', 'python')
]
).get_output_in_json()
connection_id = connections[0].get('id')

# update connection
self.cmd('containerapp connection update storage-blob --id {} --client-type dotnet'.format(connection_id),
checks = [ self.check('clientType', 'dotnet') ])

# list configuration
self.cmd('containerapp connection list-configuration --id {}'.format(connection_id))

# validate connection
self.cmd('containerapp connection validate --id {}'.format(connection_id))

# show connection
self.cmd('containerapp connection show --id {}'.format(connection_id))

# delete connection
self.cmd('containerapp connection delete --id {} --yes'.format(connection_id))

0 comments on commit cfeaead

Please sign in to comment.