Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions eng/pipelines/templates/jobs/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ jobs:
os: linux

steps:
# APIView compares the PR source with origin/<target>, including their merge base.
- checkout: self
fetchDepth: 0
fetchTags: false

- task: UsePythonVersion@0
displayName: "Use Python $(PythonVersion)"
condition: succeededOrFailed()
Expand Down
57 changes: 31 additions & 26 deletions eng/pipelines/templates/jobs/live.tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ parameters:
- name: PersistOidcToken
type: boolean
default: false
- name: DisableAzureResourceCreation
type: boolean
default: false

jobs:
- job:
Expand Down Expand Up @@ -125,30 +128,31 @@ jobs:
parameters:
BuildTargetingString: ${{ parameters.BuildTargetingString }}

- template: /eng/common/TestResources/build-test-resource-config.yml
parameters:
SubscriptionConfiguration: ${{ parameters.CloudConfig.SubscriptionConfiguration }}
SubscriptionConfigurations: ${{ parameters.CloudConfig.SubscriptionConfigurations }}
${{ if parameters.UseFederatedAuth }}:
SubscriptionConfigurationFilePaths: ${{ parameters.CloudConfig.SubscriptionConfigurationFilePaths }}
EnvVars:
Pool: $(Pool)
${{ insert }}: ${{ parameters.EnvVars }}

- ${{ each directory in coalesce(parameters.TestResourceDirectories, split(parameters.ServiceDirectory, '|')) }}:
- template: /eng/common/TestResources/deploy-test-resources.yml
- ${{ if not(parameters.DisableAzureResourceCreation) }}:
- template: /eng/common/TestResources/build-test-resource-config.yml
parameters:
Location: ${{ coalesce(parameters.Location, parameters.CloudConfig.Location) }}
ServiceDirectory: '${{ directory }}'
SubscriptionConfiguration: $(SubscriptionConfiguration)
ArmTemplateParameters: $(ArmTemplateParameters)
UseFederatedAuth: ${{ parameters.UseFederatedAuth }}
PersistOidcToken: ${{ parameters.PersistOidcToken }}
ServiceConnection: ${{ parameters.CloudConfig.ServiceConnection }}
SubscriptionConfiguration: ${{ parameters.CloudConfig.SubscriptionConfiguration }}
SubscriptionConfigurations: ${{ parameters.CloudConfig.SubscriptionConfigurations }}
${{ if parameters.UseFederatedAuth }}:
SubscriptionConfigurationFilePaths: ${{ parameters.CloudConfig.SubscriptionConfigurationFilePaths }}
EnvVars:
Pool: $(Pool)
${{ insert }}: ${{ parameters.EnvVars }}

- ${{ each directory in coalesce(parameters.TestResourceDirectories, split(parameters.ServiceDirectory, '|')) }}:
- template: /eng/common/TestResources/deploy-test-resources.yml
parameters:
Location: ${{ coalesce(parameters.Location, parameters.CloudConfig.Location) }}
ServiceDirectory: '${{ directory }}'
SubscriptionConfiguration: $(SubscriptionConfiguration)
ArmTemplateParameters: $(ArmTemplateParameters)
UseFederatedAuth: ${{ parameters.UseFederatedAuth }}
PersistOidcToken: ${{ parameters.PersistOidcToken }}
ServiceConnection: ${{ parameters.CloudConfig.ServiceConnection }}
EnvVars:
Pool: $(Pool)
${{ insert }}: ${{ parameters.EnvVars }}

# This is a heuristic to detect compiled requirements in the service
# directory. If compiled requirements are found, install dependencies for
# and configure cibuildwheel.
Expand Down Expand Up @@ -224,12 +228,13 @@ jobs:
UseFederatedAuth: ${{ parameters.UseFederatedAuth }}
ServiceConnection: ${{ parameters.CloudConfig.ServiceConnection }}

- ${{ each directory in coalesce(parameters.TestResourceDirectories, split(parameters.ServiceDirectory, '|')) }}:
- template: /eng/common/TestResources/remove-test-resources.yml
parameters:
ServiceDirectory: '${{ directory }}'
SubscriptionConfiguration: $(SubscriptionConfiguration)
UseFederatedAuth: ${{ parameters.UseFederatedAuth }}
ServiceConnection: ${{ parameters.CloudConfig.ServiceConnection }}
- ${{ if not(parameters.DisableAzureResourceCreation) }}:
- ${{ each directory in coalesce(parameters.TestResourceDirectories, split(parameters.ServiceDirectory, '|')) }}:
- template: /eng/common/TestResources/remove-test-resources.yml
parameters:
ServiceDirectory: '${{ directory }}'
SubscriptionConfiguration: $(SubscriptionConfiguration)
UseFederatedAuth: ${{ parameters.UseFederatedAuth }}
ServiceConnection: ${{ parameters.CloudConfig.ServiceConnection }}

- ${{ parameters.PostSteps }}
115 changes: 64 additions & 51 deletions eng/pipelines/templates/stages/archetype-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ parameters:
- name: AdditionalMatrixConfigs
type: object
default: []
- name: FixedAccountMatrixConfigs
type: object
default: []
- name: FixedAccountPreSteps
type: object
default: []
- name: MatrixFilters
type: object
default: []
Expand Down Expand Up @@ -116,57 +122,64 @@ extends:
- group: ${{ group }}
dependsOn: []
jobs:
- template: /eng/common/pipelines/templates/jobs/generate-job-matrix.yml
parameters:
SparseCheckoutPaths:
- sdk/${{ parameters.ServiceDirectory }}/**/*.json
JobTemplatePath: /eng/pipelines/templates/jobs/live.tests.yml
OsVmImage: ubuntu-24.04
Pool: azsdk-pool
AdditionalParameters:
ServiceDirectory: ${{ parameters.ServiceDirectory }}
TestResourceDirectories: ${{ parameters.TestResourceDirectories }}
PreSteps:
- ${{ parameters.PlatformPreSteps }}
- ${{ parameters.PreSteps }}
PostSteps:
- ${{ parameters.PlatformPostSteps }}
- ${{ parameters.PostSteps }}
EnvVars: ${{ parameters.EnvVars }}
MaxParallel: ${{ parameters.MaxParallel }}
BeforeTestSteps: ${{ parameters.BeforeTestSteps }}
AfterTestSteps: ${{ parameters.AfterTestSteps }}
AdditionalTestArgs: ${{ parameters.AdditionalTestArgs }}
BuildTargetingString: ${{ package }}
TestMarkArgument: ${{ parameters.TestMarkArgument }}
InjectedPackages: ${{ parameters.InjectedPackages }}
BuildDocs: ${{ parameters.BuildDocs }}
TestTimeoutInMinutes: ${{ parameters.TestTimeoutInMinutes }}
TestProxy: ${{ parameters.TestProxy }}
CheckEnv: ${{ parameters.CheckEnv }}
UseFederatedAuth: ${{ parameters.UseFederatedAuth }}
PersistOidcToken: ${{ parameters.PersistOidcToken }}
MatrixConfigs:
# Enumerate platforms and additional platforms based on supported clouds (sparse platform<-->cloud matrix).
- ${{ each config in parameters.MatrixConfigs }}:
- ${{ config }}
- ${{ each config in parameters.AdditionalMatrixConfigs }}:
- ${{ config }}
MatrixFilters:
- ${{ each cloudFilter in cloud.value.MatrixFilters }}:
- ${{ cloudFilter }}
- ${{ parameters.MatrixFilters }}
MatrixReplace:
- ${{ each cloudReplace in cloud.value.MatrixReplace }}:
- ${{ cloudReplace }}
- ${{ parameters.MatrixReplace }}
CloudConfig:
SubscriptionConfiguration: ${{ cloud.value.SubscriptionConfiguration }}
SubscriptionConfigurations: ${{ cloud.value.SubscriptionConfigurations }}
Location: ${{ coalesce(cloud.value.Location, parameters.Location) }}
Cloud: ${{ cloud.key }}
ServiceConnection: ${{ coalesce(cloud.value.ServiceConnection, lower(format('azure-sdk-tests-{0}', cloud.key))) }}
SubscriptionConfigurationFilePaths: ${{ cloud.value.SubscriptionConfigurationFilePaths }}
- ${{ each matrixType in split('provisioned,fixed', ',') }}:
- ${{ if or(eq(matrixType, 'provisioned'), ne(length(parameters.FixedAccountMatrixConfigs), 0)) }}:
- template: /eng/common/pipelines/templates/jobs/generate-job-matrix.yml
parameters:
${{ if eq(matrixType, 'fixed') }}:
GenerateJobName: generate_fixed_account_job_matrix
SparseCheckoutPaths:
- sdk/${{ parameters.ServiceDirectory }}/**/*.json
JobTemplatePath: /eng/pipelines/templates/jobs/live.tests.yml
OsVmImage: ubuntu-24.04
Pool: azsdk-pool
AdditionalParameters:
ServiceDirectory: ${{ parameters.ServiceDirectory }}
TestResourceDirectories: ${{ parameters.TestResourceDirectories }}
DisableAzureResourceCreation: ${{ eq(matrixType, 'fixed') }}
PreSteps:
- ${{ parameters.PlatformPreSteps }}
- ${{ parameters.PreSteps }}
- ${{ if eq(matrixType, 'fixed') }}:
- ${{ parameters.FixedAccountPreSteps }}
PostSteps:
- ${{ parameters.PlatformPostSteps }}
- ${{ parameters.PostSteps }}
EnvVars: ${{ parameters.EnvVars }}
MaxParallel: ${{ parameters.MaxParallel }}
BeforeTestSteps: ${{ parameters.BeforeTestSteps }}
AfterTestSteps: ${{ parameters.AfterTestSteps }}
AdditionalTestArgs: ${{ parameters.AdditionalTestArgs }}
BuildTargetingString: ${{ package }}
TestMarkArgument: ${{ parameters.TestMarkArgument }}
InjectedPackages: ${{ parameters.InjectedPackages }}
BuildDocs: ${{ parameters.BuildDocs }}
TestTimeoutInMinutes: ${{ parameters.TestTimeoutInMinutes }}
TestProxy: ${{ parameters.TestProxy }}
CheckEnv: ${{ parameters.CheckEnv }}
UseFederatedAuth: ${{ and(parameters.UseFederatedAuth, ne(matrixType, 'fixed')) }}
PersistOidcToken: ${{ parameters.PersistOidcToken }}
MatrixConfigs:
- ${{ if eq(matrixType, 'fixed') }}:
- ${{ parameters.FixedAccountMatrixConfigs }}
- ${{ else }}:
- ${{ parameters.MatrixConfigs }}
- ${{ parameters.AdditionalMatrixConfigs }}
MatrixFilters:
- ${{ each cloudFilter in cloud.value.MatrixFilters }}:
- ${{ cloudFilter }}
- ${{ parameters.MatrixFilters }}
MatrixReplace:
- ${{ each cloudReplace in cloud.value.MatrixReplace }}:
- ${{ cloudReplace }}
- ${{ parameters.MatrixReplace }}
CloudConfig:
SubscriptionConfiguration: ${{ cloud.value.SubscriptionConfiguration }}
SubscriptionConfigurations: ${{ cloud.value.SubscriptionConfigurations }}
Location: ${{ coalesce(cloud.value.Location, parameters.Location) }}
Cloud: ${{ cloud.key }}
ServiceConnection: ${{ coalesce(cloud.value.ServiceConnection, lower(format('azure-sdk-tests-{0}', cloud.key))) }}
SubscriptionConfigurationFilePaths: ${{ cloud.value.SubscriptionConfigurationFilePaths }}

# Analyze-weekly runs only on scheduled runs that start on a weekend (Sat/Sun, UTC).
- ${{ if eq(variables['Build.Reason'], 'Schedule') }}:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ def run_powershell(script, **environment):
)


def test_analysis_checkout_has_history_for_api_comparison():
jobs = load_template("jobs/ci.yml")
analyze = next(job for job in jobs["jobs"] if job.get("job") == "Analyze")
assert analyze["steps"][0] == {"checkout": "self", "fetchDepth": 0, "fetchTags": False}


def test_rust_support_is_disabled_by_default_and_forwarded():
archetype = load_template("stages/archetype-sdk-client.yml")
jobs = load_template("jobs/ci.yml")
Expand Down
27 changes: 27 additions & 0 deletions sdk/cosmos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,33 @@ Legacy DocumentDB SDK.

Use this package if you are maintaining legacy applications that can't be upgrade for the latest features and optimizations.

## Live-test accounts

`tests.yml` runs two matrices through the shared Python live-test templates:

- `fixed-account-platform-matrix.json` contains key-auth jobs with an `AccountSelector`.
These jobs omit subscription configuration, Azure resource deployment, provisioning login,
and resource cleanup at template expansion. Tests run in plain PowerShell without a
service-connection login or a `CI_HAS_DEPLOYED_RESOURCES` requirement.
- `live-platform-matrix.json` retains the AAD and GSI jobs. Their provisioning, AAD role
assignments, federated test authentication, and cleanup are unchanged.

The fixed-account pre-step selects credentials from the secret
`sub-config-cosmos-azure-cloud-test-resources` in the existing Cosmos live-test variable
group. It exports `ACCOUNT_HOST`, `ACCOUNT_KEY`, and optional `SECONDARY_ACCOUNT_KEY`
directly, registering each with the Azure Pipelines log scrubber before setting the
environment-visible variable. The aggregate JSON is mapped only into the resolver task.
A missing selector or invalid configuration fails the job rather than falling back to
provisioning. There is no post-deployment credential rebinding.

When adding a key-auth job, put it in the fixed-account matrix and specify its selector.
Set client-side options such as `AZURE_COSMOS_ENABLE_CIRCUIT_BREAKER` in that matrix;
fixed jobs do not receive Bicep outputs. Retained `ArmConfig` labels preserve existing
job names and filters but do not cause deployment.

The `pipeline` directory contains the CI runtime resolver template and PowerShell
script; both are required by the fixed-account jobs.

## Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a
Expand Down
63 changes: 63 additions & 0 deletions sdk/cosmos/azure-cosmos/tests/_retry_test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# The MIT License (MIT)
# Copyright (c) Microsoft Corporation. All rights reserved.

from contextlib import contextmanager
from unittest.mock import patch
from urllib.parse import urlsplit, urlunsplit

from azure.cosmos import CosmosClient, DatabaseAccount
from azure.cosmos._global_endpoint_manager import _GlobalEndpointManager


def configure_retry_regions(location_cache, read_regions, write_regions=None):
"""Build a complete synthetic topology, including the derived routing maps."""
write_regions = list(write_regions or read_regions[:1])
account = DatabaseAccount()
account._ReadableLocations = [
{"name": region, "databaseAccountEndpoint": retry_endpoint(region)} for region in read_regions
]
account._WritableLocations = [
{"name": region, "databaseAccountEndpoint": retry_endpoint(region)} for region in write_regions
]
account._EnableMultipleWritableLocations = len(write_regions) > 1
location_cache.connection_policy.PreferredLocations = list(read_regions)
location_cache.connection_policy.UseMultipleWriteLocations = len(write_regions) > 1
location_cache.enable_multiple_writable_locations = len(write_regions) > 1
location_cache.location_unavailability_info_by_endpoint.clear()
location_cache.perform_on_database_account_read(account)


def retry_endpoint(region):
return "https://{}.retry-test.invalid/".format(region.lower().replace(" ", ""))


def send_to_test_host(send, host, request, **kwargs):
"""Keep synthetic routing observable to policies, but never send it over the network."""
url = request.url
parsed = urlsplit(url)
if parsed.hostname and parsed.hostname.endswith(".retry-test.invalid"):
target = urlsplit(host)
request.url = urlunsplit((target.scheme, target.netloc, parsed.path, parsed.query, parsed.fragment))
try:
return send(request, **kwargs)
finally:
request.url = url


@contextmanager
def retry_test_client(host, key):
# Prevent startup health checks from racing the synthetic topology installed below.
with patch.object(_GlobalEndpointManager, "_start_background_refresh"):
client = CosmosClient(host, key)
try:
manager = client.client_connection._global_endpoint_manager
transport = client.client_connection.pipeline_client._pipeline._transport
send = transport.send
with patch.object(manager, "refresh_endpoint_list"), patch.object(
transport,
"send",
side_effect=lambda request, **kwargs: send_to_test_host(send, host, request, **kwargs),
):
yield client
finally:
client.close()
20 changes: 20 additions & 0 deletions sdk/cosmos/azure-cosmos/tests/_split_test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import traceback
import unittest
import uuid
from collections import Counter
from contextlib import contextmanager

from azure.cosmos import _base
Expand All @@ -22,13 +23,32 @@
"actual_count",
"missing_count",
"unexpected_count",
"duplicate_count",
"order_matches",
"ids_match",
"token_matches",
"lsn_not_regressed",
"expected_range_present",
)


def assert_change_feed_ids(actual_ids, expected_ids, diagnostics):
"""Check all expected changes exactly once, without cross-partition ordering."""
actual = Counter(actual_ids)
expected = Counter(expected_ids)
diagnostics.update(
expected_count=len(expected_ids),
actual_count=len(actual_ids),
missing_count=sum((expected - actual).values()),
unexpected_count=sum((actual - expected).values()),
duplicate_count=sum(count - 1 for count in actual.values() if count > 1),
ids_match=actual == expected,
order_matches=actual_ids == expected_ids,
)
if actual != expected:
raise AssertionError("Change feed IDs differ from the expected multiset")


def create_item(hpk):
if hpk:
item = {
Expand Down
Loading
Loading