Skip to content

Commit 9f3aac4

Browse files
authored
{Service Connector} az aks connection create: Add --no-recreate to avoid creating operation for existing connection and update kubernetesResourceName in aks connection response (#32140)
1 parent 77e2228 commit 9f3aac4

File tree

3 files changed

+74
-10
lines changed

3 files changed

+74
-10
lines changed

src/azure-cli/azure/cli/command_modules/serviceconnector/_params.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ def add_connstr_props_argument(context):
191191
validator=validate_connstr_props)
192192

193193

194+
def add_no_recreate_arguments(context):
195+
context.argument('no_recreate', options_list=['--no-recreate'],
196+
arg_type=get_three_state_flag(), default=False,
197+
help='Skip executing creation operation when no updates to an existing connection.')
198+
199+
194200
def add_target_type_argument(context, source):
195201
TARGET_TYPES = [
196202
elem.value for elem in SUPPORTED_AUTH_TYPE.get(source).keys()]
@@ -321,6 +327,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements
321327
add_customized_keys_argument(c)
322328
add_opt_out_argument(c)
323329
add_connstr_props_argument(c)
330+
add_no_recreate_arguments(c)
324331

325332
with self.argument_context('{} connection update {}'.format(source.value, target.value)) as c:
326333
add_client_type_argument(c, source, target)

src/azure-cli/azure/cli/command_modules/serviceconnector/_utils.py

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
)
1515
# from azure.cli.core._profile import Profile
1616
from ._resource_config import (
17-
SOURCE_RESOURCES_USERTOKEN,
18-
TARGET_RESOURCES_USERTOKEN,
1917
RESOURCE
2018
)
2119
from azure.cli.core import get_default_cli
@@ -126,8 +124,9 @@ def set_user_token_header(client, cli_ctx):
126124

127125
def set_user_token_by_source_and_target(client, cli_ctx, source, target):
128126
'''Set user token header to work around OBO according to source and target'''
129-
if source in SOURCE_RESOURCES_USERTOKEN or target in TARGET_RESOURCES_USERTOKEN:
130-
return set_user_token_header(client, cli_ctx)
127+
# deprecated
128+
# if source in SOURCE_RESOURCES_USERTOKEN or target in TARGET_RESOURCES_USERTOKEN:
129+
# return set_user_token_header(client, cli_ctx)
131130
return client
132131

133132

@@ -566,10 +565,49 @@ def get_aks_resource_name(linker):
566565
not (linker["targetService"]["resourceProperties"] is not None and
567566
linker["targetService"]["resourceProperties"].get("connectAsKubernetesCsiDriver")):
568567
service_account_name = f'sc-account-{linker["authInfo"].get("clientId")}'
569-
return [secret_name, service_account_name]
570-
return [secret_name]
568+
return {'secret': secret_name, 'serviceAccount': service_account_name}
569+
return {'secret': secret_name}
571570

572571

573572
def get_aks_resource_secret_name(connection_name):
574573
valid_name = re.sub(r'[^a-zA-Z0-9]', '', connection_name, flags=re.IGNORECASE)
575574
return f'sc-{valid_name}-secret'
575+
576+
577+
def compare_properties_changed(new_props, existing_props):
578+
"""
579+
Deep comparison function that checks if there are meaningful differences
580+
between new properties and existing properties, ignoring None values.
581+
Returns True if there are differences that require an update.
582+
"""
583+
def has_meaningful_changes(new_value, existing_value):
584+
if new_value is None or new_value == "":
585+
return False
586+
587+
if isinstance(new_value, dict) and isinstance(existing_value, dict):
588+
for key, value in new_value.items():
589+
if key == "mysql-identity-id" or key == 'user_object_id':
590+
continue
591+
existing_props_key = key
592+
if '_' in key:
593+
existing_props_key = to_camel_case(key)
594+
existing_nested = existing_value.get(existing_props_key) if existing_value else None
595+
if has_meaningful_changes(value, existing_nested):
596+
return True
597+
return False
598+
if isinstance(new_value, dict) and existing_value is None:
599+
for value in new_value.values():
600+
if value is not None and value != "":
601+
return True
602+
return False
603+
604+
return new_value != existing_value
605+
606+
return has_meaningful_changes(new_props, existing_props)
607+
608+
609+
def to_camel_case(snake_str):
610+
if not snake_str or '_' not in snake_str:
611+
return snake_str
612+
components = snake_str.split('_')
613+
return components[0] + ''.join(word.capitalize() for word in components[1:])

src/azure-cli/azure/cli/command_modules/serviceconnector/custom.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
ValidationError,
1515
AzureResponseError
1616
)
17+
from azure.core.exceptions import ResourceNotFoundError
1718
from ._resource_config import (
1819
CLIENT_TYPE,
1920
SUPPORTED_AUTH_TYPE,
@@ -31,6 +32,7 @@
3132
)
3233
from ._addon_factory import AddonFactory
3334
from ._utils import (
35+
compare_properties_changed,
3436
set_user_token_by_source_and_target,
3537
set_user_token_header,
3638
auto_register,
@@ -43,7 +45,7 @@
4345
get_secret_type_for_update
4446
)
4547
from ._credential_free import is_passwordless_command
46-
# pylint: disable=unused-argument,unsubscriptable-object,unsupported-membership-test,too-many-statements,too-many-locals
48+
# pylint: disable=unused-argument,unsubscriptable-object,unsupported-membership-test,too-many-statements,too-many-locals,broad-exception-caught
4749

4850

4951
logger = get_logger(__name__)
@@ -317,7 +319,8 @@ def connection_create(cmd, client, # pylint: disable=too-many-locals,too-many-s
317319
target_app_name=None, # Resource.ContainerApp
318320
connstr_props=None, # Resource.FabricSql
319321
fabric_workspace_uuid=None,
320-
fabric_sql_db_uuid=None
322+
fabric_sql_db_uuid=None,
323+
no_recreate=False,
321324
):
322325
auth_action = 'optOutAllAuth' if (opt_out_list is not None and
323326
OPT_OUT_OPTION.AUTHENTICATION.value in opt_out_list) else None
@@ -370,12 +373,13 @@ def connection_create(cmd, client, # pylint: disable=too-many-locals,too-many-s
370373
app_config_id=app_config_id,
371374
enable_appconfig_extension=enable_appconfig_extension,
372375
server=server, database=database,
373-
connstr_props=connstr_props
376+
connstr_props=connstr_props,
377+
no_recreate=no_recreate,
374378
)
375379

376380

377381
# The function is used in extension, new feature must be added in the end for backward compatibility
378-
def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-many-statements
382+
def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-many-statements,too-many-branches
379383
connection_name=None, client_type=None,
380384
source_resource_group=None, source_id=None,
381385
target_resource_group=None, target_id=None,
@@ -406,6 +410,7 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m
406410
target_app_name=None, # Resource.ContainerApp
407411
enable_appconfig_extension=False,
408412
connstr_props=None, # Resource.FabricSql
413+
no_recreate=False,
409414
**kwargs,
410415
):
411416
if not source_id:
@@ -530,6 +535,20 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m
530535
'manually and then create the connection.'.format(str(e)))
531536

532537
validate_service_state(parameters)
538+
if no_recreate:
539+
try:
540+
linker = todict(client.get(resource_uri=source_id, linker_name=connection_name))
541+
if linker is not None and linker.get('provisioningState') == 'Accepted':
542+
logger.warning('Connection provisioningState is Accepted, please retry later to avoid conflict.')
543+
return linker
544+
if linker is not None and linker.get('provisioningState') == 'Succeeded' and \
545+
not compare_properties_changed(parameters, linker):
546+
logger.warning(
547+
'Connection exists and no property to be updated, skip the update operation.')
548+
return linker
549+
except ResourceNotFoundError:
550+
logger.debug('No existing connection, continue to create it.')
551+
533552
if enable_mi_for_db_linker and auth_action != 'optOutAllAuth':
534553
new_auth_info = enable_mi_for_db_linker(
535554
cmd, source_id, target_id, auth_info, client_type, connection_name, connstr_props)

0 commit comments

Comments
 (0)