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
41 changes: 32 additions & 9 deletions src/azure-cli/azure/cli/command_modules/cosmosdb/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,7 @@
text: |
az cosmosdb fleet create \\
--resource-group MyResourceGroup \\
--fleet-name MyFleet \\
--fleet-name myfleet \\
--location westus
"""

Expand All @@ -1826,27 +1826,46 @@
helps['cosmosdb fleetspace create'] = """
type: command
short-summary: Create a new Fleetspace under a Cosmos DB Fleet.
long-summary: |
Creates a NoSQL fleetspace. serviceTier and nonempty dataRegions are required creation settings.
Omit throughputPoolConfiguration, or set it to null or {}, for non-pooled creation.
Pooling requires both positive integer throughput bounds with maxThroughput >= minThroughput.
Additional throughput constraints and supported tiers and regions are service-configured.
examples:
- name: Create a fleetspace
text: |
az cosmosdb fleetspace create \\
--resource-group MyResourceGroup \\
--fleet-name MyFleet \\
--fleetspace-name MyFleetspace \\
--fleet-name myfleet \\
--fleetspace-name myspace \\
--body @fleetspace.json
- name: Create a non-pooled fleetspace
text: |
az cosmosdb fleetspace create \\
--resource-group MyResourceGroup \\
--fleet-name myfleet \\
--fleetspace-name myspace \\
--body '{"properties":{"serviceTier":"GeneralPurpose","dataRegions":["West US 2"]}}'
"""

helps['cosmosdb fleetspace update'] = """
type: command
short-summary: Update an existing Cosmos DB Fleetspace.
long-summary: |
Both positive integer minThroughput and maxThroughput are required in throughputPoolConfiguration,
with maxThroughput >= minThroughput. Additional constraints are service-configured.
Partial updates and omitted, null or empty configurations are not supported by this CLI.
serviceTier and dataRegions are creation settings; supplied update values cannot change them.
Do not omit configuration in a raw service request to preserve pooling: omission can disable pooling.
Fleetspaces use the NoSQL API; this command does not change the API kind.
examples:
- name: Update fleetspace throughput settings
text: |
az cosmosdb fleetspace update \\
--resource-group MyResourceGroup \\
--fleet-name MyFleet \\
--fleetspace-name MyFleetspace \\
--body @fleetspace.json
--fleet-name myfleet \\
--fleetspace-name myspace \\
--body '{"properties":{"throughputPoolConfiguration":{"minThroughput":100000,"maxThroughput":300000}}}'
"""

helps['cosmosdb fleetspace list'] = """
Expand All @@ -1872,14 +1891,18 @@
helps['cosmosdb fleetspace account create'] = """
type: command
short-summary: Register an existing Cosmos DB database account to a Fleetspace.
long-summary: |
The attachment name must match the database account name in resourceId, ignoring case; aliases are not supported.
resourceId must identify a Microsoft.DocumentDB/databaseAccounts resource, not a child resource.
armLocation is mandatory and nonblank; the service derives the authoritative location from the account.
examples:
- name: Register a database account to a fleetspace
text: |
az cosmosdb fleetspace account create \\
--resource-group MyResourceGroup \\
--fleet-name MyFleet \\
--fleetspace-name MyFleetspace \\
--fleetspace-account-name MyAccount \\
--fleet-name myfleet \\
--fleetspace-name myspace \\
--fleetspace-account-name myaccount \\
--body @fleetspaceAccount.json
"""

Expand Down
21 changes: 16 additions & 5 deletions src/azure-cli/azure/cli/command_modules/cosmosdb/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
validate_mongo_user_definition_body,
validate_mongo_user_definition_id,
validate_fleetspace_body,
validate_fleetspace_create_body,
validate_fleetspaceAccount_body)

from azure.cli.command_modules.cosmosdb.actions import (
Expand Down Expand Up @@ -86,10 +87,20 @@
}"
"""

FLEETSPACE_UPDATE_PROPERTIES_EXAMPLE = """--body "{
\\"properties\\": {
\\"throughputPoolConfiguration\\": {
\\"minThroughput\\": 100000,
\\"maxThroughput\\": 300000
}
}
}"
"""

FLEETSPACE_ACCOUNT_PROPERTIES_EXAMPLE = """--body "{
\\"properties\\": {
\\"globalDatabaseAccountProperties\\": {
\\"resourceId\\": \\"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.DocumentDB/databaseAccounts/example-account\\",
\\"resourceId\\": \\"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.DocumentDB/databaseAccounts/myaccount\\",
\\"armLocation\\": \\"East US\\"
}
}
Expand Down Expand Up @@ -727,17 +738,17 @@ def load_arguments(self, _):
c.argument('fleetspace_name', options_list=['--fleetspace-name', '-n'], help='Name of the Fleetspace resource.', required=True)

with self.argument_context('cosmosdb fleetspace create') as c:
c.argument('fleetspace_body', options_list=['--body', '-b'], validator=validate_fleetspace_body, completer=FilesCompleter(), help="Fleetspace body with properties.serviceTier (required), properties.dataRegions (required), and properties.throughputPoolConfiguration (fields: minThroughput, maxThroughput). You can enter it as a string or as a file, e.g., --body @fleetspace.json or " + FLEETSPACE_PROPERTIES_EXAMPLE)
c.argument('fleetspace_body', options_list=['--body', '-b'], validator=validate_fleetspace_create_body, completer=FilesCompleter(), help="NoSQL fleetspace JSON body with required properties.serviceTier and nonempty properties.dataRegions. Omit throughputPoolConfiguration, or use null or {}, for non-pooled creation. Pooled creation requires positive integer minThroughput and maxThroughput, with max >= min; additional limits are service-configured. Use inline JSON or --body @fleetspace.json. Example: " + FLEETSPACE_PROPERTIES_EXAMPLE)

with self.argument_context('cosmosdb fleetspace update') as c:
c.argument('fleetspace_body', options_list=['--body', '-b'], validator=validate_fleetspace_body, completer=FilesCompleter(), help="Fleetspace body with properties.serviceTier (optional), properties.dataRegions (optional), and properties.throughputPoolConfiguration (fields: minThroughput, maxThroughput). You can enter it as a string or as a file, e.g., --body @fleetspace.json or " + FLEETSPACE_PROPERTIES_EXAMPLE)
c.argument('fleetspace_body', options_list=['--body', '-b'], validator=validate_fleetspace_body, completer=FilesCompleter(), help="Fleetspace JSON body requiring properties.throughputPoolConfiguration with both positive integer minThroughput and maxThroughput, with max >= min. Additional limits are service-configured. Partial updates and omitted/null/empty configurations are not supported. serviceTier and dataRegions are creation settings and cannot change existing settings. Use inline JSON or --body @fleetspace.json. Example: " + FLEETSPACE_UPDATE_PROPERTIES_EXAMPLE)

# Cosmos DB Fleetspace account
with self.argument_context('cosmosdb fleetspace account') as c:
c.argument('resource_group', options_list=['--resource-group', '-g'], help='Name of the resource group.', required=True)
c.argument('fleet_name', options_list=['--fleet-name'], help='Name of the Cosmos DB Fleet.', required=True)
c.argument('fleetspace_name', options_list=['--fleetspace-name'], help='Name of the Fleetspace resource.', required=True)
c.argument('fleetspace_account_name', options_list=['--fleetspace-account-name', '-n'], help='Name of the Fleetspace Account resource.', required=True)
c.argument('fleetspace_account_name', options_list=['--fleetspace-account-name', '-n'], help='Database account name from resourceId (case-insensitive match), not an association alias.', required=True)

with self.argument_context('cosmosdb fleetspace account create') as c:
c.argument('fleetspace_account_body', options_list=['--body', '-b'], validator=validate_fleetspaceAccount_body, completer=FilesCompleter(), help="Fleetspace Account body with properties.globalDatabaseAccountProperties (fields: armLocation, resourceId). You can enter it as a string or as a file, e.g., --body @fleetspaceAccount.json or " + FLEETSPACE_ACCOUNT_PROPERTIES_EXAMPLE)
c.argument('fleetspace_account_body', options_list=['--body', '-b'], validator=validate_fleetspaceAccount_body, completer=FilesCompleter(), help="Fleetspace Account JSON body with properties.globalDatabaseAccountProperties: resourceId must identify a Cosmos DB database account; armLocation is required and nonblank, but the service derives the authoritative account location. Use inline JSON or --body @fleetspaceAccount.json. Example: " + FLEETSPACE_ACCOUNT_PROPERTIES_EXAMPLE)
69 changes: 48 additions & 21 deletions src/azure-cli/azure/cli/command_modules/cosmosdb/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,14 @@ def _validate_pk_paths_in_cep(path, partition_key_path, policyFormatVersion, enc


def validate_fleetspace_body(cmd, ns):
_validate_fleetspace_body(ns, is_create=False)


def validate_fleetspace_create_body(cmd, ns):
_validate_fleetspace_body(ns, is_create=True)


def _validate_fleetspace_body(ns, is_create):
from azure.cli.core.util import get_file_json, shell_safe_json_parse
import os

Expand All @@ -546,27 +554,32 @@ def validate_fleetspace_body(cmd, ns):
if not isinstance(body, dict):
raise InvalidArgumentValueError('Invalid fleetspace body. Must be a JSON object.')

props = body.get('properties', {})
props = body.get('properties')
if not isinstance(props, dict):
raise InvalidArgumentValueError('Missing or invalid "properties" field in fleetspace body.')

tp_config = props.get('throughputPoolConfiguration', {})
if not isinstance(tp_config, dict):
tp_config = props.get('throughputPoolConfiguration')
if is_create and (tp_config is None or tp_config == {}):
props.pop('throughputPoolConfiguration', None)
elif not isinstance(tp_config, dict) or not tp_config:
raise InvalidArgumentValueError('Missing or invalid "throughputPoolConfiguration" in properties.')

# Check for minThroughput and maxThroughput in throughputPoolConfiguration
for field in ['minThroughput', 'maxThroughput']:
if field not in tp_config:
raise InvalidArgumentValueError(f'Missing "{field}" in throughputPoolConfiguration.')

if not isinstance(tp_config['minThroughput'], int) or tp_config['minThroughput'] <= 0:
raise InvalidArgumentValueError('"minThroughput" must be a positive integer.')

if not isinstance(tp_config['maxThroughput'], int) or tp_config['maxThroughput'] <= 0:
raise InvalidArgumentValueError('"maxThroughput" must be a positive integer.')

# Check for serviceTier and dataRegions at base properties level
if 'serviceTier' in props:
else:
for field in ['minThroughput', 'maxThroughput']:
if field not in tp_config:
raise InvalidArgumentValueError(f'Missing "{field}" in throughputPoolConfiguration.')
if not isinstance(tp_config[field], int) or isinstance(tp_config[field], bool) or tp_config[field] <= 0:
raise InvalidArgumentValueError(f'"{field}" must be a positive integer.')

if tp_config['maxThroughput'] < tp_config['minThroughput']:
raise InvalidArgumentValueError('"maxThroughput" must be greater than or equal to "minThroughput".')

if is_create:
if not isinstance(props.get('serviceTier'), str) or not props['serviceTier'].strip():
raise InvalidArgumentValueError('"serviceTier" must be a nonblank string for creation.')
regions = props.get('dataRegions')
if not isinstance(regions, list) or not regions or not all(isinstance(region, str) and region.strip() for region in regions):
raise InvalidArgumentValueError('"dataRegions" must be a nonempty list of nonblank strings for creation.')
elif 'serviceTier' in props:
if not isinstance(props['serviceTier'], str):
raise InvalidArgumentValueError('"serviceTier" must be a string.')

Expand All @@ -577,6 +590,21 @@ def validate_fleetspace_body(cmd, ns):
ns.fleetspace_body = body


def _parse_fleetspace_account_resource_id(resource_id):
from azure.mgmt.core.tools import is_valid_resource_id, parse_resource_id

if not isinstance(resource_id, str) or not is_valid_resource_id(resource_id):
raise InvalidArgumentValueError('"resourceId" must be a valid Cosmos DB database account ARM resource ID.')

parsed = parse_resource_id(resource_id)
if (not all(parsed.get(field, '').strip() for field in ['subscription', 'resource_group', 'namespace', 'type', 'name']) or
parsed['namespace'].lower() != 'microsoft.documentdb' or
parsed['type'].lower() != 'databaseaccounts' or
parsed.get('last_child_num')):
raise InvalidArgumentValueError('"resourceId" must identify a Microsoft.DocumentDB/databaseAccounts resource, not a child resource.')
return parsed


def validate_fleetspaceAccount_body(cmd, ns):
from azure.cli.core.util import get_file_json, shell_safe_json_parse
import os
Expand All @@ -598,11 +626,10 @@ def validate_fleetspaceAccount_body(cmd, ns):
if not isinstance(gdp, dict):
raise InvalidArgumentValueError('Missing or invalid "globalDatabaseAccountProperties".')

if "resourceId" not in gdp or not isinstance(gdp["resourceId"], str) or not gdp["resourceId"].startswith("/subscriptions/"):
raise InvalidArgumentValueError('"resourceId" must be a valid ARM resource ID string.')
_parse_fleetspace_account_resource_id(gdp.get('resourceId'))

if "armLocation" not in gdp or not isinstance(gdp["armLocation"], str):
raise InvalidArgumentValueError('"armLocation" must be a valid string.')
if not isinstance(gdp.get('armLocation'), str) or not gdp['armLocation'].strip():
raise InvalidArgumentValueError('"armLocation" must be a nonblank string.')

ns.fleetspace_account_body = body

Expand Down
21 changes: 17 additions & 4 deletions src/azure-cli/azure/cli/command_modules/cosmosdb/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,7 @@ def cli_cosmosdb_sql_container_update(client,
sql_container_resource.default_ttl = sql_container.resource.default_ttl
sql_container_resource.unique_key_policy = sql_container.resource.unique_key_policy
sql_container_resource.conflict_resolution_policy = sql_container.resource.conflict_resolution_policy
sql_container_resource.computed_properties = sql_container.resource.computed_properties
sql_container_resource.vector_embedding_policy = sql_container.resource.vector_embedding_policy
sql_container_resource.full_text_policy = sql_container.resource.full_text_policy

Expand Down Expand Up @@ -3636,10 +3637,13 @@ def cli_cosmosdb_fleetspace_create(client,
if not data_regions:
raise CLIError('Missing required field "dataRegions" in properties.')

throughput_pool_config = FleetspacePropertiesThroughputPoolConfiguration(
min_throughput=fleetspace_body['properties']['throughputPoolConfiguration']['minThroughput'],
max_throughput=fleetspace_body['properties']['throughputPoolConfiguration']['maxThroughput']
)
pool_properties = fleetspace_body['properties'].get('throughputPoolConfiguration')
throughput_pool_config = None
if pool_properties:
throughput_pool_config = FleetspacePropertiesThroughputPoolConfiguration(
min_throughput=pool_properties['minThroughput'],
max_throughput=pool_properties['maxThroughput']
)

fleetspace_resource = FleetspaceResource(
fleetspace_api_kind="NoSQL",
Expand Down Expand Up @@ -3697,6 +3701,15 @@ def cli_cosmosdb_fleetspace_account_create(client,

"""Creates an Azure Cosmos DB Fleetspace Account."""

from azure.cli.core.azclierror import InvalidArgumentValueError
from ._validators import _parse_fleetspace_account_resource_id

account_properties = fleetspace_account_body['properties']['globalDatabaseAccountProperties']
account_name = _parse_fleetspace_account_resource_id(account_properties['resourceId'])['name']
if fleetspace_account_name.lower() != account_name.lower():
raise InvalidArgumentValueError(
f'--fleetspace-account-name "{fleetspace_account_name}" must match the database account name "{account_name}" in resourceId.')

fleetspaceAccountPropertiesGlobalDatabaseAccountProperties = FleetspaceAccountPropertiesGlobalDatabaseAccountProperties(
resource_id=fleetspace_account_body['properties']['globalDatabaseAccountProperties']['resourceId'],
arm_location=fleetspace_account_body['properties']['globalDatabaseAccountProperties']['armLocation'],
Expand Down
Loading
Loading