integration-testing.yaml 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. name: Run OpenCost Integration Tests
  2. on:
  3. schedule:
  4. - cron: '0 14 * * *'
  5. push:
  6. branches:
  7. - develop
  8. pull_request:
  9. branches:
  10. - develop
  11. merge_group:
  12. types: [checks_requested]
  13. concurrency:
  14. group: ${{ github.event.merge_group.head.sha || github.ref }}-intg-tests
  15. cancel-in-progress: false
  16. jobs:
  17. check_actor_permissions:
  18. runs-on: ubuntu-latest
  19. permissions: {}
  20. outputs:
  21. ismaintainer: ${{ steps.check_permissions.outputs.ismaintainer }}
  22. steps:
  23. - name: Check if actor is a maintainer
  24. id: check_permissions
  25. uses: actions/github-script@v7
  26. with:
  27. script: |
  28. const { data: collaborators } = await github.rest.repos.listCollaborators({
  29. owner: context.repo.owner,
  30. repo: context.repo.repo,
  31. permission: 'push'
  32. });
  33. const writers = collaborators.map(collaborator => collaborator.login);
  34. const isActorMaintainer = writers.includes(context.actor);
  35. console.log(`Actor: ${context.actor}`);
  36. console.log(`Repository writers: ${writers.join(', ')}`);
  37. console.log(`Is actor a maintainer? ${isActorMaintainer}`);
  38. core.setOutput('ismaintainer', isActorMaintainer.toString());
  39. noop-tests:
  40. needs: check_actor_permissions
  41. permissions: {}
  42. runs-on: Kubecost-Linux-Small-x86
  43. if: ${{ always() && !cancelled() && github.event_name != 'merge_group' && github.ref != 'refs/heads/develop' && needs.check_actor_permissions.outputs.ismaintainer == 'false' }}
  44. outputs:
  45. is_noop: ${{ steps.noop-tests.outputs.is_noop }}
  46. steps:
  47. - name: Tests Not Needed
  48. id: noop-tests
  49. run: |
  50. echo "integration tests not running because you are not a maintainer. they will run automatically when a PR is merged."
  51. echo "is_noop=true" >> $GITHUB_OUTPUT
  52. wait_for_image_ready:
  53. runs-on: ubuntu-latest
  54. permissions: {}
  55. needs: check_actor_permissions
  56. if: ${{ (always() && !cancelled()) && ( github.event_name == 'merge_group' || github.ref != 'refs/heads/develop' || needs.check_actor_permissions.outputs.ismaintainer == 'true') }}
  57. outputs:
  58. IMAGE_TAG: ${{ steps.set_image_tags.outputs.IMAGE_TAG }}
  59. NAMESPACE: ${{ steps.set_image_tags.outputs.NAMESPACE }}
  60. MAINBRANCH: ${{ steps.set_image_tags.outputs.mainbranch }}
  61. passed: ${{ steps.wait_for_image_ready.outputs.passed }}
  62. steps:
  63. - uses: actions/checkout@v4
  64. with:
  65. ref: ${{ github.event.merge_group.head.sha || github.ref }}
  66. - name: Set OC SHA
  67. run: |
  68. echo "OC_SHORTHASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
  69. - name: Set image tags
  70. id: set_image_tags
  71. run: |
  72. echo "github.event_name: ${{ github.event_name }}"
  73. if [[ "${{ github.event_name }}" == "merge_group" ]]; then
  74. echo "IMAGE_TAG=ghcr.io/${{ github.repository_owner }}/opencost:test-${{ steps.sha.outputs.OC_SHORTHASH }}" >> $GITHUB_OUTPUT
  75. echo "NAMESPACE=merge-queue-${{ github.event.number }}-oc-${{ env.OC_SHORTHASH }}" >> $GITHUB_OUTPUT
  76. echo "mainbranch=false" >> $GITHUB_OUTPUT
  77. else
  78. echo "building on develop branch"
  79. echo "IMAGE_TAG=ghcr.io/${{ github.repository_owner }}/opencost:develop-${{ steps.sha.outputs.OC_SHORTHASH }}" >> $GITHUB_OUTPUT
  80. echo "NAMESPACE=develop-oc-${{ env.OC_SHORTHASH }}" >> $GITHUB_OUTPUT
  81. echo "mainbranch=true" >> $GITHUB_OUTPUT
  82. fi
  83. - name: Log into ghcr.io
  84. uses: docker/login-action@v3
  85. with:
  86. registry: ghcr.io
  87. username: ${{ github.actor }}
  88. password: ${{ secrets.GITHUB_TOKEN }}
  89. - name: wait for docker image to be ready
  90. id: wait_for_image_ready
  91. run: |
  92. max_attempts=100
  93. # Loop until the Docker image can be pulled
  94. until docker manifest inspect ${{ steps.set_image_tags.outputs.IMAGE_TAG }}; do
  95. echo "Waiting for Docker image ${{ steps.set_image_tags.outputs.IMAGE_TAG }} to be available, $max_attempts tries remain..."
  96. sleep 6
  97. max_attempts=$((max_attempts - 1))
  98. if [[ $max_attempts -eq 0 ]]; then
  99. echo "Docker image ${{ steps.set_image_tags.outputs.IMAGE_TAG }} is not available after 10 minutes. Exiting..."
  100. exit 1
  101. fi
  102. done
  103. echo "Docker image ${{ steps.set_image_tags.outputs.IMAGE_TAG }} is ready!"
  104. echo "passed=true" >> $GITHUB_OUTPUT
  105. build-test-stack:
  106. needs: wait_for_image_ready
  107. uses: opencost/opencost-infra/.github/workflows/build-stack.yaml@main
  108. secrets: inherit
  109. with:
  110. oc-container-version: "${{ needs.wait_for_image_ready.outputs.IMAGE_TAG }}"
  111. namespace: "${{ needs.wait_for_image_ready.outputs.NAMESPACE }}"
  112. wait-for-dns:
  113. needs: [wait_for_image_ready, build-test-stack]
  114. runs-on: ubuntu-latest
  115. permissions: {}
  116. steps:
  117. - name: Wait for DNS to resolve
  118. run: |
  119. echo "Waiting for ${{ needs.wait_for_image_ready.outputs.NAMESPACE }}.infra.opencost.io to resolve in DNS..."
  120. max_attempts=60
  121. until host ${{ needs.wait_for_image_ready.outputs.NAMESPACE }}.infra.opencost.io; do
  122. echo "DNS not yet resolved for ${{ needs.wait_for_image_ready.outputs.NAMESPACE }}.infra.opencost.io, $max_attempts tries remain..."
  123. sleep 10
  124. max_attempts=$((max_attempts - 1))
  125. if [[ $max_attempts -eq 0 ]]; then
  126. echo "DNS resolution failed for ${{ needs.wait_for_image_ready.outputs.NAMESPACE }}.infra.opencost.io after 10 minutes. Exiting..."
  127. exit 1
  128. fi
  129. done
  130. echo "DNS resolved successfully for ${{ needs.wait_for_image_ready.outputs.NAMESPACE }}.infra.opencost.io!"
  131. run-tests:
  132. needs: [wait_for_image_ready, build-test-stack, wait-for-dns]
  133. permissions: {}
  134. uses: opencost/opencost-infra/.github/workflows/test-stack.yaml@main
  135. secrets: inherit
  136. with:
  137. namespace: "${{ needs.wait_for_image_ready.outputs.NAMESPACE }}"
  138. target_branch: "${{ github.event.pull_request.head.ref || 'main' }}"
  139. teardown-test-stack:
  140. needs: [wait_for_image_ready, run-tests]
  141. permissions: {}
  142. uses: opencost/opencost-infra/.github/workflows/destroy-stack.yaml@main
  143. if: always()
  144. secrets: inherit
  145. with:
  146. namespace: "${{ needs.wait_for_image_ready.outputs.NAMESPACE }}"
  147. check-success:
  148. needs: [noop-tests, run-tests]
  149. permissions: {}
  150. runs-on: ubuntu-latest
  151. if: ${{ always() }}
  152. steps:
  153. - name: Check success
  154. run: |
  155. if [[ "${{ needs.noop-tests.outputs.is_noop }}" == "true" ]]; then
  156. echo "No-op tests, skipping success check"
  157. exit 0
  158. fi
  159. if [[ "${{ needs.run-tests.outputs.passed }}" != "true" ]]; then
  160. echo "One or more integration tests failed"
  161. exit 1
  162. fi
  163. echo "All integration tests passed"
  164. exit 0
  165. set-labels:
  166. needs: [wait_for_image_ready, run-tests]
  167. runs-on: ubuntu-latest
  168. permissions: {}
  169. steps:
  170. - name: label integration tests failing
  171. if: ${{ always() && contains(needs.*.result, 'failure') && !cancelled()}}
  172. uses: andymckay/labeler@1.0.4
  173. with:
  174. add-labels: "integration tests failed"
  175. - uses: mondeja/remove-labels-gh-action@v2
  176. if: ${{ always() && contains(needs.*.result, 'failure') && !cancelled()}}
  177. with:
  178. token: ${{ secrets.GITHUB_TOKEN }}
  179. labels: |
  180. integration tests passed
  181. - name: Label integration tests passing
  182. if: ${{ always() && !contains(needs.*.result, 'failure') && !cancelled()}}
  183. uses: andymckay/labeler@1.0.4
  184. with:
  185. add-labels: "integration tests passed"
  186. - uses: mondeja/remove-labels-gh-action@v2
  187. if: ${{ always() && !contains(needs.*.result, 'failure') && !cancelled()}}
  188. with:
  189. token: ${{ secrets.GITHUB_TOKEN }}
  190. labels: |
  191. integration tests failed