diff --git a/.github/workflows/labeler.yml b/.github/workflows/label-issue.yml
similarity index 72%
rename from .github/workflows/labeler.yml
rename to .github/workflows/label-issue.yml
index 4198a6044f..0e07ee6c0a 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/label-issue.yml
@@ -1,4 +1,4 @@
-name: "Issue Labeler"
+name: "Label Issue"
on:
issues:
types: [opened]
@@ -11,7 +11,7 @@ jobs:
triage:
runs-on: ubuntu-latest
steps:
- - uses: github/issue-labeler@v3.4
+ - uses: github/issue-labeler@c1b0f9f52a63158c4adc09425e858e87b32e9685 # v3.4
with:
configuration-path: .github/labeler.yml
enable-versioned-regex: 0
diff --git a/.github/workflows/label-pull-request.yml b/.github/workflows/label-pull-request.yml
new file mode 100644
index 0000000000..ffa2e31f50
--- /dev/null
+++ b/.github/workflows/label-pull-request.yml
@@ -0,0 +1,43 @@
+# Workflow: Label Merged PRs with "needs-backport"
+#
+# This workflow automatically adds a "needs-backport" label to all PRs
+# that are merged into development branches. This helps maintainers track which
+# changes need to be backported to main or release/ branches.
+#
+name: Label merged PRs for backport
+
+on:
+ # pull_request_target runs workflow code from the base branch with write permissions,
+ # while pull_request runs code from the PR branch with read-only permissions for forks.
+ # Use pull_request_target here to get write access for labeling fork PRs.
+ # Safe because this workflow only uses trusted inputs (PR number) and doesn't
+ # checkout or execute PR code.
+ pull_request_target:
+ types:
+ # To label merged PRs, trigger on all closed PRs and check for merged
+ # status later, in the job condition
+ - closed
+ branches:
+ - nightly
+
+permissions:
+ # Required to add labels to pull requests
+ pull-requests: write
+
+jobs:
+ add-backport-label:
+ # Only run if the PR was actually merged (not just closed)
+ if: github.event.pull_request.merged == true
+ runs-on: ubuntu-latest
+ steps:
+ # No checkout needed
+ - name: Add needs-backport label
+ # Use GitHub CLI (gh) which is pre-installed on GitHub-hosted runners
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # Provides repository context for gh CLI commands
+ GH_REPO: ${{ github.repository }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ run: |
+ gh pr edit "$PR_NUMBER" --add-label "needs-backport"
+ echo "Successfully labeled PR #$PR_NUMBER"
diff --git a/README.aspire-dashboard.md b/README.aspire-dashboard.md
index 5c07f7ade3..52022895f0 100644
--- a/README.aspire-dashboard.md
+++ b/README.aspire-dashboard.md
@@ -113,14 +113,15 @@ Limits are per-resource. For example, a `MaxLogCount` value of 10,000 configures
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
13.1.0, 13.1, 13, latest | [Dockerfile](src/aspire-dashboard/amd64/Dockerfile) | Azure Linux 3.0
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
13.1.0, 13.1, 13, latest | [Dockerfile](src/aspire-dashboard/arm64v8/Dockerfile) | Azure Linux 3.0
+
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/aspire-dashboard/tags/list) for all supported and unsupported tags.*
diff --git a/README.aspnet.md b/README.aspnet.md
index 4dac1e92a5..f5a083faec 100644
--- a/README.aspnet.md
+++ b/README.aspnet.md
@@ -81,7 +81,7 @@ For more information, see the [composite images section in the Image Variants do
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-amd64, 10.0-noble-amd64, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/aspnet/10.0/noble/amd64/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-amd64, 10.0-noble-chiseled-amd64, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/aspnet/10.0/noble-chiseled/amd64/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-amd64, 10.0-noble-chiseled-extra-amd64, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/aspnet/10.0/noble-chiseled-extra/amd64/Dockerfile) | Ubuntu 24.04
@@ -147,7 +147,7 @@ Tags | Dockerfile | OS Version
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-arm64v8, 10.0-noble-arm64v8, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/aspnet/10.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-arm64v8, 10.0-noble-chiseled-arm64v8, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/aspnet/10.0/noble-chiseled/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-arm64v8, 10.0-noble-chiseled-extra-arm64v8, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/aspnet/10.0/noble-chiseled-extra/arm64v8/Dockerfile) | Ubuntu 24.04
@@ -213,7 +213,7 @@ Tags | Dockerfile | OS Version
### Linux arm32 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-arm32v7, 10.0-noble-arm32v7, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/aspnet/10.0/noble/arm32v7/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-arm32v7, 10.0-noble-chiseled-arm32v7, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/aspnet/10.0/noble-chiseled/arm32v7/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-arm32v7, 10.0-noble-chiseled-extra-arm32v7, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/aspnet/10.0/noble-chiseled-extra/arm32v7/Dockerfile) | Ubuntu 24.04
@@ -256,51 +256,27 @@ Tags | Dockerfile | OS Version
8.0.23-jammy-chiseled-composite-arm32v7, 8.0-jammy-chiseled-composite-arm32v7, 8.0.23-jammy-chiseled-composite, 8.0-jammy-chiseled-composite | [Dockerfile](src/aspnet/8.0/jammy-chiseled-composite/arm32v7/Dockerfile) | Ubuntu 22.04
8.0.23-jammy-chiseled-composite-extra-arm32v7, 8.0-jammy-chiseled-composite-extra-arm32v7, 8.0.23-jammy-chiseled-composite-extra, 8.0-jammy-chiseled-composite-extra | [Dockerfile](src/aspnet/8.0/jammy-chiseled-composite-extra/arm32v7/Dockerfile) | Ubuntu 22.04
-### Nano Server 2025 amd64 Tags
+### Windows amd64 Tags
-Tag | Dockerfile
----------| ---------------
-10.0.2-nanoserver-ltsc2025, 10.0-nanoserver-ltsc2025 | [Dockerfile](src/aspnet/10.0/nanoserver-ltsc2025/amd64/Dockerfile)
-9.0.12-nanoserver-ltsc2025, 9.0-nanoserver-ltsc2025 | [Dockerfile](src/aspnet/9.0/nanoserver-ltsc2025/amd64/Dockerfile)
-8.0.23-nanoserver-ltsc2025, 8.0-nanoserver-ltsc2025 | [Dockerfile](src/aspnet/8.0/nanoserver-ltsc2025/amd64/Dockerfile)
-
-### Windows Server Core 2025 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.2-windowsservercore-ltsc2025, 10.0-windowsservercore-ltsc2025 | [Dockerfile](src/aspnet/10.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-9.0.12-windowsservercore-ltsc2025, 9.0-windowsservercore-ltsc2025 | [Dockerfile](src/aspnet/9.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-8.0.23-windowsservercore-ltsc2025, 8.0-windowsservercore-ltsc2025 | [Dockerfile](src/aspnet/8.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-
-### Nano Server 2022 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.2-nanoserver-ltsc2022, 10.0-nanoserver-ltsc2022 | [Dockerfile](src/aspnet/10.0/nanoserver-ltsc2022/amd64/Dockerfile)
-9.0.12-nanoserver-ltsc2022, 9.0-nanoserver-ltsc2022 | [Dockerfile](src/aspnet/9.0/nanoserver-ltsc2022/amd64/Dockerfile)
-8.0.23-nanoserver-ltsc2022, 8.0-nanoserver-ltsc2022 | [Dockerfile](src/aspnet/8.0/nanoserver-ltsc2022/amd64/Dockerfile)
-
-### Windows Server Core 2022 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.2-windowsservercore-ltsc2022, 10.0-windowsservercore-ltsc2022 | [Dockerfile](src/aspnet/10.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-9.0.12-windowsservercore-ltsc2022, 9.0-windowsservercore-ltsc2022 | [Dockerfile](src/aspnet/9.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-8.0.23-windowsservercore-ltsc2022, 8.0-windowsservercore-ltsc2022 | [Dockerfile](src/aspnet/8.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-
-### Nano Server, version 1809 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-9.0.12-nanoserver-1809, 9.0-nanoserver-1809 | [Dockerfile](src/aspnet/9.0/nanoserver-1809/amd64/Dockerfile)
-8.0.23-nanoserver-1809, 8.0-nanoserver-1809 | [Dockerfile](src/aspnet/8.0/nanoserver-1809/amd64/Dockerfile)
-
-### Windows Server Core 2019 amd64 Tags
+Tags | Dockerfile | OS Version
+---- | ---------- | ----------
+10.0.2-nanoserver-ltsc2025, 10.0-nanoserver-ltsc2025 | [Dockerfile](src/aspnet/10.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+10.0.2-windowsservercore-ltsc2025, 10.0-windowsservercore-ltsc2025 | [Dockerfile](src/aspnet/10.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+10.0.2-nanoserver-ltsc2022, 10.0-nanoserver-ltsc2022 | [Dockerfile](src/aspnet/10.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+10.0.2-windowsservercore-ltsc2022, 10.0-windowsservercore-ltsc2022 | [Dockerfile](src/aspnet/10.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+9.0.12-nanoserver-ltsc2025, 9.0-nanoserver-ltsc2025 | [Dockerfile](src/aspnet/9.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+9.0.12-windowsservercore-ltsc2025, 9.0-windowsservercore-ltsc2025 | [Dockerfile](src/aspnet/9.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+9.0.12-nanoserver-ltsc2022, 9.0-nanoserver-ltsc2022 | [Dockerfile](src/aspnet/9.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+9.0.12-windowsservercore-ltsc2022, 9.0-windowsservercore-ltsc2022 | [Dockerfile](src/aspnet/9.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+9.0.12-nanoserver-1809, 9.0-nanoserver-1809 | [Dockerfile](src/aspnet/9.0/nanoserver-1809/amd64/Dockerfile) | Nano Server, version 1809
+9.0.12-windowsservercore-ltsc2019, 9.0-windowsservercore-ltsc2019 | [Dockerfile](src/aspnet/9.0/windowsservercore-ltsc2019/amd64/Dockerfile) | Windows Server Core 2019
+8.0.23-nanoserver-ltsc2025, 8.0-nanoserver-ltsc2025 | [Dockerfile](src/aspnet/8.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+8.0.23-windowsservercore-ltsc2025, 8.0-windowsservercore-ltsc2025 | [Dockerfile](src/aspnet/8.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+8.0.23-nanoserver-ltsc2022, 8.0-nanoserver-ltsc2022 | [Dockerfile](src/aspnet/8.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+8.0.23-windowsservercore-ltsc2022, 8.0-windowsservercore-ltsc2022 | [Dockerfile](src/aspnet/8.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+8.0.23-nanoserver-1809, 8.0-nanoserver-1809 | [Dockerfile](src/aspnet/8.0/nanoserver-1809/amd64/Dockerfile) | Nano Server, version 1809
+8.0.23-windowsservercore-ltsc2019, 8.0-windowsservercore-ltsc2019 | [Dockerfile](src/aspnet/8.0/windowsservercore-ltsc2019/amd64/Dockerfile) | Windows Server Core 2019
-Tag | Dockerfile
----------| ---------------
-9.0.12-windowsservercore-ltsc2019, 9.0-windowsservercore-ltsc2019 | [Dockerfile](src/aspnet/9.0/windowsservercore-ltsc2019/amd64/Dockerfile)
-8.0.23-windowsservercore-ltsc2019, 8.0-windowsservercore-ltsc2019 | [Dockerfile](src/aspnet/8.0/windowsservercore-ltsc2019/amd64/Dockerfile)
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/aspnet/tags/list) for all supported and unsupported tags.*
diff --git a/README.monitor-base.md b/README.monitor-base.md
index 967cf548e7..9adf24b53f 100644
--- a/README.monitor-base.md
+++ b/README.monitor-base.md
@@ -48,7 +48,7 @@ The following Dockerfiles demonstrate how you can use this base image to build a
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.0-amd64, 10.0-amd64, 10.0.0, 10.0, 10, latest | [Dockerfile](src/monitor-base/10.0/azurelinux-distroless/amd64/Dockerfile) | Azure Linux 3.0
9.0.5-amd64, 9.0-amd64, 9.0.5, 9.0, 9 | [Dockerfile](src/monitor-base/9.0/azurelinux-distroless/amd64/Dockerfile) | Azure Linux 3.0
8.1.3-ubuntu-chiseled-amd64, 8.1-ubuntu-chiseled-amd64, 8-ubuntu-chiseled-amd64, 8.1.3-ubuntu-chiseled, 8.1-ubuntu-chiseled, 8-ubuntu-chiseled, 8.1.3, 8.1, 8 | [Dockerfile](src/monitor-base/8.1/ubuntu-chiseled/amd64/Dockerfile) | Ubuntu 22.04
@@ -57,11 +57,12 @@ Tags | Dockerfile | OS Version
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.0-arm64v8, 10.0-arm64v8, 10.0.0, 10.0, 10, latest | [Dockerfile](src/monitor-base/10.0/azurelinux-distroless/arm64v8/Dockerfile) | Azure Linux 3.0
9.0.5-arm64v8, 9.0-arm64v8, 9.0.5, 9.0, 9 | [Dockerfile](src/monitor-base/9.0/azurelinux-distroless/arm64v8/Dockerfile) | Azure Linux 3.0
8.1.3-ubuntu-chiseled-arm64v8, 8.1-ubuntu-chiseled-arm64v8, 8-ubuntu-chiseled-arm64v8, 8.1.3-ubuntu-chiseled, 8.1-ubuntu-chiseled, 8-ubuntu-chiseled, 8.1.3, 8.1, 8 | [Dockerfile](src/monitor-base/8.1/ubuntu-chiseled/arm64v8/Dockerfile) | Ubuntu 22.04
8.1.3-azurelinux-distroless-arm64v8, 8.1-azurelinux-distroless-arm64v8, 8-azurelinux-distroless-arm64v8, 8.1.3-azurelinux-distroless, 8.1-azurelinux-distroless, 8-azurelinux-distroless | [Dockerfile](src/monitor-base/8.1/azurelinux-distroless/arm64v8/Dockerfile) | Azure Linux 3.0
+
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/monitor/base/tags/list) for all supported and unsupported tags.*
diff --git a/README.monitor.md b/README.monitor.md
index d5c22bfcc4..092053b6ec 100644
--- a/README.monitor.md
+++ b/README.monitor.md
@@ -49,7 +49,7 @@ See [documentation](https://go.microsoft.com/fwlink/?linkid=2158052) for how to
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.0, 10.0, 10, latest | [Dockerfile](src/monitor/10.0/azurelinux-distroless/amd64/Dockerfile) | Azure Linux 3.0
9.0.5, 9.0, 9 | [Dockerfile](src/monitor/9.0/azurelinux-distroless/amd64/Dockerfile) | Azure Linux 3.0
8.1.3-ubuntu-chiseled, 8.1-ubuntu-chiseled, 8-ubuntu-chiseled, 8.1.3, 8.1, 8 | [Dockerfile](src/monitor/8.1/ubuntu-chiseled/amd64/Dockerfile) | Ubuntu 22.04
@@ -58,11 +58,12 @@ Tags | Dockerfile | OS Version
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.0, 10.0, 10, latest | [Dockerfile](src/monitor/10.0/azurelinux-distroless/arm64v8/Dockerfile) | Azure Linux 3.0
9.0.5, 9.0, 9 | [Dockerfile](src/monitor/9.0/azurelinux-distroless/arm64v8/Dockerfile) | Azure Linux 3.0
8.1.3-ubuntu-chiseled, 8.1-ubuntu-chiseled, 8-ubuntu-chiseled, 8.1.3, 8.1, 8 | [Dockerfile](src/monitor/8.1/ubuntu-chiseled/arm64v8/Dockerfile) | Ubuntu 22.04
8.1.3-azurelinux-distroless, 8.1-azurelinux-distroless, 8-azurelinux-distroless | [Dockerfile](src/monitor/8.1/azurelinux-distroless/arm64v8/Dockerfile) | Azure Linux 3.0
+
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/monitor/tags/list) for all supported and unsupported tags.*
diff --git a/README.runtime-deps.md b/README.runtime-deps.md
index cfe5815198..44127281b0 100644
--- a/README.runtime-deps.md
+++ b/README.runtime-deps.md
@@ -63,7 +63,7 @@ They contain the following features:
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-amd64, 10.0-noble-amd64, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/runtime-deps/10.0/noble/amd64/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-amd64, 10.0-noble-chiseled-amd64, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/runtime-deps/10.0/noble-chiseled/amd64/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-amd64, 10.0-noble-chiseled-extra-amd64, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/runtime-deps/10.0/noble-chiseled-extra/amd64/Dockerfile) | Ubuntu 24.04
@@ -103,7 +103,7 @@ Tags | Dockerfile | OS Version
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-arm64v8, 10.0-noble-arm64v8, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/runtime-deps/10.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-arm64v8, 10.0-noble-chiseled-arm64v8, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/runtime-deps/10.0/noble-chiseled/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-arm64v8, 10.0-noble-chiseled-extra-arm64v8, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/runtime-deps/10.0/noble-chiseled-extra/arm64v8/Dockerfile) | Ubuntu 24.04
@@ -143,7 +143,7 @@ Tags | Dockerfile | OS Version
### Linux arm32 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-arm32v7, 10.0-noble-arm32v7, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/runtime-deps/10.0/noble/arm32v7/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-arm32v7, 10.0-noble-chiseled-arm32v7, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/runtime-deps/10.0/noble-chiseled/arm32v7/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-arm32v7, 10.0-noble-chiseled-extra-arm32v7, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/runtime-deps/10.0/noble-chiseled-extra/arm32v7/Dockerfile) | Ubuntu 24.04
@@ -167,6 +167,7 @@ Tags | Dockerfile | OS Version
8.0.23-jammy-arm32v7, 8.0-jammy-arm32v7, 8.0.23-jammy, 8.0-jammy | [Dockerfile](src/runtime-deps/8.0/jammy/arm32v7/Dockerfile) | Ubuntu 22.04
8.0.23-jammy-chiseled-arm32v7, 8.0-jammy-chiseled-arm32v7, 8.0.23-jammy-chiseled, 8.0-jammy-chiseled | [Dockerfile](src/runtime-deps/8.0/jammy-chiseled/arm32v7/Dockerfile) | Ubuntu 22.04
8.0.23-jammy-chiseled-extra-arm32v7, 8.0-jammy-chiseled-extra-arm32v7, 8.0.23-jammy-chiseled-extra, 8.0-jammy-chiseled-extra | [Dockerfile](src/runtime-deps/8.0/jammy-chiseled-extra/arm32v7/Dockerfile) | Ubuntu 22.04
+
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/runtime-deps/tags/list) for all supported and unsupported tags.*
diff --git a/README.runtime.md b/README.runtime.md
index b4d365304d..a15312b37f 100644
--- a/README.runtime.md
+++ b/README.runtime.md
@@ -70,7 +70,7 @@ They contain the following features:
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-amd64, 10.0-noble-amd64, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/runtime/10.0/noble/amd64/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-amd64, 10.0-noble-chiseled-amd64, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/runtime/10.0/noble-chiseled/amd64/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-amd64, 10.0-noble-chiseled-extra-amd64, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/runtime/10.0/noble-chiseled-extra/amd64/Dockerfile) | Ubuntu 24.04
@@ -110,7 +110,7 @@ Tags | Dockerfile | OS Version
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-arm64v8, 10.0-noble-arm64v8, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/runtime/10.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-arm64v8, 10.0-noble-chiseled-arm64v8, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/runtime/10.0/noble-chiseled/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-arm64v8, 10.0-noble-chiseled-extra-arm64v8, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/runtime/10.0/noble-chiseled-extra/arm64v8/Dockerfile) | Ubuntu 24.04
@@ -150,7 +150,7 @@ Tags | Dockerfile | OS Version
### Linux arm32 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.2-noble-arm32v7, 10.0-noble-arm32v7, 10.0.2-noble, 10.0-noble, 10.0.2, 10.0, latest | [Dockerfile](src/runtime/10.0/noble/arm32v7/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-arm32v7, 10.0-noble-chiseled-arm32v7, 10.0.2-noble-chiseled, 10.0-noble-chiseled | [Dockerfile](src/runtime/10.0/noble-chiseled/arm32v7/Dockerfile) | Ubuntu 24.04
10.0.2-noble-chiseled-extra-arm32v7, 10.0-noble-chiseled-extra-arm32v7, 10.0.2-noble-chiseled-extra, 10.0-noble-chiseled-extra | [Dockerfile](src/runtime/10.0/noble-chiseled-extra/arm32v7/Dockerfile) | Ubuntu 24.04
@@ -175,51 +175,27 @@ Tags | Dockerfile | OS Version
8.0.23-jammy-chiseled-arm32v7, 8.0-jammy-chiseled-arm32v7, 8.0.23-jammy-chiseled, 8.0-jammy-chiseled | [Dockerfile](src/runtime/8.0/jammy-chiseled/arm32v7/Dockerfile) | Ubuntu 22.04
8.0.23-jammy-chiseled-extra-arm32v7, 8.0-jammy-chiseled-extra-arm32v7, 8.0.23-jammy-chiseled-extra, 8.0-jammy-chiseled-extra | [Dockerfile](src/runtime/8.0/jammy-chiseled-extra/arm32v7/Dockerfile) | Ubuntu 22.04
-### Nano Server 2025 amd64 Tags
+### Windows amd64 Tags
-Tag | Dockerfile
----------| ---------------
-10.0.2-nanoserver-ltsc2025, 10.0-nanoserver-ltsc2025 | [Dockerfile](src/runtime/10.0/nanoserver-ltsc2025/amd64/Dockerfile)
-9.0.12-nanoserver-ltsc2025, 9.0-nanoserver-ltsc2025 | [Dockerfile](src/runtime/9.0/nanoserver-ltsc2025/amd64/Dockerfile)
-8.0.23-nanoserver-ltsc2025, 8.0-nanoserver-ltsc2025 | [Dockerfile](src/runtime/8.0/nanoserver-ltsc2025/amd64/Dockerfile)
-
-### Windows Server Core 2025 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.2-windowsservercore-ltsc2025, 10.0-windowsservercore-ltsc2025 | [Dockerfile](src/runtime/10.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-9.0.12-windowsservercore-ltsc2025, 9.0-windowsservercore-ltsc2025 | [Dockerfile](src/runtime/9.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-8.0.23-windowsservercore-ltsc2025, 8.0-windowsservercore-ltsc2025 | [Dockerfile](src/runtime/8.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-
-### Nano Server 2022 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.2-nanoserver-ltsc2022, 10.0-nanoserver-ltsc2022 | [Dockerfile](src/runtime/10.0/nanoserver-ltsc2022/amd64/Dockerfile)
-9.0.12-nanoserver-ltsc2022, 9.0-nanoserver-ltsc2022 | [Dockerfile](src/runtime/9.0/nanoserver-ltsc2022/amd64/Dockerfile)
-8.0.23-nanoserver-ltsc2022, 8.0-nanoserver-ltsc2022 | [Dockerfile](src/runtime/8.0/nanoserver-ltsc2022/amd64/Dockerfile)
-
-### Windows Server Core 2022 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.2-windowsservercore-ltsc2022, 10.0-windowsservercore-ltsc2022 | [Dockerfile](src/runtime/10.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-9.0.12-windowsservercore-ltsc2022, 9.0-windowsservercore-ltsc2022 | [Dockerfile](src/runtime/9.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-8.0.23-windowsservercore-ltsc2022, 8.0-windowsservercore-ltsc2022 | [Dockerfile](src/runtime/8.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-
-### Nano Server, version 1809 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-9.0.12-nanoserver-1809, 9.0-nanoserver-1809 | [Dockerfile](src/runtime/9.0/nanoserver-1809/amd64/Dockerfile)
-8.0.23-nanoserver-1809, 8.0-nanoserver-1809 | [Dockerfile](src/runtime/8.0/nanoserver-1809/amd64/Dockerfile)
-
-### Windows Server Core 2019 amd64 Tags
+Tags | Dockerfile | OS Version
+---- | ---------- | ----------
+10.0.2-nanoserver-ltsc2025, 10.0-nanoserver-ltsc2025 | [Dockerfile](src/runtime/10.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+10.0.2-windowsservercore-ltsc2025, 10.0-windowsservercore-ltsc2025 | [Dockerfile](src/runtime/10.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+10.0.2-nanoserver-ltsc2022, 10.0-nanoserver-ltsc2022 | [Dockerfile](src/runtime/10.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+10.0.2-windowsservercore-ltsc2022, 10.0-windowsservercore-ltsc2022 | [Dockerfile](src/runtime/10.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+9.0.12-nanoserver-ltsc2025, 9.0-nanoserver-ltsc2025 | [Dockerfile](src/runtime/9.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+9.0.12-windowsservercore-ltsc2025, 9.0-windowsservercore-ltsc2025 | [Dockerfile](src/runtime/9.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+9.0.12-nanoserver-ltsc2022, 9.0-nanoserver-ltsc2022 | [Dockerfile](src/runtime/9.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+9.0.12-windowsservercore-ltsc2022, 9.0-windowsservercore-ltsc2022 | [Dockerfile](src/runtime/9.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+9.0.12-nanoserver-1809, 9.0-nanoserver-1809 | [Dockerfile](src/runtime/9.0/nanoserver-1809/amd64/Dockerfile) | Nano Server, version 1809
+9.0.12-windowsservercore-ltsc2019, 9.0-windowsservercore-ltsc2019 | [Dockerfile](src/runtime/9.0/windowsservercore-ltsc2019/amd64/Dockerfile) | Windows Server Core 2019
+8.0.23-nanoserver-ltsc2025, 8.0-nanoserver-ltsc2025 | [Dockerfile](src/runtime/8.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+8.0.23-windowsservercore-ltsc2025, 8.0-windowsservercore-ltsc2025 | [Dockerfile](src/runtime/8.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+8.0.23-nanoserver-ltsc2022, 8.0-nanoserver-ltsc2022 | [Dockerfile](src/runtime/8.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+8.0.23-windowsservercore-ltsc2022, 8.0-windowsservercore-ltsc2022 | [Dockerfile](src/runtime/8.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+8.0.23-nanoserver-1809, 8.0-nanoserver-1809 | [Dockerfile](src/runtime/8.0/nanoserver-1809/amd64/Dockerfile) | Nano Server, version 1809
+8.0.23-windowsservercore-ltsc2019, 8.0-windowsservercore-ltsc2019 | [Dockerfile](src/runtime/8.0/windowsservercore-ltsc2019/amd64/Dockerfile) | Windows Server Core 2019
-Tag | Dockerfile
----------| ---------------
-9.0.12-windowsservercore-ltsc2019, 9.0-windowsservercore-ltsc2019 | [Dockerfile](src/runtime/9.0/windowsservercore-ltsc2019/amd64/Dockerfile)
-8.0.23-windowsservercore-ltsc2019, 8.0-windowsservercore-ltsc2019 | [Dockerfile](src/runtime/8.0/windowsservercore-ltsc2019/amd64/Dockerfile)
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/runtime/tags/list) for all supported and unsupported tags.*
diff --git a/README.samples.md b/README.samples.md
index 2c5826d2d2..4c754488ef 100644
--- a/README.samples.md
+++ b/README.samples.md
@@ -85,7 +85,7 @@ They contain the following features:
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
dotnetapp-10.0, dotnetapp, latest | [Dockerfile](samples/dotnetapp/Dockerfile.alpine) | Alpine
dotnetapp-chiseled-10.0, dotnetapp-chiseled | [Dockerfile](samples/dotnetapp/Dockerfile.chiseled) | Ubuntu
aspnetapp-10.0, aspnetapp | [Dockerfile](samples/aspnetapp/Dockerfile.alpine) | Alpine
@@ -94,7 +94,7 @@ aspnetapp-chiseled-10.0, aspnetapp-chiseled | [Dockerfile](samples/aspnetapp/Doc
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
dotnetapp-10.0, dotnetapp, latest | [Dockerfile](samples/dotnetapp/Dockerfile.alpine) | Alpine
dotnetapp-chiseled-10.0, dotnetapp-chiseled | [Dockerfile](samples/dotnetapp/Dockerfile.chiseled) | Ubuntu
aspnetapp-10.0, aspnetapp | [Dockerfile](samples/aspnetapp/Dockerfile.alpine) | Alpine
@@ -103,18 +103,19 @@ aspnetapp-chiseled-10.0, aspnetapp-chiseled | [Dockerfile](samples/aspnetapp/Doc
### Linux arm32 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
dotnetapp-10.0, dotnetapp, latest | [Dockerfile](samples/dotnetapp/Dockerfile.alpine) | Alpine
dotnetapp-chiseled-10.0, dotnetapp-chiseled | [Dockerfile](samples/dotnetapp/Dockerfile.chiseled) | Ubuntu
aspnetapp-10.0, aspnetapp | [Dockerfile](samples/aspnetapp/Dockerfile.alpine) | Alpine
aspnetapp-chiseled-10.0, aspnetapp-chiseled | [Dockerfile](samples/aspnetapp/Dockerfile.chiseled) | Ubuntu
-### Nano Server 2022 amd64 Tags
+### Windows amd64 Tags
+
+Tags | Dockerfile | OS Version
+---- | ---------- | ----------
+dotnetapp-10.0-nanoserver-ltsc2022, dotnetapp-nanoserver-ltsc2022, dotnetapp-10.0, dotnetapp, latest | [Dockerfile](samples/dotnetapp/Dockerfile.nanoserver) | Nano Server 2022
+aspnetapp-10.0-nanoserver-ltsc2022, aspnetapp-nanoserver-ltsc2022, aspnetapp-10.0, aspnetapp | [Dockerfile](samples/aspnetapp/Dockerfile.nanoserver) | Nano Server 2022
-Tag | Dockerfile
----------| ---------------
-dotnetapp-10.0-nanoserver-ltsc2022, dotnetapp-nanoserver-ltsc2022, dotnetapp-10.0, dotnetapp, latest | [Dockerfile](samples/dotnetapp/Dockerfile.nanoserver)
-aspnetapp-10.0-nanoserver-ltsc2022, aspnetapp-nanoserver-ltsc2022, aspnetapp-10.0, aspnetapp | [Dockerfile](samples/aspnetapp/Dockerfile.nanoserver)
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/samples/tags/list) for all supported and unsupported tags.*
diff --git a/README.sdk.md b/README.sdk.md
index 43c923c3a8..cf5bc3c68b 100644
--- a/README.sdk.md
+++ b/README.sdk.md
@@ -66,7 +66,7 @@ The [Image Variants documentation](https://github.com/dotnet/dotnet-docker/blob/
### Linux amd64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.102-noble-amd64, 10.0-noble-amd64, 10.0.102-noble, 10.0-noble, 10.0.102, 10.0, latest | [Dockerfile](src/sdk/10.0/noble/amd64/Dockerfile) | Ubuntu 24.04
10.0.102-noble-aot-amd64, 10.0-noble-aot-amd64, 10.0.102-noble-aot, 10.0-noble-aot, 10.0.102-aot, 10.0-aot | [Dockerfile](src/sdk/10.0/noble-aot/amd64/Dockerfile) | Ubuntu 24.04
10.0.102-alpine3.22-amd64, 10.0-alpine3.22-amd64, 10.0.102-alpine3.22, 10.0-alpine3.22 | [Dockerfile](src/sdk/10.0/alpine3.22/amd64/Dockerfile) | Alpine 3.22
@@ -75,11 +75,11 @@ Tags | Dockerfile | OS Version
10.0.102-alpine3.23-aot-amd64, 10.0-alpine3.23-aot-amd64, 10.0-alpine-aot-amd64, 10.0.102-alpine3.23-aot, 10.0-alpine3.23-aot, 10.0-alpine-aot | [Dockerfile](src/sdk/10.0/alpine3.23-aot/amd64/Dockerfile) | Alpine 3.23
10.0.102-azurelinux3.0-amd64, 10.0-azurelinux3.0-amd64, 10.0.102-azurelinux3.0, 10.0-azurelinux3.0 | [Dockerfile](src/sdk/10.0/azurelinux3.0/amd64/Dockerfile) | Azure Linux 3.0
10.0.102-azurelinux3.0-aot-amd64, 10.0-azurelinux3.0-aot-amd64, 10.0.102-azurelinux3.0-aot, 10.0-azurelinux3.0-aot | [Dockerfile](src/sdk/10.0/azurelinux3.0-aot/amd64/Dockerfile) | Azure Linux 3.0
-9.0.309-bookworm-slim-amd64, 9.0-bookworm-slim-amd64, 9.0.309-bookworm-slim, 9.0-bookworm-slim, 9.0.309, 9.0 | [Dockerfile](src/sdk/9.0/bookworm-slim/amd64/Dockerfile) | Debian 12
-9.0.309-alpine3.22-amd64, 9.0-alpine3.22-amd64, 9.0.309-alpine3.22, 9.0-alpine3.22 | [Dockerfile](src/sdk/9.0/alpine3.22/amd64/Dockerfile) | Alpine 3.22
-9.0.309-alpine3.23-amd64, 9.0-alpine3.23-amd64, 9.0-alpine-amd64, 9.0.309-alpine3.23, 9.0-alpine3.23, 9.0-alpine | [Dockerfile](src/sdk/9.0/alpine3.23/amd64/Dockerfile) | Alpine 3.23
-9.0.309-noble-amd64, 9.0-noble-amd64, 9.0.309-noble, 9.0-noble | [Dockerfile](src/sdk/9.0/noble/amd64/Dockerfile) | Ubuntu 24.04
-9.0.309-azurelinux3.0-amd64, 9.0-azurelinux3.0-amd64, 9.0.309-azurelinux3.0, 9.0-azurelinux3.0 | [Dockerfile](src/sdk/9.0/azurelinux3.0/amd64/Dockerfile) | Azure Linux 3.0
+9.0.310-bookworm-slim-amd64, 9.0-bookworm-slim-amd64, 9.0.310-bookworm-slim, 9.0-bookworm-slim, 9.0.310, 9.0 | [Dockerfile](src/sdk/9.0/bookworm-slim/amd64/Dockerfile) | Debian 12
+9.0.310-alpine3.22-amd64, 9.0-alpine3.22-amd64, 9.0.310-alpine3.22, 9.0-alpine3.22 | [Dockerfile](src/sdk/9.0/alpine3.22/amd64/Dockerfile) | Alpine 3.22
+9.0.310-alpine3.23-amd64, 9.0-alpine3.23-amd64, 9.0-alpine-amd64, 9.0.310-alpine3.23, 9.0-alpine3.23, 9.0-alpine | [Dockerfile](src/sdk/9.0/alpine3.23/amd64/Dockerfile) | Alpine 3.23
+9.0.310-noble-amd64, 9.0-noble-amd64, 9.0.310-noble, 9.0-noble | [Dockerfile](src/sdk/9.0/noble/amd64/Dockerfile) | Ubuntu 24.04
+9.0.310-azurelinux3.0-amd64, 9.0-azurelinux3.0-amd64, 9.0.310-azurelinux3.0, 9.0-azurelinux3.0 | [Dockerfile](src/sdk/9.0/azurelinux3.0/amd64/Dockerfile) | Azure Linux 3.0
8.0.417-bookworm-slim-amd64, 8.0-bookworm-slim-amd64, 8.0.417-bookworm-slim, 8.0-bookworm-slim, 8.0.417, 8.0 | [Dockerfile](src/sdk/8.0/bookworm-slim/amd64/Dockerfile) | Debian 12
8.0.417-alpine3.22-amd64, 8.0-alpine3.22-amd64, 8.0.417-alpine3.22, 8.0-alpine3.22 | [Dockerfile](src/sdk/8.0/alpine3.22/amd64/Dockerfile) | Alpine 3.22
8.0.417-alpine3.23-amd64, 8.0-alpine3.23-amd64, 8.0-alpine-amd64, 8.0.417-alpine3.23, 8.0-alpine3.23, 8.0-alpine | [Dockerfile](src/sdk/8.0/alpine3.23/amd64/Dockerfile) | Alpine 3.23
@@ -90,7 +90,7 @@ Tags | Dockerfile | OS Version
### Linux arm64 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.102-noble-arm64v8, 10.0-noble-arm64v8, 10.0.102-noble, 10.0-noble, 10.0.102, 10.0, latest | [Dockerfile](src/sdk/10.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.102-noble-aot-arm64v8, 10.0-noble-aot-arm64v8, 10.0.102-noble-aot, 10.0-noble-aot, 10.0.102-aot, 10.0-aot | [Dockerfile](src/sdk/10.0/noble-aot/arm64v8/Dockerfile) | Ubuntu 24.04
10.0.102-alpine3.22-arm64v8, 10.0-alpine3.22-arm64v8, 10.0.102-alpine3.22, 10.0-alpine3.22 | [Dockerfile](src/sdk/10.0/alpine3.22/arm64v8/Dockerfile) | Alpine 3.22
@@ -99,11 +99,11 @@ Tags | Dockerfile | OS Version
10.0.102-alpine3.23-aot-arm64v8, 10.0-alpine3.23-aot-arm64v8, 10.0-alpine-aot-arm64v8, 10.0.102-alpine3.23-aot, 10.0-alpine3.23-aot, 10.0-alpine-aot | [Dockerfile](src/sdk/10.0/alpine3.23-aot/arm64v8/Dockerfile) | Alpine 3.23
10.0.102-azurelinux3.0-arm64v8, 10.0-azurelinux3.0-arm64v8, 10.0.102-azurelinux3.0, 10.0-azurelinux3.0 | [Dockerfile](src/sdk/10.0/azurelinux3.0/arm64v8/Dockerfile) | Azure Linux 3.0
10.0.102-azurelinux3.0-aot-arm64v8, 10.0-azurelinux3.0-aot-arm64v8, 10.0.102-azurelinux3.0-aot, 10.0-azurelinux3.0-aot | [Dockerfile](src/sdk/10.0/azurelinux3.0-aot/arm64v8/Dockerfile) | Azure Linux 3.0
-9.0.309-bookworm-slim-arm64v8, 9.0-bookworm-slim-arm64v8, 9.0.309-bookworm-slim, 9.0-bookworm-slim, 9.0.309, 9.0 | [Dockerfile](src/sdk/9.0/bookworm-slim/arm64v8/Dockerfile) | Debian 12
-9.0.309-alpine3.22-arm64v8, 9.0-alpine3.22-arm64v8, 9.0.309-alpine3.22, 9.0-alpine3.22 | [Dockerfile](src/sdk/9.0/alpine3.22/arm64v8/Dockerfile) | Alpine 3.22
-9.0.309-alpine3.23-arm64v8, 9.0-alpine3.23-arm64v8, 9.0-alpine-arm64v8, 9.0.309-alpine3.23, 9.0-alpine3.23, 9.0-alpine | [Dockerfile](src/sdk/9.0/alpine3.23/arm64v8/Dockerfile) | Alpine 3.23
-9.0.309-noble-arm64v8, 9.0-noble-arm64v8, 9.0.309-noble, 9.0-noble | [Dockerfile](src/sdk/9.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04
-9.0.309-azurelinux3.0-arm64v8, 9.0-azurelinux3.0-arm64v8, 9.0.309-azurelinux3.0, 9.0-azurelinux3.0 | [Dockerfile](src/sdk/9.0/azurelinux3.0/arm64v8/Dockerfile) | Azure Linux 3.0
+9.0.310-bookworm-slim-arm64v8, 9.0-bookworm-slim-arm64v8, 9.0.310-bookworm-slim, 9.0-bookworm-slim, 9.0.310, 9.0 | [Dockerfile](src/sdk/9.0/bookworm-slim/arm64v8/Dockerfile) | Debian 12
+9.0.310-alpine3.22-arm64v8, 9.0-alpine3.22-arm64v8, 9.0.310-alpine3.22, 9.0-alpine3.22 | [Dockerfile](src/sdk/9.0/alpine3.22/arm64v8/Dockerfile) | Alpine 3.22
+9.0.310-alpine3.23-arm64v8, 9.0-alpine3.23-arm64v8, 9.0-alpine-arm64v8, 9.0.310-alpine3.23, 9.0-alpine3.23, 9.0-alpine | [Dockerfile](src/sdk/9.0/alpine3.23/arm64v8/Dockerfile) | Alpine 3.23
+9.0.310-noble-arm64v8, 9.0-noble-arm64v8, 9.0.310-noble, 9.0-noble | [Dockerfile](src/sdk/9.0/noble/arm64v8/Dockerfile) | Ubuntu 24.04
+9.0.310-azurelinux3.0-arm64v8, 9.0-azurelinux3.0-arm64v8, 9.0.310-azurelinux3.0, 9.0-azurelinux3.0 | [Dockerfile](src/sdk/9.0/azurelinux3.0/arm64v8/Dockerfile) | Azure Linux 3.0
8.0.417-bookworm-slim-arm64v8, 8.0-bookworm-slim-arm64v8, 8.0.417-bookworm-slim, 8.0-bookworm-slim, 8.0.417, 8.0 | [Dockerfile](src/sdk/8.0/bookworm-slim/arm64v8/Dockerfile) | Debian 12
8.0.417-alpine3.22-arm64v8, 8.0-alpine3.22-arm64v8, 8.0.417-alpine3.22, 8.0-alpine3.22 | [Dockerfile](src/sdk/8.0/alpine3.22/arm64v8/Dockerfile) | Alpine 3.22
8.0.417-alpine3.23-arm64v8, 8.0-alpine3.23-arm64v8, 8.0-alpine-arm64v8, 8.0.417-alpine3.23, 8.0-alpine3.23, 8.0-alpine | [Dockerfile](src/sdk/8.0/alpine3.23/arm64v8/Dockerfile) | Alpine 3.23
@@ -114,64 +114,40 @@ Tags | Dockerfile | OS Version
### Linux arm32 Tags
Tags | Dockerfile | OS Version
------------| -------------| -------------
+---- | ---------- | ----------
10.0.102-noble-arm32v7, 10.0-noble-arm32v7, 10.0.102-noble, 10.0-noble, 10.0.102, 10.0, latest | [Dockerfile](src/sdk/10.0/noble/arm32v7/Dockerfile) | Ubuntu 24.04
10.0.102-alpine3.22-arm32v7, 10.0-alpine3.22-arm32v7, 10.0.102-alpine3.22, 10.0-alpine3.22 | [Dockerfile](src/sdk/10.0/alpine3.22/arm32v7/Dockerfile) | Alpine 3.22
10.0.102-alpine3.23-arm32v7, 10.0-alpine3.23-arm32v7, 10.0-alpine-arm32v7, 10.0.102-alpine3.23, 10.0-alpine3.23, 10.0-alpine | [Dockerfile](src/sdk/10.0/alpine3.23/arm32v7/Dockerfile) | Alpine 3.23
-9.0.309-bookworm-slim-arm32v7, 9.0-bookworm-slim-arm32v7, 9.0.309-bookworm-slim, 9.0-bookworm-slim, 9.0.309, 9.0 | [Dockerfile](src/sdk/9.0/bookworm-slim/arm32v7/Dockerfile) | Debian 12
-9.0.309-alpine3.22-arm32v7, 9.0-alpine3.22-arm32v7, 9.0.309-alpine3.22, 9.0-alpine3.22 | [Dockerfile](src/sdk/9.0/alpine3.22/arm32v7/Dockerfile) | Alpine 3.22
-9.0.309-alpine3.23-arm32v7, 9.0-alpine3.23-arm32v7, 9.0-alpine-arm32v7, 9.0.309-alpine3.23, 9.0-alpine3.23, 9.0-alpine | [Dockerfile](src/sdk/9.0/alpine3.23/arm32v7/Dockerfile) | Alpine 3.23
-9.0.309-noble-arm32v7, 9.0-noble-arm32v7, 9.0.309-noble, 9.0-noble | [Dockerfile](src/sdk/9.0/noble/arm32v7/Dockerfile) | Ubuntu 24.04
+9.0.310-bookworm-slim-arm32v7, 9.0-bookworm-slim-arm32v7, 9.0.310-bookworm-slim, 9.0-bookworm-slim, 9.0.310, 9.0 | [Dockerfile](src/sdk/9.0/bookworm-slim/arm32v7/Dockerfile) | Debian 12
+9.0.310-alpine3.22-arm32v7, 9.0-alpine3.22-arm32v7, 9.0.310-alpine3.22, 9.0-alpine3.22 | [Dockerfile](src/sdk/9.0/alpine3.22/arm32v7/Dockerfile) | Alpine 3.22
+9.0.310-alpine3.23-arm32v7, 9.0-alpine3.23-arm32v7, 9.0-alpine-arm32v7, 9.0.310-alpine3.23, 9.0-alpine3.23, 9.0-alpine | [Dockerfile](src/sdk/9.0/alpine3.23/arm32v7/Dockerfile) | Alpine 3.23
+9.0.310-noble-arm32v7, 9.0-noble-arm32v7, 9.0.310-noble, 9.0-noble | [Dockerfile](src/sdk/9.0/noble/arm32v7/Dockerfile) | Ubuntu 24.04
8.0.417-bookworm-slim-arm32v7, 8.0-bookworm-slim-arm32v7, 8.0.417-bookworm-slim, 8.0-bookworm-slim, 8.0.417, 8.0 | [Dockerfile](src/sdk/8.0/bookworm-slim/arm32v7/Dockerfile) | Debian 12
8.0.417-alpine3.22-arm32v7, 8.0-alpine3.22-arm32v7, 8.0.417-alpine3.22, 8.0-alpine3.22 | [Dockerfile](src/sdk/8.0/alpine3.22/arm32v7/Dockerfile) | Alpine 3.22
8.0.417-alpine3.23-arm32v7, 8.0-alpine3.23-arm32v7, 8.0-alpine-arm32v7, 8.0.417-alpine3.23, 8.0-alpine3.23, 8.0-alpine | [Dockerfile](src/sdk/8.0/alpine3.23/arm32v7/Dockerfile) | Alpine 3.23
8.0.417-jammy-arm32v7, 8.0-jammy-arm32v7, 8.0.417-jammy, 8.0-jammy | [Dockerfile](src/sdk/8.0/jammy/arm32v7/Dockerfile) | Ubuntu 22.04
-### Nano Server 2025 amd64 Tags
+### Windows amd64 Tags
-Tag | Dockerfile
----------| ---------------
-10.0.102-nanoserver-ltsc2025, 10.0-nanoserver-ltsc2025 | [Dockerfile](src/sdk/10.0/nanoserver-ltsc2025/amd64/Dockerfile)
-9.0.309-nanoserver-ltsc2025, 9.0-nanoserver-ltsc2025 | [Dockerfile](src/sdk/9.0/nanoserver-ltsc2025/amd64/Dockerfile)
-8.0.417-nanoserver-ltsc2025, 8.0-nanoserver-ltsc2025 | [Dockerfile](src/sdk/8.0/nanoserver-ltsc2025/amd64/Dockerfile)
-
-### Windows Server Core 2025 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.102-windowsservercore-ltsc2025, 10.0-windowsservercore-ltsc2025 | [Dockerfile](src/sdk/10.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-9.0.309-windowsservercore-ltsc2025, 9.0-windowsservercore-ltsc2025 | [Dockerfile](src/sdk/9.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-8.0.417-windowsservercore-ltsc2025, 8.0-windowsservercore-ltsc2025 | [Dockerfile](src/sdk/8.0/windowsservercore-ltsc2025/amd64/Dockerfile)
-
-### Nano Server 2022 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.102-nanoserver-ltsc2022, 10.0-nanoserver-ltsc2022 | [Dockerfile](src/sdk/10.0/nanoserver-ltsc2022/amd64/Dockerfile)
-9.0.309-nanoserver-ltsc2022, 9.0-nanoserver-ltsc2022 | [Dockerfile](src/sdk/9.0/nanoserver-ltsc2022/amd64/Dockerfile)
-8.0.417-nanoserver-ltsc2022, 8.0-nanoserver-ltsc2022 | [Dockerfile](src/sdk/8.0/nanoserver-ltsc2022/amd64/Dockerfile)
-
-### Windows Server Core 2022 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-10.0.102-windowsservercore-ltsc2022, 10.0-windowsservercore-ltsc2022 | [Dockerfile](src/sdk/10.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-9.0.309-windowsservercore-ltsc2022, 9.0-windowsservercore-ltsc2022 | [Dockerfile](src/sdk/9.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-8.0.417-windowsservercore-ltsc2022, 8.0-windowsservercore-ltsc2022 | [Dockerfile](src/sdk/8.0/windowsservercore-ltsc2022/amd64/Dockerfile)
-
-### Nano Server, version 1809 amd64 Tags
-
-Tag | Dockerfile
----------| ---------------
-9.0.309-nanoserver-1809, 9.0-nanoserver-1809 | [Dockerfile](src/sdk/9.0/nanoserver-1809/amd64/Dockerfile)
-8.0.417-nanoserver-1809, 8.0-nanoserver-1809 | [Dockerfile](src/sdk/8.0/nanoserver-1809/amd64/Dockerfile)
-
-### Windows Server Core 2019 amd64 Tags
+Tags | Dockerfile | OS Version
+---- | ---------- | ----------
+10.0.102-nanoserver-ltsc2025, 10.0-nanoserver-ltsc2025 | [Dockerfile](src/sdk/10.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+10.0.102-windowsservercore-ltsc2025, 10.0-windowsservercore-ltsc2025 | [Dockerfile](src/sdk/10.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+10.0.102-nanoserver-ltsc2022, 10.0-nanoserver-ltsc2022 | [Dockerfile](src/sdk/10.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+10.0.102-windowsservercore-ltsc2022, 10.0-windowsservercore-ltsc2022 | [Dockerfile](src/sdk/10.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+9.0.310-nanoserver-ltsc2025, 9.0-nanoserver-ltsc2025 | [Dockerfile](src/sdk/9.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+9.0.310-windowsservercore-ltsc2025, 9.0-windowsservercore-ltsc2025 | [Dockerfile](src/sdk/9.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+9.0.310-nanoserver-ltsc2022, 9.0-nanoserver-ltsc2022 | [Dockerfile](src/sdk/9.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+9.0.310-windowsservercore-ltsc2022, 9.0-windowsservercore-ltsc2022 | [Dockerfile](src/sdk/9.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+9.0.310-nanoserver-1809, 9.0-nanoserver-1809 | [Dockerfile](src/sdk/9.0/nanoserver-1809/amd64/Dockerfile) | Nano Server, version 1809
+9.0.310-windowsservercore-ltsc2019, 9.0-windowsservercore-ltsc2019 | [Dockerfile](src/sdk/9.0/windowsservercore-ltsc2019/amd64/Dockerfile) | Windows Server Core 2019
+8.0.417-nanoserver-ltsc2025, 8.0-nanoserver-ltsc2025 | [Dockerfile](src/sdk/8.0/nanoserver-ltsc2025/amd64/Dockerfile) | Nano Server 2025
+8.0.417-windowsservercore-ltsc2025, 8.0-windowsservercore-ltsc2025 | [Dockerfile](src/sdk/8.0/windowsservercore-ltsc2025/amd64/Dockerfile) | Windows Server Core 2025
+8.0.417-nanoserver-ltsc2022, 8.0-nanoserver-ltsc2022 | [Dockerfile](src/sdk/8.0/nanoserver-ltsc2022/amd64/Dockerfile) | Nano Server 2022
+8.0.417-windowsservercore-ltsc2022, 8.0-windowsservercore-ltsc2022 | [Dockerfile](src/sdk/8.0/windowsservercore-ltsc2022/amd64/Dockerfile) | Windows Server Core 2022
+8.0.417-nanoserver-1809, 8.0-nanoserver-1809 | [Dockerfile](src/sdk/8.0/nanoserver-1809/amd64/Dockerfile) | Nano Server, version 1809
+8.0.417-windowsservercore-ltsc2019, 8.0-windowsservercore-ltsc2019 | [Dockerfile](src/sdk/8.0/windowsservercore-ltsc2019/amd64/Dockerfile) | Windows Server Core 2019
-Tag | Dockerfile
----------| ---------------
-9.0.309-windowsservercore-ltsc2019, 9.0-windowsservercore-ltsc2019 | [Dockerfile](src/sdk/9.0/windowsservercore-ltsc2019/amd64/Dockerfile)
-8.0.417-windowsservercore-ltsc2019, 8.0-windowsservercore-ltsc2019 | [Dockerfile](src/sdk/8.0/windowsservercore-ltsc2019/amd64/Dockerfile)
*Tags not listed in the table above are not supported. See the [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md). See the [full list of tags](https://mcr.microsoft.com/v2/dotnet/sdk/tags/list) for all supported and unsupported tags.*
diff --git a/build-and-test.ps1 b/build-and-test.ps1
index a94921bcd1..e720262824 100644
--- a/build-and-test.ps1
+++ b/build-and-test.ps1
@@ -20,8 +20,8 @@ param(
[string]$Mode = "BuildAndTest",
# Categories of tests to run
- [ValidateSet("runtime", "runtime-deps", "aspnet", "sdk", "pre-build", "sample", "monitor", "aspire-dashboard")]
- [string[]]$TestCategories = @("runtime", "runtime-deps", "aspnet", "sdk", "monitor", "aspire-dashboard")
+ [ValidateSet("runtime", "runtime-deps", "aspnet", "sdk", "pre-build", "sample", "monitor", "aspire-dashboard", "yarp")]
+ [string[]]$TestCategories = @("runtime", "runtime-deps", "aspnet", "sdk", "monitor", "aspire-dashboard", "yarp"),
# Custom test filter expression
[string]$CustomTestFilter
diff --git a/eng/docker-tools/templates/stages/dotnet/build-and-test.yml b/eng/docker-tools/templates/stages/dotnet/build-and-test.yml
index 311b161338..8424f3f9b9 100644
--- a/eng/docker-tools/templates/stages/dotnet/build-and-test.yml
+++ b/eng/docker-tools/templates/stages/dotnet/build-and-test.yml
@@ -70,7 +70,9 @@ stages:
${{ if ne(parameters.linuxAmd64Pool, '') }}:
${{ parameters.linuxAmd64Pool }}
${{ elseif eq(variables['System.TeamProject'], parameters.publicProjectName) }}:
- vmImage: $(defaultLinuxAmd64PoolImage)
+ name: $(linuxAmd64PublicPoolName)
+ demands: ImageOverride -equals $(linuxAmd64PublicPoolImage)
+ os: linux
${{ elseif eq(variables['System.TeamProject'], parameters.internalProjectName) }}:
name: $(linuxAmd64InternalPoolName)
image: $(linuxAmd64InternalPoolImage)
@@ -80,21 +82,23 @@ stages:
linuxArm64Pool:
os: linux
hostArchitecture: Arm64
- image: $(linuxArm64PoolImage)
${{ if eq(variables['System.TeamProject'], parameters.publicProjectName) }}:
name: $(linuxArm64PublicPoolName)
+ demands: ImageOverride -equals $(linuxArm64PublicPoolImage)
${{ if eq(variables['System.TeamProject'], parameters.internalProjectName) }}:
name: $(linuxArm64InternalPoolName)
+ image: $(linuxArm64InternalPoolImage)
# Linux Arm32
linuxArm32Pool:
os: linux
hostArchitecture: Arm64
- image: $(linuxArm32PoolImage)
${{ if eq(variables['System.TeamProject'], parameters.publicProjectName) }}:
name: $(linuxArm32PublicPoolName)
+ demands: ImageOverride -equals $(linuxArm32PublicPoolImage)
${{ if eq(variables['System.TeamProject'], parameters.internalProjectName) }}:
name: $(linuxArm32InternalPoolName)
+ image: $(linuxArm32InternalPoolImage)
# Windows Server 2016
windows2016Pool:
diff --git a/eng/docker-tools/templates/stages/dotnet/publish.yml b/eng/docker-tools/templates/stages/dotnet/publish.yml
index 66cb2c8bd5..8539ce6069 100644
--- a/eng/docker-tools/templates/stages/dotnet/publish.yml
+++ b/eng/docker-tools/templates/stages/dotnet/publish.yml
@@ -49,7 +49,9 @@ stages:
${{ if ne(parameters.pool, '') }}:
${{ parameters.pool }}
${{ elseif eq(variables['System.TeamProject'], parameters.publicProjectName) }}:
- vmImage: $(defaultLinuxAmd64PoolImage)
+ name: $(linuxAmd64PublicPoolName)
+ demands: ImageOverride -equals $(linuxAmd64PublicPoolImage)
+ os: linux
${{ elseif eq(variables['System.TeamProject'], parameters.internalProjectName) }}:
name: $(linuxAmd64InternalPoolName)
image: $(linuxAmd64InternalPoolImage)
diff --git a/eng/docker-tools/templates/variables/docker-images.yml b/eng/docker-tools/templates/variables/docker-images.yml
index 999a72cf58..4c6b57e7dc 100644
--- a/eng/docker-tools/templates/variables/docker-images.yml
+++ b/eng/docker-tools/templates/variables/docker-images.yml
@@ -1,5 +1,5 @@
variables:
- imageNames.imageBuilderName: mcr.microsoft.com/dotnet-buildtools/image-builder:2877314
+ imageNames.imageBuilderName: mcr.microsoft.com/dotnet-buildtools/image-builder:2880049
imageNames.imageBuilder: $(imageNames.imageBuilderName)
imageNames.imageBuilder.withrepo: imagebuilder-withrepo:$(Build.BuildId)-$(System.JobId)
imageNames.testRunner: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux3.0-docker-testrunner
diff --git a/eng/docker-tools/templates/variables/dotnet/common.yml b/eng/docker-tools/templates/variables/dotnet/common.yml
index 8f98c09211..bae1611257 100644
--- a/eng/docker-tools/templates/variables/dotnet/common.yml
+++ b/eng/docker-tools/templates/variables/dotnet/common.yml
@@ -15,18 +15,26 @@ variables:
- name: linuxAmd64InternalPoolImage
value: 1es-ubuntu-2204
+- name: linuxAmd64PublicPoolImage
+ value: Azure-Linux-3-Amd64-Public
+- name: linuxAmd64PublicPoolName
+ value: NetCore-Public
- name: linuxAmd64InternalPoolName
value: NetCore1ESPool-Internal
-- name: linuxArm64PoolImage
- value: Mariner-2-Docker-ARM64
+- name: linuxArm64PublicPoolImage
+ value: azure-linux-3-arm64-public
+- name: linuxArm64InternalPoolImage
+ value: azure-linux-3-arm64
- name: linuxArm64PublicPoolName
value: Docker-Linux-Arm-Public
- name: linuxArm64InternalPoolName
value: Docker-Linux-Arm-Internal
-- name: linuxArm32PoolImage
- value: Mariner-2-Docker-ARM64
+- name: linuxArm32PublicPoolImage
+ value: azure-linux-3-arm64-public
+- name: linuxArm32InternalPoolImage
+ value: azure-linux-3-arm64
- name: linuxArm32PublicPoolName
value: Docker-Linux-Arm-Public
- name: linuxArm32InternalPoolName
diff --git a/eng/dockerfile-templates/yarp/Dockerfile.envs b/eng/dockerfile-templates/yarp/Dockerfile.envs
new file mode 100644
index 0000000000..d8143b25e5
--- /dev/null
+++ b/eng/dockerfile-templates/yarp/Dockerfile.envs
@@ -0,0 +1,5 @@
+ENV \
+ # Unset ASPNETCORE_HTTP_PORTS from base image
+ ASPNETCORE_HTTP_PORTS= \
+ # yarp environment variables
+ ASPNETCORE_URLS=http://+:5000
\ No newline at end of file
diff --git a/eng/dockerfile-templates/yarp/Dockerfile.linux b/eng/dockerfile-templates/yarp/Dockerfile.linux
new file mode 100644
index 0000000000..352d86e864
--- /dev/null
+++ b/eng/dockerfile-templates/yarp/Dockerfile.linux
@@ -0,0 +1,49 @@
+{{
+ set yarpVersionParts to split(PRODUCT_VERSION, ".") ^
+ set yarpMajorMinor to cat(yarpVersionParts[0], ".", yarpVersionParts[1]) ^
+ set aspnetBaseTag to
+ cat("$REPO:", VARIABLES[cat("dotnet|9.0|fixed-tag")], "-", OS_VERSION, ARCH_TAG_SUFFIX) ^
+ set installerImageTag to cat("mcr.microsoft.com/azurelinux", "/base/core:", OS_VERSION_NUMBER) ^
+
+ set baseUrl to VARIABLES[cat("yarp|", yarpMajorMinor, "|base-url|", VARIABLES["branch"])] ^
+ set baseChecksumUrl to VARIABLES[cat("yarp|", yarpMajorMinor, "|base-url|checksums|", VARIABLES["branch"])] ^
+ set isInternal to find(baseUrl, "artifacts.visualstudio.com") >= 0 ^
+
+ set buildVersion to VARIABLES[cat("yarp|", yarpMajorMinor, "|build-version")] ^
+ set yarpVersionVariable to when(find(buildVersion, '-rtm') >= 0 || find(buildVersion, '-servicing') >= 0, "product-version", "build-version") ^
+ set yarpVersion to VARIABLES[cat("yarp|", yarpMajorMinor, "|", yarpVersionVariable)] ^
+ set versionFolder to when(buildVersion != yarpVersion, buildVersion, '$yarp_version') ^
+
+ set downloadUrl to cat(baseUrl, "/reverse-proxy/", versionFolder, "/", "reverse-proxy-linux-", ARCH_SHORT, ".tar.gz") ^
+ set shaUrl to cat(baseChecksumUrl, "/reverse-proxy/", versionFolder, "/", "reverse-proxy-linux-", ARCH_SHORT, ".tar.gz.sha512") ^
+ set appDir to "/app" ^
+ set sha to VARIABLES[join(["yarp", yarpMajorMinor, "linux", ARCH_SHORT, "sha"], "|")]
+
+}}ARG REPO=mcr.microsoft.com/dotnet/aspnet
+
+# Installer image
+FROM {{installerImageTag}} AS installer{{if isInternal:
+ARG ACCESSTOKEN}}
+
+{{InsertTemplate("../Dockerfile.linux.distroless-azurelinux-installer-prereqs")}}
+
+# Retrieve YARP
+RUN yarp_version={{yarpVersion}} \
+ && {{InsertTemplate("../Dockerfile.linux.download-appliance", [
+ "product": "yarp",
+ "productVersion": yarpVersion,
+ "download-url": downloadUrl,
+ "sha-url": shaUrl,
+ "sha-url-is-bare": "true",
+ "extract-to": appDir
+ ], " ")}}
+
+
+# YARP image
+FROM {{aspnetBaseTag}}
+
+COPY --from=installer ["/app", "/app"]
+
+{{InsertTemplate("Dockerfile.envs")}}
+
+ENTRYPOINT [ "dotnet", "/app/yarp.dll", "/etc/yarp.config" ]
diff --git a/eng/mcr-tags-metadata-templates/aspnet-tags.yml b/eng/mcr-tags-metadata-templates/aspnet-tags.yml
index 6b7bdf5b40..bfc7c11944 100644
--- a/eng/mcr-tags-metadata-templates/aspnet-tags.yml
+++ b/eng/mcr-tags-metadata-templates/aspnet-tags.yml
@@ -62,17 +62,17 @@ $(McrTagsYmlTagGroup:8.0-azurelinux3.0-distroless-composite)
$(McrTagsYmlTagGroup:8.0-azurelinux3.0-distroless-composite-extra)
$(McrTagsYmlTagGroup:10.0-nanoserver-ltsc2025)
$(McrTagsYmlTagGroup:10.0-windowsservercore-ltsc2025)
-$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2025)
-$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2025)
-$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2025)
-$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2025)
$(McrTagsYmlTagGroup:10.0-nanoserver-ltsc2022)
$(McrTagsYmlTagGroup:10.0-windowsservercore-ltsc2022)
+$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2025)
+$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2025)
$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2022)
$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2022)
-$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2022)
-$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2022)
$(McrTagsYmlTagGroup:9.0-nanoserver-1809)
$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2019)
+$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2025)
+$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2025)
+$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2022)
+$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2022)
$(McrTagsYmlTagGroup:8.0-nanoserver-1809)
$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2019)
diff --git a/eng/mcr-tags-metadata-templates/runtime-tags.yml b/eng/mcr-tags-metadata-templates/runtime-tags.yml
index a3994b69bb..16785b84b0 100644
--- a/eng/mcr-tags-metadata-templates/runtime-tags.yml
+++ b/eng/mcr-tags-metadata-templates/runtime-tags.yml
@@ -36,17 +36,17 @@ $(McrTagsYmlTagGroup:8.0-azurelinux3.0-distroless)
$(McrTagsYmlTagGroup:8.0-azurelinux3.0-distroless-extra)
$(McrTagsYmlTagGroup:10.0-nanoserver-ltsc2025)
$(McrTagsYmlTagGroup:10.0-windowsservercore-ltsc2025)
-$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2025)
-$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2025)
-$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2025)
-$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2025)
$(McrTagsYmlTagGroup:10.0-nanoserver-ltsc2022)
$(McrTagsYmlTagGroup:10.0-windowsservercore-ltsc2022)
+$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2025)
+$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2025)
$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2022)
$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2022)
-$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2022)
-$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2022)
$(McrTagsYmlTagGroup:9.0-nanoserver-1809)
$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2019)
+$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2025)
+$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2025)
+$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2022)
+$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2022)
$(McrTagsYmlTagGroup:8.0-nanoserver-1809)
$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2019)
diff --git a/eng/mcr-tags-metadata-templates/sdk-tags.yml b/eng/mcr-tags-metadata-templates/sdk-tags.yml
index 6579cf946b..491fed9e6d 100644
--- a/eng/mcr-tags-metadata-templates/sdk-tags.yml
+++ b/eng/mcr-tags-metadata-templates/sdk-tags.yml
@@ -20,17 +20,17 @@ $(McrTagsYmlTagGroup:8.0-jammy)
$(McrTagsYmlTagGroup:8.0-azurelinux3.0)
$(McrTagsYmlTagGroup:10.0-nanoserver-ltsc2025)
$(McrTagsYmlTagGroup:10.0-windowsservercore-ltsc2025)
-$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2025)
-$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2025)
-$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2025)
-$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2025)
$(McrTagsYmlTagGroup:10.0-nanoserver-ltsc2022)
$(McrTagsYmlTagGroup:10.0-windowsservercore-ltsc2022)
+$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2025)
+$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2025)
$(McrTagsYmlTagGroup:9.0-nanoserver-ltsc2022)
$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2022)
-$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2022)
-$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2022)
$(McrTagsYmlTagGroup:9.0-nanoserver-1809)
$(McrTagsYmlTagGroup:9.0-windowsservercore-ltsc2019)
+$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2025)
+$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2025)
+$(McrTagsYmlTagGroup:8.0-nanoserver-ltsc2022)
+$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2022)
$(McrTagsYmlTagGroup:8.0-nanoserver-1809)
$(McrTagsYmlTagGroup:8.0-windowsservercore-ltsc2019)
diff --git a/eng/mcr-tags-metadata-templates/yarp-tags.yml b/eng/mcr-tags-metadata-templates/yarp-tags.yml
new file mode 100644
index 0000000000..96136f63a0
--- /dev/null
+++ b/eng/mcr-tags-metadata-templates/yarp-tags.yml
@@ -0,0 +1,2 @@
+$(McrTagsYmlRepo:yarp)
+$(McrTagsYmlTagGroup:2.3-preview)
\ No newline at end of file
diff --git a/eng/pipelines/dotnet-core-internal-nightly.yml b/eng/pipelines/dotnet-core-internal-nightly.yml
deleted file mode 100644
index 1b3ca4f9f0..0000000000
--- a/eng/pipelines/dotnet-core-internal-nightly.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-trigger:
- batch: true
- branches:
- include:
- - internal/release/nightly
- paths:
- include:
- - manifest.json
- - manifest.versions.json
- - src/*
-pr: none
-
-parameters:
-- name: isTest
- displayName: Is Test Pipeline Run
- type: boolean
- default: false
-
-variables:
-- template: /eng/pipelines/variables/internal-core.yml@self
-- name: officialBranches
- # comma-delimited list of branch names
- value: internal/release/nightly
-- name: ingestKustoImageInfo
- value: false
-- name: publishReadme
- value: false
-- ${{ if parameters.isTest }}:
- - name: publishRepoPrefix
- value: test/internal/private/
-- ${{ else }}:
- - name: publishRepoPrefix
- value: internal/private/
-
-resources:
- repositories:
- - repository: VersionsRepo
- type: github
- endpoint: dotnet
- name: dotnet/dotnet-docker-internal
- ref: ${{ variables['gitHubVersionsRepoInfo.branch'] }}
-
-extends:
- template: /eng/common/templates/1es-official.yml@self
- parameters:
- stages:
- - template: /eng/pipelines/stages/build-test-publish-repo.yml@self
- parameters:
- internalProjectName: ${{ variables.internalProjectName }}
- publicProjectName: ${{ variables.publicProjectName }}
- versionsRepoRef: VersionsRepo
diff --git a/eng/pipelines/dotnet-core-internal-pr.yml b/eng/pipelines/dotnet-core-internal-pr.yml
deleted file mode 100644
index aeaaeffaa1..0000000000
--- a/eng/pipelines/dotnet-core-internal-pr.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-trigger: none
-# This pipeline is intended to be run for internal PRs only in AzDO.
-# Configuring the pipeline to run for PRs in AzDO is not supported from the YAML but instead
-# is configured through the build validation policy for the appropriate branch.
-# https://docs.microsoft.com/azure/devops/pipelines/troubleshooting/troubleshooting?view=azure-devops#pull-request-triggers-not-supported-with-azure-repos
-pr: none
-
-resources:
- repositories:
- - repository: InternalVersionsRepo
- type: github
- endpoint: dotnet
- name: dotnet/dotnet-docker-internal
-
-variables:
-- template: variables/internal-core.yml
-
-stages:
-- template: stages/build-test-publish-repo.yml
- parameters:
- internalProjectName: ${{ variables.internalProjectName }}
- publicProjectName: ${{ variables.publicProjectName }}
diff --git a/eng/pipelines/dotnet-core-internal-testing.yml b/eng/pipelines/dotnet-core-internal-testing.yml
deleted file mode 100644
index a0cfd46588..0000000000
--- a/eng/pipelines/dotnet-core-internal-testing.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-trigger:
- batch: true
- branches:
- include:
- - testing/*
- paths:
- include:
- - manifest.json
- - manifest.versions.json
- - src/*
-pr: none
-
-parameters:
-- name: sourceBuildPipelineRunId
- displayName: >
- Source build pipeline run ID. This refers to runs of *this pipeline*.
- Override this parameter in combination with disabling the `Build` stage to
- test images that were built in a different pipeline run. When building new
- images, leave this value alone.
- type: string
- default: $(Build.BuildId)
-
-variables:
-- template: /eng/pipelines/variables/internal-core.yml@self
- parameters:
- sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }}
-- name: IsInternalServicingValidation
- value: true
-
-resources:
- repositories:
- - repository: VersionsRepo
- type: github
- endpoint: dotnet
- name: dotnet/versions
- ref: ${{ variables['gitHubVersionsRepoInfo.branch'] }}
-
-extends:
- template: /eng/common/templates/1es-official.yml@self
- parameters:
- serviceConnections:
- - name: $(internal-mirror.serviceConnectionName)
- - name: $(build.serviceConnectionName)
- - name: $(publish.serviceConnectionName)
- - name: $(kusto.serviceConnectionName)
- - name: $(marStatus.serviceConnectionName)
- stages:
- - template: stages/build-test-publish-repo.yml
- parameters:
- internalProjectName: ${{ variables.internalProjectName }}
- publicProjectName: ${{ variables.publicProjectName }}
- versionsRepoRef: VersionsRepo
- sourceBuildPipelineRunId: ${{ parameters.sourceBuildPipelineRunId }}
- isInternalServicingValidation: ${{ variables.IsInternalServicingValidation }}
- noCache: true
diff --git a/eng/readme-templates/About.md b/eng/readme-templates/About.md
index 582ad2a71c..d2e20f9007 100644
--- a/eng/readme-templates/About.md
+++ b/eng/readme-templates/About.md
@@ -6,8 +6,9 @@
"product-family",
when(PARENT_REPO = "monitor", cat("monitor-", SHORT_REPO), SHORT_REPO))
}}{{ARGS["top-header"]}} About
-{{if ARGS["readme-host"] = "mar":{{InsertTemplate("Announcement.md",
+{{if ARGS["readme-host"] = "mar":{{InsertTemplate("ReposProvider.md",
[
+ "template": "Announcement.md",
"leading-line-break": "true",
"readme-host": ARGS["readme-host"]
])}}}}
diff --git a/eng/readme-templates/About.yarp.md b/eng/readme-templates/About.yarp.md
new file mode 100644
index 0000000000..7aab7fc5ba
--- /dev/null
+++ b/eng/readme-templates/About.yarp.md
@@ -0,0 +1 @@
+This image contains an implementation of YARP, a reverse proxy framework in .NET.
\ No newline at end of file
diff --git a/eng/readme-templates/Announcement.md b/eng/readme-templates/Announcement.md
index 6b65c33756..bc0d750f82 100644
--- a/eng/readme-templates/Announcement.md
+++ b/eng/readme-templates/Announcement.md
@@ -1,14 +1,28 @@
{{
_ ARGS:
+ nightly-only-repos: List of nightly-only .NET product repos
+ product-family-repos: List of .NET product family repos
leading-line-break: Indicates whether to include a leading line break
trailing-line-break: Indicates whether to include a trailing line break
readme-host: Moniker of the site that will host the readme ^
+
+ set getRepoName(repo) to:{{
+ return repo[0]
+ }} ^
+
set nonNightlyRepo to when(IS_PRODUCT_FAMILY, "dotnet", join(split(REPO, "/nightly"), "")) ^
- set isNightlyRepo to match(split(REPO, "/")[1], "nightly")
+ set isNightlyRepo to match(split(REPO, "/")[1], "nightly") ^
+ set productFamilyRepo to ARGS["product-family-repos"][0][0] ^
+ set isNightlyOnly to find(map(ARGS["nightly-only-repos"],getRepoName), nonNightlyRepo) >= 0 ^
+
+ set url to when(isNightlyOnly,
+ InsertTemplate("Url.md", [ "readme-host": ARGS["readme-host"], "repo": productFamilyRepo, "is-product-family": 1 ]),
+ InsertTemplate("Url.md", [ "readme-host": ARGS["readme-host"], "repo": nonNightlyRepo, "is-product-family": IS_PRODUCT_FAMILY ]))
+
}}{{if isNightlyRepo || VARIABLES["branch"] = "nightly"
:{{if ARGS["leading-line-break"]:
}}> **Important**: The images from the dotnet/nightly repositories include last-known-good (LKG) builds for the next release of [.NET](https://github.com/dotnet/core).
>
-> See [dotnet]({{InsertTemplate("Url.md", [ "readme-host": ARGS["readme-host"], "repo": nonNightlyRepo ])}}) for images with official releases of [.NET](https://github.com/dotnet/core).
+> See [dotnet]({{url}}) for images with official releases of [.NET](https://github.com/dotnet/core).
{{if ARGS["trailing-line-break"]:
}}}}
diff --git a/eng/readme-templates/DefaultLayout.md b/eng/readme-templates/DefaultLayout.md
index 90d6567043..e8d138b7db 100644
--- a/eng/readme-templates/DefaultLayout.md
+++ b/eng/readme-templates/DefaultLayout.md
@@ -4,21 +4,21 @@
"readme-host": ARGS["readme-host"]
] ^
- set insertReposListTemplate(template) to:{{
- return InsertTemplate("ReposProvider.md", union([ "template": template ], commonArgs))
+ set insertReposListTemplate(template, args) to:{{
+ return InsertTemplate("ReposProvider.md", union([ "template": template ], commonArgs, args))
}} ^
set isNightlyRepo to match(split(REPO, "/")[1], "nightly") ^
set readmeRepoName to when(PARENT_REPO = "monitor", cat("monitor-", SHORT_REPO), SHORT_REPO)
-}}{{InsertTemplate("Announcement.md", union(commonArgs, [ "trailing-line-break": "true" ]))}}{{
+}}{{insertReposListTemplate("Announcement.md", [ "trailing-line-break": "true" ])}}{{
if !IS_PRODUCT_FAMILY:{{InsertTemplate("FeaturedTags.md", commonArgs)}}
}}{{if IS_PRODUCT_FAMILY:{{
insertReposListTemplate("FeaturedRepos.md")}}
}}
{{InsertTemplate("About.md", commonArgs)}}
-{{InsertTemplate("Use.md", commonArgs)}}{{if (find(REPO, "monitor") < 0 && find(REPO, "aspire") < 0):
+{{InsertTemplate("Use.md", commonArgs)}}{{if (find(REPO, "monitor") < 0 && find(REPO, "aspire") < 0 && find(REPO, "yarp") < 0):
{{InsertTemplate("About.variants.md", commonArgs)}}}}
diff --git a/eng/readme-templates/GitHub.header.md b/eng/readme-templates/GitHub.header.md
index 51b9478340..61a496cacd 100644
--- a/eng/readme-templates/GitHub.header.md
+++ b/eng/readme-templates/GitHub.header.md
@@ -1,10 +1,11 @@
{{
_ ARGS:
product-repos: List of .NET product repos
+ nightly-only-repos: List of nightly-only .NET product repos
product-family-repos: List of .NET product family repos
samples-repos: List of .NET samples repos ^
- set repos to cat(ARGS["product-repos"], ARGS["samples-repos"]) ^
+ set repos to cat(ARGS["product-repos"], ARGS["nightly-only-repos"], ARGS["samples-repos"]) ^
set isCurrentRepo(repo) to:{{
set repoNameParts to split(repo[0], "/") ^
diff --git a/eng/readme-templates/README.mcr.md b/eng/readme-templates/README.mcr.md
index e01cac3f08..f3d8006bb5 100644
--- a/eng/readme-templates/README.mcr.md
+++ b/eng/readme-templates/README.mcr.md
@@ -6,7 +6,7 @@
{{InsertTemplate("ReposProvider.md", union([ "template": "RelatedRepos.md" ], commonArgs))}}
-{{InsertTemplate("Use.md", commonArgs)}}{{if (find(REPO, "monitor") < 0 && find(REPO, "aspire") < 0):
+{{InsertTemplate("Use.md", commonArgs)}}{{if (find(REPO, "monitor") < 0 && find(REPO, "aspire") < 0 && find(REPO, "yarp") < 0):
{{InsertTemplate("About.variants.md", commonArgs)}}}}
diff --git a/eng/readme-templates/RelatedRepos.md b/eng/readme-templates/RelatedRepos.md
index 629b6ea94b..85cb6d8284 100644
--- a/eng/readme-templates/RelatedRepos.md
+++ b/eng/readme-templates/RelatedRepos.md
@@ -3,11 +3,13 @@
top-header: The string to use as the top-level header.
readme-host: Moniker of the site that will host the readmes
product-repos: List of .NET product repos
+ nightly-only-repos: List of nightly-only .NET product repos
product-family-repos: List of .NET product family repos
samples-repos: List of .NET samples repos
framework-repos: List of .NET Framework repos ^
set repos to ARGS["product-repos"] ^
+ set nightlyOnlyRepos to ARGS["nightly-only-repos"] ^
set productFamilyRepos to ARGS["product-family-repos"] ^
set samplesRepos to ARGS["samples-repos"] ^
set frameworkRepos to ARGS["framework-repos"] ^
@@ -54,13 +56,14 @@
_ Exclude this repo from its own readme ^
set repos to filter(repos, isNotCurrentRepo) ^
+ set nightlyOnlyRepos to filter(nightlyOnlyRepos, isNotCurrentRepo) ^
set samplesRepos to filter(samplesRepos, isNotCurrentRepo) ^
set repos to
when(isNightlyRepo,
when(IS_PRODUCT_FAMILY,
cat(productFamilyRepos, repos, samplesRepos),
- cat(productFamilyRepos, currentRepo, map(repos, insertNightly), samplesRepos)),
+ cat(productFamilyRepos, currentRepo, map(cat(repos, nightlyOnlyRepos), insertNightly), samplesRepos)),
when(IS_PRODUCT_FAMILY,
cat(map(repos, insertNightly)),
cat(productFamilyRepos, repos, map(currentRepo, insertNightly), samplesRepos)))
@@ -71,4 +74,4 @@
{{InsertTemplate("RepoList.md", [ "readme-host": ARGS["readme-host"], "repos": repos ])}}
.NET Framework:
-{{InsertTemplate("RepoList.md", [ "readme-host": ARGS["readme-host"], "repos": frameworkRepos ])}}
\ No newline at end of file
+{{InsertTemplate("RepoList.md", [ "readme-host": ARGS["readme-host"], "repos": frameworkRepos ])}}
diff --git a/eng/readme-templates/ReposProvider.md b/eng/readme-templates/ReposProvider.md
index bf7c14b6dd..0431b983e0 100644
--- a/eng/readme-templates/ReposProvider.md
+++ b/eng/readme-templates/ReposProvider.md
@@ -2,9 +2,10 @@
_ Wrapper template for providing the list of repos to other templates.
_ ARGS:
- top-header: The string to use as the top-level header.
- readme-host: Moniker of the site that will host the readme
- template: Template to pass the repo lists to ^
+ template: Template to pass the repo lists to.
+ All other args will be passed to the template. ^
+
+ set argsToForward to except(ARGS, [ "template": ARGS["template"] ]) ^
set productRepos to [
["dotnet/sdk", ".NET SDK"],
@@ -15,8 +16,11 @@
["dotnet/monitor/base", ".NET Monitor Base"],
["dotnet/aspire-dashboard", "Aspire Dashboard"]
] ^
+ set nightlyOnlyRepos to [
+ ["dotnet/yarp", "YARP (Yet Another Reverse Proxy)"]
+ ] ^
set productFamilyRepos to [
- ["dotnet", ".NET", 1],
+ ["dotnet", ".NET", 1]
] ^
set samplesRepos to [
["dotnet/samples", ".NET Samples"]
@@ -26,11 +30,10 @@
["dotnet/framework/samples", ".NET Framework, ASP.NET and WCF Samples"]
]
-}}{{InsertTemplate(ARGS["template"], [
- "top-header": ARGS["top-header"],
- "readme-host": ARGS["readme-host"],
+}}{{InsertTemplate(ARGS["template"], union(argsToForward, [
"product-repos": productRepos,
+ "nightly-only-repos": nightlyOnlyRepos,
"product-family-repos": productFamilyRepos,
"samples-repos": samplesRepos,
"framework-repos": frameworkRepos
-])}}
\ No newline at end of file
+]))}}
diff --git a/eng/readme-templates/Support.md b/eng/readme-templates/Support.md
index 92881938ef..5315bd4ef1 100644
--- a/eng/readme-templates/Support.md
+++ b/eng/readme-templates/Support.md
@@ -5,7 +5,8 @@
set isRuntimeDeps to match(SHORT_REPO, "runtime-deps") ^
set isSamples to match(SHORT_REPO, "samples") ^
set isMonitor to match(SHORT_REPO, "monitor") || match(SHORT_REPO, "base") ^
- set isAspireDashboard to match(SHORT_REPO, "aspire-dashboard")
+ set isAspireDashboard to match(SHORT_REPO, "aspire-dashboard") ^
+ set isReverseProxy to match(SHORT_REPO, "yarp")
}}{{ARGS["top-header"]}} Support
@@ -13,7 +14,8 @@
}}{{ARGS["top-header"]}}# Lifecycle
-* [Microsoft Support for .NET](https://github.com/dotnet/core/blob/main/support.md)
+{{if isReverseProxy:* [Microsoft Support for YARP](https://github.com/dotnet/yarp/blob/main/docs/roadmap.md)
+}}* [Microsoft Support for .NET](https://github.com/dotnet/core/blob/main/support.md)
* [Supported Container Platforms Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-platforms.md)
* [Supported Tags Policy](https://github.com/dotnet/dotnet-docker/blob/main/documentation/supported-tags.md)
diff --git a/eng/readme-templates/Use.yarp.md b/eng/readme-templates/Use.yarp.md
new file mode 100644
index 0000000000..55231b4375
--- /dev/null
+++ b/eng/readme-templates/Use.yarp.md
@@ -0,0 +1,71 @@
+{{
+ _ ARGS:
+ top-header: The string to use as the top-level header.
+ readme-host: Moniker of the site that will host the readme
+}}You can run this image to launch a YARP instance.
+
+{{ARGS["top-header"]}}# Configuration
+
+YARP expects the config file to be in `/etc/yarp.config`, and listens by default on port 5000.
+
+Example of configuration:
+
+```json
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*",
+ "ReverseProxy": {
+ "Routes": {
+ "route1": {
+ "ClusterId": "cluster1",
+ "Match": {
+ "Path": "/aspnetapp/{**catch-all}"
+ },
+ "Transforms": [
+ { "PathRemovePrefix": "/aspnetapp" }
+ ]
+ }
+ },
+ "Clusters": {
+ "cluster1": {
+ "Destinations": {
+ "destination1": {
+ "Address": "http://aspnetapp1:8080"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+It can then be used with the following command (where `my-config.config` is a file containing this configuration):
+
+```bash
+docker run --rm --name myaspnetapp -d -t mcr.microsoft.com/dotnet/samples:aspnetapp
+docker run --rm -v $(pwd)/my-config.config:/etc/yarp.config -p 5000:5000 --link myaspnetapp:aspnetapp1 mcr.microsoft.com/dotnet/yarp:latest
+```
+
+This example will proxy every requests from `http://localhost:5000/aspnetapp` to the `mcr.microsoft.com/dotnet/samples:aspnetapp` container deployed.
+
+The [YARP GitHub repository](https://github.com/dotnet/yarp/tree/main/samples/) contains more configuration samples.
+
+For more details, see the [documentation](https://aka.ms/YarpDocumentation) for how to configure the image and documentation for the reverse proxy configuration.
+
+{{ARGS["top-header"]}}# OpenTelemetry support
+
+This image supports OpenTelemetry. It can be configured by passing environment variables to the container:
+
+```bash
+docker run --rm -v $(pwd)/my-config.config:/etc/yarp.config -p 5000:5000 -e OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-endpoint.internal:4317 mcr.microsoft.com/dotnet/yarp:latest
+```
+
+See the [OTLP Exporter Configuration](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/) for all supported environment variables.
+
+You can skip HTTPS validation for the OTLP endpoint only by passing the environment variable `YARP_UNSAFE_OLTP_CERT_ACCEPT_ANY_SERVER_CERTIFICATE`.
\ No newline at end of file
diff --git a/manifest.versions.json b/manifest.versions.json
index 410e437a95..3f199196b5 100644
--- a/manifest.versions.json
+++ b/manifest.versions.json
@@ -197,8 +197,8 @@
"sdk|8.0|fixed-tag": "$(sdk|8.0|product-version)",
"sdk|8.0|minor-tag": "$(dotnet|8.0|minor-tag)",
- "sdk|9.0|build-version": "9.0.309",
- "sdk|9.0|product-version": "9.0.309",
+ "sdk|9.0|build-version": "9.0.310",
+ "sdk|9.0|product-version": "9.0.310",
"sdk|9.0|base-url|main": "$(dotnet|9.0|base-url|main)",
"sdk|9.0|base-url|nightly": "$(dotnet|9.0|base-url|nightly)",
"sdk|9.0|base-url|checksums|main": "$(sdk|9.0|base-url|main)",
diff --git a/src/sdk/9.0/alpine3.22/amd64/Dockerfile b/src/sdk/9.0/alpine3.22/amd64/Dockerfile
index 4d3be40bc5..5a6146beb6 100644
--- a/src/sdk/9.0/alpine3.22/amd64/Dockerfile
+++ b/src/sdk/9.0/alpine3.22/amd64/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM $REPO:9.0.12-alpine3.22-amd64 AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& wget \
https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-musl-x64.tar.gz \
@@ -25,7 +25,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Disable the invariant mode (set in base image)
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
# Enable correct mode for dotnet watch (only mode supported in a container)
diff --git a/src/sdk/9.0/alpine3.22/arm32v7/Dockerfile b/src/sdk/9.0/alpine3.22/arm32v7/Dockerfile
index f67b10f365..546fe28d53 100644
--- a/src/sdk/9.0/alpine3.22/arm32v7/Dockerfile
+++ b/src/sdk/9.0/alpine3.22/arm32v7/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM $REPO:9.0.12-alpine3.22-arm32v7 AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& wget \
https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-musl-arm.tar.gz \
@@ -25,7 +25,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Disable the invariant mode (set in base image)
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
# Enable correct mode for dotnet watch (only mode supported in a container)
diff --git a/src/sdk/9.0/alpine3.22/arm64v8/Dockerfile b/src/sdk/9.0/alpine3.22/arm64v8/Dockerfile
index 883e1fc84c..dcb1d07830 100644
--- a/src/sdk/9.0/alpine3.22/arm64v8/Dockerfile
+++ b/src/sdk/9.0/alpine3.22/arm64v8/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM $REPO:9.0.12-alpine3.22-arm64v8 AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& wget \
https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-musl-arm64.tar.gz \
@@ -25,7 +25,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Disable the invariant mode (set in base image)
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
# Enable correct mode for dotnet watch (only mode supported in a container)
diff --git a/src/sdk/9.0/alpine3.23/amd64/Dockerfile b/src/sdk/9.0/alpine3.23/amd64/Dockerfile
index 8026a5fdc9..c9e51b6087 100644
--- a/src/sdk/9.0/alpine3.23/amd64/Dockerfile
+++ b/src/sdk/9.0/alpine3.23/amd64/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM $REPO:9.0.12-alpine3.23-amd64 AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& wget \
https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-musl-x64.tar.gz \
@@ -25,7 +25,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Disable the invariant mode (set in base image)
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
# Enable correct mode for dotnet watch (only mode supported in a container)
diff --git a/src/sdk/9.0/alpine3.23/arm32v7/Dockerfile b/src/sdk/9.0/alpine3.23/arm32v7/Dockerfile
index 8badf7df33..e1bfc3ad39 100644
--- a/src/sdk/9.0/alpine3.23/arm32v7/Dockerfile
+++ b/src/sdk/9.0/alpine3.23/arm32v7/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM $REPO:9.0.12-alpine3.23-arm32v7 AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& wget \
https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-musl-arm.tar.gz \
@@ -25,7 +25,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Disable the invariant mode (set in base image)
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
# Enable correct mode for dotnet watch (only mode supported in a container)
diff --git a/src/sdk/9.0/alpine3.23/arm64v8/Dockerfile b/src/sdk/9.0/alpine3.23/arm64v8/Dockerfile
index 767b0d7e5a..462f863bcc 100644
--- a/src/sdk/9.0/alpine3.23/arm64v8/Dockerfile
+++ b/src/sdk/9.0/alpine3.23/arm64v8/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM $REPO:9.0.12-alpine3.23-arm64v8 AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& wget \
https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-musl-arm64.tar.gz \
@@ -25,7 +25,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Disable the invariant mode (set in base image)
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false \
# Enable correct mode for dotnet watch (only mode supported in a container)
diff --git a/src/sdk/9.0/azurelinux3.0/amd64/Dockerfile b/src/sdk/9.0/azurelinux3.0/amd64/Dockerfile
index eeadea71af..a355722644 100644
--- a/src/sdk/9.0/azurelinux3.0/amd64/Dockerfile
+++ b/src/sdk/9.0/azurelinux3.0/amd64/Dockerfile
@@ -7,7 +7,7 @@ RUN tdnf install -y \
&& tdnf clean all
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-x64.tar.gz \
@@ -30,7 +30,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/azurelinux3.0/arm64v8/Dockerfile b/src/sdk/9.0/azurelinux3.0/arm64v8/Dockerfile
index e407e32b99..719fe2a770 100644
--- a/src/sdk/9.0/azurelinux3.0/arm64v8/Dockerfile
+++ b/src/sdk/9.0/azurelinux3.0/arm64v8/Dockerfile
@@ -7,7 +7,7 @@ RUN tdnf install -y \
&& tdnf clean all
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-arm64.tar.gz \
@@ -30,7 +30,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/bookworm-slim/amd64/Dockerfile b/src/sdk/9.0/bookworm-slim/amd64/Dockerfile
index 5bcb2aa9a2..ddbb3a43b8 100644
--- a/src/sdk/9.0/bookworm-slim/amd64/Dockerfile
+++ b/src/sdk/9.0/bookworm-slim/amd64/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM amd64/buildpack-deps:bookworm-curl AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-x64.tar.gz \
@@ -26,7 +26,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/bookworm-slim/arm32v7/Dockerfile b/src/sdk/9.0/bookworm-slim/arm32v7/Dockerfile
index 62839d4192..690ddd71da 100644
--- a/src/sdk/9.0/bookworm-slim/arm32v7/Dockerfile
+++ b/src/sdk/9.0/bookworm-slim/arm32v7/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM arm32v7/buildpack-deps:bookworm-curl AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-arm.tar.gz \
@@ -26,7 +26,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/bookworm-slim/arm64v8/Dockerfile b/src/sdk/9.0/bookworm-slim/arm64v8/Dockerfile
index 1a38f4dea9..9962f401c0 100644
--- a/src/sdk/9.0/bookworm-slim/arm64v8/Dockerfile
+++ b/src/sdk/9.0/bookworm-slim/arm64v8/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM arm64v8/buildpack-deps:bookworm-curl AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-arm64.tar.gz \
@@ -26,7 +26,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/nanoserver-1809/amd64/Dockerfile b/src/sdk/9.0/nanoserver-1809/amd64/Dockerfile
index 21ed8ee3cc..89dff5b350 100644
--- a/src/sdk/9.0/nanoserver-1809/amd64/Dockerfile
+++ b/src/sdk/9.0/nanoserver-1809/amd64/Dockerfile
@@ -25,7 +25,7 @@ RUN powershell -Command " `
$ProgressPreference = 'SilentlyContinue'; `
`
# Retrieve .NET SDK
- $dotnet_sdk_version = '9.0.309'; `
+ $dotnet_sdk_version = '9.0.310'; `
$dotnet_version = '9.0.12'; `
$dotnet_file = 'dotnet-sdk-' + $dotnet_sdk_version + '-win-x64.zip'; `
$dotnet_checksums_file = $dotnet_version + '-sha.txt'; `
@@ -82,7 +82,7 @@ ENV `
# Do not show first run text
DOTNET_NOLOGO=true `
# SDK version
- DOTNET_SDK_VERSION=9.0.309 `
+ DOTNET_SDK_VERSION=9.0.310 `
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true `
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/nanoserver-ltsc2022/amd64/Dockerfile b/src/sdk/9.0/nanoserver-ltsc2022/amd64/Dockerfile
index b1473674cb..0a8b29f9b0 100644
--- a/src/sdk/9.0/nanoserver-ltsc2022/amd64/Dockerfile
+++ b/src/sdk/9.0/nanoserver-ltsc2022/amd64/Dockerfile
@@ -25,7 +25,7 @@ RUN powershell -Command " `
$ProgressPreference = 'SilentlyContinue'; `
`
# Retrieve .NET SDK
- $dotnet_sdk_version = '9.0.309'; `
+ $dotnet_sdk_version = '9.0.310'; `
$dotnet_version = '9.0.12'; `
$dotnet_file = 'dotnet-sdk-' + $dotnet_sdk_version + '-win-x64.zip'; `
$dotnet_checksums_file = $dotnet_version + '-sha.txt'; `
@@ -82,7 +82,7 @@ ENV `
# Do not show first run text
DOTNET_NOLOGO=true `
# SDK version
- DOTNET_SDK_VERSION=9.0.309 `
+ DOTNET_SDK_VERSION=9.0.310 `
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true `
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/nanoserver-ltsc2025/amd64/Dockerfile b/src/sdk/9.0/nanoserver-ltsc2025/amd64/Dockerfile
index 9880b46ade..a38dfa74aa 100644
--- a/src/sdk/9.0/nanoserver-ltsc2025/amd64/Dockerfile
+++ b/src/sdk/9.0/nanoserver-ltsc2025/amd64/Dockerfile
@@ -25,7 +25,7 @@ RUN powershell -Command " `
$ProgressPreference = 'SilentlyContinue'; `
`
# Retrieve .NET SDK
- $dotnet_sdk_version = '9.0.309'; `
+ $dotnet_sdk_version = '9.0.310'; `
$dotnet_version = '9.0.12'; `
$dotnet_file = 'dotnet-sdk-' + $dotnet_sdk_version + '-win-x64.zip'; `
$dotnet_checksums_file = $dotnet_version + '-sha.txt'; `
@@ -82,7 +82,7 @@ ENV `
# Do not show first run text
DOTNET_NOLOGO=true `
# SDK version
- DOTNET_SDK_VERSION=9.0.309 `
+ DOTNET_SDK_VERSION=9.0.310 `
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true `
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/noble/amd64/Dockerfile b/src/sdk/9.0/noble/amd64/Dockerfile
index 07565eb770..ea108b7ee6 100644
--- a/src/sdk/9.0/noble/amd64/Dockerfile
+++ b/src/sdk/9.0/noble/amd64/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM amd64/buildpack-deps:noble-curl AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-x64.tar.gz \
@@ -26,7 +26,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/noble/arm32v7/Dockerfile b/src/sdk/9.0/noble/arm32v7/Dockerfile
index 96dea1d276..a6f89a9adf 100644
--- a/src/sdk/9.0/noble/arm32v7/Dockerfile
+++ b/src/sdk/9.0/noble/arm32v7/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM arm32v7/buildpack-deps:jammy-curl AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-arm.tar.gz \
@@ -26,7 +26,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/noble/arm64v8/Dockerfile b/src/sdk/9.0/noble/arm64v8/Dockerfile
index f2effa920a..235d4f45d1 100644
--- a/src/sdk/9.0/noble/arm64v8/Dockerfile
+++ b/src/sdk/9.0/noble/arm64v8/Dockerfile
@@ -3,7 +3,7 @@ ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM arm64v8/buildpack-deps:noble-curl AS installer
# Install .NET SDK
-RUN dotnet_sdk_version=9.0.309 \
+RUN dotnet_sdk_version=9.0.310 \
&& dotnet_version=9.0.12 \
&& curl --fail --show-error --location \
--remote-name https://builds.dotnet.microsoft.com/dotnet/Sdk/$dotnet_sdk_version/dotnet-sdk-$dotnet_sdk_version-linux-arm64.tar.gz \
@@ -26,7 +26,7 @@ ENV \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
- DOTNET_SDK_VERSION=9.0.309 \
+ DOTNET_SDK_VERSION=9.0.310 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/windowsservercore-ltsc2019/amd64/Dockerfile b/src/sdk/9.0/windowsservercore-ltsc2019/amd64/Dockerfile
index 27898a2808..3cbf92d125 100644
--- a/src/sdk/9.0/windowsservercore-ltsc2019/amd64/Dockerfile
+++ b/src/sdk/9.0/windowsservercore-ltsc2019/amd64/Dockerfile
@@ -25,7 +25,7 @@ RUN powershell -Command " `
$ProgressPreference = 'SilentlyContinue'; `
`
# Retrieve .NET SDK
- $dotnet_sdk_version = '9.0.309'; `
+ $dotnet_sdk_version = '9.0.310'; `
$dotnet_version = '9.0.12'; `
$dotnet_file = 'dotnet-sdk-' + $dotnet_sdk_version + '-win-x64.zip'; `
$dotnet_checksums_file = $dotnet_version + '-sha.txt'; `
@@ -82,7 +82,7 @@ ENV `
# Do not show first run text
DOTNET_NOLOGO=true `
# SDK version
- DOTNET_SDK_VERSION=9.0.309 `
+ DOTNET_SDK_VERSION=9.0.310 `
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true `
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/windowsservercore-ltsc2022/amd64/Dockerfile b/src/sdk/9.0/windowsservercore-ltsc2022/amd64/Dockerfile
index 847e69ba68..22ba1e7ff8 100644
--- a/src/sdk/9.0/windowsservercore-ltsc2022/amd64/Dockerfile
+++ b/src/sdk/9.0/windowsservercore-ltsc2022/amd64/Dockerfile
@@ -25,7 +25,7 @@ RUN powershell -Command " `
$ProgressPreference = 'SilentlyContinue'; `
`
# Retrieve .NET SDK
- $dotnet_sdk_version = '9.0.309'; `
+ $dotnet_sdk_version = '9.0.310'; `
$dotnet_version = '9.0.12'; `
$dotnet_file = 'dotnet-sdk-' + $dotnet_sdk_version + '-win-x64.zip'; `
$dotnet_checksums_file = $dotnet_version + '-sha.txt'; `
@@ -82,7 +82,7 @@ ENV `
# Do not show first run text
DOTNET_NOLOGO=true `
# SDK version
- DOTNET_SDK_VERSION=9.0.309 `
+ DOTNET_SDK_VERSION=9.0.310 `
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true `
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/src/sdk/9.0/windowsservercore-ltsc2025/amd64/Dockerfile b/src/sdk/9.0/windowsservercore-ltsc2025/amd64/Dockerfile
index ff6faef9d6..325c7239cb 100644
--- a/src/sdk/9.0/windowsservercore-ltsc2025/amd64/Dockerfile
+++ b/src/sdk/9.0/windowsservercore-ltsc2025/amd64/Dockerfile
@@ -25,7 +25,7 @@ RUN powershell -Command " `
$ProgressPreference = 'SilentlyContinue'; `
`
# Retrieve .NET SDK
- $dotnet_sdk_version = '9.0.309'; `
+ $dotnet_sdk_version = '9.0.310'; `
$dotnet_version = '9.0.12'; `
$dotnet_file = 'dotnet-sdk-' + $dotnet_sdk_version + '-win-x64.zip'; `
$dotnet_checksums_file = $dotnet_version + '-sha.txt'; `
@@ -82,7 +82,7 @@ ENV `
# Do not show first run text
DOTNET_NOLOGO=true `
# SDK version
- DOTNET_SDK_VERSION=9.0.309 `
+ DOTNET_SDK_VERSION=9.0.310 `
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true `
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
diff --git a/tests/Microsoft.DotNet.Docker.Tests/Microsoft.DotNet.Docker.Tests.csproj b/tests/Microsoft.DotNet.Docker.Tests/Microsoft.DotNet.Docker.Tests.csproj
index caf42fdf14..e72fcffb52 100644
--- a/tests/Microsoft.DotNet.Docker.Tests/Microsoft.DotNet.Docker.Tests.csproj
+++ b/tests/Microsoft.DotNet.Docker.Tests/Microsoft.DotNet.Docker.Tests.csproj
@@ -25,13 +25,13 @@
-
+
PreserveNewest
-
+
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/.dockerignore b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/.dockerignore
new file mode 100644
index 0000000000..79326b93f1
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/.dockerignore
@@ -0,0 +1,8 @@
+# directories
+**/bin/
+**/obj/
+**/out/
+
+# files
+Dockerfile*
+**/*.md
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/Dockerfile b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/Dockerfile
new file mode 100644
index 0000000000..dd49cf0f77
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/Dockerfile
@@ -0,0 +1,23 @@
+# Learn about building .NET container images:
+# https://github.com/dotnet/dotnet-docker/blob/main/samples/README.md
+FROM mcr.microsoft.com/dotnet/sdk:9.0-azurelinux3.0 AS build
+WORKDIR /source
+
+# Copy project file and restore as distinct layers
+COPY --link OtlpTestListener/*.csproj ./OtlpTestListener/
+RUN dotnet restore OtlpTestListener/*.csproj
+
+# Copy source code and publish app
+COPY --link OtlpTestListener/. ./OtlpTestListener/
+WORKDIR /source/OtlpTestListener
+RUN dotnet publish -c release --no-restore -o /app
+
+
+# Runtime stage
+FROM mcr.microsoft.com/dotnet/aspnet:9.0-azurelinux3.0-distroless
+EXPOSE 8080
+EXPOSE 4317
+WORKDIR /app
+COPY --link --from=build /app .
+USER $APP_UID
+ENTRYPOINT ["./OtlpTestListener"]
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/DataModel/Common.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/DataModel/Common.cs
new file mode 100644
index 0000000000..efffdd9d29
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/DataModel/Common.cs
@@ -0,0 +1,7 @@
+namespace OtlpTestListener.DataModel;
+
+public class OtlpApplication
+{
+ public const string SERVICE_NAME = "service.name";
+ public const string SERVICE_INSTANCE_ID = "service.instance.id";
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/DataModel/TelemetryResults.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/DataModel/TelemetryResults.cs
new file mode 100644
index 0000000000..fe7140f918
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/DataModel/TelemetryResults.cs
@@ -0,0 +1,44 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace OtlpTestListener.DataModel;
+
+public class TelemetryResults
+{
+ public int SpanIdCount { get; set; }
+ public int LogMessageCount { get; set; }
+ public List MetricNames { get; init; } = new();
+ public List ResourceNames { get; init; } = new();
+ public List TraceIds { get; init; } = new();
+ [JsonIgnore]
+ public int MetricNameCount => MetricNames.Count;
+ [JsonIgnore]
+ public int TraceIdCount => TraceIds.Count;
+
+ public void Clear()
+ {
+ SpanIdCount = 0;
+ LogMessageCount = 0;
+ MetricNames.Clear();
+ ResourceNames.Clear();
+ TraceIds.Clear();
+ }
+
+ public void AddResourceName(string? resourceName)
+ {
+ if (resourceName is not null && !ResourceNames.Contains(resourceName))
+ {
+ ResourceNames.Add(resourceName);
+ }
+ }
+
+ #region JSON Serialization
+ private readonly JsonSerializerOptions _jsonOptions = new()
+ {
+ WriteIndented = true,
+ };
+
+ public string GetResultsJSON() => JsonSerializer.Serialize(this, _jsonOptions);
+
+ #endregion
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Extensions/Helpers.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Extensions/Helpers.cs
new file mode 100644
index 0000000000..4255f18519
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Extensions/Helpers.cs
@@ -0,0 +1,31 @@
+using OtlpTestListener.DataModel;
+
+namespace OtlpTestListener.Extensions;
+
+public static class Helpers
+{
+ public static string ValueString(this AnyValue value) =>
+ value.ValueCase switch
+ {
+ AnyValue.ValueOneofCase.StringValue => value.StringValue,
+ AnyValue.ValueOneofCase.IntValue => value.IntValue.ToString(),
+ AnyValue.ValueOneofCase.DoubleValue => value.DoubleValue.ToString(),
+ AnyValue.ValueOneofCase.BoolValue => value.BoolValue.ToString(),
+ AnyValue.ValueOneofCase.BytesValue => value.BytesValue.ToHexString(),
+ _ => value.ToString(),
+ };
+
+ public static string ToHexString(this Google.Protobuf.ByteString bytes)
+ {
+ if (bytes is null or { Length: 0 })
+ {
+ return null!;
+ }
+ var sb = new StringBuilder();
+ foreach (var b in bytes)
+ {
+ sb.Append($"{b:x2}");
+ }
+ return sb.ToString();
+ }
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/GlobalUsings.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/GlobalUsings.cs
new file mode 100644
index 0000000000..774f8f4fea
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/GlobalUsings.cs
@@ -0,0 +1,25 @@
+global using System.Collections;
+global using System.Collections.Concurrent;
+global using System.Diagnostics;
+global using System.Text;
+global using System.Web;
+
+global using Google.Protobuf.Collections;
+
+global using Grpc.Core;
+
+global using OpenTelemetry.Proto.Collector.Logs.V1;
+global using OpenTelemetry.Proto.Collector.Metrics.V1;
+global using OpenTelemetry.Proto.Collector.Trace.V1;
+global using OpenTelemetry.Proto.Common.V1;
+global using OpenTelemetry.Proto.Logs.V1;
+global using OpenTelemetry.Proto.Metrics.V1;
+global using OpenTelemetry.Proto.Resource.V1;
+
+global using OtlpTestListener;
+global using OtlpTestListener.DataModel;
+global using OtlpTestListener.Extensions;
+
+global using Otel = OpenTelemetry.Proto.Trace.V1;
+
+global using Microsoft.Extensions.Logging;
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/OtlpTestListener.csproj b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/OtlpTestListener.csproj
new file mode 100644
index 0000000000..5d000a0c60
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/OtlpTestListener.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+ otlp
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Program.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Program.cs
new file mode 100644
index 0000000000..124cb034b8
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Program.cs
@@ -0,0 +1,41 @@
+using OtlpTestListener.DataModel;
+using OtlpTestListener.Services;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddSingleton();
+
+// Add services to the container.
+builder.Services.AddGrpc();
+
+var otlpPort = builder.Configuration.GetValue("OTLP_PORT", 4317);
+var webPorts = builder.Configuration.GetValue("ASPNETCORE_HTTP_PORTS", "8080")!.Split(',').Select(p => int.Parse(p)).ToArray();
+
+builder.WebHost.ConfigureKestrel(options =>
+{
+ options.ListenAnyIP(otlpPort, listenOptions =>
+ {
+ listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2;
+ });
+ foreach (var port in webPorts)
+ {
+ options.ListenAnyIP(port, listenOptions =>
+ {
+ listenOptions.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http1AndHttp2;
+ });
+ }
+});
+
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+app.MapGrpcService();
+app.MapGrpcService();
+app.MapGrpcService();
+
+
+app.MapGet("/report", (TelemetryResults tr) => tr.GetResultsJSON());
+app.MapGet("/clear", (TelemetryResults tr) => tr.Clear());
+
+app.Run();
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Properties/launchSettings.json b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Properties/launchSettings.json
new file mode 100644
index 0000000000..c5aa6fa876
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Properties/launchSettings.json
@@ -0,0 +1,12 @@
+{
+ "profiles": {
+ "OtlpTestListener": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:50897;http://localhost:50898"
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultLogsService.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultLogsService.cs
new file mode 100644
index 0000000000..cd65e65f5f
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultLogsService.cs
@@ -0,0 +1,33 @@
+public class DefaultLogsService : LogsService.LogsServiceBase
+{
+ private readonly ILogger _logger;
+ private readonly TelemetryResults _telemetryResults;
+
+ public DefaultLogsService(ILogger logger, TelemetryResults telemetryResults)
+ {
+ _logger = logger;
+ _telemetryResults = telemetryResults;
+ }
+
+ public override Task Export(ExportLogsServiceRequest request, ServerCallContext context)
+ {
+ foreach (var resource in request.ResourceLogs)
+ {
+ var resourceName = resource.Resource?.Attributes.FirstOrDefault(a => a.Key == OtlpApplication.SERVICE_NAME)?.Value.ValueString();
+ _logger.LogDebug("Received {count} scope logs for resource {resourceName}", resource.ScopeLogs.Count, resourceName);
+
+ foreach (var scope in resource.ScopeLogs)
+ {
+ _logger.LogDebug($"Received {scope.LogRecords.Count} log records for scope {scope.Scope?.Name}");
+ _telemetryResults.LogMessageCount += scope.LogRecords.Count;
+ }
+ }
+
+ var resp = new ExportLogsServiceResponse
+ {
+ PartialSuccess = null
+ };
+
+ return Task.FromResult(resp);
+ }
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultMetricsService.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultMetricsService.cs
new file mode 100644
index 0000000000..4323eeceb2
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultMetricsService.cs
@@ -0,0 +1,43 @@
+using OtlpTestListener.DataModel;
+
+namespace OtlpTestListener.Services;
+
+public class DefaultMetricsService : MetricsService.MetricsServiceBase
+{
+ private readonly ILogger _logger;
+ private readonly TelemetryResults _telemetryResults;
+ public DefaultMetricsService(ILogger logger, TelemetryResults telemetryResults)
+ {
+ _logger = logger;
+ _telemetryResults = telemetryResults;
+ }
+
+ public override Task Export(ExportMetricsServiceRequest request, ServerCallContext context)
+ {
+ foreach (var resource in request.ResourceMetrics)
+ {
+ var resourceName = resource.Resource?.Attributes.FirstOrDefault(a => a.Key == OtlpApplication.SERVICE_NAME)?.Value.ValueString();
+ _logger.LogDebug("Received {count} scope metrics for resource {resourceName}", resource.ScopeMetrics.Count, resourceName);
+ _telemetryResults.AddResourceName(resourceName);
+ foreach (var scope in resource.ScopeMetrics)
+ {
+ _logger.LogDebug($"Received {scope.Metrics.Count} metrics for scope {scope.Scope?.Name}");
+ foreach (var metric in scope.Metrics)
+ {
+ if (!_telemetryResults.MetricNames.Contains(metric.Name))
+ {
+ _logger.LogDebug($"New metric seen: {metric.Name}");
+ _telemetryResults.MetricNames.Add(metric.Name);
+ }
+ }
+ }
+ }
+
+ var resp = new ExportMetricsServiceResponse
+ {
+ PartialSuccess = null
+ };
+
+ return Task.FromResult(resp);
+ }
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultTraceService.cs b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultTraceService.cs
new file mode 100644
index 0000000000..5f838ac1e1
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/Services/DefaultTraceService.cs
@@ -0,0 +1,44 @@
+namespace OtlpTestListener.Services;
+
+public class DefaultTraceService : TraceService.TraceServiceBase
+{
+ private readonly ILogger _logger;
+ private readonly TelemetryResults _telemetryResults;
+
+ public DefaultTraceService(ILogger logger, TelemetryResults telemetryResults)
+ {
+ _logger = logger;
+ _telemetryResults = telemetryResults;
+ }
+
+ public override Task Export(ExportTraceServiceRequest request, ServerCallContext context)
+ {
+ foreach (var resource in request.ResourceSpans)
+ {
+ var resourceName = resource.Resource?.Attributes.FirstOrDefault(a => a.Key == OtlpApplication.SERVICE_NAME)?.Value.ValueString();
+ _logger.LogDebug("Received {count} scope spans for resource {resourceName}", resource.ScopeSpans.Count, resourceName);
+ _telemetryResults.AddResourceName(resourceName);
+
+ foreach (var scope in resource.ScopeSpans)
+ {
+ _logger.LogDebug($"Received {scope.Spans.Count} spans for scope {scope.Scope?.Name}");
+ foreach (var span in scope.Spans)
+ {
+ var TraceId = span.TraceId.ToHexString();
+ if (!_telemetryResults.TraceIds.Contains(TraceId))
+ {
+ _logger.LogDebug($"New TraceId seen: {TraceId}");
+ _telemetryResults.TraceIds.Add(TraceId);
+ }
+ _telemetryResults.SpanIdCount += 1;
+ }
+ }
+ }
+
+ var resp = new ExportTraceServiceResponse
+ {
+ PartialSuccess = null
+ };
+ return Task.FromResult(resp);
+ }
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/README.md b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/README.md
new file mode 100644
index 0000000000..81144e8b43
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/README.md
@@ -0,0 +1,5 @@
+# OpenTelemetry Proto
+
+See [open-telemetry on GitHub](https://github.com/open-telemetry/opentelemetry-proto) for all the sources.
+
+We only support v1 at this point.
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/README.md b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/README.md
new file mode 100644
index 0000000000..f82dbb0278
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/README.md
@@ -0,0 +1,10 @@
+# OpenTelemetry Collector Proto
+
+This package describes the OpenTelemetry collector protocol.
+
+## Packages
+
+1. `common` package contains the common messages shared between different services.
+2. `trace` package contains the Trace Service protos.
+3. `metrics` package contains the Metrics Service protos.
+4. `logs` package contains the Logs Service protos.
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/logs/v1/logs_service.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/logs/v1/logs_service.proto
new file mode 100644
index 0000000000..8260d8aaeb
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/logs/v1/logs_service.proto
@@ -0,0 +1,79 @@
+// Copyright 2020, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.collector.logs.v1;
+
+import "opentelemetry/proto/logs/v1/logs.proto";
+
+option csharp_namespace = "OpenTelemetry.Proto.Collector.Logs.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.collector.logs.v1";
+option java_outer_classname = "LogsServiceProto";
+option go_package = "go.opentelemetry.io/proto/otlp/collector/logs/v1";
+
+// Service that can be used to push logs between one Application instrumented with
+// OpenTelemetry and an collector, or between an collector and a central collector (in this
+// case logs are sent/received to/from multiple Applications).
+service LogsService {
+ // For performance reasons, it is recommended to keep this RPC
+ // alive for the entire life of the application.
+ rpc Export(ExportLogsServiceRequest) returns (ExportLogsServiceResponse) {}
+}
+
+message ExportLogsServiceRequest {
+ // An array of ResourceLogs.
+ // For data coming from a single resource this array will typically contain one
+ // element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ // data from multiple origins typically batch the data before forwarding further and
+ // in that case this array will contain multiple elements.
+ repeated opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1;
+}
+
+message ExportLogsServiceResponse {
+ // The details of a partially successful export request.
+ //
+ // If the request is only partially accepted
+ // (i.e. when the server accepts only parts of the data and rejects the rest)
+ // the server MUST initialize the `partial_success` field and MUST
+ // set the `rejected_` with the number of items it rejected.
+ //
+ // Servers MAY also make use of the `partial_success` field to convey
+ // warnings/suggestions to senders even when the request was fully accepted.
+ // In such cases, the `rejected_` MUST have a value of `0` and
+ // the `error_message` MUST be non-empty.
+ //
+ // A `partial_success` message with an empty value (rejected_ = 0 and
+ // `error_message` = "") is equivalent to it not being set/present. Senders
+ // SHOULD interpret it the same way as in the full success case.
+ ExportLogsPartialSuccess partial_success = 1;
+}
+
+message ExportLogsPartialSuccess {
+ // The number of rejected log records.
+ //
+ // A `rejected_` field holding a `0` value indicates that the
+ // request was fully accepted.
+ int64 rejected_log_records = 1;
+
+ // A developer-facing human-readable message in English. It should be used
+ // either to explain why the server rejected parts of the data during a partial
+ // success or to convey warnings/suggestions during a full success. The message
+ // should offer guidance on how users can address such issues.
+ //
+ // error_message is an optional field. An error_message with an empty value
+ // is equivalent to it not being set.
+ string error_message = 2;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml
new file mode 100644
index 0000000000..507473b9b3
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml
@@ -0,0 +1,9 @@
+# This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the
+# OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway.
+type: google.api.Service
+config_version: 3
+http:
+ rules:
+ - selector: opentelemetry.proto.collector.logs.v1.LogsService.Export
+ post: /v1/logs
+ body: "*"
\ No newline at end of file
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/metrics/v1/metrics_service.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/metrics/v1/metrics_service.proto
new file mode 100644
index 0000000000..dd48f1ad3a
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/metrics/v1/metrics_service.proto
@@ -0,0 +1,79 @@
+// Copyright 2019, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.collector.metrics.v1;
+
+import "opentelemetry/proto/metrics/v1/metrics.proto";
+
+option csharp_namespace = "OpenTelemetry.Proto.Collector.Metrics.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.collector.metrics.v1";
+option java_outer_classname = "MetricsServiceProto";
+option go_package = "go.opentelemetry.io/proto/otlp/collector/metrics/v1";
+
+// Service that can be used to push metrics between one Application
+// instrumented with OpenTelemetry and a collector, or between a collector and a
+// central collector.
+service MetricsService {
+ // For performance reasons, it is recommended to keep this RPC
+ // alive for the entire life of the application.
+ rpc Export(ExportMetricsServiceRequest) returns (ExportMetricsServiceResponse) {}
+}
+
+message ExportMetricsServiceRequest {
+ // An array of ResourceMetrics.
+ // For data coming from a single resource this array will typically contain one
+ // element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ // data from multiple origins typically batch the data before forwarding further and
+ // in that case this array will contain multiple elements.
+ repeated opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
+}
+
+message ExportMetricsServiceResponse {
+ // The details of a partially successful export request.
+ //
+ // If the request is only partially accepted
+ // (i.e. when the server accepts only parts of the data and rejects the rest)
+ // the server MUST initialize the `partial_success` field and MUST
+ // set the `rejected_` with the number of items it rejected.
+ //
+ // Servers MAY also make use of the `partial_success` field to convey
+ // warnings/suggestions to senders even when the request was fully accepted.
+ // In such cases, the `rejected_` MUST have a value of `0` and
+ // the `error_message` MUST be non-empty.
+ //
+ // A `partial_success` message with an empty value (rejected_ = 0 and
+ // `error_message` = "") is equivalent to it not being set/present. Senders
+ // SHOULD interpret it the same way as in the full success case.
+ ExportMetricsPartialSuccess partial_success = 1;
+}
+
+message ExportMetricsPartialSuccess {
+ // The number of rejected data points.
+ //
+ // A `rejected_` field holding a `0` value indicates that the
+ // request was fully accepted.
+ int64 rejected_data_points = 1;
+
+ // A developer-facing human-readable message in English. It should be used
+ // either to explain why the server rejected parts of the data during a partial
+ // success or to convey warnings/suggestions during a full success. The message
+ // should offer guidance on how users can address such issues.
+ //
+ // error_message is an optional field. An error_message with an empty value
+ // is equivalent to it not being set.
+ string error_message = 2;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml
new file mode 100644
index 0000000000..a545650260
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml
@@ -0,0 +1,9 @@
+# This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the
+# OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway.
+type: google.api.Service
+config_version: 3
+http:
+ rules:
+ - selector: opentelemetry.proto.collector.metrics.v1.MetricsService.Export
+ post: /v1/metrics
+ body: "*"
\ No newline at end of file
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/trace/v1/trace_service.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/trace/v1/trace_service.proto
new file mode 100644
index 0000000000..d6fe67f9e5
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/trace/v1/trace_service.proto
@@ -0,0 +1,79 @@
+// Copyright 2019, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.collector.trace.v1;
+
+import "opentelemetry/proto/trace/v1/trace.proto";
+
+option csharp_namespace = "OpenTelemetry.Proto.Collector.Trace.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.collector.trace.v1";
+option java_outer_classname = "TraceServiceProto";
+option go_package = "go.opentelemetry.io/proto/otlp/collector/trace/v1";
+
+// Service that can be used to push spans between one Application instrumented with
+// OpenTelemetry and a collector, or between a collector and a central collector (in this
+// case spans are sent/received to/from multiple Applications).
+service TraceService {
+ // For performance reasons, it is recommended to keep this RPC
+ // alive for the entire life of the application.
+ rpc Export(ExportTraceServiceRequest) returns (ExportTraceServiceResponse) {}
+}
+
+message ExportTraceServiceRequest {
+ // An array of ResourceSpans.
+ // For data coming from a single resource this array will typically contain one
+ // element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ // data from multiple origins typically batch the data before forwarding further and
+ // in that case this array will contain multiple elements.
+ repeated opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
+}
+
+message ExportTraceServiceResponse {
+ // The details of a partially successful export request.
+ //
+ // If the request is only partially accepted
+ // (i.e. when the server accepts only parts of the data and rejects the rest)
+ // the server MUST initialize the `partial_success` field and MUST
+ // set the `rejected_` with the number of items it rejected.
+ //
+ // Servers MAY also make use of the `partial_success` field to convey
+ // warnings/suggestions to senders even when the request was fully accepted.
+ // In such cases, the `rejected_` MUST have a value of `0` and
+ // the `error_message` MUST be non-empty.
+ //
+ // A `partial_success` message with an empty value (rejected_ = 0 and
+ // `error_message` = "") is equivalent to it not being set/present. Senders
+ // SHOULD interpret it the same way as in the full success case.
+ ExportTracePartialSuccess partial_success = 1;
+}
+
+message ExportTracePartialSuccess {
+ // The number of rejected spans.
+ //
+ // A `rejected_` field holding a `0` value indicates that the
+ // request was fully accepted.
+ int64 rejected_spans = 1;
+
+ // A developer-facing human-readable message in English. It should be used
+ // either to explain why the server rejected parts of the data during a partial
+ // success or to convey warnings/suggestions during a full success. The message
+ // should offer guidance on how users can address such issues.
+ //
+ // error_message is an optional field. An error_message with an empty value
+ // is equivalent to it not being set.
+ string error_message = 2;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml
new file mode 100644
index 0000000000..d091b3a8d5
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml
@@ -0,0 +1,9 @@
+# This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the
+# OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway.
+type: google.api.Service
+config_version: 3
+http:
+ rules:
+ - selector: opentelemetry.proto.collector.trace.v1.TraceService.Export
+ post: /v1/traces
+ body: "*"
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/common/v1/common.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/common/v1/common.proto
new file mode 100644
index 0000000000..ff8a21a1fa
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/common/v1/common.proto
@@ -0,0 +1,81 @@
+// Copyright 2019, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.common.v1;
+
+option csharp_namespace = "OpenTelemetry.Proto.Common.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.common.v1";
+option java_outer_classname = "CommonProto";
+option go_package = "go.opentelemetry.io/proto/otlp/common/v1";
+
+// AnyValue is used to represent any type of attribute value. AnyValue may contain a
+// primitive value such as a string or integer or it may contain an arbitrary nested
+// object containing arrays, key-value lists and primitives.
+message AnyValue {
+ // The value is one of the listed fields. It is valid for all values to be unspecified
+ // in which case this AnyValue is considered to be "empty".
+ oneof value {
+ string string_value = 1;
+ bool bool_value = 2;
+ int64 int_value = 3;
+ double double_value = 4;
+ ArrayValue array_value = 5;
+ KeyValueList kvlist_value = 6;
+ bytes bytes_value = 7;
+ }
+}
+
+// ArrayValue is a list of AnyValue messages. We need ArrayValue as a message
+// since oneof in AnyValue does not allow repeated fields.
+message ArrayValue {
+ // Array of values. The array may be empty (contain 0 elements).
+ repeated AnyValue values = 1;
+}
+
+// KeyValueList is a list of KeyValue messages. We need KeyValueList as a message
+// since `oneof` in AnyValue does not allow repeated fields. Everywhere else where we need
+// a list of KeyValue messages (e.g. in Span) we use `repeated KeyValue` directly to
+// avoid unnecessary extra wrapping (which slows down the protocol). The 2 approaches
+// are semantically equivalent.
+message KeyValueList {
+ // A collection of key/value pairs of key-value pairs. The list may be empty (may
+ // contain 0 elements).
+ // The keys MUST be unique (it is not allowed to have more than one
+ // value with the same key).
+ repeated KeyValue values = 1;
+}
+
+// KeyValue is a key-value pair that is used to store Span attributes, Link
+// attributes, etc.
+message KeyValue {
+ string key = 1;
+ AnyValue value = 2;
+}
+
+// InstrumentationScope is a message representing the instrumentation scope information
+// such as the fully qualified name and version.
+message InstrumentationScope {
+ // An empty instrumentation scope name means the name is unknown.
+ string name = 1;
+ string version = 2;
+
+ // Additional attributes that describe the scope. [Optional].
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated KeyValue attributes = 3;
+ uint32 dropped_attributes_count = 4;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/logs/v1/logs.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/logs/v1/logs.proto
new file mode 100644
index 0000000000..0b4b649729
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/logs/v1/logs.proto
@@ -0,0 +1,203 @@
+// Copyright 2020, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.logs.v1;
+
+import "opentelemetry/proto/common/v1/common.proto";
+import "opentelemetry/proto/resource/v1/resource.proto";
+
+option csharp_namespace = "OpenTelemetry.Proto.Logs.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.logs.v1";
+option java_outer_classname = "LogsProto";
+option go_package = "go.opentelemetry.io/proto/otlp/logs/v1";
+
+// LogsData represents the logs data that can be stored in a persistent storage,
+// OR can be embedded by other protocols that transfer OTLP logs data but do not
+// implement the OTLP protocol.
+//
+// The main difference between this message and collector protocol is that
+// in this message there will not be any "control" or "metadata" specific to
+// OTLP protocol.
+//
+// When new fields are added into this message, the OTLP request MUST be updated
+// as well.
+message LogsData {
+ // An array of ResourceLogs.
+ // For data coming from a single resource this array will typically contain
+ // one element. Intermediary nodes that receive data from multiple origins
+ // typically batch the data before forwarding further and in that case this
+ // array will contain multiple elements.
+ repeated ResourceLogs resource_logs = 1;
+}
+
+// A collection of ScopeLogs from a Resource.
+message ResourceLogs {
+ reserved 1000;
+
+ // The resource for the logs in this message.
+ // If this field is not set then resource info is unknown.
+ opentelemetry.proto.resource.v1.Resource resource = 1;
+
+ // A list of ScopeLogs that originate from a resource.
+ repeated ScopeLogs scope_logs = 2;
+
+ // This schema_url applies to the data in the "resource" field. It does not apply
+ // to the data in the "scope_logs" field which have their own schema_url field.
+ string schema_url = 3;
+}
+
+// A collection of Logs produced by a Scope.
+message ScopeLogs {
+ // The instrumentation scope information for the logs in this message.
+ // Semantically when InstrumentationScope isn't set, it is equivalent with
+ // an empty instrumentation scope name (unknown).
+ opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+
+ // A list of log records.
+ repeated LogRecord log_records = 2;
+
+ // This schema_url applies to all logs in the "logs" field.
+ string schema_url = 3;
+}
+
+// Possible values for LogRecord.SeverityNumber.
+enum SeverityNumber {
+ // UNSPECIFIED is the default SeverityNumber, it MUST NOT be used.
+ SEVERITY_NUMBER_UNSPECIFIED = 0;
+ SEVERITY_NUMBER_TRACE = 1;
+ SEVERITY_NUMBER_TRACE2 = 2;
+ SEVERITY_NUMBER_TRACE3 = 3;
+ SEVERITY_NUMBER_TRACE4 = 4;
+ SEVERITY_NUMBER_DEBUG = 5;
+ SEVERITY_NUMBER_DEBUG2 = 6;
+ SEVERITY_NUMBER_DEBUG3 = 7;
+ SEVERITY_NUMBER_DEBUG4 = 8;
+ SEVERITY_NUMBER_INFO = 9;
+ SEVERITY_NUMBER_INFO2 = 10;
+ SEVERITY_NUMBER_INFO3 = 11;
+ SEVERITY_NUMBER_INFO4 = 12;
+ SEVERITY_NUMBER_WARN = 13;
+ SEVERITY_NUMBER_WARN2 = 14;
+ SEVERITY_NUMBER_WARN3 = 15;
+ SEVERITY_NUMBER_WARN4 = 16;
+ SEVERITY_NUMBER_ERROR = 17;
+ SEVERITY_NUMBER_ERROR2 = 18;
+ SEVERITY_NUMBER_ERROR3 = 19;
+ SEVERITY_NUMBER_ERROR4 = 20;
+ SEVERITY_NUMBER_FATAL = 21;
+ SEVERITY_NUMBER_FATAL2 = 22;
+ SEVERITY_NUMBER_FATAL3 = 23;
+ SEVERITY_NUMBER_FATAL4 = 24;
+}
+
+// LogRecordFlags is defined as a protobuf 'uint32' type and is to be used as
+// bit-fields. Each non-zero value defined in this enum is a bit-mask.
+// To extract the bit-field, for example, use an expression like:
+//
+// (logRecord.flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK)
+//
+enum LogRecordFlags {
+ // The zero value for the enum. Should not be used for comparisons.
+ // Instead use bitwise "and" with the appropriate mask as shown above.
+ LOG_RECORD_FLAGS_DO_NOT_USE = 0;
+
+ // Bits 0-7 are used for trace flags.
+ LOG_RECORD_FLAGS_TRACE_FLAGS_MASK = 0x000000FF;
+
+ // Bits 8-31 are reserved for future use.
+}
+
+// A log record according to OpenTelemetry Log Data Model:
+// https://github.com/open-telemetry/oteps/blob/main/text/logs/0097-log-data-model.md
+message LogRecord {
+ reserved 4;
+
+ // time_unix_nano is the time when the event occurred.
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ // Value of 0 indicates unknown or missing timestamp.
+ fixed64 time_unix_nano = 1;
+
+ // Time when the event was observed by the collection system.
+ // For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK)
+ // this timestamp is typically set at the generation time and is equal to Timestamp.
+ // For events originating externally and collected by OpenTelemetry (e.g. using
+ // Collector) this is the time when OpenTelemetry's code observed the event measured
+ // by the clock of the OpenTelemetry code. This field MUST be set once the event is
+ // observed by OpenTelemetry.
+ //
+ // For converting OpenTelemetry log data to formats that support only one timestamp or
+ // when receiving OpenTelemetry log data by recipients that support only one timestamp
+ // internally the following logic is recommended:
+ // - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ // Value of 0 indicates unknown or missing timestamp.
+ fixed64 observed_time_unix_nano = 11;
+
+ // Numerical value of the severity, normalized to values described in Log Data Model.
+ // [Optional].
+ SeverityNumber severity_number = 2;
+
+ // The severity text (also known as log level). The original string representation as
+ // it is known at the source. [Optional].
+ string severity_text = 3;
+
+ // A value containing the body of the log record. Can be for example a human-readable
+ // string message (including multi-line) describing the event in a free form or it can
+ // be a structured data composed of arrays and maps of other values. [Optional].
+ opentelemetry.proto.common.v1.AnyValue body = 5;
+
+ // Additional attributes that describe the specific event occurrence. [Optional].
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 6;
+ uint32 dropped_attributes_count = 7;
+
+ // Flags, a bit field. 8 least significant bits are the trace flags as
+ // defined in W3C Trace Context specification. 24 most significant bits are reserved
+ // and must be set to 0. Readers must not assume that 24 most significant bits
+ // will be zero and must correctly mask the bits when reading 8-bit trace flag (use
+ // flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional].
+ fixed32 flags = 8;
+
+ // A unique identifier for a trace. All logs from the same trace share
+ // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ // is zero-length and thus is also invalid).
+ //
+ // This field is optional.
+ //
+ // The receivers SHOULD assume that the log record is not associated with a
+ // trace if any of the following is true:
+ // - the field is not present,
+ // - the field contains an invalid value.
+ bytes trace_id = 9;
+
+ // A unique identifier for a span within a trace, assigned when the span
+ // is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ // other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ // is zero-length and thus is also invalid).
+ //
+ // This field is optional. If the sender specifies a valid span_id then it SHOULD also
+ // specify a valid trace_id.
+ //
+ // The receivers SHOULD assume that the log record is not associated with a
+ // span if any of the following is true:
+ // - the field is not present,
+ // - the field contains an invalid value.
+ bytes span_id = 10;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/metrics/v1/metrics.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/metrics/v1/metrics.proto
new file mode 100644
index 0000000000..da986dda18
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/metrics/v1/metrics.proto
@@ -0,0 +1,676 @@
+// Copyright 2019, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.metrics.v1;
+
+import "opentelemetry/proto/common/v1/common.proto";
+import "opentelemetry/proto/resource/v1/resource.proto";
+
+option csharp_namespace = "OpenTelemetry.Proto.Metrics.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.metrics.v1";
+option java_outer_classname = "MetricsProto";
+option go_package = "go.opentelemetry.io/proto/otlp/metrics/v1";
+
+// MetricsData represents the metrics data that can be stored in a persistent
+// storage, OR can be embedded by other protocols that transfer OTLP metrics
+// data but do not implement the OTLP protocol.
+//
+// The main difference between this message and collector protocol is that
+// in this message there will not be any "control" or "metadata" specific to
+// OTLP protocol.
+//
+// When new fields are added into this message, the OTLP request MUST be updated
+// as well.
+message MetricsData {
+ // An array of ResourceMetrics.
+ // For data coming from a single resource this array will typically contain
+ // one element. Intermediary nodes that receive data from multiple origins
+ // typically batch the data before forwarding further and in that case this
+ // array will contain multiple elements.
+ repeated ResourceMetrics resource_metrics = 1;
+}
+
+// A collection of ScopeMetrics from a Resource.
+message ResourceMetrics {
+ reserved 1000;
+
+ // The resource for the metrics in this message.
+ // If this field is not set then no resource info is known.
+ opentelemetry.proto.resource.v1.Resource resource = 1;
+
+ // A list of metrics that originate from a resource.
+ repeated ScopeMetrics scope_metrics = 2;
+
+ // This schema_url applies to the data in the "resource" field. It does not apply
+ // to the data in the "scope_metrics" field which have their own schema_url field.
+ string schema_url = 3;
+}
+
+// A collection of Metrics produced by an Scope.
+message ScopeMetrics {
+ // The instrumentation scope information for the metrics in this message.
+ // Semantically when InstrumentationScope isn't set, it is equivalent with
+ // an empty instrumentation scope name (unknown).
+ opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+
+ // A list of metrics that originate from an instrumentation library.
+ repeated Metric metrics = 2;
+
+ // This schema_url applies to all metrics in the "metrics" field.
+ string schema_url = 3;
+}
+
+// Defines a Metric which has one or more timeseries. The following is a
+// brief summary of the Metric data model. For more details, see:
+//
+// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md
+//
+//
+// The data model and relation between entities is shown in the
+// diagram below. Here, "DataPoint" is the term used to refer to any
+// one of the specific data point value types, and "points" is the term used
+// to refer to any one of the lists of points contained in the Metric.
+//
+// - Metric is composed of a metadata and data.
+// - Metadata part contains a name, description, unit.
+// - Data is one of the possible types (Sum, Gauge, Histogram, Summary).
+// - DataPoint contains timestamps, attributes, and one of the possible value type
+// fields.
+//
+// Metric
+// +------------+
+// |name |
+// |description |
+// |unit | +------------------------------------+
+// |data |---> |Gauge, Sum, Histogram, Summary, ... |
+// +------------+ +------------------------------------+
+//
+// Data [One of Gauge, Sum, Histogram, Summary, ...]
+// +-----------+
+// |... | // Metadata about the Data.
+// |points |--+
+// +-----------+ |
+// | +---------------------------+
+// | |DataPoint 1 |
+// v |+------+------+ +------+ |
+// +-----+ ||label |label |...|label | |
+// | 1 |-->||value1|value2|...|valueN| |
+// +-----+ |+------+------+ +------+ |
+// | . | |+-----+ |
+// | . | ||value| |
+// | . | |+-----+ |
+// | . | +---------------------------+
+// | . | .
+// | . | .
+// | . | .
+// | . | +---------------------------+
+// | . | |DataPoint M |
+// +-----+ |+------+------+ +------+ |
+// | M |-->||label |label |...|label | |
+// +-----+ ||value1|value2|...|valueN| |
+// |+------+------+ +------+ |
+// |+-----+ |
+// ||value| |
+// |+-----+ |
+// +---------------------------+
+//
+// Each distinct type of DataPoint represents the output of a specific
+// aggregation function, the result of applying the DataPoint's
+// associated function of to one or more measurements.
+//
+// All DataPoint types have three common fields:
+// - Attributes includes key-value pairs associated with the data point
+// - TimeUnixNano is required, set to the end time of the aggregation
+// - StartTimeUnixNano is optional, but strongly encouraged for DataPoints
+// having an AggregationTemporality field, as discussed below.
+//
+// Both TimeUnixNano and StartTimeUnixNano values are expressed as
+// UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+//
+// # TimeUnixNano
+//
+// This field is required, having consistent interpretation across
+// DataPoint types. TimeUnixNano is the moment corresponding to when
+// the data point's aggregate value was captured.
+//
+// Data points with the 0 value for TimeUnixNano SHOULD be rejected
+// by consumers.
+//
+// # StartTimeUnixNano
+//
+// StartTimeUnixNano in general allows detecting when a sequence of
+// observations is unbroken. This field indicates to consumers the
+// start time for points with cumulative and delta
+// AggregationTemporality, and it should be included whenever possible
+// to support correct rate calculation. Although it may be omitted
+// when the start time is truly unknown, setting StartTimeUnixNano is
+// strongly encouraged.
+message Metric {
+ reserved 4, 6, 8;
+
+ // name of the metric, including its DNS name prefix. It must be unique.
+ string name = 1;
+
+ // description of the metric, which can be used in documentation.
+ string description = 2;
+
+ // unit in which the metric value is reported. Follows the format
+ // described by http://unitsofmeasure.org/ucum.html.
+ string unit = 3;
+
+ // Data determines the aggregation type (if any) of the metric, what is the
+ // reported value type for the data points, as well as the relatationship to
+ // the time interval over which they are reported.
+ oneof data {
+ Gauge gauge = 5;
+ Sum sum = 7;
+ Histogram histogram = 9;
+ ExponentialHistogram exponential_histogram = 10;
+ Summary summary = 11;
+ }
+}
+
+// Gauge represents the type of a scalar metric that always exports the
+// "current value" for every data point. It should be used for an "unknown"
+// aggregation.
+//
+// A Gauge does not support different aggregation temporalities. Given the
+// aggregation is unknown, points cannot be combined using the same
+// aggregation, regardless of aggregation temporalities. Therefore,
+// AggregationTemporality is not included. Consequently, this also means
+// "StartTimeUnixNano" is ignored for all data points.
+message Gauge {
+ repeated NumberDataPoint data_points = 1;
+}
+
+// Sum represents the type of a scalar metric that is calculated as a sum of all
+// reported measurements over a time interval.
+message Sum {
+ repeated NumberDataPoint data_points = 1;
+
+ // aggregation_temporality describes if the aggregator reports delta changes
+ // since last report time, or cumulative changes since a fixed start time.
+ AggregationTemporality aggregation_temporality = 2;
+
+ // If "true" means that the sum is monotonic.
+ bool is_monotonic = 3;
+}
+
+// Histogram represents the type of a metric that is calculated by aggregating
+// as a Histogram of all reported measurements over a time interval.
+message Histogram {
+ repeated HistogramDataPoint data_points = 1;
+
+ // aggregation_temporality describes if the aggregator reports delta changes
+ // since last report time, or cumulative changes since a fixed start time.
+ AggregationTemporality aggregation_temporality = 2;
+}
+
+// ExponentialHistogram represents the type of a metric that is calculated by aggregating
+// as a ExponentialHistogram of all reported double measurements over a time interval.
+message ExponentialHistogram {
+ repeated ExponentialHistogramDataPoint data_points = 1;
+
+ // aggregation_temporality describes if the aggregator reports delta changes
+ // since last report time, or cumulative changes since a fixed start time.
+ AggregationTemporality aggregation_temporality = 2;
+}
+
+// Summary metric data are used to convey quantile summaries,
+// a Prometheus (see: https://prometheus.io/docs/concepts/metric_types/#summary)
+// and OpenMetrics (see: https://github.com/OpenObservability/OpenMetrics/blob/4dbf6075567ab43296eed941037c12951faafb92/protos/prometheus.proto#L45)
+// data type. These data points cannot always be merged in a meaningful way.
+// While they can be useful in some applications, histogram data points are
+// recommended for new applications.
+message Summary {
+ repeated SummaryDataPoint data_points = 1;
+}
+
+// AggregationTemporality defines how a metric aggregator reports aggregated
+// values. It describes how those values relate to the time interval over
+// which they are aggregated.
+enum AggregationTemporality {
+ // UNSPECIFIED is the default AggregationTemporality, it MUST not be used.
+ AGGREGATION_TEMPORALITY_UNSPECIFIED = 0;
+
+ // DELTA is an AggregationTemporality for a metric aggregator which reports
+ // changes since last report time. Successive metrics contain aggregation of
+ // values from continuous and non-overlapping intervals.
+ //
+ // The values for a DELTA metric are based only on the time interval
+ // associated with one measurement cycle. There is no dependency on
+ // previous measurements like is the case for CUMULATIVE metrics.
+ //
+ // For example, consider a system measuring the number of requests that
+ // it receives and reports the sum of these requests every second as a
+ // DELTA metric:
+ //
+ // 1. The system starts receiving at time=t_0.
+ // 2. A request is received, the system measures 1 request.
+ // 3. A request is received, the system measures 1 request.
+ // 4. A request is received, the system measures 1 request.
+ // 5. The 1 second collection cycle ends. A metric is exported for the
+ // number of requests received over the interval of time t_0 to
+ // t_0+1 with a value of 3.
+ // 6. A request is received, the system measures 1 request.
+ // 7. A request is received, the system measures 1 request.
+ // 8. The 1 second collection cycle ends. A metric is exported for the
+ // number of requests received over the interval of time t_0+1 to
+ // t_0+2 with a value of 2.
+ AGGREGATION_TEMPORALITY_DELTA = 1;
+
+ // CUMULATIVE is an AggregationTemporality for a metric aggregator which
+ // reports changes since a fixed start time. This means that current values
+ // of a CUMULATIVE metric depend on all previous measurements since the
+ // start time. Because of this, the sender is required to retain this state
+ // in some form. If this state is lost or invalidated, the CUMULATIVE metric
+ // values MUST be reset and a new fixed start time following the last
+ // reported measurement time sent MUST be used.
+ //
+ // For example, consider a system measuring the number of requests that
+ // it receives and reports the sum of these requests every second as a
+ // CUMULATIVE metric:
+ //
+ // 1. The system starts receiving at time=t_0.
+ // 2. A request is received, the system measures 1 request.
+ // 3. A request is received, the system measures 1 request.
+ // 4. A request is received, the system measures 1 request.
+ // 5. The 1 second collection cycle ends. A metric is exported for the
+ // number of requests received over the interval of time t_0 to
+ // t_0+1 with a value of 3.
+ // 6. A request is received, the system measures 1 request.
+ // 7. A request is received, the system measures 1 request.
+ // 8. The 1 second collection cycle ends. A metric is exported for the
+ // number of requests received over the interval of time t_0 to
+ // t_0+2 with a value of 5.
+ // 9. The system experiences a fault and loses state.
+ // 10. The system recovers and resumes receiving at time=t_1.
+ // 11. A request is received, the system measures 1 request.
+ // 12. The 1 second collection cycle ends. A metric is exported for the
+ // number of requests received over the interval of time t_1 to
+ // t_0+1 with a value of 1.
+ //
+ // Note: Even though, when reporting changes since last report time, using
+ // CUMULATIVE is valid, it is not recommended. This may cause problems for
+ // systems that do not use start_time to determine when the aggregation
+ // value was reset (e.g. Prometheus).
+ AGGREGATION_TEMPORALITY_CUMULATIVE = 2;
+}
+
+// DataPointFlags is defined as a protobuf 'uint32' type and is to be used as a
+// bit-field representing 32 distinct boolean flags. Each flag defined in this
+// enum is a bit-mask. To test the presence of a single flag in the flags of
+// a data point, for example, use an expression like:
+//
+// (point.flags & DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK) == DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK
+//
+enum DataPointFlags {
+ // The zero value for the enum. Should not be used for comparisons.
+ // Instead use bitwise "and" with the appropriate mask as shown above.
+ DATA_POINT_FLAGS_DO_NOT_USE = 0;
+
+ // This DataPoint is valid but has no recorded value. This value
+ // SHOULD be used to reflect explicitly missing data in a series, as
+ // for an equivalent to the Prometheus "staleness marker".
+ DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK = 1;
+
+ // Bits 2-31 are reserved for future use.
+}
+
+// NumberDataPoint is a single data point in a timeseries that describes the
+// time-varying scalar value of a metric.
+message NumberDataPoint {
+ reserved 1;
+
+ // The set of key/value pairs that uniquely identify the timeseries from
+ // where this point belongs. The list may be empty (may contain 0 elements).
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 7;
+
+ // StartTimeUnixNano is optional but strongly encouraged, see the
+ // the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 start_time_unix_nano = 2;
+
+ // TimeUnixNano is required, see the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 time_unix_nano = 3;
+
+ // The value itself. A point is considered invalid when one of the recognized
+ // value fields is not present inside this oneof.
+ oneof value {
+ double as_double = 4;
+ sfixed64 as_int = 6;
+ }
+
+ // (Optional) List of exemplars collected from
+ // measurements that were used to form the data point
+ repeated Exemplar exemplars = 5;
+
+ // Flags that apply to this specific data point. See DataPointFlags
+ // for the available flags and their meaning.
+ uint32 flags = 8;
+}
+
+// HistogramDataPoint is a single data point in a timeseries that describes the
+// time-varying values of a Histogram. A Histogram contains summary statistics
+// for a population of values, it may optionally contain the distribution of
+// those values across a set of buckets.
+//
+// If the histogram contains the distribution of values, then both
+// "explicit_bounds" and "bucket counts" fields must be defined.
+// If the histogram does not contain the distribution of values, then both
+// "explicit_bounds" and "bucket_counts" must be omitted and only "count" and
+// "sum" are known.
+message HistogramDataPoint {
+ reserved 1;
+
+ // The set of key/value pairs that uniquely identify the timeseries from
+ // where this point belongs. The list may be empty (may contain 0 elements).
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 9;
+
+ // StartTimeUnixNano is optional but strongly encouraged, see the
+ // the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 start_time_unix_nano = 2;
+
+ // TimeUnixNano is required, see the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 time_unix_nano = 3;
+
+ // count is the number of values in the population. Must be non-negative. This
+ // value must be equal to the sum of the "count" fields in buckets if a
+ // histogram is provided.
+ fixed64 count = 4;
+
+ // sum of the values in the population. If count is zero then this field
+ // must be zero.
+ //
+ // Note: Sum should only be filled out when measuring non-negative discrete
+ // events, and is assumed to be monotonic over the values of these events.
+ // Negative events *can* be recorded, but sum should not be filled out when
+ // doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ // see: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#histogram
+ optional double sum = 5;
+
+ // bucket_counts is an optional field contains the count values of histogram
+ // for each bucket.
+ //
+ // The sum of the bucket_counts must equal the value in the count field.
+ //
+ // The number of elements in bucket_counts array must be by one greater than
+ // the number of elements in explicit_bounds array.
+ repeated fixed64 bucket_counts = 6;
+
+ // explicit_bounds specifies buckets with explicitly defined bounds for values.
+ //
+ // The boundaries for bucket at index i are:
+ //
+ // (-infinity, explicit_bounds[i]] for i == 0
+ // (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds)
+ // (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds)
+ //
+ // The values in the explicit_bounds array must be strictly increasing.
+ //
+ // Histogram buckets are inclusive of their upper boundary, except the last
+ // bucket where the boundary is at infinity. This format is intentionally
+ // compatible with the OpenMetrics histogram definition.
+ repeated double explicit_bounds = 7;
+
+ // (Optional) List of exemplars collected from
+ // measurements that were used to form the data point
+ repeated Exemplar exemplars = 8;
+
+ // Flags that apply to this specific data point. See DataPointFlags
+ // for the available flags and their meaning.
+ uint32 flags = 10;
+
+ // min is the minimum value over (start_time, end_time].
+ optional double min = 11;
+
+ // max is the maximum value over (start_time, end_time].
+ optional double max = 12;
+}
+
+// ExponentialHistogramDataPoint is a single data point in a timeseries that describes the
+// time-varying values of a ExponentialHistogram of double values. A ExponentialHistogram contains
+// summary statistics for a population of values, it may optionally contain the
+// distribution of those values across a set of buckets.
+//
+message ExponentialHistogramDataPoint {
+ // The set of key/value pairs that uniquely identify the timeseries from
+ // where this point belongs. The list may be empty (may contain 0 elements).
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 1;
+
+ // StartTimeUnixNano is optional but strongly encouraged, see the
+ // the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 start_time_unix_nano = 2;
+
+ // TimeUnixNano is required, see the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 time_unix_nano = 3;
+
+ // count is the number of values in the population. Must be
+ // non-negative. This value must be equal to the sum of the "bucket_counts"
+ // values in the positive and negative Buckets plus the "zero_count" field.
+ fixed64 count = 4;
+
+ // sum of the values in the population. If count is zero then this field
+ // must be zero.
+ //
+ // Note: Sum should only be filled out when measuring non-negative discrete
+ // events, and is assumed to be monotonic over the values of these events.
+ // Negative events *can* be recorded, but sum should not be filled out when
+ // doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ // see: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#histogram
+ optional double sum = 5;
+
+ // scale describes the resolution of the histogram. Boundaries are
+ // located at powers of the base, where:
+ //
+ // base = (2^(2^-scale))
+ //
+ // The histogram bucket identified by `index`, a signed integer,
+ // contains values that are greater than (base^index) and
+ // less than or equal to (base^(index+1)).
+ //
+ // The positive and negative ranges of the histogram are expressed
+ // separately. Negative values are mapped by their absolute value
+ // into the negative range using the same scale as the positive range.
+ //
+ // scale is not restricted by the protocol, as the permissible
+ // values depend on the range of the data.
+ sint32 scale = 6;
+
+ // zero_count is the count of values that are either exactly zero or
+ // within the region considered zero by the instrumentation at the
+ // tolerated degree of precision. This bucket stores values that
+ // cannot be expressed using the standard exponential formula as
+ // well as values that have been rounded to zero.
+ //
+ // Implementations MAY consider the zero bucket to have probability
+ // mass equal to (zero_count / count).
+ fixed64 zero_count = 7;
+
+ // positive carries the positive range of exponential bucket counts.
+ Buckets positive = 8;
+
+ // negative carries the negative range of exponential bucket counts.
+ Buckets negative = 9;
+
+ // Buckets are a set of bucket counts, encoded in a contiguous array
+ // of counts.
+ message Buckets {
+ // Offset is the bucket index of the first entry in the bucket_counts array.
+ //
+ // Note: This uses a varint encoding as a simple form of compression.
+ sint32 offset = 1;
+
+ // bucket_counts is an array of count values, where bucket_counts[i] carries
+ // the count of the bucket at index (offset+i). bucket_counts[i] is the count
+ // of values greater than base^(offset+i) and less than or equal to
+ // base^(offset+i+1).
+ //
+ // Note: By contrast, the explicit HistogramDataPoint uses
+ // fixed64. This field is expected to have many buckets,
+ // especially zeros, so uint64 has been selected to ensure
+ // varint encoding.
+ repeated uint64 bucket_counts = 2;
+ }
+
+ // Flags that apply to this specific data point. See DataPointFlags
+ // for the available flags and their meaning.
+ uint32 flags = 10;
+
+ // (Optional) List of exemplars collected from
+ // measurements that were used to form the data point
+ repeated Exemplar exemplars = 11;
+
+ // min is the minimum value over (start_time, end_time].
+ optional double min = 12;
+
+ // max is the maximum value over (start_time, end_time].
+ optional double max = 13;
+
+ // ZeroThreshold may be optionally set to convey the width of the zero
+ // region. Where the zero region is defined as the closed interval
+ // [-ZeroThreshold, ZeroThreshold].
+ // When ZeroThreshold is 0, zero count bucket stores values that cannot be
+ // expressed using the standard exponential formula as well as values that
+ // have been rounded to zero.
+ double zero_threshold = 14;
+}
+
+// SummaryDataPoint is a single data point in a timeseries that describes the
+// time-varying values of a Summary metric.
+message SummaryDataPoint {
+ reserved 1;
+
+ // The set of key/value pairs that uniquely identify the timeseries from
+ // where this point belongs. The list may be empty (may contain 0 elements).
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 7;
+
+ // StartTimeUnixNano is optional but strongly encouraged, see the
+ // the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 start_time_unix_nano = 2;
+
+ // TimeUnixNano is required, see the detailed comments above Metric.
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 time_unix_nano = 3;
+
+ // count is the number of values in the population. Must be non-negative.
+ fixed64 count = 4;
+
+ // sum of the values in the population. If count is zero then this field
+ // must be zero.
+ //
+ // Note: Sum should only be filled out when measuring non-negative discrete
+ // events, and is assumed to be monotonic over the values of these events.
+ // Negative events *can* be recorded, but sum should not be filled out when
+ // doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ // see: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#summary
+ double sum = 5;
+
+ // Represents the value at a given quantile of a distribution.
+ //
+ // To record Min and Max values following conventions are used:
+ // - The 1.0 quantile is equivalent to the maximum value observed.
+ // - The 0.0 quantile is equivalent to the minimum value observed.
+ //
+ // See the following issue for more context:
+ // https://github.com/open-telemetry/opentelemetry-proto/issues/125
+ message ValueAtQuantile {
+ // The quantile of a distribution. Must be in the interval
+ // [0.0, 1.0].
+ double quantile = 1;
+
+ // The value at the given quantile of a distribution.
+ //
+ // Quantile values must NOT be negative.
+ double value = 2;
+ }
+
+ // (Optional) list of values at different quantiles of the distribution calculated
+ // from the current snapshot. The quantiles must be strictly increasing.
+ repeated ValueAtQuantile quantile_values = 6;
+
+ // Flags that apply to this specific data point. See DataPointFlags
+ // for the available flags and their meaning.
+ uint32 flags = 8;
+}
+
+// A representation of an exemplar, which is a sample input measurement.
+// Exemplars also hold information about the environment when the measurement
+// was recorded, for example the span and trace ID of the active span when the
+// exemplar was recorded.
+message Exemplar {
+ reserved 1;
+
+ // The set of key/value pairs that were filtered out by the aggregator, but
+ // recorded alongside the original measurement. Only key/value pairs that were
+ // filtered out by the aggregator should be included
+ repeated opentelemetry.proto.common.v1.KeyValue filtered_attributes = 7;
+
+ // time_unix_nano is the exact time when this exemplar was recorded
+ //
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ // 1970.
+ fixed64 time_unix_nano = 2;
+
+ // The value of the measurement that was recorded. An exemplar is
+ // considered invalid when one of the recognized value fields is not present
+ // inside this oneof.
+ oneof value {
+ double as_double = 3;
+ sfixed64 as_int = 6;
+ }
+
+ // (Optional) Span ID of the exemplar trace.
+ // span_id may be missing if the measurement is not recorded inside a trace
+ // or if the trace is not sampled.
+ bytes span_id = 4;
+
+ // (Optional) Trace ID of the exemplar trace.
+ // trace_id may be missing if the measurement is not recorded inside a trace
+ // or if the trace is not sampled.
+ bytes trace_id = 5;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/resource/v1/resource.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/resource/v1/resource.proto
new file mode 100644
index 0000000000..23c7ddd98b
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/resource/v1/resource.proto
@@ -0,0 +1,37 @@
+// Copyright 2019, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.resource.v1;
+
+import "opentelemetry/proto/common/v1/common.proto";
+
+option csharp_namespace = "OpenTelemetry.Proto.Resource.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.resource.v1";
+option java_outer_classname = "ResourceProto";
+option go_package = "go.opentelemetry.io/proto/otlp/resource/v1";
+
+// Resource information.
+message Resource {
+ // Set of attributes that describe the resource.
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 1;
+
+ // dropped_attributes_count is the number of dropped attributes. If the value is 0, then
+ // no attributes were dropped.
+ uint32 dropped_attributes_count = 2;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/trace/v1/trace.proto b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/trace/v1/trace.proto
new file mode 100644
index 0000000000..b2869edc42
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/OtlpTestListener/otlp/opentelemetry/proto/trace/v1/trace.proto
@@ -0,0 +1,276 @@
+// Copyright 2019, OpenTelemetry Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package opentelemetry.proto.trace.v1;
+
+import "opentelemetry/proto/common/v1/common.proto";
+import "opentelemetry/proto/resource/v1/resource.proto";
+
+option csharp_namespace = "OpenTelemetry.Proto.Trace.V1";
+option java_multiple_files = true;
+option java_package = "io.opentelemetry.proto.trace.v1";
+option java_outer_classname = "TraceProto";
+option go_package = "go.opentelemetry.io/proto/otlp/trace/v1";
+
+// TracesData represents the traces data that can be stored in a persistent storage,
+// OR can be embedded by other protocols that transfer OTLP traces data but do
+// not implement the OTLP protocol.
+//
+// The main difference between this message and collector protocol is that
+// in this message there will not be any "control" or "metadata" specific to
+// OTLP protocol.
+//
+// When new fields are added into this message, the OTLP request MUST be updated
+// as well.
+message TracesData {
+ // An array of ResourceSpans.
+ // For data coming from a single resource this array will typically contain
+ // one element. Intermediary nodes that receive data from multiple origins
+ // typically batch the data before forwarding further and in that case this
+ // array will contain multiple elements.
+ repeated ResourceSpans resource_spans = 1;
+}
+
+// A collection of ScopeSpans from a Resource.
+message ResourceSpans {
+ reserved 1000;
+
+ // The resource for the spans in this message.
+ // If this field is not set then no resource info is known.
+ opentelemetry.proto.resource.v1.Resource resource = 1;
+
+ // A list of ScopeSpans that originate from a resource.
+ repeated ScopeSpans scope_spans = 2;
+
+ // This schema_url applies to the data in the "resource" field. It does not apply
+ // to the data in the "scope_spans" field which have their own schema_url field.
+ string schema_url = 3;
+}
+
+// A collection of Spans produced by an InstrumentationScope.
+message ScopeSpans {
+ // The instrumentation scope information for the spans in this message.
+ // Semantically when InstrumentationScope isn't set, it is equivalent with
+ // an empty instrumentation scope name (unknown).
+ opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+
+ // A list of Spans that originate from an instrumentation scope.
+ repeated Span spans = 2;
+
+ // This schema_url applies to all spans and span events in the "spans" field.
+ string schema_url = 3;
+}
+
+// A Span represents a single operation performed by a single component of the system.
+//
+// The next available field id is 17.
+message Span {
+ // A unique identifier for a trace. All spans from the same trace share
+ // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ // is zero-length and thus is also invalid).
+ //
+ // This field is required.
+ bytes trace_id = 1;
+
+ // A unique identifier for a span within a trace, assigned when the span
+ // is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ // other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ // is zero-length and thus is also invalid).
+ //
+ // This field is required.
+ bytes span_id = 2;
+
+ // trace_state conveys information about request position in multiple distributed tracing graphs.
+ // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header
+ // See also https://github.com/w3c/distributed-tracing for more details about this field.
+ string trace_state = 3;
+
+ // The `span_id` of this span's parent span. If this is a root span, then this
+ // field must be empty. The ID is an 8-byte array.
+ bytes parent_span_id = 4;
+
+ // A description of the span's operation.
+ //
+ // For example, the name can be a qualified method name or a file name
+ // and a line number where the operation is called. A best practice is to use
+ // the same display name at the same call point in an application.
+ // This makes it easier to correlate spans in different traces.
+ //
+ // This field is semantically required to be set to non-empty string.
+ // Empty value is equivalent to an unknown span name.
+ //
+ // This field is required.
+ string name = 5;
+
+ // SpanKind is the type of span. Can be used to specify additional relationships between spans
+ // in addition to a parent/child relationship.
+ enum SpanKind {
+ // Unspecified. Do NOT use as default.
+ // Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED.
+ SPAN_KIND_UNSPECIFIED = 0;
+
+ // Indicates that the span represents an internal operation within an application,
+ // as opposed to an operation happening at the boundaries. Default value.
+ SPAN_KIND_INTERNAL = 1;
+
+ // Indicates that the span covers server-side handling of an RPC or other
+ // remote network request.
+ SPAN_KIND_SERVER = 2;
+
+ // Indicates that the span describes a request to some remote service.
+ SPAN_KIND_CLIENT = 3;
+
+ // Indicates that the span describes a producer sending a message to a broker.
+ // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship
+ // between producer and consumer spans. A PRODUCER span ends when the message was accepted
+ // by the broker while the logical processing of the message might span a much longer time.
+ SPAN_KIND_PRODUCER = 4;
+
+ // Indicates that the span describes consumer receiving a message from a broker.
+ // Like the PRODUCER kind, there is often no direct critical path latency relationship
+ // between producer and consumer spans.
+ SPAN_KIND_CONSUMER = 5;
+ }
+
+ // Distinguishes between spans generated in a particular context. For example,
+ // two spans with the same name may be distinguished using `CLIENT` (caller)
+ // and `SERVER` (callee) to identify queueing latency associated with the span.
+ SpanKind kind = 6;
+
+ // start_time_unix_nano is the start time of the span. On the client side, this is the time
+ // kept by the local machine where the span execution starts. On the server side, this
+ // is the time when the server's application handler starts running.
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ //
+ // This field is semantically required and it is expected that end_time >= start_time.
+ fixed64 start_time_unix_nano = 7;
+
+ // end_time_unix_nano is the end time of the span. On the client side, this is the time
+ // kept by the local machine where the span execution ends. On the server side, this
+ // is the time when the server application handler stops running.
+ // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ //
+ // This field is semantically required and it is expected that end_time >= start_time.
+ fixed64 end_time_unix_nano = 8;
+
+ // attributes is a collection of key/value pairs. Note, global attributes
+ // like server name can be set using the resource API. Examples of attributes:
+ //
+ // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ // "/http/server_latency": 300
+ // "example.com/myattribute": true
+ // "example.com/score": 10.239
+ //
+ // The OpenTelemetry API specification further restricts the allowed value types:
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 9;
+
+ // dropped_attributes_count is the number of attributes that were discarded. Attributes
+ // can be discarded because their keys are too long or because there are too many
+ // attributes. If this value is 0, then no attributes were dropped.
+ uint32 dropped_attributes_count = 10;
+
+ // Event is a time-stamped annotation of the span, consisting of user-supplied
+ // text description and key-value pairs.
+ message Event {
+ // time_unix_nano is the time the event occurred.
+ fixed64 time_unix_nano = 1;
+
+ // name of the event.
+ // This field is semantically required to be set to non-empty string.
+ string name = 2;
+
+ // attributes is a collection of attribute key/value pairs on the event.
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 3;
+
+ // dropped_attributes_count is the number of dropped attributes. If the value is 0,
+ // then no attributes were dropped.
+ uint32 dropped_attributes_count = 4;
+ }
+
+ // events is a collection of Event items.
+ repeated Event events = 11;
+
+ // dropped_events_count is the number of dropped events. If the value is 0, then no
+ // events were dropped.
+ uint32 dropped_events_count = 12;
+
+ // A pointer from the current span to another span in the same trace or in a
+ // different trace. For example, this can be used in batching operations,
+ // where a single batch handler processes multiple requests from different
+ // traces or when the handler receives a request from a different project.
+ message Link {
+ // A unique identifier of a trace that this linked span is part of. The ID is a
+ // 16-byte array.
+ bytes trace_id = 1;
+
+ // A unique identifier for the linked span. The ID is an 8-byte array.
+ bytes span_id = 2;
+
+ // The trace_state associated with the link.
+ string trace_state = 3;
+
+ // attributes is a collection of attribute key/value pairs on the link.
+ // Attribute keys MUST be unique (it is not allowed to have more than one
+ // attribute with the same key).
+ repeated opentelemetry.proto.common.v1.KeyValue attributes = 4;
+
+ // dropped_attributes_count is the number of dropped attributes. If the value is 0,
+ // then no attributes were dropped.
+ uint32 dropped_attributes_count = 5;
+ }
+
+ // links is a collection of Links, which are references from this span to a span
+ // in the same or different trace.
+ repeated Link links = 13;
+
+ // dropped_links_count is the number of dropped links after the maximum size was
+ // enforced. If this value is 0, then no links were dropped.
+ uint32 dropped_links_count = 14;
+
+ // An optional final status for this span. Semantically when Status isn't set, it means
+ // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0).
+ Status status = 15;
+}
+
+// The Status type defines a logical error model that is suitable for different
+// programming environments, including REST APIs and RPC APIs.
+message Status {
+ reserved 1;
+
+ // A developer-facing human readable error message.
+ string message = 2;
+
+ // For the semantics of status codes see
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status
+ enum StatusCode {
+ // The default status.
+ STATUS_CODE_UNSET = 0;
+ // The Span has been validated by an Application developer or Operator to
+ // have completed successfully.
+ STATUS_CODE_OK = 1;
+ // The Span contains an error.
+ STATUS_CODE_ERROR = 2;
+ };
+
+ // The status code.
+ StatusCode code = 3;
+}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/README.md b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/README.md
new file mode 100644
index 0000000000..c754a0ace3
--- /dev/null
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestAppArtifacts/otlptestlistener/README.md
@@ -0,0 +1,85 @@
+# OTLP Test Listener
+
+A small server that implements an OTLP endpoint.
+
+Point an application at it and it will collect the OTLP output and generate stats on what it saw
+
+Call /report to get a json response with the stats. For example:
+
+``` JSON
+{
+ "SpanIdCount": 41,
+ "LogMessageCount": 10,
+ "MetricNames": [
+ "process.runtime.dotnet.gc.collections.count",
+ "process.runtime.dotnet.gc.objects.size",
+ "process.runtime.dotnet.gc.allocations.size",
+ "process.runtime.dotnet.gc.committed_memory.size",
+ "process.runtime.dotnet.gc.heap.size",
+ "process.runtime.dotnet.gc.heap.fragmentation.size",
+ "process.runtime.dotnet.gc.duration",
+ "process.runtime.dotnet.jit.il_compiled.size",
+ "process.runtime.dotnet.jit.methods_compiled.count",
+ "process.runtime.dotnet.jit.compilation_time",
+ "process.runtime.dotnet.monitor.lock_contention.count",
+ "process.runtime.dotnet.thread_pool.threads.count",
+ "process.runtime.dotnet.thread_pool.completed_items.count",
+ "process.runtime.dotnet.thread_pool.queue.length",
+ "process.runtime.dotnet.timer.count",
+ "process.runtime.dotnet.assemblies.count",
+ "dns.lookup.duration",
+ "http.client.active_requests",
+ "http.client.open_connections",
+ "http.client.request.time_in_queue",
+ "http.client.request.duration",
+ "kestrel.active_connections",
+ "kestrel.queued_connections",
+ "kestrel.queued_requests",
+ "kestrel.tls_handshake.duration",
+ "kestrel.active_tls_handshakes",
+ "http.server.active_requests",
+ "http.server.request.duration",
+ "aspnetcore.routing.match_attempts",
+ "signalr.server.active_connections",
+ "process.runtime.dotnet.exceptions.count",
+ "signalr.server.connection.duration"
+ ],
+ "ResourceNames": [
+ "webfrontend"
+ ],
+ "TraceIds": [
+ "db56809d1fb8bcac04b90e553987b75e",
+ "cdd179788682bce95bbd9dac7546756a",
+ "1ad4c4f4c7397fed1da403bb3aaae5da",
+ "8daee78be4d5a4680258deeef42d5de9",
+ "ab9e4aa38aad08ba79abed3804b2224f",
+ "f3c57e403e821d08470e3d3e1f803508",
+ "5ec6e87f3f75ce0c4b0be6c50362929c",
+ "0d5d632101714d8fd29399e71aca7e7b",
+ "e3163da71a724718fa1a5419ae1d0d55",
+ "9583c20cb576050111733130e6914970",
+ "6e623af4d3090b5f0cf0954c06182211",
+ "2a770fbe654c5f605a03b054b239b6d3",
+ "5d3a9b5cda00ef68828e85238e906cf1",
+ "518f7be3440418dbbf91210825c2e963",
+ "66b5d2b6df92ae3fea268d7c80d8ba38",
+ "7f08a437b8c51b39eb6f4dedacde17d3",
+ "ebcae7e19a54a3959bada9f054822ecd",
+ "307b55c66f631e94203c0d17fd13f46d",
+ "fb8b4ee59bc42883a0a3a7b3b50c936f",
+ "ccb95e82fde12dc560905f3fd4bddedd",
+ "5845865ef16eda10b262ada83164a95e",
+ "5223bdda12e4cac786b2735a56a60840",
+ "434cb6b69651252bb0db7d2762e26d7e",
+ "ae0afd9aba85ae46a7b634eacb8d27fe",
+ "c04cfd5f00a429573933994a34c0f47e",
+ "43f3e198e6c1445b2f8e6d0d3697ecfc",
+ "b74acff1e0f346b0d68920433bde3dd0",
+ "2e41608348eb69ba5d9df83d4c1b457e",
+ "b1a3eab6982538ca8870ac4d79b61eb6",
+ "0bd4c8dee515f755170654e15a7733fc"
+ ],
+ "MetricNameCount": 32,
+ "TraceIdCount": 30
+}
+```
diff --git a/tests/Microsoft.DotNet.Docker.Tests/TestScenarios/ReverseProxyBasicScenario.cs b/tests/Microsoft.DotNet.Docker.Tests/TestScenarios/ReverseProxyBasicScenario.cs
index 8ca0e24f20..65d676e858 100644
--- a/tests/Microsoft.DotNet.Docker.Tests/TestScenarios/ReverseProxyBasicScenario.cs
+++ b/tests/Microsoft.DotNet.Docker.Tests/TestScenarios/ReverseProxyBasicScenario.cs
@@ -1,11 +1,19 @@
#nullable enable
+using System;
+using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
+using System.Runtime.ExceptionServices;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
+using FluentAssertions.Execution;
+using Shouldly;
using Xunit;
using Xunit.Abstractions;
+using Xunit.Sdk;
namespace Microsoft.DotNet.Docker.Tests;
@@ -21,6 +29,10 @@ public class YarpBasicScenario : ITestScenario
private readonly int _webPort;
+ private const int OtelHttpPort = 8080;
+ private const int OtelGrpcPort = 4317;
+ private static readonly TimeSpan OtelTimeout = TimeSpan.FromSeconds(1);
+
public YarpBasicScenario(
int webPort,
ProductImageData imageData,
@@ -40,9 +52,23 @@ public async Task ExecuteAsync()
string containerName = _imageData.GetIdentifier(nameof(YarpBasicScenario));
using TempFileContext configFile = FileHelper.UseTempFile();
string sampleContainer = $"{containerName}_aspnetapp";
+ string otelContainerTag = $"local-otlptestlistener";
+ string otelContainer = $"{containerName}_otel";
try
{
+ // Deploy opentelemetry endpoint
+ string sampleFolder = Path.Combine(DockerHelper.TestArtifactsDir, "otlptestlistener");
+ string dockerfilePath = $"{sampleFolder}/Dockerfile";
+ _dockerHelper.Build(otelContainerTag, dockerfilePath, contextDir: sampleFolder, pull: Config.PullImages);
+ _dockerHelper.Run(
+ image: otelContainerTag,
+ name: otelContainer,
+ detach: true,
+ optionalRunArgs: "-P",
+ skipAutoCleanup: true);
+ string otelHostPort = _dockerHelper.GetContainerHostPort(otelContainer, OtelGrpcPort);
+
// Deploy the aspnet sample app
_dockerHelper.Run(
image: "mcr.microsoft.com/dotnet/samples:aspnetapp",
@@ -51,13 +77,25 @@ public async Task ExecuteAsync()
optionalRunArgs: "",
skipAutoCleanup: true);
+ // Check that otel endpoint report is empty
+ HttpResponseMessage emptyTelemetryResponse = await WebScenario.GetHttpResponseFromContainerAsync(
+ otelContainer,
+ _dockerHelper,
+ _outputHelper,
+ OtelHttpPort,
+ pathAndQuery: "/report");
+ emptyTelemetryResponse.StatusCode.ShouldBe(HttpStatusCode.OK);
+ TelemetryResults emptyTelemetryResults = await TelemetryResults.FromHttpResponse(emptyTelemetryResponse);
+ emptyTelemetryResults.MetricNameCount.ShouldBe(0);
+ emptyTelemetryResults.LogMessageCount.ShouldBe(0);
+
File.WriteAllText(configFile.Path, ConfigFileContent);
_dockerHelper.Run(
image: _imageTag,
name: containerName,
detach: true,
- optionalRunArgs: $"-p {_webPort} -v {configFile.Path}:/etc/yarp.config --link {sampleContainer}:aspnetapp1",
+ optionalRunArgs: $"-e OTEL_EXPORTER_OTLP_ENDPOINT=http://host.docker.internal:{otelHostPort} -e OTEL_EXPORTER_OTLP_TIMEOUT={OtelTimeout.TotalMilliseconds} -p {_webPort} -v {configFile.Path}:/etc/yarp.config --link {sampleContainer}:aspnetapp1",
skipAutoCleanup: true);
// base uri should return 404
@@ -77,13 +115,31 @@ public async Task ExecuteAsync()
_outputHelper,
_webPort,
pathAndQuery: "/aspnetapp");
-
Assert.Equal(HttpStatusCode.OK, okResponse.StatusCode);
+
+ // Wait a bit for telemetry to arrive
+ await Task.Delay(OtelTimeout * 10);
+
+ // Some messages and some metrics should have been received
+ HttpResponseMessage nonEmptyTelemetryResponse = await WebScenario.GetHttpResponseFromContainerAsync(
+ otelContainer,
+ _dockerHelper,
+ _outputHelper,
+ OtelHttpPort,
+ pathAndQuery: "/report");
+ nonEmptyTelemetryResponse.StatusCode.ShouldBe(HttpStatusCode.OK);
+ TelemetryResults nonEmptyTelemetryResults = await TelemetryResults.FromHttpResponse(nonEmptyTelemetryResponse);
+ // We should see at around 20 messages so far, let's put the lower boundary to 10
+ // We do not test metrics at this point because they might be sent much later, but if logs
+ // are here we are confident that OTEL was correctly configured.
+ nonEmptyTelemetryResults.LogMessageCount.ShouldBeGreaterThan(10);
}
finally
{
_dockerHelper.DeleteContainer(containerName);
_dockerHelper.DeleteContainer(sampleContainer);
+ _dockerHelper.DeleteContainer(otelContainer);
+ _dockerHelper.DeleteImage(otelContainerTag);
}
}
@@ -121,4 +177,25 @@ public async Task ExecuteAsync()
}
}
""";
+
+ // Needs to be kept in sync with {RepoRoot}\tests\Microsoft.DotNet.Docker.Tests\TestAppArtifacts\otlptestlistener\OtlpTestListener\DataModel\TelemetryResults.cs
+ private class TelemetryResults
+ {
+ public int SpanIdCount { get; set; }
+ public int LogMessageCount { get; set; }
+ public List MetricNames { get; init; } = new();
+ public List ResourceNames { get; init; } = new();
+ public List TraceIds { get; init; } = new();
+ [JsonIgnore]
+ public int MetricNameCount => MetricNames.Count;
+ [JsonIgnore]
+ public int TraceIdCount => TraceIds.Count;
+
+ internal static async Task FromHttpResponse(HttpResponseMessage telemetryResponse)
+ {
+ TelemetryResults? results = await JsonSerializer.DeserializeAsync(telemetryResponse.Content.ReadAsStream());
+ Assert.NotNull(results);
+ return results!;
+ }
+ }
}
diff --git a/tests/Microsoft.DotNet.Docker.Tests/ReverseProxyImageTests.cs b/tests/Microsoft.DotNet.Docker.Tests/YarpImageTests.cs
similarity index 98%
rename from tests/Microsoft.DotNet.Docker.Tests/ReverseProxyImageTests.cs
rename to tests/Microsoft.DotNet.Docker.Tests/YarpImageTests.cs
index 4896359446..acd3eba161 100644
--- a/tests/Microsoft.DotNet.Docker.Tests/ReverseProxyImageTests.cs
+++ b/tests/Microsoft.DotNet.Docker.Tests/YarpImageTests.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
@@ -14,8 +15,6 @@ namespace Microsoft.DotNet.Docker.Tests;
[Trait("Category", "yarp")]
public class YarpImageTests(ITestOutputHelper outputHelper) : CommonRuntimeImageTests(outputHelper)
{
- private const string AppPath = "/app";
-
private const int YarpWebPort = 5000;
diff --git a/tests/run-tests.ps1 b/tests/run-tests.ps1
index bba3c2888a..dd5b77c05d 100644
--- a/tests/run-tests.ps1
+++ b/tests/run-tests.ps1
@@ -28,8 +28,8 @@ param(
[string]$ImageInfoPath,
- [ValidateSet("runtime", "runtime-deps", "aspnet", "sdk", "pre-build", "sample", "monitor", "aspire-dashboard")]
- [string[]]$TestCategories = @("runtime", "runtime-deps", "aspnet", "sdk", "monitor", "aspire-dashboard"),
+ [ValidateSet("runtime", "runtime-deps", "aspnet", "sdk", "pre-build", "sample", "monitor", "aspire-dashboard", "yarp")]
+ [string[]]$TestCategories = @("runtime", "runtime-deps", "aspnet", "sdk", "monitor", "aspire-dashboard", "yarp"),
[string]$CustomTestFilter,