integration-testing.yaml 11 KB

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