Skip to content

Commit ef341ab

Browse files
committed
add creation same check
1 parent 3967827 commit ef341ab

File tree

3 files changed

+68
-2
lines changed

3 files changed

+68
-2
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_force_create_arguments(context):
195+
context.argument('force', options_list=['--force', '-f'],
196+
arg_type=get_three_state_flag(), default=False,
197+
help='Indicates whether to force to execute operation to create \
198+
an existing connection even without any updates.')
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_force_create_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: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,3 +573,53 @@ def get_aks_resource_name(linker):
573573
def get_aks_resource_secret_name(connection_name):
574574
valid_name = re.sub(r'[^a-zA-Z0-9]', '', connection_name, flags=re.IGNORECASE)
575575
return f'sc-{valid_name}-secret'
576+
577+
578+
def compare_properties_changed(new_props, existing_props):
579+
"""
580+
Deep comparison function that checks if there are meaningful differences
581+
between new properties and existing properties, ignoring None values.
582+
Returns True if there are differences that require an update.
583+
"""
584+
def has_meaningful_changes(new_value, existing_value):
585+
if new_value is None:
586+
# If new value is None, no change needed
587+
return False
588+
589+
if isinstance(new_value, dict) and isinstance(existing_value, dict):
590+
# For nested dictionaries, check each key recursively
591+
for key, value in new_value.items():
592+
existing_props_key = key
593+
if '_' in key:
594+
existing_props_key = to_camel_case(key)
595+
existing_nested = existing_value.get(existing_props_key) if existing_value else None
596+
if has_meaningful_changes(value, existing_nested):
597+
return True
598+
return False
599+
elif isinstance(new_value, dict) and existing_value is None:
600+
# If existing is None but new is a dict, check if any values in new dict are meaningful
601+
for value in new_value.values():
602+
if value is not None:
603+
return True
604+
return False
605+
else:
606+
# For primitive values, compare directly
607+
return new_value != existing_value
608+
609+
# Check if any property has meaningful changes
610+
for key, new_value in new_props.items():
611+
existing_props_key = key
612+
if '_' in key:
613+
existing_props_key = to_camel_case(key)
614+
existing_value = existing_props.get(existing_props_key) if existing_props else None
615+
if has_meaningful_changes(new_value, existing_value):
616+
return True
617+
return False
618+
619+
620+
def to_camel_case(snake_str):
621+
if not snake_str or '_' not in snake_str:
622+
return snake_str
623+
components = snake_str.split('_')
624+
return components[0] + ''.join(word.capitalize() for word in components[1:])
625+

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
)
3232
from ._addon_factory import AddonFactory
3333
from ._utils import (
34+
compare_properties_changed,
3435
set_user_token_by_source_and_target,
3536
set_user_token_header,
3637
auto_register,
@@ -317,7 +318,8 @@ def connection_create(cmd, client, # pylint: disable=too-many-locals,too-many-s
317318
target_app_name=None, # Resource.ContainerApp
318319
connstr_props=None, # Resource.FabricSql
319320
fabric_workspace_uuid=None,
320-
fabric_sql_db_uuid=None
321+
fabric_sql_db_uuid=None,
322+
force=False,
321323
):
322324
auth_action = 'optOutAllAuth' if (opt_out_list is not None and
323325
OPT_OUT_OPTION.AUTHENTICATION.value in opt_out_list) else None
@@ -370,7 +372,8 @@ def connection_create(cmd, client, # pylint: disable=too-many-locals,too-many-s
370372
app_config_id=app_config_id,
371373
enable_appconfig_extension=enable_appconfig_extension,
372374
server=server, database=database,
373-
connstr_props=connstr_props
375+
connstr_props=connstr_props,
376+
force=force,
374377
)
375378

376379

@@ -406,6 +409,7 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m
406409
target_app_name=None, # Resource.ContainerApp
407410
enable_appconfig_extension=False,
408411
connstr_props=None, # Resource.FabricSql
412+
force=False,
409413
**kwargs,
410414
):
411415
if not source_id:
@@ -530,6 +534,11 @@ def connection_create_func(cmd, client, # pylint: disable=too-many-locals,too-m
530534
'manually and then create the connection.'.format(str(e)))
531535

532536
validate_service_state(parameters)
537+
linker = todict(client.get(resource_uri=source_id, linker_name=connection_name))
538+
if (linker is not None and not compare_properties_changed(parameters, linker) and force is False):
539+
logger.warning('Connection exists and no property to be updated, skip the update operation. Use --force to force the connection recreation')
540+
return linker
541+
533542
if enable_mi_for_db_linker and auth_action != 'optOutAllAuth':
534543
new_auth_info = enable_mi_for_db_linker(
535544
cmd, source_id, target_id, auth_info, client_type, connection_name, connstr_props)

0 commit comments

Comments
 (0)