Skip to content

Commit 2fff1e7

Browse files
OgeonX-AiAitomates
andauthored
fix: migrate to Flex Consumption plan and add blobContributor role for secure deployment container access (#11)
Co-authored-by: Kim Harjamäki <kim.harjamaki@prosimo.fi>
1 parent 57c21b0 commit 2fff1e7

5 files changed

Lines changed: 86 additions & 39 deletions

File tree

.planning/ROADMAP.md

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,9 @@
11
# Roadmap: CAS Reference Product
22

3-
## Phase 1: Public v0.1 Reference Application
3+
## Archived Milestones
44

5-
**Goal:** Deliver a locally runnable, cloud-ready reference workload without deploying Azure resources.
5+
- [x] [v0.1 Reference Application](milestones/v0.1-ROADMAP.md) — Delivered a locally runnable, cloud-ready reference workload without deploying Azure resources.
66

7-
**Requirements:** API-01, AGENT-01, ID-01, CONTRACT-01, OBS-01, OPS-01, PLAT-01, QUAL-01, DOC-01
7+
## Milestone v0.2
88

9-
**Success criteria:**
10-
- Local workflow request succeeds and returns canonical events.
11-
- Tests, lint, type checks, and Docker build pass.
12-
- Foundry mode uses Next Gen agent references and managed identity.
13-
- Public documentation states deployment and security boundaries.
14-
15-
Status: Complete
16-
17-
## Phase 2: Telemetry Hardening
18-
19-
**Goal:** Wire OpenTelemetry end-to-end with lifecycle span events, W3C trace propagation, and Application Insights exporter.
20-
21-
**Requirements:** TEL-01, TEL-02, TEL-03, TEL-04
22-
23-
**Success criteria:**
24-
- Every /api/v1/workflows request creates a `cas.api.workflows.execute` span with correlation_id, run_id, and intent attributes.
25-
- Span events `workflow.started`, `workflow.completed`, and `workflow.failed` are emitted at the appropriate lifecycle points.
26-
- W3C traceparent/tracestate headers on inbound requests are extracted and linked as parent context.
27-
- Application Insights exporter activates when APPLICATIONINSIGHTS_CONNECTION_STRING is set; no-op otherwise.
28-
- All telemetry behaviours verified by pytest with InMemorySpanExporter.
29-
30-
Status: Complete
31-
32-
## Phase 3: Docker + CI Publish
33-
34-
**Goal:** Containerize the app with a production-grade multi-stage Dockerfile and publish the image to GHCR on merge to main.
35-
36-
**Requirements:** DOCK-01, DOCK-02, DOCK-03
37-
38-
**Success criteria:**
39-
- Multi-stage Dockerfile builds a linux/amd64 image, runs as non-root `appuser`, exposes port 8080, and health-checks via /health/ready.
40-
- docker-compose.yml starts the local dev stack with env stubs using .env.example.
41-
- CI docker job builds, smoke-tests (/health/live + /health/ready), and on push to main pushes to ghcr.io/coding-autopilot-system/cas-reference-product.
42-
43-
Status: Complete
9+
*To be defined via /gsd-new-milestone*
File renamed without changes.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Roadmap: CAS Reference Product
2+
3+
## Phase 1: Public v0.1 Reference Application
4+
5+
**Goal:** Deliver a locally runnable, cloud-ready reference workload without deploying Azure resources.
6+
7+
**Requirements:** API-01, AGENT-01, ID-01, CONTRACT-01, OBS-01, OPS-01, PLAT-01, QUAL-01, DOC-01
8+
9+
**Success criteria:**
10+
- Local workflow request succeeds and returns canonical events.
11+
- Tests, lint, type checks, and Docker build pass.
12+
- Foundry mode uses Next Gen agent references and managed identity.
13+
- Public documentation states deployment and security boundaries.
14+
15+
Status: Complete
16+
17+
## Phase 2: Telemetry Hardening
18+
19+
**Goal:** Wire OpenTelemetry end-to-end with lifecycle span events, W3C trace propagation, and Application Insights exporter.
20+
21+
**Requirements:** TEL-01, TEL-02, TEL-03, TEL-04
22+
23+
**Success criteria:**
24+
- Every /api/v1/workflows request creates a `cas.api.workflows.execute` span with correlation_id, run_id, and intent attributes.
25+
- Span events `workflow.started`, `workflow.completed`, and `workflow.failed` are emitted at the appropriate lifecycle points.
26+
- W3C traceparent/tracestate headers on inbound requests are extracted and linked as parent context.
27+
- Application Insights exporter activates when APPLICATIONINSIGHTS_CONNECTION_STRING is set; no-op otherwise.
28+
- All telemetry behaviours verified by pytest with InMemorySpanExporter.
29+
30+
Status: Complete
31+
32+
## Phase 3: Docker + CI Publish
33+
34+
**Goal:** Containerize the app with a production-grade multi-stage Dockerfile and publish the image to GHCR on merge to main.
35+
36+
**Requirements:** DOCK-01, DOCK-02, DOCK-03
37+
38+
**Success criteria:**
39+
- Multi-stage Dockerfile builds a linux/amd64 image, runs as non-root `appuser`, exposes port 8080, and health-checks via /health/ready.
40+
- docker-compose.yml starts the local dev stack with env stubs using .env.example.
41+
- CI docker job builds, smoke-tests (/health/live + /health/ready), and on push to main pushes to ghcr.io/coding-autopilot-system/cas-reference-product.
42+
43+
Status: Complete

infra/modules/function-ingress.bicep

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
2626
httpsOnly: true
2727
publicNetworkAccess: 'Enabled'
2828
siteConfig: {
29-
linuxFxVersion: 'Python|3.12'
3029
minTlsVersion: '1.2'
3130
ftpsState: 'Disabled'
3231
appSettings: [
@@ -36,6 +35,25 @@ resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
3635
{ name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: applicationInsightsConnectionString }
3736
]
3837
}
38+
functionAppConfig: {
39+
deployment: {
40+
storage: {
41+
type: 'blobContainer'
42+
value: 'https://${storageAccountName}.blob.${environment().suffixes.storage}/app-package'
43+
authentication: {
44+
type: 'SystemAssignedIdentity'
45+
}
46+
}
47+
}
48+
scaleAndConcurrency: {
49+
maximumInstanceCount: 100
50+
instanceMemoryMB: 2048
51+
}
52+
runtime: {
53+
name: 'python'
54+
version: '3.12'
55+
}
56+
}
3957
}
4058
}
4159

@@ -53,5 +71,15 @@ resource queueContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' =
5371
}
5472
}
5573

74+
resource blobContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
75+
name: guid(storageAccountId, functionApp.id, 'blob-contributor')
76+
scope: storageAccount
77+
properties: {
78+
principalId: functionApp.identity.principalId
79+
principalType: 'ServicePrincipal'
80+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
81+
}
82+
}
83+
5684
output id string = functionApp.id
5785
output principalId string = functionApp.identity.principalId

infra/modules/storage.bicep

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ resource workQueue 'Microsoft.Storage/storageAccounts/queueServices/queues@2023-
2727
name: 'cas-work-items'
2828
}
2929

30+
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = {
31+
parent: account
32+
name: 'default'
33+
}
34+
35+
resource deploymentContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = {
36+
parent: blobService
37+
name: 'app-package'
38+
}
39+
3040
output id string = account.id
3141
output name string = account.name
3242
output queueName string = workQueue.name

0 commit comments

Comments
 (0)