feat: add Windows PowerShell scripts for cloud-deployment example#900
feat: add Windows PowerShell scripts for cloud-deployment example#900spandey1702 wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces Windows PowerShell equivalents (deploy.ps1, cleanup.ps1, and verify.ps1) for the existing bash deployment scripts, along with updated documentation in the README.md. The code review feedback identifies several critical issues in the PowerShell scripts, primarily concerning Windows compatibility. These include a syntax error with here-string redirection, missing dependency checks for Maven and the container tool, and potential failures in string comparisons due to trailing carriage returns (\r) in output captured from external CLI tools like kubectl, docker/podman, and kind.
| @" | ||
| apiVersion: v1 | ||
| kind: ConfigMap | ||
| metadata: | ||
| name: local-registry-hosting | ||
| namespace: kube-public | ||
| data: | ||
| localRegistryHosting.v1: | | ||
| host: "localhost:${RegPort}" | ||
| help: "https://kind.sigs.k8s.io/docs/user/local-registry/" | ||
| "@ | kubectl apply -f - |
There was a problem hiding this comment.
In PowerShell, the closing delimiter of a here-string ("@) must be on its own line at the very beginning of the line, with no other characters following it. Piping directly from the closing delimiter on the same line causes a syntax error in Windows PowerShell. Assigning the here-string to a variable first resolves this issue.
$registryConfig = @"
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${RegPort}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
"@
$registryConfig | kubectl apply -f -
| $running = & $ContainerTool inspect -f '{{.State.Running}}' $RegName 2>$null | ||
| if ($running -ne "true") { |
There was a problem hiding this comment.
On Windows, external CLI tools like Docker/Podman often output trailing carriage returns (\r). When PowerShell captures this output, $running will contain "true\r", making the exact string comparison $running -ne "true" evaluate to $true even when the container is running. This will cause the script to attempt to recreate the registry container, failing with a name conflict error. Using -notmatch "^true\s*$" makes the check robust against trailing whitespace and carriage returns.
$running = & $ContainerTool inspect -f '{{.State.Running}}' $RegName 2>$null
if ($running -notmatch "^true\s*$") {
| $networkInfo = & $ContainerTool inspect -f '{{json .NetworkSettings.Networks.kind}}' $RegName 2>$null | ||
| if ($networkInfo -eq "null" -or [string]::IsNullOrEmpty($networkInfo)) { |
There was a problem hiding this comment.
Similar to the registry running check, if $networkInfo contains a trailing carriage return ("null\r"), the exact comparison $networkInfo -eq "null" will evaluate to $false. This will cause the script to skip connecting the registry to the cluster network, leading to ImagePullBackOff errors when nodes try to pull images. Using -match "null" or trimming the string avoids this issue.
$networkInfo = & $ContainerTool inspect -f '{{json .NetworkSettings.Networks.kind}}' $RegName 2>$null
if ([string]::IsNullOrEmpty($networkInfo) -or $networkInfo -match "null") {
| # Check if kubectl is installed | ||
| if (-not (Get-Command kubectl -ErrorAction SilentlyContinue)) { | ||
| Write-Host "Error: kubectl is not installed" -ForegroundColor Red | ||
| Write-Host "Please install kubectl first: https://kubernetes.io/docs/tasks/tools/" | ||
| exit 1 | ||
| } |
There was a problem hiding this comment.
The script runs $ContainerTool and mvn without verifying if they are installed and available in the system's PATH. If they are missing, PowerShell will throw a CommandNotFoundException but may continue executing subsequent steps (since $ErrorActionPreference is not set to Stop), leading to silent failures or confusing errors. Adding explicit checks ensures the script fails fast with a clear error message.
# Check if kubectl is installed
if (-not (Get-Command kubectl -ErrorAction SilentlyContinue)) {
Write-Host "Error: kubectl is not installed" -ForegroundColor Red
Write-Host "Please install kubectl first: https://kubernetes.io/docs/tasks/tools/"
exit 1
}
# Check if container tool is installed
if (-not (Get-Command $ContainerTool -ErrorAction SilentlyContinue)) {
Write-Host "Error: $ContainerTool is not installed or not in PATH" -ForegroundColor Red
exit 1
}
# Check if Maven is installed
if (-not (Get-Command mvn -ErrorAction SilentlyContinue)) {
Write-Host "Error: Maven (mvn) is not installed" -ForegroundColor Red
Write-Host "Please install Maven first: https://maven.apache.org/install.html"
exit 1
}
| $clusters = kind get clusters 2>$null | ||
| if ($clusters -notmatch "(?m)^kind$") { |
There was a problem hiding this comment.
In .NET regular expressions, $ matches before \n but does not match before \r. On Windows, kind get clusters may return "kind\r\n", which will fail to match (?m)^kind$. Adding \r? before $ makes the regex robust on Windows.
$clusters = kind get clusters 2>$null
if ($clusters -notmatch "(?m)^kind\r?$") {
| $postgresReady = kubectl get pods -n a2a-demo -l app=postgres ` | ||
| -o "jsonpath={.items[0].status.conditions[?(@.type=='Ready')].status}" 2>$null | ||
| if ($postgresReady -eq "True") { |
There was a problem hiding this comment.
On Windows, kubectl output captured by PowerShell may contain trailing carriage returns (\r). An exact comparison like $postgresReady -eq "True" will fail if the value is "True\r", incorrectly reporting that PostgreSQL is not ready. Using -match "^True\s*$" is much more robust.
$postgresReady = kubectl get pods -n a2a-demo -l app=postgres `
-o "jsonpath={.items[0].status.conditions[?(@.type=='Ready')].status}" 2>$null
if ($postgresReady -match "^True\s*$") {
| $kafkaReady = kubectl get kafka a2a-kafka -n kafka ` | ||
| -o "jsonpath={.status.conditions[?(@.type=='Ready')].status}" 2>$null | ||
| if ($kafkaReady -eq "True") { |
There was a problem hiding this comment.
Using -match "^True\s*$" ensures that trailing carriage returns (\r) from kubectl output do not cause the readiness check to fail incorrectly.
$kafkaReady = kubectl get kafka a2a-kafka -n kafka `
-o "jsonpath={.status.conditions[?(@.type=='Ready')].status}" 2>$null
if ($kafkaReady -match "^True\s*$") {
| $agentStatusJson = kubectl get pods -n a2a-demo -l app=a2a-agent ` | ||
| -o "jsonpath={range .items[*]}{.status.conditions[?(@.type=='Ready')].status}{'\n'}{end}" 2>$null | ||
| $agentReady = ($agentStatusJson -split "`n" | Where-Object { $_ -eq "True" }).Count |
There was a problem hiding this comment.
PowerShell automatically captures multi-line output from external commands as an array of strings, so splitting by \n is unnecessary. Furthermore, using -match "^True\s*$" instead of -eq "True" protects against trailing carriage returns (\r) on Windows.
$agentStatusJson = kubectl get pods -n a2a-demo -l app=a2a-agent `
-o "jsonpath={range .items[*]}{.status.conditions[?(@.type=='Ready')].status}{'\n'}{end}" 2>$null
$agentReady = ($agentStatusJson | Where-Object { $_ -match "^True\s*$" }).Count
…ct#395) Add deploy.ps1, cleanup.ps1, and verify.ps1 as Windows equivalents of the existing bash scripts. Fixes a2aproject#395. - deploy.ps1: full cluster setup (Kind, registry, Strimzi, PostgreSQL, Kafka, agent) with -ContainerTool docker|podman parameter - cleanup.ps1: reverse-order teardown with confirmation prompt - verify.ps1: health checks for all deployed components Key translation notes: - Bash heredocs -> PowerShell here-strings piped to kubectl/docker stdin - export VAR=val -> $env:VAR = 'val' - grep/|| true -> -match / $LASTEXITCODE checks - sleep N -> Start-Sleep -Seconds N - curl -> curl.exe (avoids PowerShell Invoke-WebRequest alias) - SKIP_ENTITY_OPERATOR_WAIT and SKIP_AGENT_DEPLOY env vars supported Also updates README.md with Windows quick-start instructions, PowerShell execution policy note, and updated project structure table.
14da7a7 to
58311ac
Compare
- Add missing dependency checks for Maven and container tool - Trim docker/podman inspect output to handle CRLF on Windows - Use \r? in kind cluster regex to match Windows line endings - Use -match '^True\s*$' instead of -eq 'True' in verify.ps1 to handle trailing whitespace/carriage returns in kubectl output
|
Addressed all Gemini review comments — added .Trim() for CRLF handling, \r? in the kind cluster regex, -match "^True\s*$" in verify.ps1, and dependency checks for Maven and the container tool. |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces Windows PowerShell scripts (deploy.ps1, cleanup.ps1, and verify.ps1) as equivalents to the existing bash scripts for the cloud deployment example, along with corresponding updates to the README.md. The review feedback highlights several critical issues in the new PowerShell scripts: incorrect package names for the test client in deploy.ps1 and verify.ps1 that would cause a ClassNotFoundException, potential runtime exceptions from calling .Trim() on null values when inspecting containers, and a lack of error checking after kubectl wait commands which allows the deployment script to silently continue upon failure.
| Write-Host "To run the test client (demonstrating load balancing):" | ||
| Write-Host " cd ..\server" | ||
| Write-Host " mvn test-compile exec:java -Dexec.classpathScope=test ``" | ||
| Write-Host " -Dexec.mainClass=`"io.a2a.examples.cloud.A2ACloudExampleClient`" ``" |
There was a problem hiding this comment.
| Write-Host "To run the test client (demonstrating load balancing):" | ||
| Write-Host " cd ..\server" | ||
| Write-Host " mvn test-compile exec:java -Dexec.classpathScope=test ``" | ||
| Write-Host " -Dexec.mainClass=`"io.a2a.examples.cloud.A2ACloudExampleClient`" ``" |
There was a problem hiding this comment.
|
|
||
| # Create registry container if it doesn't exist | ||
| # .Trim() strips trailing \r\n from docker/podman output on Windows | ||
| $running = (& $ContainerTool inspect -f '{{.State.Running}}' $RegName 2>$null).Trim() |
There was a problem hiding this comment.
If the registry container does not exist, the inspect command will fail and return $null. Calling .Trim() directly on $null will throw a PowerShell runtime exception: You cannot call a method on a null-valued expression.
Casting the result to [string] first ensures that a null value becomes an empty string, which can be safely trimmed without throwing an error.
$running = ([string](& $ContainerTool inspect -f '{{.State.Running}}' $RegName 2>$null)).Trim()
| Write-Host "" | ||
| Write-Host "Connecting registry to cluster network..." | ||
| # .Trim() strips trailing \r\n from docker/podman output on Windows | ||
| $networkInfo = (& $ContainerTool inspect -f '{{json .NetworkSettings.Networks.kind}}' $RegName 2>$null).Trim() |
There was a problem hiding this comment.
| kubectl wait --for=condition=Available deployment/strimzi-cluster-operator -n kafka --timeout=300s | ||
| kubectl wait --for=condition=Ready pod -l name=strimzi-cluster-operator -n kafka --timeout=300s | ||
| Write-Host "✓ Strimzi operator installed" -ForegroundColor Green |
There was a problem hiding this comment.
If the Strimzi operator fails to become ready within the timeout, the script will silently continue and fail later during Kafka deployment. It is better to check $LASTEXITCODE and exit early with a clear error message.
kubectl wait --for=condition=Available deployment/strimzi-cluster-operator -n kafka --timeout=300s
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Strimzi operator deployment failed to become available" -ForegroundColor Red
exit 1
}
kubectl wait --for=condition=Ready pod -l name=strimzi-cluster-operator -n kafka --timeout=300s
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Strimzi operator pod failed to become ready" -ForegroundColor Red
exit 1
}
Write-Host "✓ Strimzi operator installed" -ForegroundColor Green
| kubectl wait --for=condition=Ready pod -l app=postgres -n a2a-demo --timeout=120s | ||
| Write-Host "✓ PostgreSQL deployed" -ForegroundColor Green |
There was a problem hiding this comment.
If PostgreSQL fails to become ready, the script should fail-fast instead of continuing to deploy Kafka and the agent.
kubectl wait --for=condition=Ready pod -l app=postgres -n a2a-demo --timeout=120s
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: PostgreSQL failed to become ready" -ForegroundColor Red
exit 1
}
Write-Host "✓ PostgreSQL deployed" -ForegroundColor Green
| kubectl wait --for=condition=Ready pod -l app=a2a-agent -n a2a-demo --timeout=120s | ||
| Write-Host "✓ Agent deployed" -ForegroundColor Green |
There was a problem hiding this comment.
If the agent pods fail to become ready, the script should report an error and exit with a non-zero code.
kubectl wait --for=condition=Ready pod -l app=a2a-agent -n a2a-demo --timeout=120s
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Agent pods failed to become ready" -ForegroundColor Red
exit 1
}
Write-Host "✓ Agent deployed" -ForegroundColor Green
|
Hi @spandey1702 thank you. I think it could be good to have these scripts tested on CI, similar to what we do for Linux in https://github.com/a2aproject/a2a-java/blob/main/.github/workflows/cloud-deployment-example.yml AIUI GH Actions also provides Windows runners. |
Add deploy.ps1, cleanup.ps1, and verify.ps1 as Windows equivalents of the existing bash scripts. Fixes #395.
Key translation notes: