Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Release History
===============
upcoming
++++++
* Add support for binding managed MySQL Flexible server to a containerapp
* Removed preview tag for some command groups and params (e.g. 'az containerapp job', 'az containerapp env storage', 'az containerapp env workload-profile')
* 'az containerapp env': --enable-workload-profiles allowed values:true, false
* 'az containerapp auth': support --token-store, --sas-url-secret, --sas-url-secret-name, --yes
Expand Down
54 changes: 51 additions & 3 deletions src/containerapp/azext_containerapp/_managed_service_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def build_cosmosdb_service_connector_def(subscription_id, resource_group_name, s
return {"linker_name": binding_name, "parameters": parameters, "resource_id": resource_id}


class ManagedPostgreSQLUtils:
class ManagedPostgreSQLFlexibleUtils:

@staticmethod
def build_postgres_resource_id(subscription_id, resource_group_name, service_name, arg_dict):
Expand Down Expand Up @@ -126,8 +126,56 @@ def build_postgresql_service_connector_def(subscription_id, resource_group_name,
if not all(key in arg_dict for key in ["database", "username", "password"]):
raise ValidationError(
"Managed PostgreSQL Flexible Server needs the database, username, and password arguments.")
resource_id = ManagedPostgreSQLUtils.build_postgres_resource_id(subscription_id, resource_group_name,
resource_id = ManagedPostgreSQLFlexibleUtils.build_postgres_resource_id(subscription_id, resource_group_name,
service_name, arg_dict)
parameters = ManagedPostgreSQLFlexibleUtils.build_postgres_params(resource_id, name, arg_dict["username"],
arg_dict["password"], key_vault_id=None)
return {"linker_name": binding_name, "parameters": parameters, "resource_id": resource_id}


class ManagedMySQLFlexibleUtils:
Copy link
Copy Markdown
Contributor

@Greedygre Greedygre Jun 16, 2023

Choose a reason for hiding this comment

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

Please add tests for command
az containerapp create/update --bind to test the MySQL service binding.
Otherwise LGTM.

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.

test function added, thank you.


@staticmethod
def build_mysql_resource_id(subscription_id, resource_group_name, service_name, arg_dict):
url_fmt = "/subscriptions/{}/resourceGroups/{}/providers/Microsoft.DBforMySQL/flexibleServers/" \
"{}/databases/{}"
resource_id = url_fmt.format(
subscription_id,
resource_group_name,
service_name,
arg_dict["database"])
return resource_id

@staticmethod
def build_mysql_params(resource_id, capp_name, username, password, key_vault_id):
parameters = {
'target_service': {
"type": "AzureResource",
"id": resource_id
},
'auth_info': {
'authType': "secret",
'secret_info': {
'secret_type': "rawValue",
'value': password,
},
'name': username
},
'secret_store': {
'key_vault_id': key_vault_id,
},
'scope': capp_name,
}
return parameters

@staticmethod
def build_mysql_service_connector_def(subscription_id, resource_group_name, service_name, arg_dict, name,
binding_name):
if not all(key in arg_dict for key in ["database", "username", "password"]):
raise ValidationError(
"Managed MySQL Flexible Server needs the database, username, and password arguments.")
resource_id = ManagedMySQLFlexibleUtils.build_mysql_resource_id(subscription_id, resource_group_name,
service_name, arg_dict)
parameters = ManagedPostgreSQLUtils.build_postgres_params(resource_id, name, arg_dict["username"],
parameters = ManagedMySQLFlexibleUtils.build_mysql_params(resource_id, name, arg_dict["username"],
arg_dict["password"], key_vault_id=None)
return {"linker_name": binding_name, "parameters": parameters, "resource_id": resource_id}
13 changes: 9 additions & 4 deletions src/containerapp/azext_containerapp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
ManagedCertificateEnvelop as ManagedCertificateEnvelopModel,
ServiceConnector as ServiceConnectorModel)
from ._models import OryxMarinerRunImgTagProperty
from ._managed_service_utils import ManagedRedisUtils, ManagedCosmosDBUtils, ManagedPostgreSQLUtils
from ._managed_service_utils import ManagedRedisUtils, ManagedCosmosDBUtils, ManagedPostgreSQLFlexibleUtils, ManagedMySQLFlexibleUtils


class AppType(Enum):
Expand Down Expand Up @@ -439,9 +439,14 @@ def process_service(cmd, resource_list, service_name, arg_dict, subscription_id,
name, binding_name))
elif service["type"] == "Microsoft.DBforPostgreSQL/flexibleServers":
service_connector_def_list.append(
ManagedPostgreSQLUtils.build_postgresql_service_connector_def(subscription_id, resource_group_name,
service_name, arg_dict,
name, binding_name))
ManagedPostgreSQLFlexibleUtils.build_postgresql_service_connector_def(subscription_id, resource_group_name,
service_name, arg_dict,
name, binding_name))
elif service["type"] == "Microsoft.DBforMySQL/flexibleServers":
service_connector_def_list.append(
ManagedMySQLFlexibleUtils.build_mysql_service_connector_def(subscription_id, resource_group_name,
service_name, arg_dict,
name, binding_name))
elif service["type"] == "Microsoft.App/containerApps":
containerapp_def = ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name,
name=service_name)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import json
import os
import time
import unittest
Expand Down Expand Up @@ -768,6 +769,44 @@ def test_containerapp_dev_service_binding_e2e(self, resource_group):
JMESPathCheck('length(@)', 0),
])

@AllowLargeResponse(8192)
Comment thread
scrappywyrm marked this conversation as resolved.
@ResourceGroupPreparer(location="eastus2")
def test_containerapp_managed_service_binding_e2e(self, resource_group):
self.cmd('configure --defaults location={}'.format(TEST_LOCATION))

env_name = self.create_random_name(prefix='containerapp-env', length=24)
ca_name = self.create_random_name(prefix='containerapp', length=24)
mysqlserver = "mysqlflexsb"
postgresqlserver = "postgresqlflexsb"

mysqlflex_json= self.cmd('mysql flexible-server create --resource-group {} --name {} --public-access {} -y'.format(resource_group, mysqlserver, "None")).output
postgresqlflex_json= self.cmd('postgres flexible-server create --resource-group {} --name {} --public-access {} -y'.format(resource_group, postgresqlserver, "None")).output
mysqlflex_dict = json.loads(mysqlflex_json)

mysqlusername = mysqlflex_dict['username']
mysqlpassword = mysqlflex_dict['password']

mysqldb = mysqlflex_dict['databaseName']
flex_binding="mysqlflex_binding"
postgresqlflex_dict = json.loads(postgresqlflex_json)
postgresqlusername = postgresqlflex_dict['username']
postgresqlpassword = postgresqlflex_dict['password']
postgresqldb = postgresqlflex_dict['databaseName']
create_containerapp_env(self, env_name, resource_group)

self.cmd('containerapp create -g {} -n {} --environment {} --bind {}:{},database={},username={},password={}'.format(
resource_group, ca_name, env_name, mysqlserver, flex_binding, mysqldb , mysqlusername, mysqlpassword))
self.cmd('containerapp show -g {} -n {}'.format(resource_group, ca_name), checks=[
JMESPathCheck('length(properties.template.containers[0].env[?name==`AZURE_MYSQL_HOST`])', 1)
])

self.cmd('containerapp update -g {} -n {} --bind {},database={},username={},password={}'.format(
resource_group, ca_name, postgresqlserver, postgresqldb , postgresqlusername, postgresqlpassword))
self.cmd('containerapp show -g {} -n {}'.format(resource_group, ca_name), checks=[
JMESPathCheck('length(properties.template.containers[0].env[?name==`AZURE_MYSQL_HOST`])', 1),
JMESPathCheck('length(properties.template.containers[0].env[?name==`AZURE_POSTGRESQL_HOST`])', 1)
])


class ContainerappEnvStorageTests(ScenarioTest):
@AllowLargeResponse(8192)
Expand Down