Skip to content

Commit edaa9fe

Browse files
committed
feat: add github action to switch between wait-for and status
1 parent f200fb1 commit edaa9fe

File tree

8 files changed

+246
-81
lines changed

8 files changed

+246
-81
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
name: Juju Wait For
2+
description: Wait for a Juju application, unit or model to reach a specific state.
3+
inputs:
4+
type:
5+
description: "The type of resource to wait for"
6+
required: true
7+
type: choice
8+
options:
9+
- application
10+
- unit
11+
- model
12+
name:
13+
description: "The name of the resource to wait for"
14+
required: true
15+
query:
16+
description: "Optional query string for filtering (Juju 3.x only)"
17+
required: false
18+
timeout_minutes:
19+
description: "Timeout duration in minutes"
20+
required: false
21+
default: "5"
22+
status:
23+
description: "Expected status for Juju 4.x polling. Comma-separated for multiple statuses."
24+
required: false
25+
default: "active"
26+
juju-version:
27+
description: "Juju version to use for determining wait strategy"
28+
required: true
29+
30+
runs:
31+
using: composite
32+
steps:
33+
- name: Extract major version
34+
id: version
35+
shell: bash
36+
run: |
37+
MAJOR=$(echo "${{ inputs.juju-version }}" | cut -d'.' -f1)
38+
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
39+
echo "Juju major version: $MAJOR"
40+
- name: Wait for Juju resource
41+
if: ${{ steps.version.outputs.major < 4 }}
42+
uses: nick-fields/retry@v3
43+
with:
44+
timeout_minutes: 1
45+
max_attempts: 5
46+
command: |
47+
CMD="juju wait-for ${{ inputs.type }} '${{ inputs.name }}' --timeout=${{ inputs.timeout_minutes }}m"
48+
if [ -n "${{ inputs.query }}" ]; then
49+
CMD="$CMD --query='${{ inputs.query }}'"
50+
fi
51+
echo "Running: $CMD"
52+
eval "$CMD"
53+
- name: Wait for Juju resource
54+
if: ${{ steps.version.outputs.major >= 4 }}
55+
shell: bash
56+
run: |
57+
set -euo pipefail
58+
59+
TYPE="${{ inputs.type }}"
60+
NAME="${{ inputs.name }}"
61+
TIMEOUT_MINUTES="${{ inputs.timeout_minutes }}"
62+
EXPECTED_STATUS="${{ inputs.status }}"
63+
64+
# Convert timeout to seconds for polling
65+
TIMEOUT_SECONDS=$(($TIMEOUT_MINUTES * 60))
66+
echo "Polling juju status"
67+
echo "Waiting for $TYPE: $NAME"
68+
69+
START_TIME=$(date +%s)
70+
while true; do
71+
CURRENT_TIME=$(date +%s)
72+
ELAPSED=$((CURRENT_TIME - START_TIME))
73+
74+
if [ $ELAPSED -ge $TIMEOUT_SECONDS ]; then
75+
echo "Timeout waiting for $TYPE $NAME"
76+
exit 1
77+
fi
78+
79+
# Get status
80+
STATUS=$(juju status "$NAME" --format=json 2>/dev/null | jq -r '.. | .status? // empty' | head -1)
81+
if [ -z "$STATUS" ]; then
82+
STATUS="unknown"
83+
fi
84+
85+
echo "Current status: $STATUS (elapsed: ${ELAPSED}s)"
86+
87+
# Check if status matches any of the expected statuses
88+
IFS=',' read -ra STATUS_ARRAY <<< "$EXPECTED_STATUS"
89+
for expected in "${STATUS_ARRAY[@]}"; do
90+
expected=$(echo "$expected" | xargs) # trim whitespace
91+
if [ "$STATUS" == "$expected" ]; then
92+
echo "$TYPE $NAME reached status: $STATUS"
93+
exit 0
94+
fi
95+
done
96+
97+
# Check for error states
98+
if [ "$STATUS" == "error" ]; then
99+
echo "ERROR: $TYPE $NAME is in error state"
100+
juju status "$NAME" 2>/dev/null || true
101+
exit 1
102+
fi
103+
104+
sleep 5
105+
done

.github/actions/setup-jimm/action.yml

Lines changed: 91 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ inputs:
1717
description: Channel of Juju to use.
1818
required: false
1919
default: 3/stable
20+
juju-version:
21+
description: The version of Juju.
22+
required: true
2023
outputs:
2124
jimm-controller-name:
2225
description: Name of the JIMM workloads controller
@@ -59,14 +62,13 @@ runs:
5962
juju add-model iam
6063
juju deploy identity-platform --trust --channel latest/edge
6164
- name: Wait for IAM
62-
uses: nick-fields/retry@v3
65+
uses: ./.github/actions/juju-wait-for
6366
with:
67+
type: model
68+
name: iam
6469
timeout_minutes: 1
65-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
66-
command: |
67-
# Wait for everything to be ready except kratos-external-idp-integrator
68-
# which will remain as blocked as we're not using an external identity provider.
69-
juju wait-for model iam --timeout=1m --query='forEach(applications, app => (app.name == "kratos-external-idp-integrator" && app.status=="blocked") || (app.name != "kratos-external-idp-integrator" && app.status=="active"))'
70+
query: 'forEach(applications, app => (app.name == "kratos-external-idp-integrator" && app.status=="blocked") || (app.name != "kratos-external-idp-integrator" && app.status=="active"))'
71+
juju-version: ${{ inputs.juju-version }}
7072
- name: Create IAM offers for cross-model relations
7173
shell: bash
7274
run: |
@@ -76,11 +78,12 @@ runs:
7678
shell: bash
7779
run: juju config kratos enforce_mfa=False
7880
- name: Wait for Kratos
79-
uses: nick-fields/retry@v3
81+
uses: ./.github/actions/juju-wait-for
8082
with:
83+
type: unit
84+
name: kratos/0
8185
timeout_minutes: 1
82-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
83-
command: juju wait-for unit kratos/0 --timeout=1m
86+
juju-version: ${{ inputs.juju-version }}
8487
- name: Create admin user
8588
shell: bash
8689
run: |
@@ -98,41 +101,61 @@ runs:
98101
juju deploy nginx-ingress-integrator --channel=latest/stable --trust ingress
99102
juju trust postgresql --scope=cluster
100103
- name: Wait for Postgres
101-
uses: nick-fields/retry@v3
104+
uses: ./.github/actions/juju-wait-for
102105
with:
106+
type: unit
107+
name: postgresql/0
103108
timeout_minutes: 1
104-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
105-
command: juju wait-for unit postgresql/0 --timeout=1m # Wait for postgres to prevent issues when relating to other apps: https://github.com/canonical/openfga-operator/issues/25.
109+
juju-version: ${{ inputs.juju-version }}
106110
- name: Add JIMM relations
107111
shell: bash
108112
run: |
109113
juju relate jimm:nginx-route ingress
110114
juju relate jimm:openfga openfga
111115
juju relate jimm:database postgresql
112-
- name: Wait for Postgres
113-
uses: nick-fields/retry@v3
116+
- name: Wait for Postgres (before openfga)
117+
uses: ./.github/actions/juju-wait-for
114118
with:
119+
type: unit
120+
name: postgresql/0
115121
timeout_minutes: 1
116-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
117-
command: juju wait-for unit postgresql/0 --timeout=1m # Wait for postgres to prevent issues when relating to openfga: https://github.com/canonical/openfga-operator/issues/25.
122+
juju-version: ${{ inputs.juju-version }}
118123
- name: Add openfga relation
119124
shell: bash
120125
run: juju relate openfga:database postgresql
126+
- name: Check and fix openfga error state
127+
shell: bash
128+
run: |
129+
STATUS=$(juju status --format json | yq .applications.openfga.application-status.current)
130+
if [ "$STATUS" == "error" ]; then
131+
# Sometimes the postgresql unit has a PrematureDataAccessError error so remove the relation and try again.
132+
echo "OpenFGA is in error state, removing and re-adding relation..."
133+
juju remove-relation openfga:database postgresql
134+
fi
135+
- name: Wait for openfga to be blocked
136+
if: ${{ success() }}
137+
uses: ./.github/actions/juju-wait-for
138+
with:
139+
type: application
140+
name: openfga
141+
timeout_minutes: 1
142+
status: blocked
143+
juju-version: ${{ inputs.juju-version }}
144+
- name: Re-relate openfga if needed
145+
if: ${{ success() }}
146+
shell: bash
147+
run: |
148+
STATUS=$(juju status --format json | yq .applications.openfga.application-status.current)
149+
if [ "$STATUS" == "blocked" ]; then
150+
juju relate openfga:database postgresql
151+
fi
121152
- name: Wait for openfga
122-
uses: nick-fields/retry@v3
153+
uses: ./.github/actions/juju-wait-for
123154
with:
155+
type: unit
156+
name: openfga/0
124157
timeout_minutes: 1
125-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
126-
command: |
127-
STATUS=$(juju status --format json | yq .applications.openfga.application-status.current)
128-
if [ "$STATUS" == "error" ]; then
129-
# Sometimes the postgresql unit has a PrematureDataAccessError error so remove the relation and try again.
130-
juju remove-relation openfga:database postgresql
131-
# The unit will be blocked when it is waiting for the relation
132-
juju wait-for application openfga --timeout=1m --query='name=="openfga" && status=="blocked"'
133-
juju relate openfga:database postgresql
134-
fi
135-
juju wait-for unit openfga/0 --timeout=1m
158+
juju-version: ${{ inputs.juju-version }}
136159
- name: Add JIMM relations and certs
137160
shell: bash
138161
run: |
@@ -150,18 +173,22 @@ runs:
150173
KEYS=$(go run github.com/go-macaroon-bakery/macaroon-bakery/cmd/bakery-keygen/v3@latest)
151174
juju config jimm public-key=$(echo $KEYS | yq .public)
152175
juju config jimm private-key=$(echo $KEYS | yq .private)
176+
- name: Check and resolve ingress error state
177+
shell: bash
178+
run: |
179+
STATUS=$(juju status --format json | yq .applications.ingress.application-status.current)
180+
if [ "$STATUS" == "error" ]; then
181+
# Sometimes the ingress unit will get into an error state, so resolve and try again.
182+
echo "Ingress is in error state, resolving..."
183+
juju resolved ingress/0
184+
fi
153185
- name: Wait for ingress
154-
uses: nick-fields/retry@v3
186+
uses: ./.github/actions/juju-wait-for
155187
with:
188+
type: unit
189+
name: ingress/0
156190
timeout_minutes: 1
157-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
158-
command: |
159-
STATUS=$(juju status --format json | yq .applications.ingress.application-status.current)
160-
if [ "$STATUS" == "error" ]; then
161-
# Sometimes the ingress unit will get into an error state, so resolve and try again.
162-
juju resolved ingress/0
163-
fi
164-
juju wait-for unit ingress/0 --timeout=1m
191+
juju-version: ${{ inputs.juju-version }}
165192
- name: Set up ingress hostname and certs
166193
shell: bash
167194
run: |
@@ -170,11 +197,12 @@ runs:
170197
juju run --wait=2m jimm-cert/0 get-ca-certificate --quiet | yq .ca-certificate | sudo tee /usr/local/share/ca-certificates/jimm-test.crt
171198
sudo update-ca-certificates --fresh
172199
- name: Wait for JIMM
173-
uses: nick-fields/retry@v3
200+
uses: ./.github/actions/juju-wait-for
174201
with:
202+
type: unit
203+
name: jimm/0
175204
timeout_minutes: 1
176-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
177-
command: juju wait-for unit jimm/0 --timeout=1m # Wait for JIMM to be ready if it's still making config changes.
205+
juju-version: ${{ inputs.juju-version }}
178206
- name: Bootstrap workloads controller
179207
id: workloads-controller
180208
shell: bash
@@ -205,29 +233,35 @@ runs:
205233
run: |
206234
juju jaas register-controller '${{ steps.workloads-controller.outputs.name }}' --local --tls-hostname juju-apiserver
207235
juju update-credentials microk8s --controller ${{ steps.workloads-controller.outputs.public }}
236+
- name: Switch to JIMM model
237+
shell: bash
238+
run: juju switch '${{ steps.jimm-controller.outputs.name }}:jimm'
208239
- name: Wait for JIMM
209-
uses: nick-fields/retry@v3
240+
uses: ./.github/actions/juju-wait-for
210241
with:
242+
type: unit
243+
name: jimm/0
211244
timeout_minutes: 1
212-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
213-
command: |
214-
juju switch '${{ steps.jimm-controller.outputs.name }}:jimm'
215-
# Wait for JIMM to be ready if it's still making config changes.
216-
juju wait-for unit jimm/0 --timeout=1m
245+
juju-version: ${{ inputs.juju-version }}
217246
- name: Restart JIMM
218247
shell: bash
219248
run: sudo microk8s kubectl delete pod jimm-0 -n jimm # Fix ListModels issue:
249+
- name: Switch to JIMM model and check status
250+
shell: bash
251+
run: |
252+
juju switch '${{ steps.jimm-controller.outputs.name }}:jimm'
253+
STATUS=$(juju status --format json | yq .applications.jimm.application-status.current)
254+
if [ "$STATUS" == "error" ]; then
255+
# Sometimes the jimm unit will get into an error state, so resolve and try again.
256+
echo "JIMM is in error state, resolving..."
257+
juju resolved jimm/0
258+
fi
220259
- name: Wait for JIMM pod to be recreated
221-
uses: nick-fields/retry@v3
260+
continue-on-error: true
261+
uses: ./.github/actions/juju-wait-for
222262
with:
263+
type: application
264+
name: jimm
223265
timeout_minutes: 1
224-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
225-
continue_on_error: true # Sometimes the error persists after a retry but it seems to work anyway.
226-
command: |
227-
juju switch '${{ steps.jimm-controller.outputs.name }}:jimm'
228-
STATUS=$(juju status --format json | yq .applications.jimm.application-status.current)
229-
if [ "$STATUS" == "error" ]; then
230-
# Sometimes the jimm unit will get into an error state, so resolve and try again.
231-
juju resolved jimm/0
232-
fi
233-
juju wait-for application jimm --timeout=1m --query='name=="jimm" && (status=="active" || status=="error")'
266+
status: active,error
267+
juju-version: ${{ inputs.juju-version }}

.github/actions/setup-k8s-charm/action.yml

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ inputs:
2323
- latest/stable
2424
- latest/beta
2525
- latest/edge
26+
juju-version:
27+
description: The version of Juju.
28+
required: true
2629

2730
runs:
2831
using: "composite"
@@ -73,18 +76,22 @@ runs:
7376
- name: Integrate charm
7477
run: juju integrate '${{ inputs.controller-app }}' dashboard
7578
shell: bash
79+
- name: Check and resolve error state
80+
shell: bash
81+
run: |
82+
STATUS=$(juju status --format json | yq .applications.dashboard.application-status.current)
83+
if [ "$STATUS" == "error" ]; then
84+
# Sometimes the dashboard unit will get into an error state, so resolve and try again.
85+
echo "Dashboard is in error state, resolving..."
86+
juju resolved dashboard/0
87+
fi
7688
- name: Wait for charm to be ready
77-
uses: nick-fields/retry@v3
89+
uses: ./.github/actions/juju-wait-for
7890
with:
91+
type: application
92+
name: dashboard
7993
timeout_minutes: 1
80-
max_attempts: 5 # Retry the wait-for with a short timeout in case it gets stuck and times out. If it fails after multiple tries it's probably a real error.
81-
command: |
82-
STATUS=$(juju status --format json | yq .applications.dashboard.application-status.current)
83-
if [ "$STATUS" == "error" ]; then
84-
# Sometimes the dashboard unit will get into an error state, so resolve and try again.
85-
juju resolved dashboard/0
86-
fi
87-
juju wait-for application dashboard --timeout=1m
94+
juju-version: ${{ inputs.juju-version }}
8895
- name: Disable analytics
8996
run: juju config dashboard analytics-enabled=false
9097
shell: bash

0 commit comments

Comments
 (0)