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

[Service Connector] az webapp connection create fabric-sql: Support Fabric SQL as target #29822

Merged
merged 17 commits into from
Oct 24, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ def is_passwordless_command(cmd, auth_info):
target_type = get_target_resource_name(cmd)
if source_type not in {RESOURCE.WebApp, RESOURCE.ContainerApp, RESOURCE.SpringCloud, RESOURCE.SpringCloudDeprecated, RESOURCE.FunctionApp, RESOURCE.Local}:
return False
if target_type not in {RESOURCE.Sql, RESOURCE.Postgres, RESOURCE.PostgresFlexible, RESOURCE.MysqlFlexible}:
if target_type not in {RESOURCE.Sql, RESOURCE.Postgres, RESOURCE.PostgresFlexible, RESOURCE.MysqlFlexible, RESOURCE.FabricSql}:
return False
return True
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
)
from ._addon_factory import AddonFactory
from knack.arguments import CLIArgumentType
from .action import AddCustomizedKeys
from .action import AddCustomizedKeys, AddAdditionalConnectionStringProperties


def add_source_resource_block(context, source, enable_id=True):
Expand Down Expand Up @@ -172,6 +172,13 @@ def add_customized_keys_argument(context):
'name, value is the customized name.')


def add_connstr_props_argument(context):
# linter: length '--additional-connection-string-properties' longer than 22, so use abbreviation
context.argument('connstr_props', options_list=['--connstr-props'],
action=AddAdditionalConnectionStringProperties, nargs='*',
help='The addtional connection string properties used to for building connection string.')


def add_target_type_argument(context, source):
TARGET_TYPES = [
elem.value for elem in SUPPORTED_AUTH_TYPE.get(source).keys()]
Expand Down Expand Up @@ -301,6 +308,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
add_connection_string_argument(c, source, target)
add_customized_keys_argument(c)
add_opt_out_argument(c)
add_connstr_props_argument(c)
with self.argument_context('{} connection update {}'.format(source.value, target.value)) as c:
add_client_type_argument(c, source, target)
add_connection_name_argument(c, source)
Expand All @@ -312,6 +320,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
add_connection_string_argument(c, source, target)
add_customized_keys_argument(c)
add_opt_out_argument(c)
add_connstr_props_argument(c)

# special target resource: independent implementation
target = RESOURCE.ConfluentKafka
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class RESOURCE(Enum):
SignalR = 'signalr'
WebPubSub = 'webpubsub'
ConfluentKafka = 'confluent-cloud'
FabricSql = 'fabric-sql'
AppInsights = 'app-insights'
CognitiveServices = 'cognitiveservices'

Expand Down Expand Up @@ -152,7 +153,9 @@ class CLIENT_TYPE(Enum):

RESOURCE.CognitiveServices: '/subscriptions/{subscription}/resourceGroups/{target_resource_group}/providers/Microsoft.CognitiveServices/accounts/{account}',

RESOURCE.ContainerApp: '/subscriptions/{subscription}/resourceGroups/{target_resource_group}/providers/Microsoft.App/containerApps/{target_app_name}'
RESOURCE.ContainerApp: '/subscriptions/{subscription}/resourceGroups/{target_resource_group}/providers/Microsoft.App/containerApps/{target_app_name}',

RESOURCE.FabricSql: 'https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/SqlDatabases/{sql_id}'
}


Expand Down Expand Up @@ -665,6 +668,8 @@ class CLIENT_TYPE(Enum):
'help': 'Name of the target container app',
'placeholder': 'MyTargetContainerApp'
}
},
RESOURCE.FabricSql: {
}
}

Expand Down Expand Up @@ -826,6 +831,7 @@ class CLIENT_TYPE(Enum):
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],
RESOURCE.FabricSql: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity],
RESOURCE.AppInsights: [AUTH_TYPE.SecretAuto],

RESOURCE.CognitiveServices: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret]
Expand Down Expand Up @@ -857,6 +863,7 @@ class CLIENT_TYPE(Enum):
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],
RESOURCE.FabricSql: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity],
RESOURCE.AppInsights: [AUTH_TYPE.SecretAuto],

RESOURCE.CognitiveServices: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret]
Expand Down Expand Up @@ -919,6 +926,7 @@ class CLIENT_TYPE(Enum):
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],
RESOURCE.FabricSql: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.UserIdentity],
RESOURCE.AppInsights: [AUTH_TYPE.SecretAuto],

RESOURCE.CognitiveServices: [AUTH_TYPE.SystemIdentity, AUTH_TYPE.SecretAuto, AUTH_TYPE.UserIdentity, AUTH_TYPE.ServicePrincipalSecret],
Expand Down Expand Up @@ -1162,6 +1170,14 @@ class CLIENT_TYPE(Enum):
CLIENT_TYPE.SpringBoot,
CLIENT_TYPE.Blank
],
RESOURCE.FabricSql: [
CLIENT_TYPE.Dotnet,
CLIENT_TYPE.Java,
CLIENT_TYPE.Python,
CLIENT_TYPE.Go,
CLIENT_TYPE.Php,
CLIENT_TYPE.Blank
],
RESOURCE.AppInsights: [
CLIENT_TYPE.Dotnet,
CLIENT_TYPE.DotnetInternal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,11 @@ def validate_target_resource_id(cmd, namespace):
'''Validate resource id of a target resource
'''
if getattr(namespace, 'target_id', None):
if not is_valid_resource_id(namespace.target_id):
target = get_target_resource_name(cmd)
if not (target == RESOURCE.FabricSql) and not is_valid_resource_id(namespace.target_id):
e = InvalidArgumentValueError('Resource id is invalid: {}'.format(namespace.target_id))
telemetry.set_exception(e, 'target-id-invalid')
raise e

target = get_target_resource_name(cmd)
pattern = TARGET_RESOURCES.get(target)
matched = re.match(get_resource_regex(pattern), namespace.target_id, re.IGNORECASE)
if matched:
Expand Down Expand Up @@ -946,8 +945,14 @@ def _validate_and_apply(validate, apply):
def validate_service_state(linker_parameters):
'''Validate whether user provided params are applicable to service state
'''
target_type = None
target_id = linker_parameters.get('target_service', dict()).get('id')
target_type = linker_parameters.get('target_service', dict()).get('type')

# AzureResource and other types (e.g., FabricResource, SelfHostedResource)
if target_type == "AzureResource":
target_id = linker_parameters.get('target_service', dict()).get('id')
else :
target_id = linker_parameters.get('target_service', dict()).get('endpoint')

for target, resource_id in TARGET_RESOURCES.items():
matched = re.match(get_resource_regex(resource_id), target_id, re.IGNORECASE)
if matched:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ def get_action(self, values, option_string): # pylint: disable=no-self-use
raise ValidationError('Usage error: {} [KEY=VALUE ...]'.format(option_string))


class AddAdditionalConnectionStringProperties(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
action = self.get_action(values, option_string)
namespace.connstr_props = action

def get_action(self, values, option_string): # pylint: disable=no-self-use
try:
properties = defaultdict(list)
for (k, v) in (x.split('=', 1) for x in values):
properties[k] = v
properties = dict(properties)
print(properties.keys(), properties.values(), properties.items())
brycechen1849 marked this conversation as resolved.
Show resolved Hide resolved
return properties
except ValueError:
raise ValidationError('Usage error: {} [KEY=VALUE ...]'.format(option_string))


def is_k8s_source(command_name):
source_name = command_name.split(' ')[0]
return source_name.lower() == "aks"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ def connection_create(cmd, client, # pylint: disable=too-many-locals,too-many-s
signalr=None, # Resource.SignalR
appinsights=None, # Resource.AppInsights
target_app_name=None, # Resource.ContainerApp
connstr_props=None, # Resource.FabricSql
):
auth_action = 'optOutAllAuth' if (opt_out_list is not None and
OPT_OUT_OPTION.AUTHENTICATION.value in opt_out_list) else None
Expand Down Expand Up @@ -341,7 +342,8 @@ def connection_create(cmd, client, # pylint: disable=too-many-locals,too-many-s
cluster, scope, enable_csi,
customized_keys=customized_keys,
opt_out_list=opt_out_list,
app_config_id=app_config_id)
app_config_id=app_config_id,
connstr_props=connstr_props)
raise CLIInternalError("Fail to install `serviceconnector-passwordless` extension. Please manually install it"
" with `az extension add --name serviceconnector-passwordless --upgrade`"
" and rerun the command")
Expand All @@ -360,8 +362,8 @@ def connection_create(cmd, client, # pylint: disable=too-many-locals,too-many-s
cluster, scope, enable_csi,
customized_keys=customized_keys,
opt_out_list=opt_out_list,
app_config_id=app_config_id
)
app_config_id=app_config_id,
connstr_props=connstr_props)


# The function is used in extension, new feature must be added in the end for backward compatibility
Expand Down Expand Up @@ -394,6 +396,7 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m
opt_out_list=None,
app_config_id=None,
target_app_name=None, # Resource.ContainerApp
connstr_props=None, # Resource.FabricSql
**kwargs,
):
if not source_id:
Expand All @@ -419,12 +422,20 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m

public_network_action = 'optOut' if (opt_out_list is not None and
OPT_OUT_OPTION.PUBLIC_NETWORK.value in opt_out_list) else None

parameters = {
'target_service': {

if target_type == RESOURCE.FabricSql:
targetService = {
"type": "FabricPlatform",
"endpoint": target_id
}
else:
targetService = {
"type": "AzureResource",
"id": target_id
},
}

parameters = {
'target_service': targetService,
'auth_info': auth_info,
'secret_store': {
'key_vault_id': key_vault_id,
Expand All @@ -436,6 +447,7 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m
'configurationStore': {
'appConfigurationId': app_config_id,
},
'additionalConnectionStringProperties': connstr_props,
'action': config_action
},
'publicNetworkSolution': {
Expand Down Expand Up @@ -498,8 +510,9 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m

validate_service_state(parameters)
if enable_mi_for_db_linker and auth_action != 'optOutAllAuth':
logger.warning('Start creating mi for db linker')
brycechen1849 marked this conversation as resolved.
Show resolved Hide resolved
brycechen1849 marked this conversation as resolved.
Show resolved Hide resolved
new_auth_info = enable_mi_for_db_linker(
cmd, source_id, target_id, auth_info, client_type, connection_name)
cmd, source_id, target_id, auth_info, client_type, connection_name, connstr_props)
parameters['auth_info'] = new_auth_info or parameters['auth_info']

# migration warning for Spring Azure Cloud
Expand All @@ -509,7 +522,7 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m
logger.warning(springboot_migration_warning(require_update=False,
check_version=(not isSecretType),
both_version=isSecretType))

logger.warning('request payload: %s', parameters)
brycechen1849 marked this conversation as resolved.
Show resolved Hide resolved
brycechen1849 marked this conversation as resolved.
Show resolved Hide resolved
return auto_register(sdk_no_wait, no_wait,
client.begin_create_or_update,
resource_uri=source_id,
Expand Down Expand Up @@ -651,6 +664,7 @@ def connection_update(cmd, client, # pylint: disable=too-many-locals, too-many-
site=None, slot=None, # Resource.WebApp
spring=None, app=None, deployment=None, # Resource.SpringCloud
customized_keys=None,
connstr_props=None, # Resource.FabricSql
opt_out_list=None,
):

Expand Down Expand Up @@ -704,6 +718,10 @@ def connection_update(cmd, client, # pylint: disable=too-many-locals, too-many-
if linker.get('configurationInfo') and linker.get('configurationInfo').get('customizedKeys'):
customized_keys = customized_keys or linker.get('configurationInfo').get('customizedKeys')

if linker.get('configurationInfo') and linker.get('configurationInfo').get('additionalConnectionStringProperties'):
connstr_props = connstr_props or linker.get(
'configurationInfo').get('additionalConnectionStringProperties')

config_action = 'optOut' if (opt_out_list is not None and
OPT_OUT_OPTION.CONFIGURATION_INFO.value in opt_out_list) else None
public_network_action = 'optOut' if (opt_out_list is not None and
Expand All @@ -723,6 +741,7 @@ def connection_update(cmd, client, # pylint: disable=too-many-locals, too-many-
'configurationStore': {
'appConfigurationId': app_config_id
},
'additionalConnectionStringProperties': connstr_props,
'action': config_action
},
'publicNetworkSolution': {
Expand Down Expand Up @@ -1294,3 +1313,26 @@ def connection_update_kafka(cmd, client, # pylint: disable=too-many-locals
return client.begin_create_or_update(resource_uri=source_id,
linker_name=connection_name,
parameters=parameters)


{
"properties": {
"targetService": {
"type": "ConfluentBootstrapServer",
brycechen1849 marked this conversation as resolved.
Show resolved Hide resolved
"endpoint": "https://api.fabric.microsoft.com/v1/workspaces/13c65326-ecab-43f6-8a05-60927aaa4cec/SqlDatabases/4fdf6efe-23a9-4d74-8c4a-4ecc70c4d323"
},
"publicNetworkSolution": {
"action": "optOut"
},
"authInfo": {
"authType": "systemAssignedIdentity",
"username": "DotNetAppSqlDb20240704"
},
"configurationInfo": {
"additionalConnectionStringProperties": {
"Server": "tcp:renzo-srv-6ae35870-c362-44b9-8389-ada214a46bb5-51240650dd56.database.windows.net,1433",
"Database": "AzureServiceConnectorTestSqlDb-4fdf6efe-23a9-4d74-8c4a-4ecc70c4d323"
}
}
}
}
2 changes: 1 addition & 1 deletion src/azure-cli/requirements.py3.Darwin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ azure-mgmt-security==6.0.0
azure-mgmt-servicebus==8.2.0
azure-mgmt-servicefabric==2.1.0
azure-mgmt-servicefabricmanagedclusters==2.0.0b6
azure-mgmt-servicelinker==1.2.0b2
azure-mgmt-servicelinker==1.2.0b3
azure-mgmt-sql==4.0.0b19
azure-mgmt-signalr==2.0.0b2
azure-mgmt-sqlvirtualmachine==1.0.0b5
Expand Down
2 changes: 1 addition & 1 deletion src/azure-cli/requirements.py3.Linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ azure-mgmt-security==6.0.0
azure-mgmt-servicebus==8.2.0
azure-mgmt-servicefabric==2.1.0
azure-mgmt-servicefabricmanagedclusters==2.0.0b6
azure-mgmt-servicelinker==1.2.0b2
azure-mgmt-servicelinker==1.2.0b3
azure-mgmt-sql==4.0.0b19
azure-mgmt-signalr==2.0.0b2
azure-mgmt-sqlvirtualmachine==1.0.0b5
Expand Down
2 changes: 1 addition & 1 deletion src/azure-cli/requirements.py3.windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ azure-mgmt-security==6.0.0
azure-mgmt-servicebus==8.2.0
azure-mgmt-servicefabric==2.1.0
azure-mgmt-servicefabricmanagedclusters==2.0.0b6
azure-mgmt-servicelinker==1.2.0b2
azure-mgmt-servicelinker==1.2.0b3
azure-mgmt-sql==4.0.0b19
azure-mgmt-signalr==2.0.0b2
azure-mgmt-sqlvirtualmachine==1.0.0b5
Expand Down
2 changes: 1 addition & 1 deletion src/azure-cli/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
'azure-mgmt-security==6.0.0',
'azure-mgmt-servicebus~=8.2.0',
'azure-mgmt-servicefabricmanagedclusters==2.0.0b6',
'azure-mgmt-servicelinker==1.2.0b2',
'azure-mgmt-servicelinker==1.2.0b3',
'azure-mgmt-servicefabric~=2.1.0',
'azure-mgmt-signalr==2.0.0b2',
'azure-mgmt-sqlvirtualmachine==1.0.0b5',
Expand Down