Skip to content

Commit

Permalink
Re-orchestrate regression jobs (Azure#23550)
Browse files Browse the repository at this point in the history
* refactoring test-regression.yml step template to regression.yml, a job template
* multiplexing the regression checks by service
* allow dynamic generation of service matrix so we never miss anything and also do not execute unnecessary work
  • Loading branch information
scbedd authored Mar 18, 2022
1 parent 30c2e62 commit 03f9e2b
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 86 deletions.
51 changes: 31 additions & 20 deletions eng/pipelines/templates/jobs/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,25 +187,36 @@ jobs:
CondaArtifacts: ${{ parameters.CondaArtifacts}}
TestProxy: ${{ parameters.TestProxy }}

- job: 'RunRegression'
condition: and(succeededOrFailed(), or(eq(variables['Run.Regression'], 'true'), and(eq(variables['Build.Reason'], 'Schedule'), eq(variables['System.TeamProject'],'internal'))))
displayName: 'Run Regression'
timeoutInMinutes: 180
variables:
- template: ../variables/globals.yml

dependsOn:
- 'Build'

pool:
name: azsdk-pool-mms-ubuntu-2004-general
vmImage: MMSUbuntu20.04

steps:
- template: /eng/pipelines/templates/steps/targeting-string-resolve.yml
parameters:
- template: /eng/common/pipelines/templates/jobs/archetype-sdk-tests-generate.yml
parameters:
JobTemplatePath: /eng/pipelines/templates/jobs/regression.yml
GenerateJobName: generate_regression_matrix
SparseCheckoutPaths: [ "scripts/", "sdk/" ]
MatrixConfigs:
- Name: Python_regression_envs
Path: eng/pipelines/templates/stages/regression-job-matrix.json
Selection: sparse
GenerateVMJobs: true
PreGenerationSteps:
- script: |
pip install packaging==20.4
displayName: 'Prep Environment'
- template: /eng/pipelines/templates/steps/targeting-string-resolve.yml
parameters:
BuildTargetingString: ${{ parameters.BuildTargetingString }}
- task: PythonScript@0
displayName: 'Ensure service coverage'
inputs:
scriptPath: '$(Build.SourcesDirectory)/scripts/devops_tasks/update_regression_services.py'
arguments: >-
"$(TargetingString)"
--service="${{ parameters.ServiceDirectory }}"
--json=$(Build.SourcesDirectory)/eng/pipelines/templates/stages/regression-job-matrix.json
CloudConfig:
Cloud: Public
DependsOn:
- 'Build'
AdditionalParameters:
BuildTargetingString: ${{ parameters.BuildTargetingString }}

- template: ../steps/test_regression.yml
parameters:
ServiceDirectory: ${{ parameters.ServiceDirectory }}
TestTimeoutInMinutes: 90
121 changes: 121 additions & 0 deletions eng/pipelines/templates/jobs/regression.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
parameters:
- name: ServiceDirectory
type: string
default: ''
- name: Matrix
type: string
- name: DependsOn
type: string
default: ''
- name: BuildStagingDirectory
type: string
default: '$(Build.ArtifactStagingDirectory)'
- name: DevFeedName
type: string
default: 'public/azure-sdk-for-python'
- name: UsePlatformContainer
type: boolean
default: false
- name: CloudConfig
type: object
default: {}
- name: TestTimeoutInMinutes
type: number
default: 60
- name: BuildTargetingString
type: string
default: 'azure-*'

# The variable $(DependentService) is set from the matrix configuration.

jobs:
- job:
displayName: 'RegressTest'
condition: |
and(
succeededOrFailed(),
ne(${{ parameters.Matrix }}, '{}'),
or(
eq(variables['Run.Regression'], 'true'),
and(
eq(variables['System.TeamProject'], 'internal'),
eq(variables['Build.Reason'], 'Schedule')
)
)
)
timeoutInMinutes: ${{ parameters.TestTimeoutInMinutes }}

dependsOn:
- ${{ parameters.DependsOn }}

strategy:
matrix: $[ ${{ parameters.Matrix }} ]

pool:
name: $(Pool)
vmImage: $(OSVmImage)

${{ if eq(parameters.UsePlatformContainer, 'true') }}:
# Add a default so the job doesn't fail when the matrix is empty
container: $[ variables['Container'] ]

variables:
- template: ../variables/globals.yml
- name: PROXY_URL
value: "http://localhost:5000"

# Please use `$(TargetingString)` to refer to the python packages glob string. This was previously `${{ parameters.BuildTargetingString }}`.
steps:
- template: /eng/pipelines/templates/steps/targeting-string-resolve.yml
parameters:
BuildTargetingString: ${{ parameters.BuildTargetingString }}

- task: UsePythonVersion@0
displayName: 'Use Python 3.9'
inputs:
versionSpec: '3.9'

- task: DownloadPipelineArtifact@2
inputs:
artifactName: 'packages'
targetPath: $(Build.ArtifactStagingDirectory)

- script: |
pip install -r eng/regression_tools.txt
displayName: 'Prep Environment'
- template: /eng/common/testproxy/test-proxy-tool.yml
parameters:
runProxy: false

- template: ../steps/set-dev-build.yml
parameters:
ServiceDirectory: ${{ parameters.ServiceDirectory }}

- ${{if eq(variables['System.TeamProject'], 'internal') }}:
- template: ../steps/auth-dev-feed.yml
parameters:
DevFeedName: ${{ parameters.DevFeedName }}

- task: PythonScript@0
displayName: 'Test Latest Released Dependents'
inputs:
scriptPath: 'scripts/devops_tasks/test_regression.py'
arguments: >-
"$(TargetingString)"
--service="${{ parameters.ServiceDirectory }}"
--dependent-service="$(DependentService)"
--whl-dir="${{ parameters.BuildStagingDirectory }}"
--mark-arg="not cosmosEmulator"
- task: PythonScript@0
displayName: 'Test Oldest Released Dependents'
inputs:
scriptPath: 'scripts/devops_tasks/test_regression.py'
arguments: >-
"$(TargetingString)"
--service="${{ parameters.ServiceDirectory }}"
--dependent-service="$(DependentService)"
--whl-dir="${{ parameters.BuildStagingDirectory }}"
--verify-latest=False
--mark-arg="not cosmosEmulator"
45 changes: 45 additions & 0 deletions eng/pipelines/templates/stages/regression-job-matrix.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"matrix": {
"Agent": {
"ubuntu-20.04": {
"OSVmImage": "MMSUbuntu20.04",
"Pool": "azsdk-pool-mms-ubuntu-2004-general"
}
},
"DependentService": [
"schemaregistry",
"remoterendering",
"eventhub",
"cognitivelanguage",
"tables",
"purview",
"eventgrid",
"confidentialledger",
"mixedreality",
"agrifood",
"monitor",
"attestation",
"digitaltwins",
"synapse",
"servicebus",
"keyvault",
"videoanalyzer",
"communication",
"search",
"cosmos",
"modelsrepository",
"containerregistry",
"identity",
"core",
"template",
"appconfiguration",
"webpubsub",
"textanalytics",
"metricsadvisor",
"storage",
"formrecognizer",
"translation",
"deviceupdate"
]
}
}
55 changes: 0 additions & 55 deletions eng/pipelines/templates/steps/test_regression.yml

This file was deleted.

34 changes: 23 additions & 11 deletions scripts/devops_tasks/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import argparse
import glob
import pdb
import sys
import os
import logging
Expand Down Expand Up @@ -312,22 +313,24 @@ def _is_package_installed(self, package, version):


# This method identifies package dependency map for all packages in azure sdk
def find_package_dependency(glob_string, repo_root_dir):
def find_package_dependency(glob_string, repo_root_dir, dependent_service):
package_paths = process_glob_string(glob_string, repo_root_dir, "", "Regression")
dependent_service_filter = os.path.join('sdk', dependent_service.lower())

dependency_map = {}
for pkg_root in package_paths:
_, _, _, requires = parse_setup(pkg_root)
if dependent_service_filter in pkg_root:
_, _, _, requires = parse_setup(pkg_root)

# Get a list of package names from install requires
required_pkgs = [parse_require(r)[0] for r in requires]
required_pkgs = [p for p in required_pkgs if p.startswith("azure")]
# Get a list of package names from install requires
required_pkgs = [parse_require(r)[0] for r in requires]
required_pkgs = [p for p in required_pkgs if p.startswith("azure")]

for req_pkg in required_pkgs:
if req_pkg not in dependency_map:
dependency_map[req_pkg] = []
dependency_map[req_pkg].append(pkg_root)
for req_pkg in required_pkgs:
if req_pkg not in dependency_map:
dependency_map[req_pkg] = []
dependency_map[req_pkg].append(pkg_root)

logging.info("Package dependency: {}".format(dependency_map))
return dependency_map


Expand Down Expand Up @@ -368,7 +371,9 @@ def run_main(args):
logging.info("Path {} already exists. Skipping step to clone github repo".format(code_repo_root))

# find package dependency map for azure sdk
pkg_dependency = find_package_dependency(AZURE_GLOB_STRING, code_repo_root)
pkg_dependency = find_package_dependency(AZURE_GLOB_STRING, code_repo_root, args.dependent_service)

logging.info("Package dependency: {}".format(pkg_dependency))

# Create regression text context. One context object will be reused for all packages
context = RegressionContext(args.whl_dir, temp_dir, str_to_bool(args.verify_latest), args.mark_arg)
Expand Down Expand Up @@ -397,6 +402,13 @@ def run_main(args):
"--service",
help=("Name of service directory (under sdk/) to test." "Example: --service applicationinsights"),
)

parser.add_argument(
"--dependent-service",
dest="dependent_service",
default="",
help=("Optional filter to force regression testing of only dependent packages of service X."),
)

parser.add_argument(
"--whl-dir",
Expand Down
Loading

0 comments on commit 03f9e2b

Please sign in to comment.