Skip to content

Commit 15a61cf

Browse files
[App Config] az appconfig kv export/import/list/delete, az appconfig revision list, and az appconfig restore: Support filtering by tags (#30694)
1 parent e6d7a76 commit 15a61cf

39 files changed

+713234
-630474
lines changed

src/azure-cli/azure/cli/command_modules/appconfig/_help.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@
149149
text: az appconfig kv delete --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --key color --label MyLabel
150150
- name: Delete a key using your 'az login' credentials and App Configuration store endpoint.
151151
text: az appconfig kv delete --endpoint https://myappconfiguration.azconfig.io --key color --auth-mode login --yes
152+
- name: Delete a key with key name "color" and has tags "tag1=value1" and "tag2=value2".
153+
text: az appconfig kv delete -n MyAppConfiguration --key color --tags tag1=value1 tag2=value2 --yes
152154
"""
153155

154156
helps['appconfig kv export'] = """
@@ -171,6 +173,10 @@
171173
text: az appconfig kv export -n MyAppConfiguration --label test -d file --path D:/abc.json --format json --profile appconfig/kvset
172174
- name: Export all keys to another App Configuration store from a snapshot of the source configuration
173175
text: az appconfig kv export -n MyAppConfiguration -d appconfig --dest-name AnotherAppConfiguration --snapshot MySnapshot
176+
- name: Export all keys and feature flags with specific tags to another App Configuration store.
177+
text: az appconfig kv export -n MyAppConfiguration -d appconfig --tags tag1=value1 tag2=value2 --dest-name AnotherAppConfiguration
178+
- name: Export all keys and feature flags to another App Configuration store and apply new tags.
179+
text: az appconfig kv export -n MyAppConfiguration -d appconfig --dest-name AnotherAppConfiguration --dest-tags newtag1=newvalue1
174180
"""
175181

176182
helps['appconfig kv import'] = """
@@ -195,6 +201,11 @@
195201
text: az appconfig kv import -s appconfig --endpoint https://myappconfiguration.azconfig.io --auth-mode login --src-endpoint https://anotherappconfiguration.azconfig.io --src-auth-mode login --src-key * --src-label * --preserve-labels
196202
- name: Import all keys and feature flags from a file using the appconfig/kvset format.
197203
text: az appconfig kv import -n MyAppConfiguration -s file --path D:/abc.json --format json --profile appconfig/kvset
204+
- name: Import all keys and feature flags with specific tags from an App Configuration store and apply new tags.
205+
text: az appconfig kv import -n MyAppConfiguration -s appconfig --src-name AnotherAppConfiguration --src-tags tag1=value1 tag2=value2 --tags newtag1=newvalue1
206+
- name: Import allkeys and feature flags from a file and apply new tags.
207+
text: az appconfig kv import -n MyAppConfiguration -s file --path D:/abc.json --format json --tags tag1=value1
208+
198209
"""
199210

200211
helps['appconfig kv list'] = """
@@ -215,6 +226,12 @@
215226
text: az appconfig kv list --endpoint https://myappconfiguration.azconfig.io --auth-mode login
216227
- name: List all key-values in a given snapshot of the app configuration store.
217228
text: az appconfig kv list --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --snapshot MySnapshot
229+
- name: List all key-values with specific tags
230+
text: az appconfig kv list -n MyAppConfiguration --tags tag1=value1 tag2=value2
231+
- name: List all key-values with tag name "tag1" with empty value
232+
text: az appconfig kv list -n MyAppConfiguration --tags tag1=
233+
- name: List all key-values with tag name "tag1" with null value
234+
text: az appconfig kv list -n MyAppConfiguration --tags tag1=\\0
218235
"""
219236

220237
helps['appconfig kv lock'] = """
@@ -235,6 +252,8 @@
235252
text: az appconfig kv restore -n MyAppConfiguration --datetime "2019-05-01T11:24:12Z"
236253
- name: Restore a specific key for any label start with v1. using connection string to a specific point in time.
237254
text: az appconfig kv restore --key color --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --label v1.* --datetime "2019-05-01T11:24:12Z"
255+
- name: Restore all key-values with specific tags to a specific point in time.
256+
text: az appconfig kv restore -n MyAppConfiguration --tags tag1=value1 tag2=value2 --datetime "2019-05-01T11:24:12Z"
238257
"""
239258

240259
helps['appconfig kv set'] = """
@@ -312,6 +331,12 @@
312331
text: az appconfig revision list --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --key color --datetime "2019-05-01T11:24:12Z"
313332
- name: List revision history for all items and query only key, value and last_modified.
314333
text: az appconfig revision list --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --fields key value last_modified
334+
- name: List revision history for all items with specific tags.
335+
text: az appconfig revision list -n MyAppConfiguration --tags tag1=value1 tag2=value2
336+
- name: List revision history for all items with tag name "tag1" with empty value.
337+
text: az appconfig revision list -n MyAppConfiguration --tags tag1=
338+
- name : List revision history for all items with tag name "tag1" with null value
339+
text: az appconfig revision list -n MyAppConfiguration --tags tag1=\\0
315340
"""
316341

317342
helps['appconfig replica'] = """
@@ -632,6 +657,9 @@
632657
- name: Create a snapshot of all keys starting with 'app/' and no label as default, then override the key-values with keys with the label 'prod' if they exist.
633658
text:
634659
az appconfig snapshot create -s MySnapshot -n MyAppConfiguration --filters '{\\"key\\":\\"app/*\\"}' '{\\"key\\":\\"app/*\\", \\"label\\":\\"prod\\"}' --composition-type 'key'
660+
- name: Create a snapshot of all keys starting with 'Test' and have tags 'tag1=value1' and 'tag2=value2'.
661+
text:
662+
az appconfig snapshot create -s MySnapshot -n MyAppConfiguration --filters '{\\"key\\":\\"Test*\\", \\"tags\\":[\\"tag1=value1\\", \\"tag2=value2\\"]}'
635663
"""
636664

637665
helps['appconfig snapshot show'] = """

src/azure-cli/azure/cli/command_modules/appconfig/_kv_helpers.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from ._constants import KeyVaultConstants, StatusCodes
2626
from ._diff_utils import __print_diff
27-
from ._utils import prep_label_filter_for_url_encoding
27+
from ._utils import prep_filter_for_url_encoding, parse_tags_to_dict
2828
from ._models import (
2929
convert_configurationsetting_to_keyvalue,
3030
convert_keyvalue_to_configurationsetting,
@@ -40,6 +40,7 @@ def __read_kv_from_config_store(
4040
azconfig_client,
4141
key=None,
4242
label=None,
43+
tags=None,
4344
snapshot=None,
4445
datetime=None,
4546
fields=None,
@@ -57,7 +58,9 @@ def __read_kv_from_config_store(
5758
# In delete, import & export commands, we treat missing --label as null label
5859
# In list, restore & list_revision commands, we treat missing --label as all labels
5960

60-
label = prep_label_filter_for_url_encoding(label)
61+
label = prep_filter_for_url_encoding(label)
62+
63+
prepped_tags = [prep_filter_for_url_encoding(tag) for tag in tags] if tags else []
6164

6265
query_fields = []
6366
if fields:
@@ -87,6 +90,7 @@ def __read_kv_from_config_store(
8790
configsetting_iterable = azconfig_client.list_configuration_settings(
8891
key_filter=key,
8992
label_filter=label,
93+
tags_filter=prepped_tags,
9094
accept_datetime=datetime,
9195
fields=query_fields,
9296
headers={HttpHeaders.CORRELATION_REQUEST_ID: correlation_request_id},
@@ -153,6 +157,7 @@ def __write_kv_and_features_to_config_store(
153157
azconfig_client,
154158
key_values,
155159
features=None,
160+
tags=None,
156161
label=None,
157162
preserve_labels=False,
158163
content_type=None,
@@ -170,6 +175,11 @@ def __write_kv_and_features_to_config_store(
170175
set_kv = convert_keyvalue_to_configurationsetting(kv)
171176
if not preserve_labels:
172177
set_kv.label = label
178+
if tags is not None:
179+
if tags == "": # Empty string explicitly clears existing tags
180+
set_kv.tags = {}
181+
else:
182+
set_kv.tags = parse_tags_to_dict(tags)
173183

174184
# Don't overwrite the content type of feature flags or key vault references
175185
if (

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
validate_identity, validate_auth_mode,
2727
validate_resolve_keyvault, validate_export_profile, validate_import_profile,
2828
validate_strict_import, validate_export_as_reference, validate_snapshot_filters,
29-
validate_snapshot_export, validate_snapshot_import)
29+
validate_snapshot_export, validate_snapshot_import, validate_tag_filters,
30+
validate_import_tag_filters)
3031

3132

3233
def load_arguments(self, _):
@@ -110,7 +111,7 @@ def load_arguments(self, _):
110111
options_list=['--filters'],
111112
validator=validate_snapshot_filters,
112113
nargs='+',
113-
help='Space-separated list of escaped JSON objects that represent the key and label filters used to build an App Configuration snapshot.'
114+
help='Space-separated list of escaped JSON objects that represent the key, label and tag filters used to build an App Configuration snapshot.'
114115
)
115116
snapshot_status_arg_type = CLIArgumentType(
116117
options_list=['--status'],
@@ -129,7 +130,11 @@ def load_arguments(self, _):
129130
option_list=['--enable-arm-private-network-access'],
130131
arg_type=get_three_state_flag(),
131132
help="Enable access to the App Configuration store via ARM Private Link if resource is restricted to private network access. Requires Pass-through ARM authentication mode."
133+
)
132134

135+
tags_arg_type = CLIArgumentType(
136+
nargs='*',
137+
validator=validate_tag_filters
133138
)
134139

135140
# Used with data plane commands. These take either a store name or connection string argument.
@@ -216,6 +221,7 @@ def load_arguments(self, _):
216221

217222
with self.argument_context('appconfig kv import') as c:
218223
c.argument('label', help="Imported KVs and feature flags will be assigned with this label. If no label specified, will assign null label.")
224+
c.argument('tags', nargs="*", help="Imported KVs and feature flags will be assigned with these tags. If no tags are specified, imported KVs and feature flags will retain existing tags. Support space-separated tags: key[=value] [key[=value] ...]. Use "" to clear existing tags.")
219225
c.argument('prefix', help="This prefix will be appended to the front of imported keys. Prefix will be ignored for feature flags.")
220226
c.argument('source', options_list=['--source', '-s'], arg_type=get_enum_type(['file', 'appconfig', 'appservice']), validator=validate_import, help="The source of importing. Note that importing feature flags from appservice is not supported.")
221227
c.argument('yes', help="Do not prompt for preview.")
@@ -229,7 +235,7 @@ def load_arguments(self, _):
229235
c.argument('depth', validator=validate_import_depth, help="Depth for flattening the json or yaml file to key-value pairs. Flatten to the deepest level by default if --separator is provided. Not applicable for property files or feature flags.")
230236
# bypass cli allowed values limitation
231237
c.argument('separator', validator=validate_separator, help="Delimiter for flattening the json or yaml file to key-value pairs. Separator will be ignored for property files and feature flags. Supported values: '.', ',', ';', '-', '_', '__', '/', ':' ")
232-
c.argument('profile', validator=validate_import_profile, arg_type=get_enum_type([ImportExportProfiles.DEFAULT, ImportExportProfiles.KVSET]), help="Import profile to be used for importing the key-values. Options 'depth', 'separator', 'content-type', 'label', 'skip-features' and, 'prefix' are not supported when using '{}' profile.".format(ImportExportProfiles.KVSET))
238+
c.argument('profile', validator=validate_import_profile, arg_type=get_enum_type([ImportExportProfiles.DEFAULT, ImportExportProfiles.KVSET]), help="Import profile to be used for importing the key-values. Options 'depth', 'separator', 'content-type', 'label', 'skip-features', 'tags' and, 'prefix' are not supported when using '{}' profile.".format(ImportExportProfiles.KVSET))
233239
c.argument('strict', validator=validate_strict_import, arg_type=get_three_state_flag(), help="Delete all other key-values in the store with specified prefix and label")
234240

235241
with self.argument_context('appconfig kv import', arg_group='AppConfig') as c:
@@ -243,6 +249,7 @@ def load_arguments(self, _):
243249
help='Auth mode for connecting to source App Configuration store. For details, refer to "--auth-mode" argument.')
244250
c.argument('src_snapshot', validator=validate_snapshot_import,
245251
help='Import all keys in a given snapshot of the source App Configuration store. If no snapshot is specified, the keys currently in the store are imported based on the specified key and label filters.')
252+
c.argument('src_tags', nargs="*", validator=validate_import_tag_filters, help="Key-values which contain the specified tags in source AppConfig will be imported. If no tags are specified, all key-values with any tags can be imported. Support space-separated tag filters: key[=value] [key[=value] ...].")
246253

247254
with self.argument_context('appconfig kv import', arg_group='AppService') as c:
248255
c.argument('appservice_account', validator=validate_appservice_name_or_id, help='ARM ID for AppService OR the name of the AppService, assuming it is in the same subscription and resource group as the App Configuration store. Required for AppService arguments')
@@ -257,6 +264,7 @@ def load_arguments(self, _):
257264
c.argument('skip_keyvault', help="Export items excluding all key vault references. By default, all key vault references with the specified label will be exported.", arg_type=get_three_state_flag())
258265
c.argument('snapshot', validator=validate_snapshot_export,
259266
help="Export all keys in a given snapshot of the App Configuration store. If no snapshot is specified, the keys currently in the store are exported based on the specified key and label filters.")
267+
c.argument('tags', arg_type=tags_arg_type, help="Key-values which contain the specified tags in source AppConfig will be exported. If no tags are specified, all key-values with any tags can be exported. Support space-separated tag filters: key[=value] [key[=value] ...].")
260268

261269
with self.argument_context('appconfig kv export', arg_group='File') as c:
262270
c.argument('path', help='Local configuration file path. Required for file arguments.')
@@ -266,7 +274,7 @@ def load_arguments(self, _):
266274
c.argument('separator', validator=validate_separator, help="Delimiter for flattening the key-value pairs to json or yaml file. Required for exporting hierarchical structure. Separator will be ignored for property files and feature flags. Supported values: '.', ',', ';', '-', '_', '__', '/', ':' ")
267275
c.argument('naming_convention', arg_type=get_enum_type(['pascal', 'camel', 'underscore', 'hyphen']), help='Naming convention to be used for "Feature Management" section of file. Example: pascal = FeatureManagement, camel = featureManagement, underscore = feature_management, hyphen = feature-management.')
268276
c.argument('resolve_keyvault', arg_type=get_three_state_flag(), validator=validate_resolve_keyvault, help="Resolve the content of key vault reference.")
269-
c.argument('profile', validator=validate_export_profile, arg_type=get_enum_type([ImportExportProfiles.DEFAULT, ImportExportProfiles.KVSET]), help="Export profile to be used for exporting the key-values. Options 'depth', 'separator', 'naming-convention', 'prefix', 'dest-label' and, 'resolve-keyvault' are not supported when using '{}' profile".format(ImportExportProfiles.KVSET))
277+
c.argument('profile', validator=validate_export_profile, arg_type=get_enum_type([ImportExportProfiles.DEFAULT, ImportExportProfiles.KVSET]), help="Export profile to be used for exporting the key-values. Options 'depth', 'separator', 'naming-convention', 'prefix', 'dest-label' , 'dest-tags' and, 'resolve-keyvault' are not supported when using '{}' profile".format(ImportExportProfiles.KVSET))
270278

271279
with self.argument_context('appconfig kv export', arg_group='AppConfig') as c:
272280
c.argument('dest_name', help='The name of the destination App Configuration store.')
@@ -276,6 +284,7 @@ def load_arguments(self, _):
276284
c.argument('dest_endpoint', help='If --dest-auth-mode is "login", provide endpoint URL of the destination App Configuration store.')
277285
c.argument('dest_auth_mode', arg_type=get_enum_type(['login', 'key']),
278286
help='Auth mode for connecting to the destination App Configuration store. For details, refer to "--auth-mode" argument.')
287+
c.argument('dest_tags', nargs="*", help="Exported KVs and feature flags will be assigned with these tags. If no tags are specified, exported KVs and features will retain existing tags. Support space-separated tags: key[=value] [key[=value] ...]. Use "" to clear existing tags.")
279288

280289
with self.argument_context('appconfig kv export', arg_group='AppService') as c:
281290
c.argument('appservice_account', validator=validate_appservice_name_or_id, help='ARM ID for AppService OR the name of the AppService, assuming it is in the same subscription and resource group as the App Configuration store. Required for AppService arguments')
@@ -298,6 +307,7 @@ def load_arguments(self, _):
298307
with self.argument_context('appconfig kv delete') as c:
299308
c.argument('key', validator=validate_key, help='Support star sign as filters, for instance * means all key and abc* means keys with abc as prefix.')
300309
c.argument('label', help="If no label specified, delete entry with null label. Support star sign as filters, for instance * means all label and abc* means labels with abc as prefix.")
310+
c.argument('tags', arg_type=tags_arg_type, help="If no tags are specified, delete all key-values with any tags. Support space-separated tags: key[=value] [key[=value] ...].")
301311

302312
with self.argument_context('appconfig kv show') as c:
303313
c.argument('key', help='Key to be showed.')
@@ -306,12 +316,14 @@ def load_arguments(self, _):
306316
with self.argument_context('appconfig kv list') as c:
307317
c.argument('key', help='If no key specified, return all keys by default. Support star sign as filters, for instance abc* means keys with abc as prefix.')
308318
c.argument('label', help="If no label specified, list all labels. Support star sign as filters, for instance abc* means labels with abc as prefix. Use '\\0' for null label.")
319+
c.argument('tags', arg_type=tags_arg_type, help="If no tags are specified, return all key-values with any tags. Support space-separated tags: key[=value] [key[=value] ...].")
309320
c.argument('snapshot', help="List all keys in a given snapshot of the App Configuration store. If no snapshot is specified, the keys currently in the store are listed.")
310321
c.argument('resolve_keyvault', arg_type=get_three_state_flag(), help="Resolve the content of key vault reference. This argument should NOT be specified along with --fields. Instead use --query for customized query.")
311322

312323
with self.argument_context('appconfig kv restore') as c:
313324
c.argument('key', help='If no key specified, restore all keys by default. Support star sign as filters, for instance abc* means keys with abc as prefix.')
314325
c.argument('label', help="If no label specified, restore all key-value pairs with all labels. Support star sign as filters, for instance abc* means labels with abc as prefix. Use '\\0' for null label.")
326+
c.argument('tags', arg_type=tags_arg_type, help="If no tags are specified, restore all key-values with any tags. Support space-separated tags: key[=value] [key[=value] ...].")
315327

316328
with self.argument_context('appconfig kv lock') as c:
317329
c.argument('key', help='Key to be locked.')
@@ -325,6 +337,7 @@ def load_arguments(self, _):
325337
c.argument('name', arg_type=data_plane_name_arg_type)
326338
c.argument('key', help='If no key specified, return all keys by default. Support star sign as filters, for instance abc* means keys with abc as prefix.')
327339
c.argument('label', help="If no label specified, list all labels. Support star sign as filters, for instance abc* means labels with abc as prefix. Use '\\0' for null label.")
340+
c.argument('tags', arg_type=tags_arg_type, help="If no tags are specified, return all key-values with any tags. Support space-separated tags: key[=value] [key[=value] ...].")
328341

329342
with self.argument_context('appconfig feature') as c:
330343
c.argument('name', arg_type=data_plane_name_arg_type)

0 commit comments

Comments
 (0)