Skip to content

Commit 08f70ce

Browse files
authored
Merge pull request #1411 from rostalan/test-results
RHIDP-8881: Added integration tests
2 parents a68fd74 + e74f147 commit 08f70ce

File tree

8 files changed

+551
-2
lines changed

8 files changed

+551
-2
lines changed

.github/workflows/pr-actions.yaml

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
.split(/\r?\n/)
2323
.map(l => l.trim())
2424
.filter(l => l.length > 0);
25-
const allowed = new Set(['/publish', '/update-versions', '/update-commit']);
25+
const allowed = new Set(['/publish', '/update-versions', '/update-commit', '/test']);
2626
const matchingCommands = lines.filter(l => allowed.has(l));
2727
2828
if (matchingCommands.length > 1) {
@@ -58,7 +58,7 @@ jobs:
5858
with:
5959
script: |
6060
const errorMessage = core.getInput('error_message');
61-
const body = `**Error**: ${errorMessage}\n\nValid commands are:\n- \`/publish\` - Publish dynamic plugin images\n- \`/update-versions\` - Update versions from release branch\n- \`/update-commit\` - Update commit from automatic discovery`;
61+
const body = `**Error**: ${errorMessage}\n\nValid commands are:\n- \`/publish\` - Publish dynamic plugin images\n- \`/update-versions\` - Update versions from release branch\n- \`/update-commit\` - Update commit from automatic discovery\n- \`/test\` - Run integration tests`;
6262
await github.rest.issues.createComment({
6363
issue_number: context.issue.number,
6464
owner: context.repo.owner,
@@ -183,6 +183,41 @@ jobs:
183183
packages: write
184184
id-token: write
185185

186+
uploadPublishedExportsArtifact:
187+
name: Upload published-exports artifact
188+
needs:
189+
- parse
190+
- prepare
191+
- export
192+
193+
concurrency:
194+
group: uploadPublishedExportsArtifact-${{ github.ref_name }}-${{ github.event.issue.number }}
195+
cancel-in-progress: false
196+
197+
if: needs.parse.outputs.command_name == 'publish' && needs.prepare.outputs.overlay-branch != '' && needs.prepare.outputs.workspace != '' && needs.export.outputs.published-exports != ''
198+
runs-on: ubuntu-latest
199+
steps:
200+
- name: Write published exports and meta
201+
env:
202+
PUBLISHED_EXPORTS: ${{ needs.export.outputs.published-exports }}
203+
WORKSPACE: ${{ needs.prepare.outputs.workspace }}
204+
OVERLAY_BRANCH: ${{ needs.prepare.outputs.overlay-branch }}
205+
OVERLAY_REPO: ${{ needs.prepare.outputs.overlay-repo }}
206+
PR_NUMBER: ${{ needs.prepare.outputs.pr-number }}
207+
run: |
208+
mkdir -p published-exports
209+
printf "%s\n" "$PUBLISHED_EXPORTS" > published-exports/published-exports.txt
210+
cat > published-exports/meta.json <<EOF
211+
{"workspace":"${WORKSPACE}","overlayBranch":"${OVERLAY_BRANCH}","overlayRepo":"${OVERLAY_REPO}","overlayCommit":"${{ needs.prepare.outputs.overlay-commit }}","pr":${PR_NUMBER}}
212+
EOF
213+
- name: Upload published-exports artifact
214+
uses: actions/upload-artifact@v4
215+
with:
216+
name: published-exports
217+
path: published-exports
218+
if-no-files-found: error
219+
retention-days: 7
220+
186221
checkBackstageCompatibility:
187222
name: Check workspace backstage compatibility
188223
needs:
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
name: Parse the Test Config modify with Published OCI References and export as artifact
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
overlay-branch:
7+
required: true
8+
type: string
9+
overlay-repo:
10+
required: true
11+
type: string
12+
workspace-path:
13+
required: true
14+
type: string
15+
16+
jobs:
17+
prepareTestConfig:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Print input values for debugging
21+
run: |
22+
echo "overlay-branch: ${{ inputs.overlay-branch }}"
23+
echo "overlay-repo: ${{ inputs.overlay-repo }}"
24+
echo "workspace-path: ${{ inputs.workspace-path }}"
25+
26+
- name: Checkout
27+
uses: actions/checkout@v4
28+
with:
29+
ref: ${{ inputs.overlay-branch }}
30+
repository: ${{ inputs.overlay-repo }}
31+
32+
- name: Download last published-exports artifact
33+
uses: dawidd6/action-download-artifact@v6
34+
with:
35+
workflow: pr-actions.yaml
36+
name: published-exports
37+
workflow_conclusion: success
38+
workflow_search: true
39+
search_artifacts: true
40+
check_artifacts: true
41+
if_no_artifact_found: warn
42+
allow_forks: true
43+
44+
- name: Setup yq
45+
uses: mikefarah/yq@v4
46+
47+
- name: Build dynamic-plugins.test.yaml
48+
run: |
49+
WORKSPACE_PATH="${{ inputs.workspace-path }}"
50+
PUBLISHED_EXPORTS=""
51+
52+
# Fallback: read from downloaded artifact if input was empty
53+
if [ -f "published-exports.txt" ]; then
54+
PUBLISHED_EXPORTS="$(cat published-exports.txt)"
55+
echo "Artifact content: $PUBLISHED_EXPORTS"
56+
fi
57+
58+
if [ -z "$PUBLISHED_EXPORTS" ]; then
59+
echo "No published exports provided or found via artifact. Nothing to do."
60+
exit 0
61+
fi
62+
63+
OUT_DIR="$WORKSPACE_PATH/tests"
64+
OUT_FILE="$OUT_DIR/dynamic-plugins.test.yaml"
65+
mkdir -p "$OUT_DIR"
66+
67+
# Build map of <stripped packageName> -> metadata file path
68+
declare -A META_MAP
69+
for file in "$WORKSPACE_PATH"/metadata/*.yaml; do
70+
[ -e "$file" ] || continue
71+
pkg=$(yq -r '.packageName // ""' "$file")
72+
if [ -n "$pkg" ]; then
73+
stripped=$(echo "$pkg" | sed 's|^@||; s|/|-|')
74+
META_MAP["$stripped"]="$file"
75+
fi
76+
done
77+
78+
# Always include the root-level default config
79+
ROOT_CONFIG="$GITHUB_WORKSPACE/tests/app-config.yaml"
80+
if [ -f "$ROOT_CONFIG" ]; then
81+
echo "Copying root app-config.yaml into artifact"
82+
cp "$ROOT_CONFIG" "$OUT_DIR/app-config.yaml"
83+
else
84+
echo "Root-level tests/app-config.yaml not found at $ROOT_CONFIG"
85+
fi
86+
87+
# copy optional workspace-level test configs into artefact
88+
[ -f "$WORKSPACE_PATH/tests/app-config.test.yaml" ] && cp "$WORKSPACE_PATH/tests/app-config.test.yaml" "$OUT_DIR/"
89+
[ -f "$WORKSPACE_PATH/tests/test.env" ] && cp "$WORKSPACE_PATH/tests/test.env" "$OUT_DIR/"
90+
91+
echo "Contents of $OUT_DIR after copy:" && ls -la "$OUT_DIR"
92+
93+
# Start the resulting YAML file
94+
echo "plugins:" > "$OUT_FILE"
95+
96+
# For each published export, extract plugin name and read its metadata
97+
# Expected export format: ghcr.io/<repo_path>/<plugin_name>:<tag>
98+
for export in $PUBLISHED_EXPORTS; do
99+
echo "Processing export: $export"
100+
if [[ "$export" =~ ^ghcr\.io/(.+):([^[:space:]]+)$ ]]; then
101+
IMAGE_PATH_AND_PLUGIN="${BASH_REMATCH[1]}" # <repo_path>/<plugin_name>
102+
NEW_TAG="${BASH_REMATCH[2]}" # <tag>
103+
PLUGIN_NAME="${IMAGE_PATH_AND_PLUGIN##*/}"
104+
METADATA_FILE="${META_MAP[$PLUGIN_NAME]}"
105+
if [ -z "$METADATA_FILE" ]; then
106+
echo "Metadata mapping not found for $PLUGIN_NAME, skipping"
107+
continue
108+
fi
109+
110+
PACKAGE_NAME=$(yq -r '.packageName' "$METADATA_FILE")
111+
if [ -z "$PACKAGE_NAME" ] || [ "$PACKAGE_NAME" = "null" ]; then
112+
echo "packageName not found in $METADATA_FILE, skipping"
113+
continue
114+
fi
115+
116+
# Extract dynamicPlugins block under configs.default
117+
if ! yq -e '.configs.default.dynamicPlugins' "$METADATA_FILE" >/dev/null 2>&1; then
118+
echo "configs.default.dynamicPlugins not found in $METADATA_FILE, skipping"
119+
continue
120+
fi
121+
122+
STRIPPED=$(echo "$PACKAGE_NAME" | sed 's|^@||; s|/|-|')
123+
echo "- package: \"oci://ghcr.io/${IMAGE_PATH_AND_PLUGIN}:${NEW_TAG}!${STRIPPED}\"" >> "$OUT_FILE"
124+
echo " disabled: false" >> "$OUT_FILE"
125+
echo " pluginConfig:" >> "$OUT_FILE"
126+
echo " dynamicPlugins:" >> "$OUT_FILE"
127+
# Append the dynamicPlugins block indented by 6 spaces under pluginConfig.dynamicPlugins
128+
yq -o=yaml '.configs.default.dynamicPlugins' "$METADATA_FILE" | sed 's/^/ /' >> "$OUT_FILE"
129+
else
130+
echo "Export did not match expected format, skipping: $export"
131+
fi
132+
done
133+
134+
- name: Upload integration-test artefact
135+
uses: actions/upload-artifact@v4
136+
with:
137+
name: integration-test-artifacts
138+
path: ${{ format('{0}/tests', inputs.workspace-path) }}
139+
if-no-files-found: error
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
name: Run Plugin Integration Tests
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
workspace-path:
7+
required: true
8+
type: string
9+
outputs:
10+
success:
11+
description: Overall integration test result
12+
value: ${{ jobs.run.outputs.success }}
13+
failed-plugins:
14+
description: Newline-separated list of plugins that failed to load
15+
value: ${{ jobs.run.outputs.failed-plugins }}
16+
17+
jobs:
18+
run:
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 25
21+
permissions:
22+
contents: read
23+
packages: read
24+
outputs:
25+
success: ${{ steps.collect-results.outputs.success }}
26+
failed-plugins: ${{ steps.collect-results.outputs.failed-plugins }}
27+
steps:
28+
- name: Download integration test artifacts
29+
uses: actions/download-artifact@v4
30+
with:
31+
name: integration-test-artifacts
32+
path: ./artifacts
33+
34+
- name: Log in to GitHub Container Registry
35+
uses: docker/login-action@v3
36+
with:
37+
registry: ghcr.io
38+
username: ${{ github.actor }}
39+
password: ${{ secrets.GITHUB_TOKEN }}
40+
41+
- name: Start RHDH with Layered Test Config
42+
env:
43+
WORKSPACE_PATH: ${{ inputs.workspace-path }}
44+
run: |
45+
set -euo pipefail
46+
ls -la ./artifacts/ || true
47+
48+
if [ ! -f "./artifacts/dynamic-plugins.test.yaml" ]; then
49+
echo "Error: dynamic-plugins.test.yaml not found in artifacts"
50+
exit 1
51+
fi
52+
53+
echo "dynamic-plugins.test.yaml contents:"
54+
sed -n '1,40p' ./artifacts/dynamic-plugins.test.yaml || true
55+
56+
# Build Docker run command with conditional volume mounts
57+
ENV_ARGS=$( [ -f ./artifacts/test.env ] && echo "--env-file ./artifacts/test.env" || echo "" )
58+
DOCKER_CMD="docker run -d --name rhdh -p 7007:7007 $ENV_ARGS"
59+
if [ -f "./artifacts/app-config.yaml" ]; then
60+
DOCKER_CMD="$DOCKER_CMD -v $(pwd)/artifacts/app-config.yaml:/opt/app-root/src/app-config.yaml"
61+
fi
62+
if [ -f "./artifacts/app-config.test.yaml" ]; then
63+
DOCKER_CMD="$DOCKER_CMD -v $(pwd)/artifacts/app-config.test.yaml:/opt/app-root/src/app-config.test.yaml"
64+
fi
65+
DOCKER_CMD="$DOCKER_CMD -v $(pwd)/artifacts/dynamic-plugins.test.yaml:/opt/app-root/src/dynamic-plugins.yaml"
66+
67+
# Add Docker config and environment variables
68+
echo "Using docker auth file: $HOME/.docker/config.json"
69+
DOCKER_CMD="$DOCKER_CMD -v $HOME/.docker/config.json:/root/.docker/config.json:ro"
70+
DOCKER_CMD="$DOCKER_CMD -e REGISTRY_AUTH_FILE=/root/.docker/config.json"
71+
72+
# Add image and command
73+
DOCKER_CMD="$DOCKER_CMD --entrypoint /bin/bash quay.io/rhdh-community/rhdh:1.6 -c '"
74+
DOCKER_CMD="$DOCKER_CMD set -ex; "
75+
DOCKER_CMD="$DOCKER_CMD PLUGINS_ROOT=/opt/app-root/src/dynamic-plugins-root; "
76+
DOCKER_CMD="$DOCKER_CMD GENERATED_CONFIG=\$PLUGINS_ROOT/app-config.dynamic-plugins.yaml; "
77+
DOCKER_CMD="$DOCKER_CMD INSTALL_SCRIPT=/opt/app-root/src/install-dynamic-plugins.sh; "
78+
DOCKER_CMD="$DOCKER_CMD mkdir -p \$PLUGINS_ROOT; "
79+
DOCKER_CMD="$DOCKER_CMD \$INSTALL_SCRIPT \$PLUGINS_ROOT; "
80+
DOCKER_CMD="$DOCKER_CMD exec node packages/backend"
81+
82+
# Add config files to command (optional)
83+
[ -f "./artifacts/app-config.yaml" ] && DOCKER_CMD="$DOCKER_CMD --config /opt/app-root/src/app-config.yaml"
84+
[ -f "./artifacts/app-config.test.yaml" ] && DOCKER_CMD="$DOCKER_CMD --config /opt/app-root/src/app-config.test.yaml"
85+
DOCKER_CMD="$DOCKER_CMD --config /opt/app-root/src/dynamic-plugins.yaml"
86+
DOCKER_CMD="$DOCKER_CMD --config \$GENERATED_CONFIG'"
87+
88+
echo "Running: $DOCKER_CMD"
89+
eval "$DOCKER_CMD"
90+
91+
- name: Wait for RHDH to be ready
92+
run: |
93+
set -e
94+
for i in $(seq 1 10); do
95+
if curl -fsS http://localhost:7007/health >/dev/null; then
96+
echo "RHDH is ready"; exit 0; fi
97+
echo "Waiting for RHDH... (Attempt ${i}/10)"
98+
# Check if container is still running
99+
if ! docker ps | grep -q rhdh; then
100+
echo "Container stopped unexpectedly."
101+
exit 1
102+
fi
103+
sleep 10
104+
done
105+
echo "RHDH did not become ready in time."
106+
exit 1
107+
108+
- name: Check plugin installation folder structure
109+
run: docker exec rhdh ls -l /opt/app-root/src/dynamic-plugins-root
110+
111+
- name: Check contents of app-config.dynamic-plugins.yaml
112+
run: docker exec rhdh cat /opt/app-root/src/dynamic-plugins-root/app-config.dynamic-plugins.yaml
113+
114+
- name: Basic health check
115+
run: |
116+
curl -fsS http://localhost:7007/health
117+
118+
- name: Verify plugin loading
119+
id: collect-results
120+
run: |
121+
set -e
122+
PLUGINS=$(grep -Eo '!([^[:space:]]+)' ./artifacts/dynamic-plugins.test.yaml | sed -e 's/^!//' -e 's/"$//' | sort -u)
123+
if [ -z "$PLUGINS" ]; then
124+
echo "No plugins found in dynamic-plugins.test.yaml"
125+
echo "success=false" >> "$GITHUB_OUTPUT"
126+
echo "failed-plugins<<EOF" >> "$GITHUB_OUTPUT"
127+
echo "(no-plugins)" >> "$GITHUB_OUTPUT"
128+
echo "EOF" >> "$GITHUB_OUTPUT"
129+
exit 0
130+
fi
131+
LOGS=$(docker logs rhdh || true)
132+
failures=()
133+
echo "===== Checking logs for plugin loaded messages"
134+
for plugin in $PLUGINS; do
135+
echo "Asserting plugin loaded: $plugin"
136+
# Match either the unpack path or the canonical "loaded dynamic ... plugin '<pkg>' from" message
137+
if echo "$LOGS" | grep -qiE "loaded dynamic .* plugin .*${plugin}(-dynamic)?'" ; then
138+
echo "Plugin loaded: $plugin"
139+
else
140+
echo "Plugin NOT loaded: $plugin"
141+
failures+=("$plugin")
142+
fi
143+
done
144+
if echo "$LOGS" | grep -E "(InstallException|Error while adding OCI plugin|Failed to load dynamic plugin|dynamic plugin.*error)" >/dev/null; then
145+
echo "Detected dynamic plugin loading errors in logs"
146+
failures+=("(log-errors)")
147+
fi
148+
if [ ${#failures[@]} -eq 0 ]; then
149+
echo "success=true" >> "$GITHUB_OUTPUT"
150+
echo "failed-plugins<<EOF" >> "$GITHUB_OUTPUT"
151+
echo "" >> "$GITHUB_OUTPUT"
152+
echo "EOF" >> "$GITHUB_OUTPUT"
153+
else
154+
echo "success=false" >> "$GITHUB_OUTPUT"
155+
echo "failed-plugins<<EOF" >> "$GITHUB_OUTPUT"
156+
printf "%s\n" "${failures[@]}" >> "$GITHUB_OUTPUT"
157+
echo "EOF" >> "$GITHUB_OUTPUT"
158+
fi
159+
160+
- name: Fail if any plugin failed to load
161+
if: ${{ steps.collect-results.outputs.success != 'true' }}
162+
run: |
163+
echo "The following plugins failed to load to RHDH:"
164+
echo "${{ steps.collect-results.outputs.failed-plugins }}"
165+
exit 1
166+
167+
- name: Container logs
168+
if: always()
169+
run: docker logs rhdh || true
170+
171+
- name: Cleanup
172+
if: always()
173+
run: docker rm -f rhdh || true

0 commit comments

Comments
 (0)