Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
f4a8326
make the delimiter configurable for validate headers
nagendramishr Feb 14, 2026
e3e4d79
remove unused starttimer code
nagendramishr Feb 14, 2026
738917a
remove blocking wait, replace with async await
nagendramishr Feb 14, 2026
bcad764
cache validate headers for perf
nagendramishr Feb 14, 2026
c9484b8
replace blocking calls with await
nagendramishr Feb 14, 2026
26d0710
update docs for validate wildcard matching
nagendramishr Feb 14, 2026
b1b3449
update docs for validate wildcard matching
nagendramishr Feb 14, 2026
61de08c
potential memory leax of response stream
nagendramishr Feb 14, 2026
43bed1f
check in demo scripts
nagendramishr Feb 14, 2026
bf7a4e4
pull out duplicate code into WriteErrorToClientAsync
nagendramishr Feb 14, 2026
2da322a
extract methods CreateHostIterator, ResolveHttpRequestErrorStatus fro…
nagendramishr Feb 14, 2026
aef2cdd
convert to host collection Snapshots
nagendramishr Feb 14, 2026
e9fff12
implement CompleteAllUsageProcessor and update tests
nagendramishr Feb 26, 2026
67a1264
rename fullURL=>requestPath, streamline path patocessing, add optiona…
nagendramishr Feb 27, 2026
46084f6
respond to default probe path, add try-catch for broken pipe
nagendramishr Feb 27, 2026
9159517
add test cases for load balancer testing
nagendramishr Feb 27, 2026
12081ab
update docs, release notes
nagendramishr Feb 27, 2026
71ec486
streamline event creation and logging
nagendramishr Feb 27, 2026
4581f97
have each event client register with CompositeEventClient
nagendramishr Feb 27, 2026
aa8a5b4
cleanup how application insights is registered ( only once )
nagendramishr Feb 27, 2026
83df0ac
allow EVENT_LOGGERS to define what is instantiated
nagendramishr Feb 27, 2026
acbb30a
rename eventhubclient to eventclient, dont die if event hub client do…
nagendramishr Feb 27, 2026
35aa74d
update docs to reflect EVENT_LOGGERS, doc fixes
nagendramishr Feb 27, 2026
3fbf2ee
update docs for loggers
nagendramishr Feb 27, 2026
8822919
pulled read env vars for event hub into EventHubConfig
nagendramishr Feb 27, 2026
f1d5e48
EVENT_LOGGERS can now be extended with custom logging code
nagendramishr Feb 27, 2026
b0f35ef
EVENT_LOGGERS can now be extended with custom logging code
nagendramishr Feb 27, 2026
42033a8
Merge pull request #123 from microsoft/feature-direct-backend
nagendramishr Feb 27, 2026
fcf6992
integrate app configuration
nagendramishr Mar 2, 2026
de82023
update to deploy settings at once, prefix with warm: and cold:, inclu…
nagendramishr Mar 3, 2026
597057d
reorg config params into hot, cold and hidden
nagendramishr Mar 3, 2026
3acd0c6
bug fix for incorrectly parsing values with special characters
nagendramishr Mar 3, 2026
a1b8642
read the app config settings if they exist on startup
nagendramishr Mar 3, 2026
b3d11de
update project path for the null server
nagendramishr Mar 4, 2026
8dcafaf
nop
nagendramishr Mar 4, 2026
8cae303
use reflection to populate the settings
nagendramishr Mar 4, 2026
fd4f791
implement pub sub pattern for config changes
nagendramishr Mar 4, 2026
c818e0f
migrate env to Dictionary<string, string>
nagendramishr Mar 4, 2026
b8e349c
fixes for AllUsage-2 parser
nagendramishr Mar 5, 2026
dc78296
reworked config code for reading from appConfig
nagendramishr Mar 5, 2026
b01f870
remove unused file
nagendramishr Mar 5, 2026
770db54
call subscribers once when multiple configs change
nagendramishr Mar 5, 2026
7483ba1
refactor and improve efficiency
nagendramishr Mar 6, 2026
75dc7bd
remove references to strings that may not be used but get refershed e…
nagendramishr Mar 6, 2026
a9fd5fe
get host parsing to read changes
nagendramishr Mar 6, 2026
988ab7d
activate hosts via HostCollectionManager
nagendramishr Mar 6, 2026
96e8b64
updated version to 2.2.10
nagendramishr Mar 6, 2026
c2f1339
Merge pull request #124 from microsoft/feature-direct-backend
nagendramishr Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions ReleaseNotes/version2.2.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
# Release Notes #


2.2.10

Proxy:

* Implement app configuration
* Added AppConfiguration reader
* Moved default values out of BackendHostConfigurationExtension and into BackendOptions
* Read WarmOptions every 30 seconds ( AZURE_APPCONFIG_REFRESH_SECONDS )
* Markup BackendOptions as either: warm, cold or hidden
* Bug fix for AllUsage-2 processor
* Enable HealthProbe Sidecar as a Warm config parameter
* Enable Host settings to be activated via appconfig

Deployment:
* Add AppConfiguration/appdeploy.sh to setup appconfiguration configure ACA to use it
* bug fix: Create multple entries at once rather than one at a time

2.2.10-D1

Proxy:
* Implement partial matches for validated parameters
* Remove blocking operations during shutdown
* Convert HostHealthCollection to to HostCollectionSnapshot
* Added CompleteAllUsageProcessor for parsing the entire file
* Add StripPrefix to host config to optionally remove the match part of the path
* EVENT_LOGGERS can now be used to specify spcific loggers, including custom handlers

## 2.2.9-D2

Deployment:
Expand Down
4 changes: 1 addition & 3 deletions SimpleL7Proxy.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7FB4896E-B
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "test\ProxyWorkerTests\Tests.csproj", "{F562EE95-23FC-48A2-B5CD-21438BFE8926}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nullserver", "nullserver", "{8911A389-8D4E-4268-90B7-9AC12C59861E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nullserver", "test\nullserver\nullserver\nullserver.csproj", "{9F502DB0-81A5-4AAB-B915-AAABAA55B0A3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nullserver", "test\nullserver\dotnet\nullserver.csproj", "{9F502DB0-81A5-4AAB-B915-AAABAA55B0A3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "generator", "generator", "{5315AE06-D9C2-456F-BE0C-4B6996540CC9}"
EndProject
Expand Down
155 changes: 155 additions & 0 deletions deployment/AppConfiguration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Azure App Configuration Deployment

Provisions an Azure App Configuration store and populates it with the proxy's
**publishable** settings — both **Warm** (hot-reloaded) and **Cold**
(requires restart). The running proxy watches a single sentinel key and
hot-reloads all warm settings when it changes — no restart required.
Cold settings are published so they can be centrally managed, but changing
them requires a Container App restart.

## Config Modes

Every `BackendOptions` property can be decorated with
`[ConfigOption("Category:Name")]`. The `Mode` parameter controls how
the property is treated:

| Mode | Published to App Config? | Hot-reloaded? | Notes |
|---|---|---|---|
| **Warm** (default) | ✅ | ✅ | Value changes take effect within ~30 s |
| **Cold** | ✅ | ❌ | Requires a Container App restart |
| **Hidden** | ❌ | ❌ | Composite / derived at runtime — skipped by deploy.sh |

## Prerequisites

| Requirement | Details |
|---|---|
| **Azure CLI** | `az` ≥ 2.50 with the `containerapp` extension |
| **jq** | Used to parse the Container App JSON |
| **Azure login** | `az login` (the script will prompt if needed) |
| **A running Container App** | The script reads its env vars as the source of truth for warm values |
| **Bash 4+** | Uses associative arrays (`declare -A`) |

## Quick Start

```bash
cd deployment/AppConfiguration

# 1. Create your parameters file
cp deploy.parameters.example.sh deploy.parameters.sh

# 2. Edit deploy.parameters.sh with your values
# (see Parameters section below)

# 3. Run
./deploy.sh
```

## Parameters

All parameters are set in `deploy.parameters.sh`.

| Parameter | Description |
|---|---|
| `CONTAINER_APP_NAME` | Name of the Container App whose env vars are the source of warm values |
| `CONTAINER_APP_RESOURCE_GROUP` | Resource group where the Container App lives |
| `RESOURCE_GROUP` | Resource group for the App Configuration store (created if missing) |
| `LOCATION` | Azure region for the App Configuration store |
| `APPCONFIG_NAME` | Name of the App Configuration store (created if missing) |
| `APPCONFIG_SKU` | `free` or `standard` |
| `APPCONFIG_LABEL` | Label applied to all `Warm:*` keys (empty string = null / no label) |
| `AZURE_APPCONFIG_REFRESH_SECONDS` | Refresh interval written to `Warm:RefreshSeconds` |
| `UPDATE_CONTAINER_APP_ENV` | `true` to push `AZURE_APPCONFIG_ENDPOINT`, `AZURE_APPCONFIG_LABEL`, and `AZURE_APPCONFIG_REFRESH_SECONDS` env vars onto the Container App |

> **Do not commit `deploy.parameters.sh`** — it contains environment-specific values.
> Only `deploy.parameters.example.sh` is checked in.

## What the Script Does

### 1. Read the live Container App

Queries the Container App deployment and loads every env var from
`containers[0].env` into memory. Also discovers the container name
(needed for the optional env-var update at the end).

### 2. Ensure the App Configuration store exists

Creates the resource group and App Configuration store if they don't
already exist.

### 3. Assign RBAC (first run only)

Checks whether the signed-in Azure identity has the
**App Configuration Data Owner** role on the store. If not, assigns it
and waits 30 seconds for propagation.

### 4. Discover config properties from source code

Parses `BackendOptions.cs` with `awk`, scanning for the `[ConfigOption]` attribute:

- **`[ConfigOption("Category:Name")]`** — marks a property as warm-reloadable
(the default mode) and defines the key path under the `Warm:` prefix.
The env var name defaults to the property name.
- **`[ConfigOption("Category:Name", ConfigName = "EnvVar")]`** — overrides
the env var name when it differs from the property name (e.g.,
`CONTAINER_APP_NAME` for the `ContainerApp` property).
- **`[ConfigOption("Category:Name", Mode = ConfigMode.Cold)]`** — the
property is published to App Config but **not** hot-reloaded. Changing
the value requires a Container App restart.
- **`[ConfigOption("Category:Name", Mode = ConfigMode.Hidden)]`** — the
property is **skipped by deploy.sh**. Its runtime value is composite or
derived (e.g., `IDStr` is built from a prefix + replicaID at startup).
- **`[ParsedConfig("SourceConfig")]`** — marks a non-publishable property
whose default comes from a parsed composite config string (e.g.,
`AsyncBlobStorageConfig`, `AsyncSBConfig`).

Each discovered property (with Mode ≠ Hidden) produces a quad:
`PropertyName | KeyPath | ConfigName | Mode`.

### 5. Resolve values and publish

For each publishable property (Warm or Cold):

1. **Container App env** — look up `ConfigName` in the env vars loaded in
step 1.
2. **Local shell env** — if not found on the Container App, fall back to a
local shell variable with the same name.
3. **Skip** — if neither has a value, the key is skipped.

Found values are written to the App Config store as `Warm:<KeyPath>`.
The output shows the source of each value (`container-app` or `local-env`).

### 6. Bump the sentinel

Writes `Warm:Sentinel` with the current UTC timestamp and
`Warm:RefreshSeconds` with the configured interval. The proxy SDK watches
only `Warm:Sentinel` — when it changes, all `Warm:*` keys are reloaded as
a batch.

### 7. Update Container App env vars (optional)

If `UPDATE_CONTAINER_APP_ENV=true`, pushes three env vars onto the
Container App so the proxy knows where to connect:

- `AZURE_APPCONFIG_ENDPOINT`
- `AZURE_APPCONFIG_LABEL`
- `AZURE_APPCONFIG_REFRESH_SECONDS`

## Re-running

The script is idempotent. Run it again any time you want to sync the
Container App's current env var values into App Configuration. The
sentinel bump ensures the proxy picks up the new values on its next
refresh cycle.

## How the Proxy Consumes These Settings

The proxy's `AzureAppConfigurationRefreshService` (a `BackgroundService`):

1. Connects to the App Configuration endpoint using managed identity.
2. Selects all keys matching `Warm:*`.
3. Registers `Warm:Sentinel` as the refresh trigger (`refreshAll: true`).
4. Every `RefreshSeconds`, checks if the sentinel changed.
5. On change, reloads all `Warm:*` keys and applies **only Warm-mode**
properties to `BackendOptions` via reflection using the `[ConfigOption]`
attribute metadata. Cold properties are present in the store but
are **not** applied at runtime — they require a restart to take effect.
37 changes: 37 additions & 0 deletions deployment/AppConfiguration/deploy.parameters.example.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash

# Deployment Parameters for App Configuration warm settings sync
#
# 1) Copy this file to deploy.parameters.sh
# 2) Update values for your environment
# 3) Run ./deploy.sh
#
# The script reads the live Container App to discover env vars and
# container name. Values not found on the Container App fall back to
# local shell variables.
#
# Do not commit deploy.parameters.sh with real values.

# =============================================================================
# Container App (source of env var values)
# =============================================================================
export CONTAINER_APP_NAME="myapp"
export CONTAINER_APP_RESOURCE_GROUP="rg-myapp-prod"

# =============================================================================
# App Configuration store
# =============================================================================
export RESOURCE_GROUP="rg-myapp-appconfig"
export LOCATION="eastus"
export APPCONFIG_NAME="myapp-appcfg"
export APPCONFIG_SKU="standard"

# Label applied to Warm:* keys. Use '\0' for null label.
export APPCONFIG_LABEL=""

# Refresh interval (seconds) written to Warm:RefreshSeconds
export AZURE_APPCONFIG_REFRESH_SECONDS="30"

# Set to "true" to push AZURE_APPCONFIG_ENDPOINT/LABEL/REFRESH_SECONDS
# env vars onto the Container App after publishing keys.
export UPDATE_CONTAINER_APP_ENV="true"
Loading