Skip to content

Test two samples end to end on the LocalStack Azure emulator - #41

Merged
HarshCasper merged 6 commits into
mainfrom
ci/test-samples-on-emulator
Oct 15, 2026
Merged

HarshCasper merged 6 commits into
mainfrom
ci/test-samples-on-emulator

Conversation

@HarshCasper

@HarshCasper HarshCasper commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

The repo only linted its workflows. Nothing checked that the samples deploy and work on LocalStack for Azure.

  • New workflow Test samples on LocalStack for Azure, one job per sample app, Python and .NET (all 20):
    • A Select samples job builds the matrix from every samples/<sample>/<language> app, as the localstack-azure-samples Samples CI does, so a new sample needs no workflow change.
    • A pull request or a push to main runs the apps whose folders it changes. It runs all 20 when it changes the cluster script, the test or the workflow.
    • A manual run takes run_mode: changed (the default) or all.
    • Each job:
      • starts the emulator with lstk start --type azure (lstk pinned to 1.4.0) and routes az to it with lstk az start-interception
      • creates the cluster with scripts/01-system-assigned-managed-identity.sh, piping in the answers to its prompts
      • runs the app's 01, 02, 04 and 05 scripts
      • runs the test, and prints pods, events, app logs and emulator logs on failure
    • Emulator settings, both as in the localstack-azure-samples CI:
      • LOCALSTACK_MSSQL_ACCEPT_EULA=Y for the SQL Database sample.
      • LOCALSTACK_GITHUB_API_TOKEN set to the run's read-only token, so the emulator's k3d release lookups don't hit the anonymous GitHub API limit when 20 jobs start at once.
    • Fork PRs are skipped (they get no secrets), and a new push cancels the run it supersedes.
  • New tests/test-vacation-planner.sh <namespace> <service>:
    • It waits for the Deployment to roll out and checks that GET /health reports the data service as reachable.
    • It adds an activity through the web form and checks that the page lists it. It then deletes it and checks it is gone while a second activity stays listed, so a page that lists nothing fails.
    • It talks to the app the way a browser does: it keeps cookies and sends the antiforgery token the .NET Razor Pages apps require. It posts the Azure Files delete to /delete with activity_id.
    • It stops if kubectl port-forward can't bind, instead of testing whatever owns the port.
  • Sample fixes:
    • SQL Database: every sqlcmd call passes -N -C. mssql-tools18 encrypts by default and validates the emulator's certificate.
    • In-Cluster PostgreSQL: uses PREFIX='local' like the other samples, so it pulls from the registry attached to the cluster (it used zeus and its own registry).
  • scripts/01-user-assigned-managed-identity.sh: the network menu heading said "Run Docker Container (1-3)". It now matches the system-assigned script's "Select Network Plugin and Policy (1-3)".
  • README:
    • The "Run against LocalStack" steps installed lstk but started the emulator with the legacy localstack CLI. They now use lstk start --type azure.
    • A new "Testing a deployed sample" section documents the test and when the workflow runs which apps.

Verified in CI: all 20 apps create the cluster, deploy and pass every check (run). actionlint, shellcheck, zizmor and gitleaks report nothing.

Add a workflow that creates the AKS cluster on LocalStack for Azure, deploys
the in-cluster PostgreSQL and Blob Storage samples, and checks each web app
stores, lists and deletes an activity. Start the emulator with lstk in the
README, since lstk does not ship the localstack CLI.

Claude-Session: https://claude.ai/code/session_01DZfDmzUBPqNAu3yFKyfCNM
The script asks for the network configuration and node pool sizes. With no
input the menu left the network settings empty and az aks create failed.
Also fix the menu heading, which was copied from a Docker script.

Claude-Session: https://claude.ai/code/session_01DZfDmzUBPqNAu3yFKyfCNM

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The pull-request workflow exposes the LocalStack credential to same-repository PR-controlled code.

1 open finding
What changed in this PR

Adds LocalStack-based end-to-end validation for two Python AKS samples.

Changes:

  • Adds a matrix workflow deploying and testing both samples.
  • Adds deployment health and CRUD validation.
  • Updates LocalStack instructions and network prompts.
File Description
.github/​workflows/​samples.yml Adds LocalStack sample CI.
tests/​test-vacation-planner.sh Adds end-to-end application checks.
scripts/​01-system-assigned-managed-identity.sh Clarifies network prompt.
scripts/​01-user-assigned-managed-identity.sh Clarifies network prompt.
README.md Documents LocalStack startup and testing.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/samples.yml

@paolosalvatori paolosalvatori left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Deploying the samples on the emulator in CI closes a real gap, and both jobs are green. The one blocker is coverage: like the Samples CI in localstack-azure-samples, this workflow should test every sample in both languages, with the same options (a pull request runs the samples it touches, and a manual run can run all of them), and the HIGH comment carries a workflow and a test script for that.

1. Rules applied

Read from localstack/localstack-pro at origin/main (d223998f5a). Every file is paths:-scoped to localstack-pro-azure/**, so here they apply as principles, cited as "principle applied". I kept to this repo's own script conventions (#!/bin/bash, echo for progress, no strict mode in the sample scripts) and raised nothing against them.

  • localstack-pro-azure/CLAUDE.md
  • .claude/rules/azure/common/: coding-style.md, hooks.md, patterns.md, security.md, testing.md
  • .claude/rules/azure/python/: cloud-pipeline.md, coding-style.md, hooks.md, patterns.md, security.md, testing.md (no Python in the diff, so none applies)
  • .claude/rules/azure/scripts/: coding-style.md, hooks.md, patterns.md, security.md, testing.md
  • .claude/rules/azure/emulator/azure-kubernetes-service.md, as background on the emulated AKS only

House precedent: the Samples CI in localstack/localstack-azure-samples (.github/workflows/run-samples.yml, run-samples.sh). This repo has no CLAUDE.md or AGENTS.md; its own gates are zizmor and gitleaks (pre-commit) plus actionlint. No rule file was missing or unreadable.

2. Existing comments

Copilot (one review, one HIGH comment on lines 13-15) asks to move this job off pull_request because same-repository branches can run code with LOCALSTACK_AUTH_TOKEN. I disagree with that remedy: only people with write access can push a branch here, and per GitHub, "any user with write access to your repository has read access to all secrets configured in your repository" (Secure use reference), so push or workflow_dispatch only would drop the PR check without narrowing who can reach the token. Fork PRs get no secrets at all, and the sibling Samples CI runs on pull_request the same way. If the token needs tighter scoping, the lever is an environment secret with protection rules (zizmor's auditor persona reports secrets-outside-env here), not the trigger. Copilot is right that fork PRs cannot pass, though: lstk start fails with authentication required: set LOCALSTACK_AUTH_TOKEN or run in interactive mode. The workflow attached to the HIGH comment skips them with a job-level if:.

3. Findings

All four are inline:

  • HIGH, .github/workflows/samples.yml L29-L34: the workflow deploys 2 of the 20 sample apps (a Python app of two samples, no .NET at all). The comment attaches a workflow with one job per sample and language, for the samples a change touches or for all 20 on a manual run, and a test that drives both apps; the test runs green against real apps of both kinds locally.
  • MEDIUM, tests/test-vacation-planner.sh L51-L60: the "it is gone" check also passes on a page that lists nothing.
  • MEDIUM, tests/test-vacation-planner.sh L19-L21: a port-forward that cannot bind is silenced, and the checks then run against whatever owns the port.
  • LOW, .github/workflows/samples.yml L45: lstk is installed unpinned.

Both MEDIUMs were reproduced on a throwaway kind cluster with real kubectl and fixed by the suggested code; the fixes are also in the attached test script.

Out of scope (pre-existing, not in the diff)

samples/web-app-in-cluster-postgresql/{python,dotnet}/scripts/00-variables.sh set PREFIX='zeus', while the cluster scripts and the other nine samples use local. In this workflow the sample therefore creates its own zeus-rg and zeusacrtest and deploys from a registry the cluster was never attached to (--attach-acr localacrtest): the job log shows the push to zeusacrtest.azurecr.azure.localhost.localstack.cloud:4566 and a clean rollout. That works only because the emulated registry accepts unauthenticated access: az acr login logs Registry '...' did not issue a challenge., which azure-cli prints when GET /v2/ does not answer 401 with WWW-Authenticate. On Azure, --attach-acr grants AcrPull to the kubelet identity on the attached registry only (AKS and ACR integration), and anonymous pull is off by default (anonymous pull access), so the same steps would leave the pods in ImagePullBackOff. PREFIX='local' in both files would make this job exercise the attached registry. The emulator side (a registry data plane with no auth challenge, while the ARM resource reports anonymousPullEnabled: false) is a parity gap worth an issue in localstack-pro.

4. Clean files

  • scripts/01-system-assigned-managed-identity.sh
  • scripts/01-user-assigned-managed-identity.sh

The heading now names what the menu selects. Also checked in the workflow, no change needed: printf '1\n\n\n' matches the prompts (select reads 1, and both node-count prompts take their defaults on an empty line); lstk start --type azure records the type in config and returns once the emulator is ready; and lstk logs follows only with --follow (lstk v1.4.0, cmd/logs.go), so lstk logs | tail -300 cannot hang the failure step.

5. Verdict

Request changes. The workflow covers 2 of the 20 sample apps and should cover all of them, with the same run options as the sibling Samples CI. The rest is in good shape: the two MEDIUMs make the test fail closed instead of open, and the LOW pins lstk.

Comment thread .github/workflows/samples.yml Outdated
Comment thread tests/test-vacation-planner.sh Outdated
Comment thread tests/test-vacation-planner.sh Outdated
Comment thread .github/workflows/samples.yml Outdated
@paolosalvatori

paolosalvatori commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Thanks for the PR @HarshCasper, see GitHub Copilot and Claude Code PR reviews above. Please, make sure that all the Python and .NET samples are test in the GitHub Actions workflow, just like we do in run-samples.yml workflow in the LocalStack for Azure Samples repo. Or better, the run-samples.yml workflow only runs the samples touched by a PR, but you can also manually run it on demand and choose to run all the samples. This workflow should provide the same options.

@paolosalvatori

paolosalvatori commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

@HarshCasper I created and merged #44 to fix #35 issue raised by a user, an ex Microsoft colleague who kindly accepted to test our emulator. My PR modifies the /scripts/01-system-assigned-managed-identity.sh and scripts/01-user-assigned-managed-identity.sh scripts and this will result in a conflict in this PR. The PR adds some code at the beginning of the script but it also changes Run Docker Container (1-3) not with Select the network configuration (1-3) as per this PR, but with Select Network Plugin and Policy (1-3).

Apply the review on #41:
- The workflow builds its matrix from every samples/<sample>/<language>
  app. A pull request or push runs the apps it changes, and every app
  when the cluster script, the test or the workflow changes. A manual
  run picks run_mode changed (default) or all. Fork PRs are skipped,
  lstk is pinned to 1.4.0, and superseded runs are cancelled.
- The test keeps cookies and sends the Razor Pages antiforgery token,
  so it drives the .NET apps too, and posts the Azure Files delete to
  /delete with activity_id.
- The delete check keeps a second activity listed, so a page that
  lists nothing no longer passes.
- The test stops when kubectl port-forward cannot bind instead of
  testing whatever owns the port.
- The in-cluster PostgreSQL sample uses PREFIX local like the other
  samples, so it pulls from the registry attached to the cluster.

Claude-Session: https://claude.ai/code/session_01DZfDmzUBPqNAu3yFKyfCNM
…ulator

# Conflicts:
#	scripts/01-system-assigned-managed-identity.sh
…cate

The SQL Database jobs failed with EulaNotAccepted on az sql server
create. Set LOCALSTACK_MSSQL_ACCEPT_EULA=Y when starting the emulator,
as the sample's README and the localstack-azure-samples CI do.

mssql-tools18 encrypts by default and validates the server
certificate, so pass -N -C to every sqlcmd call, as the
localstack-azure-samples version of this sample does.

Claude-Session: https://claude.ai/code/session_01DZfDmzUBPqNAu3yFKyfCNM
A cosmosdb-nosql-api job failed to create its AKS cluster: the emulator
looks up the k3d release on api.github.com, and its anonymous calls hit
the runner IP's 60 requests an hour with twenty jobs starting at once.
Pass the run's read-only token as LOCALSTACK_GITHUB_API_TOKEN, as the
localstack-azure-samples CI does.

Claude-Session: https://claude.ai/code/session_01DZfDmzUBPqNAu3yFKyfCNM
@paolosalvatori

Copy link
Copy Markdown
Collaborator

Thanks @HarshCasper for the contribution and fixes. Please read my comments above and feel free to merge the PR when you are done with changes.

@HarshCasper
HarshCasper merged commit 1f731b2 into main Oct 15, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants