Skip to content

Commit 3696051

Browse files
github-actions[bot]paulmedynskipriyankatiwari08
authored
[6.1.6 Cherry-pick] Create YAML Kerberos Test Pipeline (#4263)
* Cherry-pick of #4252 requires manual resolution To resolve, run: git cherry-pick a3648e4 * Create YAML Kerberos Test Pipeline (#4252) Co-authored-by: Priyanka Tiwari <prtiwar@microsoft.com> * Adapted the kerberos pipeline to work with the 6.1 style targets in build.proj. * Fix kerberos coverage template parameters * Fix Windows AKV restore caching and Linux per-TF build * Split Windows Kerberos job into netcore and netfx * Move Windows NetFx into its own parallel stage * Replace .NET Core terminology with .NET in comments * Remove .NET 10 from kerberos release/6.1 pipeline * Fix Kerberos coverage results path handling --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Co-authored-by: Priyanka Tiwari <prtiwar@microsoft.com>
1 parent 5797b27 commit 3696051

7 files changed

Lines changed: 696 additions & 0 deletions

eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ jobs:
124124
}
125125
}
126126
127+
if ($jobs.Count -eq 0) {
128+
Write-Error "No .coverage files found in $InputDirectoryPath. Cannot merge."
129+
exit 1
130+
}
131+
127132
Write-Host "Merging started..."
128133
Wait-Job -Job $jobs
129134

eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pr:
2828
- build.proj
2929
- NuGet.config
3030
exclude:
31+
- eng/pipelines/kerberos/*
3132
- eng/pipelines/onebranch/*
3233

3334
# Commit triggers for CI runs on specified branches.

eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pr:
2828
- build.proj
2929
- NuGet.config
3030
exclude:
31+
- eng/pipelines/kerberos/*
3132
- eng/pipelines/onebranch/*
3233

3334
# Commit triggers for CI runs on specified branches.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#################################################################################
2+
# Licensed to the .NET Foundation under one or more agreements. #
3+
# The .NET Foundation licenses this file to you under the MIT license. #
4+
# See the LICENSE file in the project root for more information. #
5+
#################################################################################
6+
7+
# Shared build-and-test steps used by both the Windows and Linux Kerberos jobs.
8+
#
9+
# Parameters:
10+
# buildTargets — Ordered build.proj targets to run before tests (for
11+
# example BuildNetCore, BuildNetFx, BuildTestsNetCore).
12+
# testFramework — The TFM to test against (e.g. net9.0, net462).
13+
# testRunTitle — Title for the published test results (displayed in the ADO
14+
# Tests tab).
15+
# artifactName — Name of the published pipeline artifact that carries the
16+
# test results and coverage files.
17+
18+
parameters:
19+
20+
# Ordered build.proj targets to run before test execution.
21+
- name: buildTargets
22+
type: object
23+
24+
# TFM to pass to the test targets (-p:TestFramework).
25+
- name: testFramework
26+
type: string
27+
28+
# Title shown in the ADO Tests tab for this run.
29+
- name: testRunTitle
30+
type: string
31+
32+
# Pipeline artifact name for test results and coverage.
33+
- name: artifactName
34+
type: string
35+
36+
steps:
37+
38+
# ---------------------------------------------------------------------------
39+
# Build
40+
# ---------------------------------------------------------------------------
41+
42+
# Build the configured targets in order.
43+
#
44+
# The test stages build as part of their targets, but this separate step isolates build failures
45+
# so we can fail fast before running tests. Retries are enabled intentionally (1 attempt for
46+
# build, 2 attempts for test steps) to reduce transient infrastructure-related failures.
47+
#
48+
- ${{ each target in parameters.buildTargets }}:
49+
- task: DotNetCoreCLI@2
50+
displayName: Build (${{ target }})
51+
retryCountOnTaskFailure: 1
52+
inputs:
53+
command: build
54+
projects: build.proj
55+
arguments: >-
56+
-t:${{ target }}
57+
-p:Configuration=Release
58+
-p:TF=${{ parameters.testFramework }}
59+
60+
# ---------------------------------------------------------------------------
61+
# Run tests in separate steps to permit focused retries.
62+
# ---------------------------------------------------------------------------
63+
64+
# Set a shared job variable so all test and publish steps use one results path.
65+
- pwsh: |
66+
Write-Host "##vso[task.setvariable variable=ResultsDirectory]$(Build.SourcesDirectory)/TestResults"
67+
displayName: Set test results directory variable
68+
69+
# Run the Unit Test suite.
70+
- task: DotNetCoreCLI@2
71+
displayName: Run Unit Tests (${{ parameters.testFramework }})
72+
retryCountOnTaskFailure: 2
73+
inputs:
74+
command: build
75+
projects: build.proj
76+
arguments: >-
77+
-t:RunUnitTests
78+
-p:TF=${{ parameters.testFramework }}
79+
-p:Configuration=Release
80+
-p:ResultsDirectory=$(ResultsDirectory)
81+
82+
# Run the Functional Test suite.
83+
- task: DotNetCoreCLI@2
84+
displayName: Run Functional Tests (${{ parameters.testFramework }})
85+
retryCountOnTaskFailure: 2
86+
inputs:
87+
command: build
88+
projects: build.proj
89+
arguments: >-
90+
-t:RunFunctionalTests
91+
-p:TF=${{ parameters.testFramework }}
92+
-p:Configuration=Release
93+
-p:ResultsDirectory=$(ResultsDirectory)
94+
95+
# Run the Manual Test suite.
96+
- task: DotNetCoreCLI@2
97+
displayName: Run Manual Tests (${{ parameters.testFramework }})
98+
retryCountOnTaskFailure: 2
99+
inputs:
100+
command: build
101+
projects: build.proj
102+
arguments: >-
103+
-t:RunManualTests
104+
-p:TF=${{ parameters.testFramework }}
105+
-p:Configuration=Release
106+
-p:ResultsDirectory=$(ResultsDirectory)
107+
108+
# ---------------------------------------------------------------------------
109+
# Publish results & coverage
110+
# ---------------------------------------------------------------------------
111+
112+
# Azure Pipelines task conditions do not support path existence checks directly,
113+
# so compute this once and gate later steps on the variable.
114+
- pwsh: |
115+
$resultsDir = "$(ResultsDirectory)"
116+
if (Test-Path -LiteralPath $resultsDir) {
117+
Write-Host "##vso[task.setvariable variable=HasTestResultsDir]true"
118+
}
119+
else {
120+
Write-Host "##vso[task.setvariable variable=HasTestResultsDir]false"
121+
}
122+
displayName: Detect TestResults directory
123+
condition: succeededOrFailed()
124+
125+
# Publish the TRX test results to the pipeline run.
126+
- task: PublishTestResults@2
127+
displayName: Publish Test Results
128+
condition: and(succeededOrFailed(), eq(variables['HasTestResultsDir'], 'true'))
129+
inputs:
130+
testResultsFormat: VSTest
131+
# build.proj defaults ResultsDirectory to 'TestResults' (relative to the source root).
132+
# We override it to an absolute path in the test steps above.
133+
testResultsFiles: $(ResultsDirectory)/**/*.trx
134+
mergeTestResults: true
135+
testRunTitle: ${{ parameters.testRunTitle }}
136+
buildConfiguration: Release
137+
138+
# Give our coverage files a unique name to make it clear where they originated when we download
139+
# the artifacts from all jobs in the merge stage.
140+
- pwsh: |
141+
cd $(ResultsDirectory)
142+
Get-ChildItem -Filter "*.coverage" -Recurse |
143+
Rename-Item -NewName { "${{ parameters.testFramework }}" + $_.Name }
144+
displayName: Rename coverage files
145+
condition: and(succeededOrFailed(), eq(variables['HasTestResultsDir'], 'true'))
146+
147+
# Publish TRX test results and coverage files as pipeline artifacts. The merge stage needs the
148+
# coverage files from all of the jobs.
149+
- task: PublishPipelineArtifact@1
150+
displayName: Publish Test Artifacts
151+
condition: and(succeededOrFailed(), eq(variables['HasTestResultsDir'], 'true'))
152+
inputs:
153+
targetPath: $(ResultsDirectory)
154+
artifact: ${{ parameters.artifactName }}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#################################################################################
2+
# Licensed to the .NET Foundation under one or more agreements. #
3+
# The .NET Foundation licenses this file to you under the MIT license. #
4+
# See the LICENSE file in the project root for more information. #
5+
#################################################################################
6+
7+
# This template leaves the Active Directory domain and destroys Kerberos
8+
# credentials. It should be referenced at the end of any job that called
9+
# linux-init-step.yml.
10+
#
11+
# All steps use condition: always() so that cleanup runs even when previous
12+
# steps fail.
13+
14+
parameters:
15+
16+
# The Active Directory domain to leave (e.g. mydomain.contoso.com).
17+
- name: kerberosDomain
18+
type: string
19+
20+
# The domain user account used during the join.
21+
- name: kerberosDomainUser
22+
type: string
23+
24+
# The password for the domain user account.
25+
- name: kerberosDomainPassword
26+
type: string
27+
28+
steps:
29+
30+
- bash: |
31+
set -uo pipefail
32+
33+
DOMAIN="${{ parameters.kerberosDomain }}"
34+
DOMAIN_USER="${{ parameters.kerberosDomainUser }}"
35+
DOMAIN_PASSWORD="${{ parameters.kerberosDomainPassword }}"
36+
DOMAIN_UPPER=$(echo "$DOMAIN" | tr '[:lower:]' '[:upper:]')
37+
38+
# Leave the domain
39+
echo "$DOMAIN_PASSWORD" | sudo realm leave "$DOMAIN_UPPER" --verbose \
40+
-U "$DOMAIN_USER@$DOMAIN_UPPER" || true
41+
42+
# Destroy the TGT and credential cache
43+
kdestroy || true
44+
displayName: Clean up Kerberos (domain leave + kdestroy)
45+
condition: always()
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#################################################################################
2+
# Licensed to the .NET Foundation under one or more agreements. #
3+
# The .NET Foundation licenses this file to you under the MIT license. #
4+
# See the LICENSE file in the project root for more information. #
5+
#################################################################################
6+
7+
# This template joins a Linux agent to an Active Directory domain using Kerberos
8+
# and acquires a TGT (Ticket-Granting Ticket) for the specified domain user.
9+
#
10+
# Prerequisites:
11+
# - The agent must be running on Ubuntu/Debian (uses apt-get).
12+
# - The domain controller must be reachable from the agent network.
13+
#
14+
# After this step completes successfully, the agent will have:
15+
# - Kerberos packages installed (krb5-user, realmd, sssd, adcli, etc.)
16+
# - Hostname set to FQDN within the domain
17+
# - NTP synchronized with the domain controller
18+
# - Machine joined to the AD domain
19+
# - A valid Kerberos TGT for the specified user
20+
21+
parameters:
22+
23+
# The Active Directory domain to join (e.g. mydomain.contoso.com).
24+
- name: kerberosDomain
25+
type: string
26+
27+
# The Organizational Unit in which to place the computer account.
28+
- name: kerberosDomainOU
29+
type: string
30+
31+
# The domain user account to authenticate with (sAMAccountName, without @realm).
32+
- name: kerberosDomainUser
33+
type: string
34+
35+
# The password for the domain user account.
36+
- name: kerberosDomainPassword
37+
type: string
38+
39+
steps:
40+
41+
- bash: |
42+
set -euo pipefail
43+
44+
DOMAIN="${{ parameters.kerberosDomain }}"
45+
DOMAIN_OU="${{ parameters.kerberosDomainOU }}"
46+
DOMAIN_USER="${{ parameters.kerberosDomainUser }}"
47+
DOMAIN_PASSWORD="${{ parameters.kerberosDomainPassword }}"
48+
DOMAIN_UPPER=$(echo "$DOMAIN" | tr '[:lower:]' '[:upper:]')
49+
50+
echo "Domain: $DOMAIN"
51+
echo "Realm: $DOMAIN_UPPER"
52+
echo "User: $DOMAIN_USER"
53+
echo "OU: $DOMAIN_OU"
54+
55+
if [ -z "$DOMAIN_PASSWORD" ]; then
56+
echo "##vso[task.logissue type=error]KerberosDomainPassword is empty"
57+
exit 1
58+
fi
59+
60+
# -----------------------------------------------------------------------
61+
# Install Kerberos and AD integration packages
62+
# -----------------------------------------------------------------------
63+
echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
64+
65+
sudo apt-get -y update
66+
sudo apt-get install -y dialog apt-utils
67+
sudo apt-get install -y \
68+
krb5-user samba sssd sssd-tools libnss-sss libpam-sss \
69+
ntp ntpdate realmd adcli
70+
71+
# -----------------------------------------------------------------------
72+
# Set the hostname to FQDN within the domain
73+
# -----------------------------------------------------------------------
74+
CURRENT_HOSTNAME="$(hostname)"
75+
if [ "$CURRENT_HOSTNAME" = "$DOMAIN" ] || [[ "$CURRENT_HOSTNAME" == *".$DOMAIN" ]]; then
76+
echo "Hostname already uses domain suffix '.$DOMAIN': $CURRENT_HOSTNAME"
77+
else
78+
sudo hostnamectl set-hostname "$CURRENT_HOSTNAME.$DOMAIN"
79+
fi
80+
81+
# -----------------------------------------------------------------------
82+
# Synchronize time with the domain controller (required for Kerberos)
83+
# -----------------------------------------------------------------------
84+
if ! sudo grep -Fqx "server $DOMAIN" /etc/ntp.conf; then
85+
echo "server $DOMAIN" | sudo tee -a /etc/ntp.conf
86+
fi
87+
sudo systemctl stop ntp
88+
sudo ntpdate "$DOMAIN"
89+
sudo systemctl start ntp
90+
91+
# -----------------------------------------------------------------------
92+
# Configure Kerberos realm
93+
# -----------------------------------------------------------------------
94+
echo "[libdefaults]
95+
default_realm = $DOMAIN_UPPER
96+
rdns = false" | sudo tee /etc/krb5.conf
97+
98+
# -----------------------------------------------------------------------
99+
# Discover and join the domain
100+
# -----------------------------------------------------------------------
101+
sudo realm discover "$DOMAIN_UPPER"
102+
103+
echo "$DOMAIN_PASSWORD" | sudo realm join --verbose "$DOMAIN_UPPER" \
104+
-U "$DOMAIN_USER@$DOMAIN_UPPER" \
105+
--computer-ou "OU=$DOMAIN_OU"
106+
107+
realm list
108+
109+
# -----------------------------------------------------------------------
110+
# Acquire a Kerberos TGT
111+
# -----------------------------------------------------------------------
112+
echo "$DOMAIN_PASSWORD" | kinit "$DOMAIN_USER@$DOMAIN_UPPER"
113+
114+
klist
115+
sudo ip addr
116+
sudo ip route
117+
displayName: Initialize Kerberos (domain join + kinit)

0 commit comments

Comments
 (0)