mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-10 11:41:27 +00:00
Compare commits
49 Commits
gha-runner
...
responsive
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4876c5d03 | ||
|
|
f58dd76763 | ||
|
|
90b68fec1a | ||
|
|
1be410ba80 | ||
|
|
930c9db6e7 | ||
|
|
a152741a1a | ||
|
|
80d848339e | ||
|
|
8535a24135 | ||
|
|
b349ded2be | ||
|
|
6276c84493 | ||
|
|
4a8420ce96 | ||
|
|
a62ca3d853 | ||
|
|
4eb038eaa1 | ||
|
|
b2c6992e84 | ||
|
|
0a6208e38d | ||
|
|
2cc793a835 | ||
|
|
894732732a | ||
|
|
e45ac190e2 | ||
|
|
d0fb7206a4 | ||
|
|
9afd93065f | ||
|
|
3be7128f9a | ||
|
|
3bda9bb240 | ||
|
|
ab92e4edc3 | ||
|
|
fa7a4f584e | ||
|
|
9b51f25800 | ||
|
|
ea13873f14 | ||
|
|
a6d87c46cd | ||
|
|
51c70a64c3 | ||
|
|
a1b8e0cc3d | ||
|
|
2889029bc5 | ||
|
|
87f2e00971 | ||
|
|
d9af241a7d | ||
|
|
49490c4421 | ||
|
|
109750f816 | ||
|
|
9e191cdd21 | ||
|
|
f965dfef73 | ||
|
|
4ee49fee14 | ||
|
|
8075e5ee74 | ||
|
|
963ae48a3f | ||
|
|
98854ef9c0 | ||
|
|
1987d9eb2e | ||
|
|
0006dd5eb1 | ||
|
|
86f1714354 | ||
|
|
f68bbad579 | ||
|
|
d3a8a34bb2 | ||
|
|
d515b4a6e0 | ||
|
|
d971fedbe8 | ||
|
|
6c6d061f0a | ||
|
|
5b9b9f7ca2 |
@@ -47,7 +47,7 @@ runs:
|
|||||||
-d '{"ref": "main", "inputs": { "arc_name": "${{inputs.arc-name}}" } }'
|
-d '{"ref": "main", "inputs": { "arc_name": "${{inputs.arc-name}}" } }'
|
||||||
|
|
||||||
- name: Fetch workflow run & job ids
|
- name: Fetch workflow run & job ids
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v7
|
||||||
id: query_workflow
|
id: query_workflow
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
@@ -128,7 +128,7 @@ runs:
|
|||||||
|
|
||||||
- name: Wait for workflow to start running
|
- name: Wait for workflow to start running
|
||||||
if: inputs.wait-to-running == 'true' && inputs.wait-to-finish == 'false'
|
if: inputs.wait-to-running == 'true' && inputs.wait-to-finish == 'false'
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
function sleep(ms) {
|
function sleep(ms) {
|
||||||
@@ -156,7 +156,7 @@ runs:
|
|||||||
|
|
||||||
- name: Wait for workflow to finish successfully
|
- name: Wait for workflow to finish successfully
|
||||||
if: inputs.wait-to-finish == 'true'
|
if: inputs.wait-to-finish == 'true'
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
// Wait 5 minutes and make sure the workflow run we triggered completed with result 'success'
|
// Wait 5 minutes and make sure the workflow run we triggered completed with result 'success'
|
||||||
|
|||||||
6
.github/actions/setup-arc-e2e/action.yaml
vendored
6
.github/actions/setup-arc-e2e/action.yaml
vendored
@@ -27,7 +27,7 @@ runs:
|
|||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
with:
|
with:
|
||||||
# Pinning v0.9.1 for Buildx and BuildKit v0.10.6
|
# Pinning v0.9.1 for Buildx and BuildKit v0.10.6
|
||||||
# BuildKit v0.11 which has a bug causing intermittent
|
# BuildKit v0.11 which has a bug causing intermittent
|
||||||
@@ -36,7 +36,7 @@ runs:
|
|||||||
driver-opts: image=moby/buildkit:v0.10.6
|
driver-opts: image=moby/buildkit:v0.10.6
|
||||||
|
|
||||||
- name: Build controller image
|
- name: Build controller image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -56,7 +56,7 @@ runs:
|
|||||||
|
|
||||||
- name: Get configure token
|
- name: Get configure token
|
||||||
id: config-token
|
id: config-token
|
||||||
uses: peter-murray/workflow-application-token-action@8e1ba3bf1619726336414f1014e37f17fbadf1db
|
uses: peter-murray/workflow-application-token-action@dc0413987a085fa17d19df9e47d4677cf81ffef3
|
||||||
with:
|
with:
|
||||||
application_id: ${{ inputs.app-id }}
|
application_id: ${{ inputs.app-id }}
|
||||||
application_private_key: ${{ inputs.app-pk }}
|
application_private_key: ${{ inputs.app-pk }}
|
||||||
|
|||||||
@@ -24,23 +24,23 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
if: ${{ github.event_name == 'release' || github.event_name == 'push' && github.ref == 'refs/heads/master' && inputs.password != '' }}
|
if: ${{ github.event_name == 'release' || github.event_name == 'push' && github.ref == 'refs/heads/master' && inputs.password != '' }}
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ inputs.username }}
|
username: ${{ inputs.username }}
|
||||||
password: ${{ inputs.password }}
|
password: ${{ inputs.password }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
if: ${{ github.event_name == 'release' || github.event_name == 'push' && github.ref == 'refs/heads/master' && inputs.ghcr_password != '' }}
|
if: ${{ github.event_name == 'release' || github.event_name == 'push' && github.ref == 'refs/heads/master' && inputs.ghcr_password != '' }}
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ inputs.ghcr_username }}
|
username: ${{ inputs.ghcr_username }}
|
||||||
|
|||||||
12
.github/workflows/arc-publish-chart.yaml
vendored
12
.github/workflows/arc-publish-chart.yaml
vendored
@@ -40,12 +40,12 @@ jobs:
|
|||||||
publish-chart: ${{ steps.publish-chart-step.outputs.publish }}
|
publish-chart: ${{ steps.publish-chart-step.outputs.publish }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
uses: azure/setup-helm@v3.4
|
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
run: helm template --values charts/.ci/values-kube-score.yaml charts/* | ./kube-score score - --ignore-test pod-networkpolicy --ignore-test deployment-has-poddisruptionbudget --ignore-test deployment-has-host-podantiaffinity --ignore-test container-security-context --ignore-test pod-probes --ignore-test container-image-tag --enable-optional-test container-security-context-privileged --enable-optional-test container-security-context-readonlyrootfilesystem
|
run: helm template --values charts/.ci/values-kube-score.yaml charts/* | ./kube-score score - --ignore-test pod-networkpolicy --ignore-test deployment-has-poddisruptionbudget --ignore-test deployment-has-host-podantiaffinity --ignore-test container-security-context --ignore-test pod-probes --ignore-test container-image-tag --enable-optional-test container-security-context-privileged --enable-optional-test container-security-context-readonlyrootfilesystem
|
||||||
|
|
||||||
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get Token
|
- name: Get Token
|
||||||
id: get_workflow_token
|
id: get_workflow_token
|
||||||
uses: peter-murray/workflow-application-token-action@8e1ba3bf1619726336414f1014e37f17fbadf1db
|
uses: peter-murray/workflow-application-token-action@dc0413987a085fa17d19df9e47d4677cf81ffef3
|
||||||
with:
|
with:
|
||||||
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
||||||
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
||||||
@@ -184,7 +184,7 @@ jobs:
|
|||||||
# this workaround is intended to move the index.yaml to the target repo
|
# this workaround is intended to move the index.yaml to the target repo
|
||||||
# where the github pages are hosted
|
# where the github pages are hosted
|
||||||
- name: Checkout target repository
|
- name: Checkout target repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: ${{ env.CHART_TARGET_ORG }}/${{ env.CHART_TARGET_REPO }}
|
repository: ${{ env.CHART_TARGET_ORG }}/${{ env.CHART_TARGET_REPO }}
|
||||||
path: ${{ env.CHART_TARGET_REPO }}
|
path: ${{ env.CHART_TARGET_REPO }}
|
||||||
|
|||||||
6
.github/workflows/arc-publish.yaml
vendored
6
.github/workflows/arc-publish.yaml
vendored
@@ -39,9 +39,9 @@ jobs:
|
|||||||
if: ${{ !startsWith(github.event.inputs.release_tag_name, 'gha-runner-scale-set-') }}
|
if: ${{ !startsWith(github.event.inputs.release_tag_name, 'gha-runner-scale-set-') }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get Token
|
- name: Get Token
|
||||||
id: get_workflow_token
|
id: get_workflow_token
|
||||||
uses: peter-murray/workflow-application-token-action@8e1ba3bf1619726336414f1014e37f17fbadf1db
|
uses: peter-murray/workflow-application-token-action@dc0413987a085fa17d19df9e47d4677cf81ffef3
|
||||||
with:
|
with:
|
||||||
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
||||||
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
||||||
|
|||||||
4
.github/workflows/arc-release-runners.yaml
vendored
4
.github/workflows/arc-release-runners.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
name: Trigger Build and Push of Runner Images
|
name: Trigger Build and Push of Runner Images
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Get runner version
|
- name: Get runner version
|
||||||
id: versions
|
id: versions
|
||||||
run: |
|
run: |
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get Token
|
- name: Get Token
|
||||||
id: get_workflow_token
|
id: get_workflow_token
|
||||||
uses: peter-murray/workflow-application-token-action@8e1ba3bf1619726336414f1014e37f17fbadf1db
|
uses: peter-murray/workflow-application-token-action@dc0413987a085fa17d19df9e47d4677cf81ffef3
|
||||||
with:
|
with:
|
||||||
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
||||||
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
container_hooks_current_version: ${{ steps.container_hooks_versions.outputs.container_hooks_current_version }}
|
container_hooks_current_version: ${{ steps.container_hooks_versions.outputs.container_hooks_current_version }}
|
||||||
container_hooks_latest_version: ${{ steps.container_hooks_versions.outputs.container_hooks_latest_version }}
|
container_hooks_latest_version: ${{ steps.container_hooks_versions.outputs.container_hooks_latest_version }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get runner current and latest versions
|
- name: Get runner current and latest versions
|
||||||
id: runner_versions
|
id: runner_versions
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
echo "CONTAINER_HOOKS_CURRENT_VERSION=${{ needs.check_versions.outputs.container_hooks_current_version }}"
|
echo "CONTAINER_HOOKS_CURRENT_VERSION=${{ needs.check_versions.outputs.container_hooks_current_version }}"
|
||||||
echo "CONTAINER_HOOKS_LATEST_VERSION=${{ needs.check_versions.outputs.container_hooks_latest_version }}"
|
echo "CONTAINER_HOOKS_LATEST_VERSION=${{ needs.check_versions.outputs.container_hooks_latest_version }}"
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: PR Name
|
- name: PR Name
|
||||||
id: pr_name
|
id: pr_name
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
PR_NAME: ${{ needs.check_pr.outputs.pr_name }}
|
PR_NAME: ${{ needs.check_pr.outputs.pr_name }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: New branch
|
- name: New branch
|
||||||
run: git checkout -b update-runner-"$(date +%Y-%m-%d)"
|
run: git checkout -b update-runner-"$(date +%Y-%m-%d)"
|
||||||
|
|||||||
8
.github/workflows/arc-validate-chart.yaml
vendored
8
.github/workflows/arc-validate-chart.yaml
vendored
@@ -40,13 +40,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
# Using https://github.com/Azure/setup-helm/releases/tag/v3.5
|
# Using https://github.com/Azure/setup-helm/releases/tag/v4.2
|
||||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78
|
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
--enable-optional-test container-security-context-readonlyrootfilesystem
|
--enable-optional-test container-security-context-readonlyrootfilesystem
|
||||||
|
|
||||||
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/arc-validate-runners.yaml
vendored
4
.github/workflows/arc-validate-runners.yaml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
name: runner / shellcheck
|
name: runner / shellcheck
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: shellcheck
|
- name: shellcheck
|
||||||
uses: reviewdog/action-shellcheck@v1
|
uses: reviewdog/action-shellcheck@v1
|
||||||
with:
|
with:
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
20
.github/workflows/gha-e2e-tests.yaml
vendored
20
.github/workflows/gha-e2e-tests.yaml
vendored
@@ -16,7 +16,7 @@ env:
|
|||||||
TARGET_ORG: actions-runner-controller
|
TARGET_ORG: actions-runner-controller
|
||||||
TARGET_REPO: arc_e2e_test_dummy
|
TARGET_REPO: arc_e2e_test_dummy
|
||||||
IMAGE_NAME: "arc-test-image"
|
IMAGE_NAME: "arc-test-image"
|
||||||
IMAGE_VERSION: "0.9.0"
|
IMAGE_VERSION: "0.9.3"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
# This will make sure we only apply the concurrency limits on pull requests
|
# This will make sure we only apply the concurrency limits on pull requests
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: arc-test-dind-workflow.yaml
|
WORKFLOW_FILE: arc-test-dind-workflow.yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: "arc-test-kubernetes-workflow.yaml"
|
WORKFLOW_FILE: "arc-test-kubernetes-workflow.yaml"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -402,7 +402,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -503,7 +503,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -598,7 +598,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
WORKFLOW_FILE: "arc-test-workflow.yaml"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -718,7 +718,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: "arc-test-sleepy-matrix.yaml"
|
WORKFLOW_FILE: "arc-test-sleepy-matrix.yaml"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{github.head_ref}}
|
ref: ${{github.head_ref}}
|
||||||
|
|
||||||
@@ -888,7 +888,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WORKFLOW_FILE: arc-test-workflow.yaml
|
WORKFLOW_FILE: arc-test-workflow.yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.head_ref }}
|
ref: ${{ github.head_ref }}
|
||||||
|
|
||||||
|
|||||||
22
.github/workflows/gha-publish-chart.yaml
vendored
22
.github/workflows/gha-publish-chart.yaml
vendored
@@ -45,7 +45,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# If inputs.ref is empty, it'll resolve to the default branch
|
# If inputs.ref is empty, it'll resolve to the default branch
|
||||||
ref: ${{ inputs.ref }}
|
ref: ${{ inputs.ref }}
|
||||||
@@ -72,10 +72,10 @@ jobs:
|
|||||||
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
with:
|
with:
|
||||||
# Pinning v0.9.1 for Buildx and BuildKit v0.10.6
|
# Pinning v0.9.1 for Buildx and BuildKit v0.10.6
|
||||||
# BuildKit v0.11 which has a bug causing intermittent
|
# BuildKit v0.11 which has a bug causing intermittent
|
||||||
@@ -84,14 +84,14 @@ jobs:
|
|||||||
driver-opts: image=moby/buildkit:v0.10.6
|
driver-opts: image=moby/buildkit:v0.10.6
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build & push controller image
|
- name: Build & push controller image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
@@ -121,7 +121,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# If inputs.ref is empty, it'll resolve to the default branch
|
# If inputs.ref is empty, it'll resolve to the default branch
|
||||||
ref: ${{ inputs.ref }}
|
ref: ${{ inputs.ref }}
|
||||||
@@ -140,8 +140,8 @@ jobs:
|
|||||||
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
# Using https://github.com/Azure/setup-helm/releases/tag/v3.5
|
# Using https://github.com/Azure/setup-helm/releases/tag/v4.2
|
||||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78
|
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# If inputs.ref is empty, it'll resolve to the default branch
|
# If inputs.ref is empty, it'll resolve to the default branch
|
||||||
ref: ${{ inputs.ref }}
|
ref: ${{ inputs.ref }}
|
||||||
@@ -188,8 +188,8 @@ jobs:
|
|||||||
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
# Using https://github.com/Azure/setup-helm/releases/tag/v3.5
|
# Using https://github.com/Azure/setup-helm/releases/tag/v4.2
|
||||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78
|
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
|
|||||||
12
.github/workflows/gha-validate-chart.yaml
vendored
12
.github/workflows/gha-validate-chart.yaml
vendored
@@ -36,13 +36,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Helm
|
- name: Set up Helm
|
||||||
# Using https://github.com/Azure/setup-helm/releases/tag/v3.5
|
# Using https://github.com/Azure/setup-helm/releases/tag/v4.2
|
||||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78
|
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814
|
||||||
with:
|
with:
|
||||||
version: ${{ env.HELM_VERSION }}
|
version: ${{ env.HELM_VERSION }}
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
--enable-optional-test container-security-context-readonlyrootfilesystem
|
--enable-optional-test container-security-context-readonlyrootfilesystem
|
||||||
|
|
||||||
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
# python is a requirement for the chart-testing action below (supports yamllint among other tests)
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
|
||||||
@@ -84,13 +84,13 @@ jobs:
|
|||||||
ct lint --config charts/.ci/ct-config-gha.yaml
|
ct lint --config charts/.ci/ct-config-gha.yaml
|
||||||
|
|
||||||
- name: Set up docker buildx
|
- name: Set up docker buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
|
||||||
- name: Build controller image
|
- name: Build controller image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
if: steps.list-changed.outputs.changed == 'true'
|
if: steps.list-changed.outputs.changed == 'true'
|
||||||
with:
|
with:
|
||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
|
|||||||
14
.github/workflows/global-publish-canary.yaml
vendored
14
.github/workflows/global-publish-canary.yaml
vendored
@@ -55,11 +55,11 @@ jobs:
|
|||||||
TARGET_REPO: actions-runner-controller
|
TARGET_REPO: actions-runner-controller
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Get Token
|
- name: Get Token
|
||||||
id: get_workflow_token
|
id: get_workflow_token
|
||||||
uses: peter-murray/workflow-application-token-action@8e1ba3bf1619726336414f1014e37f17fbadf1db
|
uses: peter-murray/workflow-application-token-action@dc0413987a085fa17d19df9e47d4677cf81ffef3
|
||||||
with:
|
with:
|
||||||
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
application_id: ${{ secrets.ACTIONS_ACCESS_APP_ID }}
|
||||||
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
application_private_key: ${{ secrets.ACTIONS_ACCESS_PK }}
|
||||||
@@ -90,10 +90,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -110,16 +110,16 @@ jobs:
|
|||||||
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
echo "repository_owner=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
|
|
||||||
# Unstable builds - run at your own risk
|
# Unstable builds - run at your own risk
|
||||||
- name: Build and Push
|
- name: Build and Push
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
|
|||||||
4
.github/workflows/global-run-codeql.yaml
vendored
4
.github/workflows/global-run-codeql.yaml
vendored
@@ -25,10 +25,10 @@ jobs:
|
|||||||
security-events: write
|
security-events: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
check_for_first_interaction:
|
check_for_first_interaction:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/first-interaction@main
|
- uses: actions/first-interaction@main
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
18
.github/workflows/go.yaml
vendored
18
.github/workflows/go.yaml
vendored
@@ -29,8 +29,8 @@ jobs:
|
|||||||
fmt:
|
fmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
cache: false
|
cache: false
|
||||||
@@ -42,13 +42,13 @@ jobs:
|
|||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
cache: false
|
cache: false
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v6
|
||||||
with:
|
with:
|
||||||
only-new-issues: true
|
only-new-issues: true
|
||||||
version: v1.55.2
|
version: v1.55.2
|
||||||
@@ -56,8 +56,8 @@ jobs:
|
|||||||
generate:
|
generate:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
cache: false
|
cache: false
|
||||||
@@ -69,8 +69,8 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
- run: make manifests
|
- run: make manifests
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
run:
|
run:
|
||||||
timeout: 3m
|
timeout: 3m
|
||||||
output:
|
output:
|
||||||
format: github-actions
|
formats:
|
||||||
|
- format: github-actions
|
||||||
|
path: stdout
|
||||||
linters-settings:
|
linters-settings:
|
||||||
errcheck:
|
errcheck:
|
||||||
exclude-functions:
|
exclude-functions:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build the manager binary
|
# Build the manager binary
|
||||||
FROM --platform=$BUILDPLATFORM golang:1.22.1 as builder
|
FROM --platform=$BUILDPLATFORM golang:1.22.4 as builder
|
||||||
|
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -6,7 +6,7 @@ endif
|
|||||||
DOCKER_USER ?= $(shell echo ${DOCKER_IMAGE_NAME} | cut -d / -f1)
|
DOCKER_USER ?= $(shell echo ${DOCKER_IMAGE_NAME} | cut -d / -f1)
|
||||||
VERSION ?= dev
|
VERSION ?= dev
|
||||||
COMMIT_SHA = $(shell git rev-parse HEAD)
|
COMMIT_SHA = $(shell git rev-parse HEAD)
|
||||||
RUNNER_VERSION ?= 2.314.1
|
RUNNER_VERSION ?= 2.319.1
|
||||||
TARGETPLATFORM ?= $(shell arch)
|
TARGETPLATFORM ?= $(shell arch)
|
||||||
RUNNER_NAME ?= ${DOCKER_USER}/actions-runner
|
RUNNER_NAME ?= ${DOCKER_USER}/actions-runner
|
||||||
RUNNER_TAG ?= ${VERSION}
|
RUNNER_TAG ?= ${VERSION}
|
||||||
@@ -68,7 +68,7 @@ endif
|
|||||||
all: manager
|
all: manager
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint:v1.55.2 golangci-lint run
|
docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint:v1.57.2 golangci-lint run
|
||||||
|
|
||||||
GO_TEST_ARGS ?= -short
|
GO_TEST_ARGS ?= -short
|
||||||
|
|
||||||
@@ -310,7 +310,7 @@ github-release: release
|
|||||||
# Otherwise we get errors like the below:
|
# Otherwise we get errors like the below:
|
||||||
# Error: failed to install CRD crds/actions.summerwind.dev_runnersets.yaml: CustomResourceDefinition.apiextensions.k8s.io "runnersets.actions.summerwind.dev" is invalid: [spec.validation.openAPIV3Schema.properties[spec].properties[template].properties[spec].properties[containers].items.properties[ports].items.properties[protocol].default: Required value: this property is in x-kubernetes-list-map-keys, so it must have a default or be a required property, spec.validation.openAPIV3Schema.properties[spec].properties[template].properties[spec].properties[initContainers].items.properties[ports].items.properties[protocol].default: Required value: this property is in x-kubernetes-list-map-keys, so it must have a default or be a required property]
|
# Error: failed to install CRD crds/actions.summerwind.dev_runnersets.yaml: CustomResourceDefinition.apiextensions.k8s.io "runnersets.actions.summerwind.dev" is invalid: [spec.validation.openAPIV3Schema.properties[spec].properties[template].properties[spec].properties[containers].items.properties[ports].items.properties[protocol].default: Required value: this property is in x-kubernetes-list-map-keys, so it must have a default or be a required property, spec.validation.openAPIV3Schema.properties[spec].properties[template].properties[spec].properties[initContainers].items.properties[ports].items.properties[protocol].default: Required value: this property is in x-kubernetes-list-map-keys, so it must have a default or be a required property]
|
||||||
#
|
#
|
||||||
# Note that controller-gen newer than 0.6.0 is needed due to https://github.com/kubernetes-sigs/controller-tools/issues/448
|
# Note that controller-gen newer than 0.6.1 is needed due to https://github.com/kubernetes-sigs/controller-tools/issues/448
|
||||||
# Otherwise ObjectMeta embedded in Spec results in empty on the storage.
|
# Otherwise ObjectMeta embedded in Spec results in empty on the storage.
|
||||||
controller-gen:
|
controller-gen:
|
||||||
ifeq (, $(shell which controller-gen))
|
ifeq (, $(shell which controller-gen))
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.9.0
|
version: 0.9.3
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
appVersion: "0.9.0"
|
appVersion: "0.9.3"
|
||||||
|
|
||||||
home: https://github.com/actions/actions-runner-controller
|
home: https://github.com/actions/actions-runner-controller
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,3 @@ Create the name of the service account to use
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
{{- $names | join ","}}
|
{{- $names | join ","}}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- define "gha-runner-scale-set-controller.serviceMonitorName" -}}
|
|
||||||
{{- include "gha-runner-scale-set-controller.fullname" . }}-service-monitor
|
|
||||||
{{- end }}
|
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ spec:
|
|||||||
- "--listener-metrics-endpoint="
|
- "--listener-metrics-endpoint="
|
||||||
- "--metrics-addr=0"
|
- "--metrics-addr=0"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- range .Values.flags.excludeLabelPropagationPrefixes }}
|
||||||
|
- "--exclude-label-propagation-prefix={{ . }}"
|
||||||
|
{{- end }}
|
||||||
command:
|
command:
|
||||||
- "/manager"
|
- "/manager"
|
||||||
{{- with .Values.metrics }}
|
{{- with .Values.metrics }}
|
||||||
@@ -128,6 +131,10 @@ spec:
|
|||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- with .Values.topologySpreadConstraints }}
|
||||||
|
topologySpreadConstraints:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
|
|||||||
@@ -345,6 +345,7 @@ func TestTemplate_ControllerDeployment_Defaults(t *testing.T) {
|
|||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 0)
|
assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 0)
|
||||||
assert.Nil(t, deployment.Spec.Template.Spec.Affinity)
|
assert.Nil(t, deployment.Spec.Template.Spec.Affinity)
|
||||||
|
assert.Len(t, deployment.Spec.Template.Spec.TopologySpreadConstraints, 0)
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0)
|
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0)
|
||||||
|
|
||||||
managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev"
|
managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev"
|
||||||
@@ -424,6 +425,9 @@ func TestTemplate_ControllerDeployment_Customize(t *testing.T) {
|
|||||||
"tolerations[0].key": "foo",
|
"tolerations[0].key": "foo",
|
||||||
"affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key": "foo",
|
"affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key": "foo",
|
||||||
"affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator": "bar",
|
"affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator": "bar",
|
||||||
|
"topologySpreadConstraints[0].labelSelector.matchLabels.foo": "bar",
|
||||||
|
"topologySpreadConstraints[0].maxSkew": "1",
|
||||||
|
"topologySpreadConstraints[0].topologyKey": "foo",
|
||||||
"priorityClassName": "test-priority-class",
|
"priorityClassName": "test-priority-class",
|
||||||
"flags.updateStrategy": "eventual",
|
"flags.updateStrategy": "eventual",
|
||||||
"flags.logLevel": "info",
|
"flags.logLevel": "info",
|
||||||
@@ -487,6 +491,11 @@ func TestTemplate_ControllerDeployment_Customize(t *testing.T) {
|
|||||||
assert.Equal(t, "foo", deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key)
|
assert.Equal(t, "foo", deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key)
|
||||||
assert.Equal(t, "bar", string(deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Operator))
|
assert.Equal(t, "bar", string(deployment.Spec.Template.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Operator))
|
||||||
|
|
||||||
|
assert.Len(t, deployment.Spec.Template.Spec.TopologySpreadConstraints, 1)
|
||||||
|
assert.Equal(t, "bar", deployment.Spec.Template.Spec.TopologySpreadConstraints[0].LabelSelector.MatchLabels["foo"])
|
||||||
|
assert.Equal(t, int32(1), deployment.Spec.Template.Spec.TopologySpreadConstraints[0].MaxSkew)
|
||||||
|
assert.Equal(t, "foo", deployment.Spec.Template.Spec.TopologySpreadConstraints[0].TopologyKey)
|
||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 1)
|
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 1)
|
||||||
assert.Equal(t, "foo", deployment.Spec.Template.Spec.Tolerations[0].Key)
|
assert.Equal(t, "foo", deployment.Spec.Template.Spec.Tolerations[0].Key)
|
||||||
|
|
||||||
@@ -745,6 +754,7 @@ func TestTemplate_ControllerDeployment_WatchSingleNamespace(t *testing.T) {
|
|||||||
|
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 0)
|
assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 0)
|
||||||
assert.Nil(t, deployment.Spec.Template.Spec.Affinity)
|
assert.Nil(t, deployment.Spec.Template.Spec.Affinity)
|
||||||
|
assert.Len(t, deployment.Spec.Template.Spec.TopologySpreadConstraints, 0)
|
||||||
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0)
|
assert.Len(t, deployment.Spec.Template.Spec.Tolerations, 0)
|
||||||
|
|
||||||
managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev"
|
managerImage := "ghcr.io/actions/gha-runner-scale-set-controller:dev"
|
||||||
@@ -1025,3 +1035,41 @@ func TestControllerDeployment_MetricsPorts(t *testing.T) {
|
|||||||
assert.Equal(t, value.frequency, 1, fmt.Sprintf("frequency of %q is not 1", key))
|
assert.Equal(t, value.frequency, 1, fmt.Sprintf("frequency of %q is not 1", key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeployment_excludeLabelPropagationPrefixes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Path to the helm chart we will test
|
||||||
|
helmChartPath, err := filepath.Abs("../../gha-runner-scale-set-controller")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
chartContent, err := os.ReadFile(filepath.Join(helmChartPath, "Chart.yaml"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
chart := new(Chart)
|
||||||
|
err = yaml.Unmarshal(chartContent, chart)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
releaseName := "test-arc"
|
||||||
|
namespaceName := "test-" + strings.ToLower(random.UniqueId())
|
||||||
|
|
||||||
|
options := &helm.Options{
|
||||||
|
Logger: logger.Discard,
|
||||||
|
SetValues: map[string]string{
|
||||||
|
"flags.excludeLabelPropagationPrefixes[0]": "prefix.com/",
|
||||||
|
"flags.excludeLabelPropagationPrefixes[1]": "complete.io/label",
|
||||||
|
},
|
||||||
|
KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName),
|
||||||
|
}
|
||||||
|
|
||||||
|
output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/deployment.yaml"})
|
||||||
|
|
||||||
|
var deployment appsv1.Deployment
|
||||||
|
helm.UnmarshalK8SYaml(t, output, &deployment)
|
||||||
|
|
||||||
|
require.Len(t, deployment.Spec.Template.Spec.Containers, 1, "Expected one container")
|
||||||
|
container := deployment.Spec.Template.Spec.Containers[0]
|
||||||
|
|
||||||
|
assert.Contains(t, container.Args, "--exclude-label-propagation-prefix=prefix.com/")
|
||||||
|
assert.Contains(t, container.Args, "--exclude-label-propagation-prefix=complete.io/label")
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ tolerations: []
|
|||||||
|
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|
||||||
|
topologySpreadConstraints: []
|
||||||
|
|
||||||
# Mount volumes in the container.
|
# Mount volumes in the container.
|
||||||
volumes: []
|
volumes: []
|
||||||
volumeMounts: []
|
volumeMounts: []
|
||||||
@@ -81,9 +83,9 @@ volumeMounts: []
|
|||||||
# PriorityClass: system-cluster-critical
|
# PriorityClass: system-cluster-critical
|
||||||
priorityClassName: ""
|
priorityClassName: ""
|
||||||
|
|
||||||
## If `metrics:` object is not provided, or commented out, the following flags
|
## If `metrics:` object is not provided, or commented out, the following flags
|
||||||
## will be applied the controller-manager and listener pods with empty values:
|
## will be applied the controller-manager and listener pods with empty values:
|
||||||
## `--metrics-addr`, `--listener-metrics-addr`, `--listener-metrics-endpoint`.
|
## `--metrics-addr`, `--listener-metrics-addr`, `--listener-metrics-endpoint`.
|
||||||
## This will disable metrics.
|
## This will disable metrics.
|
||||||
##
|
##
|
||||||
## To enable metrics, uncomment the following lines.
|
## To enable metrics, uncomment the following lines.
|
||||||
@@ -119,3 +121,12 @@ flags:
|
|||||||
## This can lead to a longer time to apply the change but it will ensure
|
## This can lead to a longer time to apply the change but it will ensure
|
||||||
## that you don't have any overprovisioning of runners.
|
## that you don't have any overprovisioning of runners.
|
||||||
updateStrategy: "immediate"
|
updateStrategy: "immediate"
|
||||||
|
|
||||||
|
## Defines a list of prefixes that should not be propagated to internal resources.
|
||||||
|
## This is useful when you have labels that are used for internal purposes and should not be propagated to internal resources.
|
||||||
|
## See https://github.com/actions/actions-runner-controller/issues/3533 for more information.
|
||||||
|
##
|
||||||
|
## By default, all labels are propagated to internal resources
|
||||||
|
## Labels that match prefix specified in the list are excluded from propagation.
|
||||||
|
# excludeLabelPropagationPrefixes:
|
||||||
|
# - "argocd.argoproj.io/instance"
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.9.0
|
version: 0.9.3
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
appVersion: "0.9.0"
|
appVersion: "0.9.3"
|
||||||
|
|
||||||
home: https://github.com/actions/actions-runner-controller
|
home: https://github.com/actions/actions-runner-controller
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ githubConfigSecret:
|
|||||||
# kubernetesModeServiceAccount:
|
# kubernetesModeServiceAccount:
|
||||||
# annotations:
|
# annotations:
|
||||||
|
|
||||||
## template is the PodSpec for each listener Pod
|
## listenerTemplate is the PodSpec for each listener Pod
|
||||||
## For reference: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec
|
## For reference: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec
|
||||||
# listenerTemplate:
|
# listenerTemplate:
|
||||||
# spec:
|
# spec:
|
||||||
|
|||||||
@@ -117,15 +117,19 @@ func (app *App) Run(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g, ctx := errgroup.WithContext(ctx)
|
g, ctx := errgroup.WithContext(ctx)
|
||||||
|
metricsCtx, cancelMetrics := context.WithCancelCause(ctx)
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
app.logger.Info("Starting listener")
|
app.logger.Info("Starting listener")
|
||||||
return app.listener.Listen(ctx, app.worker)
|
listnerErr := app.listener.Listen(ctx, app.worker)
|
||||||
|
cancelMetrics(fmt.Errorf("Listener exited: %w", listnerErr))
|
||||||
|
return listnerErr
|
||||||
})
|
})
|
||||||
|
|
||||||
if app.metrics != nil {
|
if app.metrics != nil {
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
app.logger.Info("Starting metrics server")
|
app.logger.Info("Starting metrics server")
|
||||||
return app.metrics.ListenAndServe(ctx)
|
return app.metrics.ListenAndServe(metricsCtx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const (
|
|||||||
type Client interface {
|
type Client interface {
|
||||||
GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*actions.AcquirableJobList, error)
|
GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*actions.AcquirableJobList, error)
|
||||||
CreateMessageSession(ctx context.Context, runnerScaleSetId int, owner string) (*actions.RunnerScaleSetSession, error)
|
CreateMessageSession(ctx context.Context, runnerScaleSetId int, owner string) (*actions.RunnerScaleSetSession, error)
|
||||||
GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64) (*actions.RunnerScaleSetMessage, error)
|
GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64, maxCapacity int) (*actions.RunnerScaleSetMessage, error)
|
||||||
DeleteMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, messageId int64) error
|
DeleteMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, messageId int64) error
|
||||||
AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error)
|
AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error)
|
||||||
RefreshMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) (*actions.RunnerScaleSetSession, error)
|
RefreshMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) (*actions.RunnerScaleSetSession, error)
|
||||||
@@ -80,6 +80,7 @@ type Listener struct {
|
|||||||
|
|
||||||
// updated fields
|
// updated fields
|
||||||
lastMessageID int64 // The ID of the last processed message.
|
lastMessageID int64 // The ID of the last processed message.
|
||||||
|
maxCapacity int // The maximum number of runners that can be created.
|
||||||
session *actions.RunnerScaleSetSession // The session for managing the runner scale set.
|
session *actions.RunnerScaleSetSession // The session for managing the runner scale set.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,10 +90,11 @@ func New(config Config) (*Listener, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
listener := &Listener{
|
listener := &Listener{
|
||||||
scaleSetID: config.ScaleSetID,
|
scaleSetID: config.ScaleSetID,
|
||||||
client: config.Client,
|
client: config.Client,
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
metrics: metrics.Discard,
|
metrics: metrics.Discard,
|
||||||
|
maxCapacity: config.MaxRunners,
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Metrics != nil {
|
if config.Metrics != nil {
|
||||||
@@ -164,11 +166,16 @@ func (l *Listener) Listen(ctx context.Context, handler Handler) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if msg == nil {
|
if msg == nil {
|
||||||
|
_, err := handler.HandleDesiredRunnerCount(ctx, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("handling nil message failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// New context is created to avoid cancelation during message handling.
|
// Remove cancellation from the context to avoid cancelling the message handling.
|
||||||
if err := l.handleMessage(context.Background(), handler, msg); err != nil {
|
if err := l.handleMessage(context.WithoutCancel(ctx), handler, msg); err != nil {
|
||||||
return fmt.Errorf("failed to handle message: %w", err)
|
return fmt.Errorf("failed to handle message: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +269,7 @@ func (l *Listener) createSession(ctx context.Context) error {
|
|||||||
|
|
||||||
func (l *Listener) getMessage(ctx context.Context) (*actions.RunnerScaleSetMessage, error) {
|
func (l *Listener) getMessage(ctx context.Context) (*actions.RunnerScaleSetMessage, error) {
|
||||||
l.logger.Info("Getting next message", "lastMessageID", l.lastMessageID)
|
l.logger.Info("Getting next message", "lastMessageID", l.lastMessageID)
|
||||||
msg, err := l.client.GetMessage(ctx, l.session.MessageQueueUrl, l.session.MessageQueueAccessToken, l.lastMessageID)
|
msg, err := l.client.GetMessage(ctx, l.session.MessageQueueUrl, l.session.MessageQueueAccessToken, l.lastMessageID, l.maxCapacity)
|
||||||
if err == nil { // if NO error
|
if err == nil { // if NO error
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
@@ -278,7 +285,7 @@ func (l *Listener) getMessage(ctx context.Context) (*actions.RunnerScaleSetMessa
|
|||||||
|
|
||||||
l.logger.Info("Getting next message", "lastMessageID", l.lastMessageID)
|
l.logger.Info("Getting next message", "lastMessageID", l.lastMessageID)
|
||||||
|
|
||||||
msg, err = l.client.GetMessage(ctx, l.session.MessageQueueUrl, l.session.MessageQueueAccessToken, l.lastMessageID)
|
msg, err = l.client.GetMessage(ctx, l.session.MessageQueueUrl, l.session.MessageQueueAccessToken, l.lastMessageID, l.maxCapacity)
|
||||||
if err != nil { // if NO error
|
if err != nil { // if NO error
|
||||||
return nil, fmt.Errorf("failed to get next message after message session refresh: %w", err)
|
return nil, fmt.Errorf("failed to get next message after message session refresh: %w", err)
|
||||||
}
|
}
|
||||||
@@ -288,8 +295,23 @@ func (l *Listener) getMessage(ctx context.Context) (*actions.RunnerScaleSetMessa
|
|||||||
|
|
||||||
func (l *Listener) deleteLastMessage(ctx context.Context) error {
|
func (l *Listener) deleteLastMessage(ctx context.Context) error {
|
||||||
l.logger.Info("Deleting last message", "lastMessageID", l.lastMessageID)
|
l.logger.Info("Deleting last message", "lastMessageID", l.lastMessageID)
|
||||||
if err := l.client.DeleteMessage(ctx, l.session.MessageQueueUrl, l.session.MessageQueueAccessToken, l.lastMessageID); err != nil {
|
err := l.client.DeleteMessage(ctx, l.session.MessageQueueUrl, l.session.MessageQueueAccessToken, l.lastMessageID)
|
||||||
return fmt.Errorf("failed to delete message: %w", err)
|
if err == nil { // if NO error
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
expiredError := &actions.MessageQueueTokenExpiredError{}
|
||||||
|
if !errors.As(err, &expiredError) {
|
||||||
|
return fmt.Errorf("failed to delete last message: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.refreshSession(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l.client.DeleteMessage(ctx, l.session.MessageQueueUrl, l.session.MessageQueueAccessToken, l.lastMessageID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete last message after message session refresh: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -123,13 +123,14 @@ func TestListener_getMessage(t *testing.T) {
|
|||||||
config := Config{
|
config := Config{
|
||||||
ScaleSetID: 1,
|
ScaleSetID: 1,
|
||||||
Metrics: metrics.Discard,
|
Metrics: metrics.Discard,
|
||||||
|
MaxRunners: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
client := listenermocks.NewClient(t)
|
client := listenermocks.NewClient(t)
|
||||||
want := &actions.RunnerScaleSetMessage{
|
want := &actions.RunnerScaleSetMessage{
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
}
|
}
|
||||||
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything).Return(want, nil).Once()
|
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything, 10).Return(want, nil).Once()
|
||||||
config.Client = client
|
config.Client = client
|
||||||
|
|
||||||
l, err := New(config)
|
l, err := New(config)
|
||||||
@@ -148,10 +149,11 @@ func TestListener_getMessage(t *testing.T) {
|
|||||||
config := Config{
|
config := Config{
|
||||||
ScaleSetID: 1,
|
ScaleSetID: 1,
|
||||||
Metrics: metrics.Discard,
|
Metrics: metrics.Discard,
|
||||||
|
MaxRunners: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
client := listenermocks.NewClient(t)
|
client := listenermocks.NewClient(t)
|
||||||
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, &actions.HttpClientSideError{Code: http.StatusNotFound}).Once()
|
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything, 10).Return(nil, &actions.HttpClientSideError{Code: http.StatusNotFound}).Once()
|
||||||
config.Client = client
|
config.Client = client
|
||||||
|
|
||||||
l, err := New(config)
|
l, err := New(config)
|
||||||
@@ -170,6 +172,7 @@ func TestListener_getMessage(t *testing.T) {
|
|||||||
config := Config{
|
config := Config{
|
||||||
ScaleSetID: 1,
|
ScaleSetID: 1,
|
||||||
Metrics: metrics.Discard,
|
Metrics: metrics.Discard,
|
||||||
|
MaxRunners: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
client := listenermocks.NewClient(t)
|
client := listenermocks.NewClient(t)
|
||||||
@@ -185,12 +188,12 @@ func TestListener_getMessage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
client.On("RefreshMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once()
|
client.On("RefreshMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once()
|
||||||
|
|
||||||
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, &actions.MessageQueueTokenExpiredError{}).Once()
|
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything, 10).Return(nil, &actions.MessageQueueTokenExpiredError{}).Once()
|
||||||
|
|
||||||
want := &actions.RunnerScaleSetMessage{
|
want := &actions.RunnerScaleSetMessage{
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
}
|
}
|
||||||
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything).Return(want, nil).Once()
|
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything, 10).Return(want, nil).Once()
|
||||||
|
|
||||||
config.Client = client
|
config.Client = client
|
||||||
|
|
||||||
@@ -214,6 +217,7 @@ func TestListener_getMessage(t *testing.T) {
|
|||||||
config := Config{
|
config := Config{
|
||||||
ScaleSetID: 1,
|
ScaleSetID: 1,
|
||||||
Metrics: metrics.Discard,
|
Metrics: metrics.Discard,
|
||||||
|
MaxRunners: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
client := listenermocks.NewClient(t)
|
client := listenermocks.NewClient(t)
|
||||||
@@ -229,7 +233,7 @@ func TestListener_getMessage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
client.On("RefreshMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once()
|
client.On("RefreshMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once()
|
||||||
|
|
||||||
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, &actions.MessageQueueTokenExpiredError{}).Twice()
|
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything, 10).Return(nil, &actions.MessageQueueTokenExpiredError{}).Twice()
|
||||||
|
|
||||||
config.Client = client
|
config.Client = client
|
||||||
|
|
||||||
@@ -373,6 +377,93 @@ func TestListener_deleteLastMessage(t *testing.T) {
|
|||||||
err = l.deleteLastMessage(ctx)
|
err = l.deleteLastMessage(ctx)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("RefreshAndSucceeds", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
config := Config{
|
||||||
|
ScaleSetID: 1,
|
||||||
|
Metrics: metrics.Discard,
|
||||||
|
}
|
||||||
|
|
||||||
|
client := listenermocks.NewClient(t)
|
||||||
|
|
||||||
|
newUUID := uuid.New()
|
||||||
|
session := &actions.RunnerScaleSetSession{
|
||||||
|
SessionId: &newUUID,
|
||||||
|
OwnerName: "example",
|
||||||
|
RunnerScaleSet: &actions.RunnerScaleSet{},
|
||||||
|
MessageQueueUrl: "https://example.com",
|
||||||
|
MessageQueueAccessToken: "1234567890",
|
||||||
|
Statistics: nil,
|
||||||
|
}
|
||||||
|
client.On("RefreshMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once()
|
||||||
|
|
||||||
|
client.On("DeleteMessage", ctx, mock.Anything, mock.Anything, mock.Anything).Return(&actions.MessageQueueTokenExpiredError{}).Once()
|
||||||
|
|
||||||
|
client.On("DeleteMessage", ctx, mock.Anything, mock.Anything, mock.MatchedBy(func(lastMessageID any) bool {
|
||||||
|
return lastMessageID.(int64) == int64(5)
|
||||||
|
})).Return(nil).Once()
|
||||||
|
config.Client = client
|
||||||
|
|
||||||
|
l, err := New(config)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
oldUUID := uuid.New()
|
||||||
|
l.session = &actions.RunnerScaleSetSession{
|
||||||
|
SessionId: &oldUUID,
|
||||||
|
RunnerScaleSet: &actions.RunnerScaleSet{},
|
||||||
|
}
|
||||||
|
l.lastMessageID = 5
|
||||||
|
|
||||||
|
config.Client = client
|
||||||
|
|
||||||
|
err = l.deleteLastMessage(ctx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("RefreshAndFails", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
config := Config{
|
||||||
|
ScaleSetID: 1,
|
||||||
|
Metrics: metrics.Discard,
|
||||||
|
}
|
||||||
|
|
||||||
|
client := listenermocks.NewClient(t)
|
||||||
|
|
||||||
|
newUUID := uuid.New()
|
||||||
|
session := &actions.RunnerScaleSetSession{
|
||||||
|
SessionId: &newUUID,
|
||||||
|
OwnerName: "example",
|
||||||
|
RunnerScaleSet: &actions.RunnerScaleSet{},
|
||||||
|
MessageQueueUrl: "https://example.com",
|
||||||
|
MessageQueueAccessToken: "1234567890",
|
||||||
|
Statistics: nil,
|
||||||
|
}
|
||||||
|
client.On("RefreshMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once()
|
||||||
|
|
||||||
|
client.On("DeleteMessage", ctx, mock.Anything, mock.Anything, mock.Anything).Return(&actions.MessageQueueTokenExpiredError{}).Twice()
|
||||||
|
|
||||||
|
config.Client = client
|
||||||
|
|
||||||
|
l, err := New(config)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
oldUUID := uuid.New()
|
||||||
|
l.session = &actions.RunnerScaleSetSession{
|
||||||
|
SessionId: &oldUUID,
|
||||||
|
RunnerScaleSet: &actions.RunnerScaleSet{},
|
||||||
|
}
|
||||||
|
l.lastMessageID = 5
|
||||||
|
|
||||||
|
config.Client = client
|
||||||
|
|
||||||
|
err = l.deleteLastMessage(ctx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListener_Listen(t *testing.T) {
|
func TestListener_Listen(t *testing.T) {
|
||||||
@@ -450,6 +541,7 @@ func TestListener_Listen(t *testing.T) {
|
|||||||
config := Config{
|
config := Config{
|
||||||
ScaleSetID: 1,
|
ScaleSetID: 1,
|
||||||
Metrics: metrics.Discard,
|
Metrics: metrics.Discard,
|
||||||
|
MaxRunners: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
client := listenermocks.NewClient(t)
|
client := listenermocks.NewClient(t)
|
||||||
@@ -470,7 +562,7 @@ func TestListener_Listen(t *testing.T) {
|
|||||||
MessageType: "RunnerScaleSetJobMessages",
|
MessageType: "RunnerScaleSetJobMessages",
|
||||||
Statistics: &actions.RunnerScaleSetStatistic{},
|
Statistics: &actions.RunnerScaleSetStatistic{},
|
||||||
}
|
}
|
||||||
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything).
|
client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything, 10).
|
||||||
Return(msg, nil).
|
Return(msg, nil).
|
||||||
Run(
|
Run(
|
||||||
func(mock.Arguments) {
|
func(mock.Arguments) {
|
||||||
@@ -479,8 +571,8 @@ func TestListener_Listen(t *testing.T) {
|
|||||||
).
|
).
|
||||||
Once()
|
Once()
|
||||||
|
|
||||||
// Ensure delete message is called with background context
|
// Ensure delete message is called without cancel
|
||||||
client.On("DeleteMessage", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
client.On("DeleteMessage", context.WithoutCancel(ctx), mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
|
||||||
config.Client = client
|
config.Client = client
|
||||||
|
|
||||||
|
|||||||
@@ -123,25 +123,25 @@ func (_m *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMessage provides a mock function with given fields: ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId
|
// GetMessage provides a mock function with given fields: ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity
|
||||||
func (_m *Client) GetMessage(ctx context.Context, messageQueueUrl string, messageQueueAccessToken string, lastMessageId int64) (*actions.RunnerScaleSetMessage, error) {
|
func (_m *Client) GetMessage(ctx context.Context, messageQueueUrl string, messageQueueAccessToken string, lastMessageId int64, maxCapacity int) (*actions.RunnerScaleSetMessage, error) {
|
||||||
ret := _m.Called(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
ret := _m.Called(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
|
|
||||||
var r0 *actions.RunnerScaleSetMessage
|
var r0 *actions.RunnerScaleSetMessage
|
||||||
var r1 error
|
var r1 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*actions.RunnerScaleSetMessage, error)); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64, int) (*actions.RunnerScaleSetMessage, error)); ok {
|
||||||
return rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
return rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
}
|
}
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *actions.RunnerScaleSetMessage); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64, int) *actions.RunnerScaleSetMessage); ok {
|
||||||
r0 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
r0 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(*actions.RunnerScaleSetMessage)
|
r0 = ret.Get(0).(*actions.RunnerScaleSetMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok {
|
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64, int) error); ok {
|
||||||
r1 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
r1 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Error(1)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
@@ -338,7 +339,9 @@ func (e *exporter) ListenAndServe(ctx context.Context) error {
|
|||||||
e.logger.Info("starting metrics server", "addr", e.srv.Addr)
|
e.logger.Info("starting metrics server", "addr", e.srv.Addr)
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
e.logger.Info("stopping metrics server")
|
e.logger.Info("stopping metrics server", "err", ctx.Err())
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
e.srv.Shutdown(ctx)
|
e.srv.Shutdown(ctx)
|
||||||
}()
|
}()
|
||||||
return e.srv.ListenAndServe()
|
return e.srv.ListenAndServe()
|
||||||
|
|||||||
@@ -38,20 +38,20 @@ type Config struct {
|
|||||||
// The Worker's role is to process the messages it receives from the listener.
|
// The Worker's role is to process the messages it receives from the listener.
|
||||||
// It then initiates Kubernetes API requests to carry out the necessary actions.
|
// It then initiates Kubernetes API requests to carry out the necessary actions.
|
||||||
type Worker struct {
|
type Worker struct {
|
||||||
clientset *kubernetes.Clientset
|
clientset *kubernetes.Clientset
|
||||||
config Config
|
config Config
|
||||||
lastPatch int
|
lastPatch int
|
||||||
lastPatchID int
|
patchSeq int
|
||||||
logger *logr.Logger
|
logger *logr.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ listener.Handler = (*Worker)(nil)
|
var _ listener.Handler = (*Worker)(nil)
|
||||||
|
|
||||||
func New(config Config, options ...Option) (*Worker, error) {
|
func New(config Config, options ...Option) (*Worker, error) {
|
||||||
w := &Worker{
|
w := &Worker{
|
||||||
config: config,
|
config: config,
|
||||||
lastPatch: -1,
|
lastPatch: -1,
|
||||||
lastPatchID: -1,
|
patchSeq: -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := rest.InClusterConfig()
|
conf, err := rest.InClusterConfig()
|
||||||
@@ -163,27 +163,8 @@ func (w *Worker) HandleJobStarted(ctx context.Context, jobInfo *actions.JobStart
|
|||||||
// The function then scales the ephemeral runner set by applying the merge patch.
|
// The function then scales the ephemeral runner set by applying the merge patch.
|
||||||
// Finally, it logs the scaled ephemeral runner set details and returns nil if successful.
|
// Finally, it logs the scaled ephemeral runner set details and returns nil if successful.
|
||||||
// If any error occurs during the process, it returns an error with a descriptive message.
|
// If any error occurs during the process, it returns an error with a descriptive message.
|
||||||
func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int, jobsCompleted int) (int, error) {
|
func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count, jobsCompleted int) (int, error) {
|
||||||
// Max runners should always be set by the resource builder either to the configured value,
|
patchID := w.setDesiredWorkerState(count, jobsCompleted)
|
||||||
// or the maximum int32 (resourcebuilder.newAutoScalingListener()).
|
|
||||||
targetRunnerCount := min(w.config.MinRunners+count, w.config.MaxRunners)
|
|
||||||
|
|
||||||
logValues := []any{
|
|
||||||
"assigned job", count,
|
|
||||||
"decision", targetRunnerCount,
|
|
||||||
"min", w.config.MinRunners,
|
|
||||||
"max", w.config.MaxRunners,
|
|
||||||
"currentRunnerCount", w.lastPatch,
|
|
||||||
"jobsCompleted", jobsCompleted,
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.lastPatch == targetRunnerCount && jobsCompleted == 0 {
|
|
||||||
w.logger.Info("Skipping patch", logValues...)
|
|
||||||
return targetRunnerCount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
w.lastPatchID++
|
|
||||||
w.lastPatch = targetRunnerCount
|
|
||||||
|
|
||||||
original, err := json.Marshal(
|
original, err := json.Marshal(
|
||||||
&v1alpha1.EphemeralRunnerSet{
|
&v1alpha1.EphemeralRunnerSet{
|
||||||
@@ -200,8 +181,8 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int, jobsCo
|
|||||||
patch, err := json.Marshal(
|
patch, err := json.Marshal(
|
||||||
&v1alpha1.EphemeralRunnerSet{
|
&v1alpha1.EphemeralRunnerSet{
|
||||||
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
Spec: v1alpha1.EphemeralRunnerSetSpec{
|
||||||
Replicas: targetRunnerCount,
|
Replicas: w.lastPatch,
|
||||||
PatchID: w.lastPatchID,
|
PatchID: patchID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -210,14 +191,13 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int, jobsCo
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.logger.Info("Compare", "original", string(original), "patch", string(patch))
|
||||||
mergePatch, err := jsonpatch.CreateMergePatch(original, patch)
|
mergePatch, err := jsonpatch.CreateMergePatch(original, patch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("failed to create merge patch json for ephemeral runner set: %w", err)
|
return 0, fmt.Errorf("failed to create merge patch json for ephemeral runner set: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.logger.Info("Created merge patch json for EphemeralRunnerSet update", "json", string(mergePatch))
|
w.logger.Info("Preparing EphemeralRunnerSet update", "json", string(mergePatch))
|
||||||
|
|
||||||
w.logger.Info("Scaling ephemeral runner set", logValues...)
|
|
||||||
|
|
||||||
patchedEphemeralRunnerSet := &v1alpha1.EphemeralRunnerSet{}
|
patchedEphemeralRunnerSet := &v1alpha1.EphemeralRunnerSet{}
|
||||||
err = w.clientset.RESTClient().
|
err = w.clientset.RESTClient().
|
||||||
@@ -238,5 +218,40 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int, jobsCo
|
|||||||
"name", w.config.EphemeralRunnerSetName,
|
"name", w.config.EphemeralRunnerSetName,
|
||||||
"replicas", patchedEphemeralRunnerSet.Spec.Replicas,
|
"replicas", patchedEphemeralRunnerSet.Spec.Replicas,
|
||||||
)
|
)
|
||||||
return targetRunnerCount, nil
|
return w.lastPatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateDesiredState calculates the desired state of the worker based on the desired count and the the number of jobs completed.
|
||||||
|
func (w *Worker) setDesiredWorkerState(count, jobsCompleted int) int {
|
||||||
|
// Max runners should always be set by the resource builder either to the configured value,
|
||||||
|
// or the maximum int32 (resourcebuilder.newAutoScalingListener()).
|
||||||
|
targetRunnerCount := min(w.config.MinRunners+count, w.config.MaxRunners)
|
||||||
|
w.patchSeq++
|
||||||
|
desiredPatchID := w.patchSeq
|
||||||
|
|
||||||
|
if count == 0 && jobsCompleted == 0 { // empty batch
|
||||||
|
targetRunnerCount = max(w.lastPatch, targetRunnerCount)
|
||||||
|
if targetRunnerCount == w.config.MinRunners {
|
||||||
|
// We have an empty batch, and the last patch was the min runners.
|
||||||
|
// Since this is an empty batch, and we are at the min runners, they should all be idle.
|
||||||
|
// If controller created few more pods on accident (during scale down events),
|
||||||
|
// this situation allows the controller to scale down to the min runners.
|
||||||
|
// However, it is important to keep the patch sequence increasing so we don't ignore one batch.
|
||||||
|
desiredPatchID = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.lastPatch = targetRunnerCount
|
||||||
|
|
||||||
|
w.logger.Info(
|
||||||
|
"Calculated target runner count",
|
||||||
|
"assigned job", count,
|
||||||
|
"decision", targetRunnerCount,
|
||||||
|
"min", w.config.MinRunners,
|
||||||
|
"max", w.config.MaxRunners,
|
||||||
|
"currentRunnerCount", w.lastPatch,
|
||||||
|
"jobsCompleted", jobsCompleted,
|
||||||
|
)
|
||||||
|
|
||||||
|
return desiredPatchID
|
||||||
}
|
}
|
||||||
|
|||||||
326
cmd/ghalistener/worker/worker_test.go
Normal file
326
cmd/ghalistener/worker/worker_test.go
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
package worker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetDesiredWorkerState_MinMaxDefaults(t *testing.T) {
|
||||||
|
logger := logr.Discard()
|
||||||
|
newEmptyWorker := func() *Worker {
|
||||||
|
return &Worker{
|
||||||
|
config: Config{
|
||||||
|
MinRunners: 0,
|
||||||
|
MaxRunners: math.MaxInt32,
|
||||||
|
},
|
||||||
|
lastPatch: -1,
|
||||||
|
patchSeq: -1,
|
||||||
|
logger: &logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("init calculate with acquired 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("init calculate with acquired 1", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(1, 0)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("increment patch when job done", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(1, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 1)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("increment patch when called with same parameters", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(1, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(1, 0)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("calculate desired scale when acquired > 0 and completed > 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(1, 1)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("re-use the last state when acquired == 0 and completed == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(1, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("adjust when acquired == 0 and completed == 1", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(1, 1)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 1)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetDesiredWorkerState_MinSet(t *testing.T) {
|
||||||
|
logger := logr.Discard()
|
||||||
|
newEmptyWorker := func() *Worker {
|
||||||
|
return &Worker{
|
||||||
|
config: Config{
|
||||||
|
MinRunners: 1,
|
||||||
|
MaxRunners: math.MaxInt32,
|
||||||
|
},
|
||||||
|
lastPatch: -1,
|
||||||
|
patchSeq: -1,
|
||||||
|
logger: &logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("initial scale when acquired == 0 and completed == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("re-use the old state on count == 0 and completed == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(2, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 3, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("request back to 0 on job done", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(2, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 1)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("desired patch is 0 but sequence continues on empty batch and min runners", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(3, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 4, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
|
||||||
|
patchID = w.setDesiredWorkerState(0, 3)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
|
||||||
|
// Empty batch on min runners
|
||||||
|
patchID = w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 0, patchID) // forcing the state
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 2, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetDesiredWorkerState_MaxSet(t *testing.T) {
|
||||||
|
logger := logr.Discard()
|
||||||
|
newEmptyWorker := func() *Worker {
|
||||||
|
return &Worker{
|
||||||
|
config: Config{
|
||||||
|
MinRunners: 0,
|
||||||
|
MaxRunners: 5,
|
||||||
|
},
|
||||||
|
lastPatch: -1,
|
||||||
|
patchSeq: -1,
|
||||||
|
logger: &logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("initial scale when acquired == 0 and completed == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("re-use the old state on count == 0 and completed == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(2, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 2, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("request back to 0 on job done", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(2, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 1)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("scale up to max when count > max", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(6, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 5, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("scale to max when count == max", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
w.setDesiredWorkerState(5, 0)
|
||||||
|
assert.Equal(t, 5, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("scale to max when count > max and completed > 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(1, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(6, 1)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 5, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("scale back to 0 when count was > max", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(6, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 1)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("force 0 on empty batch and last patch == min runners", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(3, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 3, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
|
||||||
|
patchID = w.setDesiredWorkerState(0, 3)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
|
||||||
|
// Empty batch on min runners
|
||||||
|
patchID = w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 0, patchID) // forcing the state
|
||||||
|
assert.Equal(t, 0, w.lastPatch)
|
||||||
|
assert.Equal(t, 2, w.patchSeq)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetDesiredWorkerState_MinMaxSet(t *testing.T) {
|
||||||
|
logger := logr.Discard()
|
||||||
|
newEmptyWorker := func() *Worker {
|
||||||
|
return &Worker{
|
||||||
|
config: Config{
|
||||||
|
MinRunners: 1,
|
||||||
|
MaxRunners: 3,
|
||||||
|
},
|
||||||
|
lastPatch: -1,
|
||||||
|
patchSeq: -1,
|
||||||
|
logger: &logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("initial scale when acquired == 0 and completed == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("re-use the old state on count == 0 and completed == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(2, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 3, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("scale to min when count == 0", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(2, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
patchID = w.setDesiredWorkerState(0, 1)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("scale up to max when count > max", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(4, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 3, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("scale to max when count == max", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(3, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 3, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("force 0 on empty batch and last patch == min runners", func(t *testing.T) {
|
||||||
|
w := newEmptyWorker()
|
||||||
|
patchID := w.setDesiredWorkerState(3, 0)
|
||||||
|
assert.Equal(t, 0, patchID)
|
||||||
|
assert.Equal(t, 3, w.lastPatch)
|
||||||
|
assert.Equal(t, 0, w.patchSeq)
|
||||||
|
|
||||||
|
patchID = w.setDesiredWorkerState(0, 3)
|
||||||
|
assert.Equal(t, 1, patchID)
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 1, w.patchSeq)
|
||||||
|
|
||||||
|
// Empty batch on min runners
|
||||||
|
patchID = w.setDesiredWorkerState(0, 0)
|
||||||
|
assert.Equal(t, 0, patchID) // forcing the state
|
||||||
|
assert.Equal(t, 1, w.lastPatch)
|
||||||
|
assert.Equal(t, 2, w.patchSeq)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -129,7 +129,7 @@ func (m *AutoScalerClient) Close() error {
|
|||||||
return m.client.Close()
|
return m.client.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AutoScalerClient) GetRunnerScaleSetMessage(ctx context.Context, handler func(msg *actions.RunnerScaleSetMessage) error) error {
|
func (m *AutoScalerClient) GetRunnerScaleSetMessage(ctx context.Context, handler func(msg *actions.RunnerScaleSetMessage) error, maxCapacity int) error {
|
||||||
if m.initialMessage != nil {
|
if m.initialMessage != nil {
|
||||||
err := handler(m.initialMessage)
|
err := handler(m.initialMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -141,7 +141,7 @@ func (m *AutoScalerClient) GetRunnerScaleSetMessage(ctx context.Context, handler
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
message, err := m.client.GetMessage(ctx, m.lastMessageId)
|
message, err := m.client.GetMessage(ctx, m.lastMessageId, maxCapacity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get message failed from refreshing client. %w", err)
|
return fmt.Errorf("get message failed from refreshing client. %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ func TestGetRunnerScaleSetMessage(t *testing.T) {
|
|||||||
Statistics: &actions.RunnerScaleSetStatistic{},
|
Statistics: &actions.RunnerScaleSetStatistic{},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
||||||
mockSessionClient.On("GetMessage", ctx, int64(0)).Return(&actions.RunnerScaleSetMessage{
|
mockSessionClient.On("GetMessage", ctx, int64(0), mock.Anything).Return(&actions.RunnerScaleSetMessage{
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
MessageType: "test",
|
MessageType: "test",
|
||||||
Body: "test",
|
Body: "test",
|
||||||
@@ -332,7 +332,7 @@ func TestGetRunnerScaleSetMessage(t *testing.T) {
|
|||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.NoError(t, err, "Error getting message")
|
assert.NoError(t, err, "Error getting message")
|
||||||
assert.Equal(t, int64(0), asClient.lastMessageId, "Initial message")
|
assert.Equal(t, int64(0), asClient.lastMessageId, "Initial message")
|
||||||
@@ -340,7 +340,7 @@ func TestGetRunnerScaleSetMessage(t *testing.T) {
|
|||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.NoError(t, err, "Error getting message")
|
assert.NoError(t, err, "Error getting message")
|
||||||
assert.Equal(t, int64(1), asClient.lastMessageId, "Last message id should be updated")
|
assert.Equal(t, int64(1), asClient.lastMessageId, "Last message id should be updated")
|
||||||
@@ -368,7 +368,7 @@ func TestGetRunnerScaleSetMessage_HandleFailed(t *testing.T) {
|
|||||||
Statistics: &actions.RunnerScaleSetStatistic{},
|
Statistics: &actions.RunnerScaleSetStatistic{},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
||||||
mockSessionClient.On("GetMessage", ctx, int64(0)).Return(&actions.RunnerScaleSetMessage{
|
mockSessionClient.On("GetMessage", ctx, int64(0), mock.Anything).Return(&actions.RunnerScaleSetMessage{
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
MessageType: "test",
|
MessageType: "test",
|
||||||
Body: "test",
|
Body: "test",
|
||||||
@@ -383,14 +383,14 @@ func TestGetRunnerScaleSetMessage_HandleFailed(t *testing.T) {
|
|||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.NoError(t, err, "Error getting message")
|
assert.NoError(t, err, "Error getting message")
|
||||||
|
|
||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return fmt.Errorf("error")
|
return fmt.Errorf("error")
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.ErrorContains(t, err, "handle message failed. error", "Error getting message")
|
assert.ErrorContains(t, err, "handle message failed. error", "Error getting message")
|
||||||
assert.Equal(t, int64(0), asClient.lastMessageId, "Last message id should not be updated")
|
assert.Equal(t, int64(0), asClient.lastMessageId, "Last message id should not be updated")
|
||||||
@@ -419,7 +419,7 @@ func TestGetRunnerScaleSetMessage_HandleInitialMessage(t *testing.T) {
|
|||||||
TotalAssignedJobs: 2,
|
TotalAssignedJobs: 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything, mock.Anything).Return(session, nil)
|
||||||
mockActionsClient.On("GetAcquirableJobs", ctx, 1).Return(&actions.AcquirableJobList{
|
mockActionsClient.On("GetAcquirableJobs", ctx, 1).Return(&actions.AcquirableJobList{
|
||||||
Count: 1,
|
Count: 1,
|
||||||
Jobs: []actions.AcquirableJob{
|
Jobs: []actions.AcquirableJob{
|
||||||
@@ -439,7 +439,7 @@ func TestGetRunnerScaleSetMessage_HandleInitialMessage(t *testing.T) {
|
|||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.NoError(t, err, "Error getting message")
|
assert.NoError(t, err, "Error getting message")
|
||||||
assert.Nil(t, asClient.initialMessage, "Initial message should be nil")
|
assert.Nil(t, asClient.initialMessage, "Initial message should be nil")
|
||||||
@@ -488,7 +488,7 @@ func TestGetRunnerScaleSetMessage_HandleInitialMessageFailed(t *testing.T) {
|
|||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return fmt.Errorf("error")
|
return fmt.Errorf("error")
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.ErrorContains(t, err, "fail to process initial message. error", "Error getting message")
|
assert.ErrorContains(t, err, "fail to process initial message. error", "Error getting message")
|
||||||
assert.NotNil(t, asClient.initialMessage, "Initial message should be nil")
|
assert.NotNil(t, asClient.initialMessage, "Initial message should be nil")
|
||||||
@@ -516,8 +516,8 @@ func TestGetRunnerScaleSetMessage_RetryUntilGetMessage(t *testing.T) {
|
|||||||
Statistics: &actions.RunnerScaleSetStatistic{},
|
Statistics: &actions.RunnerScaleSetStatistic{},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
||||||
mockSessionClient.On("GetMessage", ctx, int64(0)).Return(nil, nil).Times(3)
|
mockSessionClient.On("GetMessage", ctx, int64(0), mock.Anything).Return(nil, nil).Times(3)
|
||||||
mockSessionClient.On("GetMessage", ctx, int64(0)).Return(&actions.RunnerScaleSetMessage{
|
mockSessionClient.On("GetMessage", ctx, int64(0), mock.Anything).Return(&actions.RunnerScaleSetMessage{
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
MessageType: "test",
|
MessageType: "test",
|
||||||
Body: "test",
|
Body: "test",
|
||||||
@@ -532,13 +532,13 @@ func TestGetRunnerScaleSetMessage_RetryUntilGetMessage(t *testing.T) {
|
|||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
assert.NoError(t, err, "Error getting initial message")
|
assert.NoError(t, err, "Error getting initial message")
|
||||||
|
|
||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.NoError(t, err, "Error getting message")
|
assert.NoError(t, err, "Error getting message")
|
||||||
assert.Equal(t, int64(1), asClient.lastMessageId, "Last message id should be updated")
|
assert.Equal(t, int64(1), asClient.lastMessageId, "Last message id should be updated")
|
||||||
@@ -565,7 +565,7 @@ func TestGetRunnerScaleSetMessage_ErrorOnGetMessage(t *testing.T) {
|
|||||||
Statistics: &actions.RunnerScaleSetStatistic{},
|
Statistics: &actions.RunnerScaleSetStatistic{},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
||||||
mockSessionClient.On("GetMessage", ctx, int64(0)).Return(nil, fmt.Errorf("error"))
|
mockSessionClient.On("GetMessage", ctx, int64(0), mock.Anything).Return(nil, fmt.Errorf("error"))
|
||||||
|
|
||||||
asClient, err := NewAutoScalerClient(ctx, mockActionsClient, &logger, 1, func(asc *AutoScalerClient) {
|
asClient, err := NewAutoScalerClient(ctx, mockActionsClient, &logger, 1, func(asc *AutoScalerClient) {
|
||||||
asc.client = mockSessionClient
|
asc.client = mockSessionClient
|
||||||
@@ -575,12 +575,12 @@ func TestGetRunnerScaleSetMessage_ErrorOnGetMessage(t *testing.T) {
|
|||||||
// process initial message
|
// process initial message
|
||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
assert.NoError(t, err, "Error getting initial message")
|
assert.NoError(t, err, "Error getting initial message")
|
||||||
|
|
||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
return fmt.Errorf("Should not be called")
|
return fmt.Errorf("Should not be called")
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.ErrorContains(t, err, "get message failed from refreshing client. error", "Error should be returned")
|
assert.ErrorContains(t, err, "get message failed from refreshing client. error", "Error should be returned")
|
||||||
assert.Equal(t, int64(0), asClient.lastMessageId, "Last message id should be updated")
|
assert.Equal(t, int64(0), asClient.lastMessageId, "Last message id should be updated")
|
||||||
@@ -608,7 +608,7 @@ func TestDeleteRunnerScaleSetMessage_Error(t *testing.T) {
|
|||||||
Statistics: &actions.RunnerScaleSetStatistic{},
|
Statistics: &actions.RunnerScaleSetStatistic{},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
mockActionsClient.On("CreateMessageSession", ctx, 1, mock.Anything).Return(session, nil)
|
||||||
mockSessionClient.On("GetMessage", ctx, int64(0)).Return(&actions.RunnerScaleSetMessage{
|
mockSessionClient.On("GetMessage", ctx, int64(0), mock.Anything).Return(&actions.RunnerScaleSetMessage{
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
MessageType: "test",
|
MessageType: "test",
|
||||||
Body: "test",
|
Body: "test",
|
||||||
@@ -623,13 +623,13 @@ func TestDeleteRunnerScaleSetMessage_Error(t *testing.T) {
|
|||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
assert.NoError(t, err, "Error getting initial message")
|
assert.NoError(t, err, "Error getting initial message")
|
||||||
|
|
||||||
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
err = asClient.GetRunnerScaleSetMessage(ctx, func(msg *actions.RunnerScaleSetMessage) error {
|
||||||
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
logger.Info("Message received", "messageId", msg.MessageId, "messageType", msg.MessageType, "body", msg.Body)
|
||||||
return nil
|
return nil
|
||||||
})
|
}, 10)
|
||||||
|
|
||||||
assert.ErrorContains(t, err, "delete message failed from refreshing client. error", "Error getting message")
|
assert.ErrorContains(t, err, "delete message failed from refreshing client. error", "Error getting message")
|
||||||
assert.Equal(t, int64(1), asClient.lastMessageId, "Last message id should be updated")
|
assert.Equal(t, int64(1), asClient.lastMessageId, "Last message id should be updated")
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func (s *Service) Start() error {
|
|||||||
s.logger.Info("service is stopped.")
|
s.logger.Info("service is stopped.")
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
err := s.rsClient.GetRunnerScaleSetMessage(s.ctx, s.processMessage)
|
err := s.rsClient.GetRunnerScaleSetMessage(s.ctx, s.processMessage, s.settings.MaxRunners)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get and process message. %w", err)
|
return fmt.Errorf("could not get and process message. %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func TestStart(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything).Run(func(args mock.Arguments) { cancel() }).Return(nil).Once()
|
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything, mock.Anything).Run(func(mock.Arguments) { cancel() }).Return(nil).Once()
|
||||||
|
|
||||||
err = service.Start()
|
err = service.Start()
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ func TestStart_ScaleToMinRunners(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockRsClient.On("GetRunnerScaleSetMessage", ctx, mock.Anything).Run(func(args mock.Arguments) {
|
mockRsClient.On("GetRunnerScaleSetMessage", ctx, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||||
_ = service.scaleForAssignedJobCount(5)
|
_ = service.scaleForAssignedJobCount(5)
|
||||||
}).Return(nil)
|
}).Return(nil)
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ func TestStart_ScaleToMinRunnersFailed(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c := mockKubeManager.On("ScaleEphemeralRunnerSet", ctx, service.settings.Namespace, service.settings.ResourceName, 5).Return(fmt.Errorf("error")).Once()
|
c := mockKubeManager.On("ScaleEphemeralRunnerSet", ctx, service.settings.Namespace, service.settings.ResourceName, 5).Return(fmt.Errorf("error")).Once()
|
||||||
mockRsClient.On("GetRunnerScaleSetMessage", ctx, mock.Anything).Run(func(args mock.Arguments) {
|
mockRsClient.On("GetRunnerScaleSetMessage", ctx, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||||
_ = service.scaleForAssignedJobCount(5)
|
_ = service.scaleForAssignedJobCount(5)
|
||||||
}).Return(c.ReturnArguments.Get(0))
|
}).Return(c.ReturnArguments.Get(0))
|
||||||
|
|
||||||
@@ -172,8 +172,8 @@ func TestStart_GetMultipleMessages(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything).Return(nil).Times(5)
|
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything, mock.Anything).Return(nil).Times(5)
|
||||||
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything).Run(func(args mock.Arguments) { cancel() }).Return(nil).Once()
|
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { cancel() }).Return(nil).Once()
|
||||||
|
|
||||||
err = service.Start()
|
err = service.Start()
|
||||||
|
|
||||||
@@ -207,8 +207,8 @@ func TestStart_ErrorOnMessage(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything).Return(nil).Times(2)
|
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything, mock.Anything).Return(nil).Times(2)
|
||||||
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything).Return(fmt.Errorf("error")).Once()
|
mockRsClient.On("GetRunnerScaleSetMessage", service.ctx, mock.Anything, mock.Anything).Return(fmt.Errorf("error")).Once()
|
||||||
|
|
||||||
err = service.Start()
|
err = service.Start()
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ import (
|
|||||||
|
|
||||||
//go:generate mockery --inpackage --name=RunnerScaleSetClient
|
//go:generate mockery --inpackage --name=RunnerScaleSetClient
|
||||||
type RunnerScaleSetClient interface {
|
type RunnerScaleSetClient interface {
|
||||||
GetRunnerScaleSetMessage(ctx context.Context, handler func(msg *actions.RunnerScaleSetMessage) error) error
|
GetRunnerScaleSetMessage(ctx context.Context, handler func(msg *actions.RunnerScaleSetMessage) error, maxCapacity int) error
|
||||||
AcquireJobsForRunnerScaleSet(ctx context.Context, requestIds []int64) error
|
AcquireJobsForRunnerScaleSet(ctx context.Context, requestIds []int64) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ func (_m *MockRunnerScaleSetClient) AcquireJobsForRunnerScaleSet(ctx context.Con
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRunnerScaleSetMessage provides a mock function with given fields: ctx, handler
|
// GetRunnerScaleSetMessage provides a mock function with given fields: ctx, handler, maxCapacity
|
||||||
func (_m *MockRunnerScaleSetClient) GetRunnerScaleSetMessage(ctx context.Context, handler func(*actions.RunnerScaleSetMessage) error) error {
|
func (_m *MockRunnerScaleSetClient) GetRunnerScaleSetMessage(ctx context.Context, handler func(*actions.RunnerScaleSetMessage) error, maxCapacity int) error {
|
||||||
ret := _m.Called(ctx, handler)
|
ret := _m.Called(ctx, handler, maxCapacity)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, func(*actions.RunnerScaleSetMessage) error) error); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, func(*actions.RunnerScaleSetMessage) error, int) error); ok {
|
||||||
r0 = rf(ctx, handler)
|
r0 = rf(ctx, handler, maxCapacity)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,12 @@ func newSessionClient(client actions.ActionsService, logger *logr.Logger, sessio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SessionRefreshingClient) GetMessage(ctx context.Context, lastMessageId int64) (*actions.RunnerScaleSetMessage, error) {
|
func (m *SessionRefreshingClient) GetMessage(ctx context.Context, lastMessageId int64, maxCapacity int) (*actions.RunnerScaleSetMessage, error) {
|
||||||
message, err := m.client.GetMessage(ctx, m.session.MessageQueueUrl, m.session.MessageQueueAccessToken, lastMessageId)
|
if maxCapacity < 0 {
|
||||||
|
return nil, fmt.Errorf("maxCapacity must be greater than or equal to 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
message, err := m.client.GetMessage(ctx, m.session.MessageQueueUrl, m.session.MessageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return message, nil
|
return message, nil
|
||||||
}
|
}
|
||||||
@@ -42,7 +46,7 @@ func (m *SessionRefreshingClient) GetMessage(ctx context.Context, lastMessageId
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.session = session
|
m.session = session
|
||||||
message, err = m.client.GetMessage(ctx, m.session.MessageQueueUrl, m.session.MessageQueueAccessToken, lastMessageId)
|
message, err = m.client.GetMessage(ctx, m.session.MessageQueueUrl, m.session.MessageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("delete message failed after refresh message session. %w", err)
|
return nil, fmt.Errorf("delete message failed after refresh message session. %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ func TestGetMessage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0)).Return(nil, nil).Once()
|
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0), 10).Return(nil, nil).Once()
|
||||||
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0)).Return(&actions.RunnerScaleSetMessage{MessageId: 1}, nil).Once()
|
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0), 10).Return(&actions.RunnerScaleSetMessage{MessageId: 1}, nil).Once()
|
||||||
|
|
||||||
client := newSessionClient(mockActionsClient, &logger, session)
|
client := newSessionClient(mockActionsClient, &logger, session)
|
||||||
|
|
||||||
msg, err := client.GetMessage(ctx, 0)
|
msg, err := client.GetMessage(ctx, 0, 10)
|
||||||
require.NoError(t, err, "GetMessage should not return an error")
|
require.NoError(t, err, "GetMessage should not return an error")
|
||||||
|
|
||||||
assert.Nil(t, msg, "GetMessage should return nil message")
|
assert.Nil(t, msg, "GetMessage should return nil message")
|
||||||
|
|
||||||
msg, err = client.GetMessage(ctx, 0)
|
msg, err = client.GetMessage(ctx, 0, 10)
|
||||||
require.NoError(t, err, "GetMessage should not return an error")
|
require.NoError(t, err, "GetMessage should not return an error")
|
||||||
|
|
||||||
assert.Equal(t, int64(1), msg.MessageId, "GetMessage should return a message with id 1")
|
assert.Equal(t, int64(1), msg.MessageId, "GetMessage should return a message with id 1")
|
||||||
@@ -146,11 +146,11 @@ func TestGetMessage_Error(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0)).Return(nil, fmt.Errorf("error")).Once()
|
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0), 10).Return(nil, fmt.Errorf("error")).Once()
|
||||||
|
|
||||||
client := newSessionClient(mockActionsClient, &logger, session)
|
client := newSessionClient(mockActionsClient, &logger, session)
|
||||||
|
|
||||||
msg, err := client.GetMessage(ctx, 0)
|
msg, err := client.GetMessage(ctx, 0, 10)
|
||||||
assert.ErrorContains(t, err, "get message failed. error", "GetMessage should return an error")
|
assert.ErrorContains(t, err, "get message failed. error", "GetMessage should return an error")
|
||||||
assert.Nil(t, msg, "GetMessage should return nil message")
|
assert.Nil(t, msg, "GetMessage should return nil message")
|
||||||
assert.True(t, mockActionsClient.AssertExpectations(t), "All expected calls to mockActionsClient should have been made")
|
assert.True(t, mockActionsClient.AssertExpectations(t), "All expected calls to mockActionsClient should have been made")
|
||||||
@@ -227,8 +227,8 @@ func TestGetMessage_RefreshToken(t *testing.T) {
|
|||||||
Id: 1,
|
Id: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0)).Return(nil, &actions.MessageQueueTokenExpiredError{}).Once()
|
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0), 10).Return(nil, &actions.MessageQueueTokenExpiredError{}).Once()
|
||||||
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, "token2", int64(0)).Return(&actions.RunnerScaleSetMessage{
|
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, "token2", int64(0), 10).Return(&actions.RunnerScaleSetMessage{
|
||||||
MessageId: 1,
|
MessageId: 1,
|
||||||
MessageType: "test",
|
MessageType: "test",
|
||||||
Body: "test",
|
Body: "test",
|
||||||
@@ -243,7 +243,7 @@ func TestGetMessage_RefreshToken(t *testing.T) {
|
|||||||
}, nil).Once()
|
}, nil).Once()
|
||||||
|
|
||||||
client := newSessionClient(mockActionsClient, &logger, session)
|
client := newSessionClient(mockActionsClient, &logger, session)
|
||||||
msg, err := client.GetMessage(ctx, 0)
|
msg, err := client.GetMessage(ctx, 0, 10)
|
||||||
assert.NoError(t, err, "Error getting message")
|
assert.NoError(t, err, "Error getting message")
|
||||||
assert.Equal(t, int64(1), msg.MessageId, "message id should be updated")
|
assert.Equal(t, int64(1), msg.MessageId, "message id should be updated")
|
||||||
assert.Equal(t, "token2", client.session.MessageQueueAccessToken, "Message queue access token should be updated")
|
assert.Equal(t, "token2", client.session.MessageQueueAccessToken, "Message queue access token should be updated")
|
||||||
@@ -340,11 +340,11 @@ func TestGetMessage_RefreshToken_Failed(t *testing.T) {
|
|||||||
Id: 1,
|
Id: 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0)).Return(nil, &actions.MessageQueueTokenExpiredError{}).Once()
|
mockActionsClient.On("GetMessage", ctx, session.MessageQueueUrl, session.MessageQueueAccessToken, int64(0), 10).Return(nil, &actions.MessageQueueTokenExpiredError{}).Once()
|
||||||
mockActionsClient.On("RefreshMessageSession", ctx, session.RunnerScaleSet.Id, session.SessionId).Return(nil, fmt.Errorf("error"))
|
mockActionsClient.On("RefreshMessageSession", ctx, session.RunnerScaleSet.Id, session.SessionId).Return(nil, fmt.Errorf("error"))
|
||||||
|
|
||||||
client := newSessionClient(mockActionsClient, &logger, session)
|
client := newSessionClient(mockActionsClient, &logger, session)
|
||||||
msg, err := client.GetMessage(ctx, 0)
|
msg, err := client.GetMessage(ctx, 0, 10)
|
||||||
assert.ErrorContains(t, err, "refresh message session failed. error", "Error should be returned")
|
assert.ErrorContains(t, err, "refresh message session failed. error", "Error should be returned")
|
||||||
assert.Nil(t, msg, "Message should be nil")
|
assert.Nil(t, msg, "Message should be nil")
|
||||||
assert.Equal(t, "token", client.session.MessageQueueAccessToken, "Message queue access token should not be updated")
|
assert.Equal(t, "token", client.session.MessageQueueAccessToken, "Message queue access token should not be updated")
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ type AutoscalingListenerReconciler struct {
|
|||||||
ListenerMetricsAddr string
|
ListenerMetricsAddr string
|
||||||
ListenerMetricsEndpoint string
|
ListenerMetricsEndpoint string
|
||||||
|
|
||||||
resourceBuilder resourceBuilder
|
ResourceBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete
|
// +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete
|
||||||
@@ -242,17 +242,27 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl.
|
|||||||
return r.createListenerPod(ctx, &autoscalingRunnerSet, autoscalingListener, serviceAccount, mirrorSecret, log)
|
return r.createListenerPod(ctx, &autoscalingRunnerSet, autoscalingListener, serviceAccount, mirrorSecret, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The listener pod failed might mean the mirror secret is out of date
|
cs := listenerContainerStatus(listenerPod)
|
||||||
// Delete the listener pod and re-create it to make sure the mirror secret is up to date
|
switch {
|
||||||
if listenerPod.Status.Phase == corev1.PodFailed && listenerPod.DeletionTimestamp.IsZero() {
|
case cs == nil:
|
||||||
log.Info("Listener pod failed, deleting it and re-creating it", "namespace", listenerPod.Namespace, "name", listenerPod.Name, "reason", listenerPod.Status.Reason, "message", listenerPod.Status.Message)
|
log.Info("Listener pod is not ready", "namespace", listenerPod.Namespace, "name", listenerPod.Name)
|
||||||
if err := r.Delete(ctx, listenerPod); err != nil && !kerrors.IsNotFound(err) {
|
return ctrl.Result{}, nil
|
||||||
log.Error(err, "Unable to delete the listener pod", "namespace", listenerPod.Namespace, "name", listenerPod.Name)
|
case cs.State.Terminated != nil:
|
||||||
return ctrl.Result{}, err
|
log.Info("Listener pod is terminated", "namespace", listenerPod.Namespace, "name", listenerPod.Name, "reason", cs.State.Terminated.Reason, "message", cs.State.Terminated.Message)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if listenerPod.Status.Phase == corev1.PodRunning {
|
if err := r.publishRunningListener(autoscalingListener, false); err != nil {
|
||||||
|
log.Error(err, "Unable to publish runner listener down metric", "namespace", listenerPod.Namespace, "name", listenerPod.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if listenerPod.DeletionTimestamp.IsZero() {
|
||||||
|
log.Info("Deleting the listener pod", "namespace", listenerPod.Namespace, "name", listenerPod.Name)
|
||||||
|
if err := r.Delete(ctx, listenerPod); err != nil && !kerrors.IsNotFound(err) {
|
||||||
|
log.Error(err, "Unable to delete the listener pod", "namespace", listenerPod.Namespace, "name", listenerPod.Name)
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
case cs.State.Running != nil:
|
||||||
if err := r.publishRunningListener(autoscalingListener, true); err != nil {
|
if err := r.publishRunningListener(autoscalingListener, true); err != nil {
|
||||||
log.Error(err, "Unable to publish running listener", "namespace", listenerPod.Namespace, "name", listenerPod.Name)
|
log.Error(err, "Unable to publish running listener", "namespace", listenerPod.Namespace, "name", listenerPod.Name)
|
||||||
// stop reconciling. We should never get to this point but if we do,
|
// stop reconciling. We should never get to this point but if we do,
|
||||||
@@ -260,8 +270,8 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl.
|
|||||||
// notify the reconciler again.
|
// notify the reconciler again.
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,7 +383,7 @@ func (r *AutoscalingListenerReconciler) cleanupResources(ctx context.Context, au
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoscalingListenerReconciler) createServiceAccountForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, logger logr.Logger) (ctrl.Result, error) {
|
func (r *AutoscalingListenerReconciler) createServiceAccountForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, logger logr.Logger) (ctrl.Result, error) {
|
||||||
newServiceAccount := r.resourceBuilder.newScaleSetListenerServiceAccount(autoscalingListener)
|
newServiceAccount := r.ResourceBuilder.newScaleSetListenerServiceAccount(autoscalingListener)
|
||||||
|
|
||||||
if err := ctrl.SetControllerReference(autoscalingListener, newServiceAccount, r.Scheme); err != nil {
|
if err := ctrl.SetControllerReference(autoscalingListener, newServiceAccount, r.Scheme); err != nil {
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
@@ -458,7 +468,7 @@ func (r *AutoscalingListenerReconciler) createListenerPod(ctx context.Context, a
|
|||||||
|
|
||||||
logger.Info("Creating listener config secret")
|
logger.Info("Creating listener config secret")
|
||||||
|
|
||||||
podConfig, err := r.resourceBuilder.newScaleSetListenerConfig(autoscalingListener, secret, metricsConfig, cert)
|
podConfig, err := r.ResourceBuilder.newScaleSetListenerConfig(autoscalingListener, secret, metricsConfig, cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "Failed to build listener config secret")
|
logger.Error(err, "Failed to build listener config secret")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
@@ -477,7 +487,7 @@ func (r *AutoscalingListenerReconciler) createListenerPod(ctx context.Context, a
|
|||||||
return ctrl.Result{Requeue: true}, nil
|
return ctrl.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newPod, err := r.resourceBuilder.newScaleSetListenerPod(autoscalingListener, &podConfig, serviceAccount, secret, metricsConfig, envs...)
|
newPod, err := r.ResourceBuilder.newScaleSetListenerPod(autoscalingListener, &podConfig, serviceAccount, secret, metricsConfig, envs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, "Failed to build listener pod")
|
logger.Error(err, "Failed to build listener pod")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
@@ -537,7 +547,7 @@ func (r *AutoscalingListenerReconciler) certificate(ctx context.Context, autosca
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoscalingListenerReconciler) createSecretsForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret, logger logr.Logger) (ctrl.Result, error) {
|
func (r *AutoscalingListenerReconciler) createSecretsForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret, logger logr.Logger) (ctrl.Result, error) {
|
||||||
newListenerSecret := r.resourceBuilder.newScaleSetListenerSecretMirror(autoscalingListener, secret)
|
newListenerSecret := r.ResourceBuilder.newScaleSetListenerSecretMirror(autoscalingListener, secret)
|
||||||
|
|
||||||
if err := ctrl.SetControllerReference(autoscalingListener, newListenerSecret, r.Scheme); err != nil {
|
if err := ctrl.SetControllerReference(autoscalingListener, newListenerSecret, r.Scheme); err != nil {
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
@@ -609,7 +619,7 @@ func (r *AutoscalingListenerReconciler) updateSecretsForListener(ctx context.Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoscalingListenerReconciler) createRoleForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, logger logr.Logger) (ctrl.Result, error) {
|
func (r *AutoscalingListenerReconciler) createRoleForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, logger logr.Logger) (ctrl.Result, error) {
|
||||||
newRole := r.resourceBuilder.newScaleSetListenerRole(autoscalingListener)
|
newRole := r.ResourceBuilder.newScaleSetListenerRole(autoscalingListener)
|
||||||
|
|
||||||
logger.Info("Creating listener role", "namespace", newRole.Namespace, "name", newRole.Name, "rules", newRole.Rules)
|
logger.Info("Creating listener role", "namespace", newRole.Namespace, "name", newRole.Name, "rules", newRole.Rules)
|
||||||
if err := r.Create(ctx, newRole); err != nil {
|
if err := r.Create(ctx, newRole); err != nil {
|
||||||
@@ -637,7 +647,7 @@ func (r *AutoscalingListenerReconciler) updateRoleForListener(ctx context.Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoscalingListenerReconciler) createRoleBindingForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, listenerRole *rbacv1.Role, serviceAccount *corev1.ServiceAccount, logger logr.Logger) (ctrl.Result, error) {
|
func (r *AutoscalingListenerReconciler) createRoleBindingForListener(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, listenerRole *rbacv1.Role, serviceAccount *corev1.ServiceAccount, logger logr.Logger) (ctrl.Result, error) {
|
||||||
newRoleBinding := r.resourceBuilder.newScaleSetListenerRoleBinding(autoscalingListener, listenerRole, serviceAccount)
|
newRoleBinding := r.ResourceBuilder.newScaleSetListenerRoleBinding(autoscalingListener, listenerRole, serviceAccount)
|
||||||
|
|
||||||
logger.Info("Creating listener role binding",
|
logger.Info("Creating listener role binding",
|
||||||
"namespace", newRoleBinding.Namespace,
|
"namespace", newRoleBinding.Namespace,
|
||||||
@@ -690,30 +700,6 @@ func (r *AutoscalingListenerReconciler) publishRunningListener(autoscalingListen
|
|||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
func (r *AutoscalingListenerReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
func (r *AutoscalingListenerReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
groupVersionIndexer := func(rawObj client.Object) []string {
|
|
||||||
groupVersion := v1alpha1.GroupVersion.String()
|
|
||||||
owner := metav1.GetControllerOf(rawObj)
|
|
||||||
if owner == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...make sure it is owned by this controller
|
|
||||||
if owner.APIVersion != groupVersion || owner.Kind != "AutoscalingListener" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and if so, return it
|
|
||||||
return []string{owner.Name}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &corev1.Pod{}, resourceOwnerKey, groupVersionIndexer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &corev1.ServiceAccount{}, resourceOwnerKey, groupVersionIndexer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
labelBasedWatchFunc := func(_ context.Context, obj client.Object) []reconcile.Request {
|
labelBasedWatchFunc := func(_ context.Context, obj client.Object) []reconcile.Request {
|
||||||
var requests []reconcile.Request
|
var requests []reconcile.Request
|
||||||
labels := obj.GetLabels()
|
labels := obj.GetLabels()
|
||||||
@@ -746,3 +732,13 @@ func (r *AutoscalingListenerReconciler) SetupWithManager(mgr ctrl.Manager) error
|
|||||||
WithEventFilter(predicate.ResourceVersionChangedPredicate{}).
|
WithEventFilter(predicate.ResourceVersionChangedPredicate{}).
|
||||||
Complete(r)
|
Complete(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listenerContainerStatus(pod *corev1.Pod) *corev1.ContainerStatus {
|
||||||
|
for i := range pod.Status.ContainerStatuses {
|
||||||
|
cs := &pod.Status.ContainerStatuses[i]
|
||||||
|
if cs.Name == autoscalingListenerContainerName {
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
actionsv1alpha1 "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
autoscalingListenerTestTimeout = time.Second * 5
|
autoscalingListenerTestTimeout = time.Second * 20
|
||||||
autoscalingListenerTestInterval = time.Millisecond * 250
|
autoscalingListenerTestInterval = time.Millisecond * 250
|
||||||
autoscalingListenerTestGitHubToken = "gh_token"
|
autoscalingListenerTestGitHubToken = "gh_token"
|
||||||
)
|
)
|
||||||
@@ -34,9 +34,9 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
var mgr ctrl.Manager
|
var mgr ctrl.Manager
|
||||||
var autoscalingNS *corev1.Namespace
|
var autoscalingNS *corev1.Namespace
|
||||||
var autoscalingRunnerSet *actionsv1alpha1.AutoscalingRunnerSet
|
var autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
|
||||||
var configSecret *corev1.Secret
|
var configSecret *corev1.Secret
|
||||||
var autoscalingListener *actionsv1alpha1.AutoscalingListener
|
var autoscalingListener *v1alpha1.AutoscalingListener
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
@@ -53,12 +53,12 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
|
|
||||||
min := 1
|
min := 1
|
||||||
max := 10
|
max := 10
|
||||||
autoscalingRunnerSet = &actionsv1alpha1.AutoscalingRunnerSet{
|
autoscalingRunnerSet = &v1alpha1.AutoscalingRunnerSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asrs",
|
Name: "test-asrs",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingRunnerSetSpec{
|
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
MaxRunners: &max,
|
MaxRunners: &max,
|
||||||
@@ -79,12 +79,12 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
||||||
|
|
||||||
autoscalingListener = &actionsv1alpha1.AutoscalingListener{
|
autoscalingListener = &v1alpha1.AutoscalingListener{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asl",
|
Name: "test-asl",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingListenerSpec{
|
Spec: v1alpha1.AutoscalingListenerSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
RunnerScaleSetId: 1,
|
RunnerScaleSetId: 1,
|
||||||
@@ -119,7 +119,7 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
).Should(Succeed(), "Config secret should be created")
|
).Should(Succeed(), "Config secret should be created")
|
||||||
|
|
||||||
// Check if finalizer is added
|
// Check if finalizer is added
|
||||||
created := new(actionsv1alpha1.AutoscalingListener)
|
created := new(v1alpha1.AutoscalingListener)
|
||||||
Eventually(
|
Eventually(
|
||||||
func() (string, error) {
|
func() (string, error) {
|
||||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, created)
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, created)
|
||||||
@@ -298,7 +298,7 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
// The AutoScalingListener should be deleted
|
// The AutoScalingListener should be deleted
|
||||||
Eventually(
|
Eventually(
|
||||||
func() error {
|
func() error {
|
||||||
listenerList := new(actionsv1alpha1.AutoscalingListenerList)
|
listenerList := new(v1alpha1.AutoscalingListenerList)
|
||||||
err := k8sClient.List(ctx, listenerList, client.InNamespace(autoscalingListener.Namespace), client.MatchingFields{".metadata.name": autoscalingListener.Name})
|
err := k8sClient.List(ctx, listenerList, client.InNamespace(autoscalingListener.Namespace), client.MatchingFields{".metadata.name": autoscalingListener.Name})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -351,6 +351,53 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
autoscalingListenerTestInterval).Should(BeEquivalentTo(rulesForListenerRole([]string{updated.Spec.EphemeralRunnerSetName})), "Role should be updated")
|
autoscalingListenerTestInterval).Should(BeEquivalentTo(rulesForListenerRole([]string{updated.Spec.EphemeralRunnerSetName})), "Role should be updated")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("It should re-create pod whenever listener container is terminated", func() {
|
||||||
|
// Waiting for the pod is created
|
||||||
|
pod := new(corev1.Pod)
|
||||||
|
Eventually(
|
||||||
|
func() (string, error) {
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, pod)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pod.Name, nil
|
||||||
|
},
|
||||||
|
autoscalingListenerTestTimeout,
|
||||||
|
autoscalingListenerTestInterval,
|
||||||
|
).Should(BeEquivalentTo(autoscalingListener.Name), "Pod should be created")
|
||||||
|
|
||||||
|
oldPodUID := string(pod.UID)
|
||||||
|
updated := pod.DeepCopy()
|
||||||
|
updated.Status.ContainerStatuses = []corev1.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: autoscalingListenerContainerName,
|
||||||
|
State: corev1.ContainerState{
|
||||||
|
Terminated: &corev1.ContainerStateTerminated{
|
||||||
|
ExitCode: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := k8sClient.Status().Update(ctx, updated)
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "failed to update test pod")
|
||||||
|
|
||||||
|
// Waiting for the new pod is created
|
||||||
|
Eventually(
|
||||||
|
func() (string, error) {
|
||||||
|
pod := new(corev1.Pod)
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, pod)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pod.UID), nil
|
||||||
|
},
|
||||||
|
autoscalingListenerTestTimeout,
|
||||||
|
autoscalingListenerTestInterval,
|
||||||
|
).ShouldNot(BeEquivalentTo(oldPodUID), "Pod should be re-created")
|
||||||
|
})
|
||||||
|
|
||||||
It("It should update mirror secrets to match secret used by AutoScalingRunnerSet", func() {
|
It("It should update mirror secrets to match secret used by AutoScalingRunnerSet", func() {
|
||||||
// Waiting for the pod is created
|
// Waiting for the pod is created
|
||||||
pod := new(corev1.Pod)
|
pod := new(corev1.Pod)
|
||||||
@@ -373,7 +420,18 @@ var _ = Describe("Test AutoScalingListener controller", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred(), "failed to update test secret")
|
Expect(err).NotTo(HaveOccurred(), "failed to update test secret")
|
||||||
|
|
||||||
updatedPod := pod.DeepCopy()
|
updatedPod := pod.DeepCopy()
|
||||||
updatedPod.Status.Phase = corev1.PodFailed
|
// Ignore status running and consult the container state
|
||||||
|
updatedPod.Status.Phase = corev1.PodRunning
|
||||||
|
updatedPod.Status.ContainerStatuses = []corev1.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: autoscalingListenerContainerName,
|
||||||
|
State: corev1.ContainerState{
|
||||||
|
Terminated: &corev1.ContainerStateTerminated{
|
||||||
|
ExitCode: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
err = k8sClient.Status().Update(ctx, updatedPod)
|
err = k8sClient.Status().Update(ctx, updatedPod)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to update test pod to failed")
|
Expect(err).NotTo(HaveOccurred(), "failed to update test pod to failed")
|
||||||
|
|
||||||
@@ -415,11 +473,12 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
var mgr ctrl.Manager
|
var mgr ctrl.Manager
|
||||||
var autoscalingNS *corev1.Namespace
|
var autoscalingNS *corev1.Namespace
|
||||||
var autoscalingRunnerSet *actionsv1alpha1.AutoscalingRunnerSet
|
var autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
|
||||||
var configSecret *corev1.Secret
|
var configSecret *corev1.Secret
|
||||||
var autoscalingListener *actionsv1alpha1.AutoscalingListener
|
var autoscalingListener *v1alpha1.AutoscalingListener
|
||||||
|
|
||||||
var runAsUser int64 = 1001
|
var runAsUser int64 = 1001
|
||||||
|
const sidecarContainerName = "sidecar"
|
||||||
|
|
||||||
listenerPodTemplate := corev1.PodTemplateSpec{
|
listenerPodTemplate := corev1.PodTemplateSpec{
|
||||||
Spec: corev1.PodSpec{
|
Spec: corev1.PodSpec{
|
||||||
@@ -432,7 +491,7 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "sidecar",
|
Name: sidecarContainerName,
|
||||||
ImagePullPolicy: corev1.PullIfNotPresent,
|
ImagePullPolicy: corev1.PullIfNotPresent,
|
||||||
Image: "busybox",
|
Image: "busybox",
|
||||||
},
|
},
|
||||||
@@ -458,12 +517,12 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
|
|
||||||
min := 1
|
min := 1
|
||||||
max := 10
|
max := 10
|
||||||
autoscalingRunnerSet = &actionsv1alpha1.AutoscalingRunnerSet{
|
autoscalingRunnerSet = &v1alpha1.AutoscalingRunnerSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asrs",
|
Name: "test-asrs",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingRunnerSetSpec{
|
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
MaxRunners: &max,
|
MaxRunners: &max,
|
||||||
@@ -484,12 +543,12 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
||||||
|
|
||||||
autoscalingListener = &actionsv1alpha1.AutoscalingListener{
|
autoscalingListener = &v1alpha1.AutoscalingListener{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asltest",
|
Name: "test-asltest",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingListenerSpec{
|
Spec: v1alpha1.AutoscalingListenerSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
RunnerScaleSetId: 1,
|
RunnerScaleSetId: 1,
|
||||||
@@ -512,7 +571,7 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
Context("When creating a new AutoScalingListener", func() {
|
Context("When creating a new AutoScalingListener", func() {
|
||||||
It("It should create customized pod with applied configuration", func() {
|
It("It should create customized pod with applied configuration", func() {
|
||||||
// Check if finalizer is added
|
// Check if finalizer is added
|
||||||
created := new(actionsv1alpha1.AutoscalingListener)
|
created := new(v1alpha1.AutoscalingListener)
|
||||||
Eventually(
|
Eventually(
|
||||||
func() (string, error) {
|
func() (string, error) {
|
||||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, created)
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, created)
|
||||||
@@ -525,7 +584,8 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
return created.Finalizers[0], nil
|
return created.Finalizers[0], nil
|
||||||
},
|
},
|
||||||
autoscalingListenerTestTimeout,
|
autoscalingListenerTestTimeout,
|
||||||
autoscalingListenerTestInterval).Should(BeEquivalentTo(autoscalingListenerFinalizerName), "AutoScalingListener should have a finalizer")
|
autoscalingListenerTestInterval,
|
||||||
|
).Should(BeEquivalentTo(autoscalingListenerFinalizerName), "AutoScalingListener should have a finalizer")
|
||||||
|
|
||||||
// Check if config is created
|
// Check if config is created
|
||||||
config := new(corev1.Secret)
|
config := new(corev1.Secret)
|
||||||
@@ -559,30 +619,119 @@ var _ = Describe("Test AutoScalingListener customization", func() {
|
|||||||
Expect(pod.Spec.Containers[0].SecurityContext.RunAsUser).To(Equal(&runAsUser), "Pod should have the correct security context")
|
Expect(pod.Spec.Containers[0].SecurityContext.RunAsUser).To(Equal(&runAsUser), "Pod should have the correct security context")
|
||||||
Expect(pod.Spec.Containers[0].ImagePullPolicy).To(Equal(corev1.PullAlways), "Pod should have the correct image pull policy")
|
Expect(pod.Spec.Containers[0].ImagePullPolicy).To(Equal(corev1.PullAlways), "Pod should have the correct image pull policy")
|
||||||
|
|
||||||
Expect(pod.Spec.Containers[1].Name).To(Equal("sidecar"), "Pod should have the correct container name")
|
Expect(pod.Spec.Containers[1].Name).To(Equal(sidecarContainerName), "Pod should have the correct container name")
|
||||||
Expect(pod.Spec.Containers[1].Image).To(Equal("busybox"), "Pod should have the correct image")
|
Expect(pod.Spec.Containers[1].Image).To(Equal("busybox"), "Pod should have the correct image")
|
||||||
Expect(pod.Spec.Containers[1].ImagePullPolicy).To(Equal(corev1.PullIfNotPresent), "Pod should have the correct image pull policy")
|
Expect(pod.Spec.Containers[1].ImagePullPolicy).To(Equal(corev1.PullIfNotPresent), "Pod should have the correct image pull policy")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("When AutoscalingListener pod has interuptions", func() {
|
||||||
|
It("Should re-create pod when it is deleted", func() {
|
||||||
|
pod := new(corev1.Pod)
|
||||||
|
Eventually(
|
||||||
|
func() (string, error) {
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, pod)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pod.Name, nil
|
||||||
|
},
|
||||||
|
autoscalingListenerTestTimeout,
|
||||||
|
autoscalingListenerTestInterval,
|
||||||
|
).Should(BeEquivalentTo(autoscalingListener.Name), "Pod should be created")
|
||||||
|
|
||||||
|
Expect(len(pod.Spec.Containers)).To(Equal(2), "Pod should have 2 containers")
|
||||||
|
oldPodUID := string(pod.UID)
|
||||||
|
|
||||||
|
err := k8sClient.Delete(ctx, pod)
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "failed to delete pod")
|
||||||
|
|
||||||
|
pod = new(corev1.Pod)
|
||||||
|
Eventually(
|
||||||
|
func() (string, error) {
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, pod)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pod.UID), nil
|
||||||
|
},
|
||||||
|
autoscalingListenerTestTimeout,
|
||||||
|
autoscalingListenerTestInterval,
|
||||||
|
).ShouldNot(BeEquivalentTo(oldPodUID), "Pod should be created")
|
||||||
|
})
|
||||||
|
|
||||||
|
It("Should re-create pod when the listener pod is terminated", func() {
|
||||||
|
pod := new(corev1.Pod)
|
||||||
|
Eventually(
|
||||||
|
func() (string, error) {
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, pod)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pod.Name, nil
|
||||||
|
},
|
||||||
|
autoscalingListenerTestTimeout,
|
||||||
|
autoscalingListenerTestInterval,
|
||||||
|
).Should(BeEquivalentTo(autoscalingListener.Name), "Pod should be created")
|
||||||
|
|
||||||
|
updated := pod.DeepCopy()
|
||||||
|
oldPodUID := string(pod.UID)
|
||||||
|
updated.Status.ContainerStatuses = []corev1.ContainerStatus{
|
||||||
|
{
|
||||||
|
Name: autoscalingListenerContainerName,
|
||||||
|
State: corev1.ContainerState{
|
||||||
|
Terminated: &corev1.ContainerStateTerminated{
|
||||||
|
ExitCode: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: sidecarContainerName,
|
||||||
|
State: corev1.ContainerState{
|
||||||
|
Running: &corev1.ContainerStateRunning{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := k8sClient.Status().Update(ctx, updated)
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "failed to update pod status")
|
||||||
|
|
||||||
|
pod = new(corev1.Pod)
|
||||||
|
Eventually(
|
||||||
|
func() (string, error) {
|
||||||
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingListener.Name, Namespace: autoscalingListener.Namespace}, pod)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pod.UID), nil
|
||||||
|
},
|
||||||
|
autoscalingListenerTestTimeout,
|
||||||
|
autoscalingListenerTestInterval,
|
||||||
|
).ShouldNot(BeEquivalentTo(oldPodUID), "Pod should be created")
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = Describe("Test AutoScalingListener controller with proxy", func() {
|
var _ = Describe("Test AutoScalingListener controller with proxy", func() {
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
var mgr ctrl.Manager
|
var mgr ctrl.Manager
|
||||||
var autoscalingNS *corev1.Namespace
|
var autoscalingNS *corev1.Namespace
|
||||||
var autoscalingRunnerSet *actionsv1alpha1.AutoscalingRunnerSet
|
var autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
|
||||||
var configSecret *corev1.Secret
|
var configSecret *corev1.Secret
|
||||||
var autoscalingListener *actionsv1alpha1.AutoscalingListener
|
var autoscalingListener *v1alpha1.AutoscalingListener
|
||||||
|
|
||||||
createRunnerSetAndListener := func(proxy *actionsv1alpha1.ProxyConfig) {
|
createRunnerSetAndListener := func(proxy *v1alpha1.ProxyConfig) {
|
||||||
min := 1
|
min := 1
|
||||||
max := 10
|
max := 10
|
||||||
autoscalingRunnerSet = &actionsv1alpha1.AutoscalingRunnerSet{
|
autoscalingRunnerSet = &v1alpha1.AutoscalingRunnerSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asrs",
|
Name: "test-asrs",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingRunnerSetSpec{
|
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
MaxRunners: &max,
|
MaxRunners: &max,
|
||||||
@@ -604,12 +753,12 @@ var _ = Describe("Test AutoScalingListener controller with proxy", func() {
|
|||||||
err := k8sClient.Create(ctx, autoscalingRunnerSet)
|
err := k8sClient.Create(ctx, autoscalingRunnerSet)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
||||||
|
|
||||||
autoscalingListener = &actionsv1alpha1.AutoscalingListener{
|
autoscalingListener = &v1alpha1.AutoscalingListener{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asl",
|
Name: "test-asl",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingListenerSpec{
|
Spec: v1alpha1.AutoscalingListenerSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
RunnerScaleSetId: 1,
|
RunnerScaleSetId: 1,
|
||||||
@@ -658,12 +807,12 @@ var _ = Describe("Test AutoScalingListener controller with proxy", func() {
|
|||||||
err := k8sClient.Create(ctx, proxyCredentials)
|
err := k8sClient.Create(ctx, proxyCredentials)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create proxy credentials secret")
|
Expect(err).NotTo(HaveOccurred(), "failed to create proxy credentials secret")
|
||||||
|
|
||||||
proxy := &actionsv1alpha1.ProxyConfig{
|
proxy := &v1alpha1.ProxyConfig{
|
||||||
HTTP: &actionsv1alpha1.ProxyServerConfig{
|
HTTP: &v1alpha1.ProxyServerConfig{
|
||||||
Url: "http://localhost:8080",
|
Url: "http://localhost:8080",
|
||||||
CredentialSecretRef: "proxy-credentials",
|
CredentialSecretRef: "proxy-credentials",
|
||||||
},
|
},
|
||||||
HTTPS: &actionsv1alpha1.ProxyServerConfig{
|
HTTPS: &v1alpha1.ProxyServerConfig{
|
||||||
Url: "https://localhost:8443",
|
Url: "https://localhost:8443",
|
||||||
CredentialSecretRef: "proxy-credentials",
|
CredentialSecretRef: "proxy-credentials",
|
||||||
},
|
},
|
||||||
@@ -766,19 +915,19 @@ var _ = Describe("Test AutoScalingListener controller with template modification
|
|||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
var mgr ctrl.Manager
|
var mgr ctrl.Manager
|
||||||
var autoscalingNS *corev1.Namespace
|
var autoscalingNS *corev1.Namespace
|
||||||
var autoscalingRunnerSet *actionsv1alpha1.AutoscalingRunnerSet
|
var autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
|
||||||
var configSecret *corev1.Secret
|
var configSecret *corev1.Secret
|
||||||
var autoscalingListener *actionsv1alpha1.AutoscalingListener
|
var autoscalingListener *v1alpha1.AutoscalingListener
|
||||||
|
|
||||||
createRunnerSetAndListener := func(listenerTemplate *corev1.PodTemplateSpec) {
|
createRunnerSetAndListener := func(listenerTemplate *corev1.PodTemplateSpec) {
|
||||||
min := 1
|
min := 1
|
||||||
max := 10
|
max := 10
|
||||||
autoscalingRunnerSet = &actionsv1alpha1.AutoscalingRunnerSet{
|
autoscalingRunnerSet = &v1alpha1.AutoscalingRunnerSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asrs",
|
Name: "test-asrs",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingRunnerSetSpec{
|
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
MaxRunners: &max,
|
MaxRunners: &max,
|
||||||
@@ -800,12 +949,12 @@ var _ = Describe("Test AutoScalingListener controller with template modification
|
|||||||
err := k8sClient.Create(ctx, autoscalingRunnerSet)
|
err := k8sClient.Create(ctx, autoscalingRunnerSet)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
||||||
|
|
||||||
autoscalingListener = &actionsv1alpha1.AutoscalingListener{
|
autoscalingListener = &v1alpha1.AutoscalingListener{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asl",
|
Name: "test-asl",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingListenerSpec{
|
Spec: v1alpha1.AutoscalingListenerSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
RunnerScaleSetId: 1,
|
RunnerScaleSetId: 1,
|
||||||
@@ -887,7 +1036,6 @@ var _ = Describe("Test AutoScalingListener controller with template modification
|
|||||||
|
|
||||||
g.Expect(pod.ObjectMeta.Annotations).To(HaveKeyWithValue("test-annotation-key", "test-annotation-value"), "pod annotations should be copied from runner set template")
|
g.Expect(pod.ObjectMeta.Annotations).To(HaveKeyWithValue("test-annotation-key", "test-annotation-value"), "pod annotations should be copied from runner set template")
|
||||||
g.Expect(pod.ObjectMeta.Labels).To(HaveKeyWithValue("test-label-key", "test-label-value"), "pod labels should be copied from runner set template")
|
g.Expect(pod.ObjectMeta.Labels).To(HaveKeyWithValue("test-label-key", "test-label-value"), "pod labels should be copied from runner set template")
|
||||||
|
|
||||||
},
|
},
|
||||||
autoscalingListenerTestTimeout,
|
autoscalingListenerTestTimeout,
|
||||||
autoscalingListenerTestInterval).Should(Succeed(), "failed to create listener pod with proxy details")
|
autoscalingListenerTestInterval).Should(Succeed(), "failed to create listener pod with proxy details")
|
||||||
@@ -915,9 +1063,9 @@ var _ = Describe("Test GitHub Server TLS configuration", func() {
|
|||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
var mgr ctrl.Manager
|
var mgr ctrl.Manager
|
||||||
var autoscalingNS *corev1.Namespace
|
var autoscalingNS *corev1.Namespace
|
||||||
var autoscalingRunnerSet *actionsv1alpha1.AutoscalingRunnerSet
|
var autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
|
||||||
var configSecret *corev1.Secret
|
var configSecret *corev1.Secret
|
||||||
var autoscalingListener *actionsv1alpha1.AutoscalingListener
|
var autoscalingListener *v1alpha1.AutoscalingListener
|
||||||
var rootCAConfigMap *corev1.ConfigMap
|
var rootCAConfigMap *corev1.ConfigMap
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
@@ -955,16 +1103,16 @@ var _ = Describe("Test GitHub Server TLS configuration", func() {
|
|||||||
|
|
||||||
min := 1
|
min := 1
|
||||||
max := 10
|
max := 10
|
||||||
autoscalingRunnerSet = &actionsv1alpha1.AutoscalingRunnerSet{
|
autoscalingRunnerSet = &v1alpha1.AutoscalingRunnerSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asrs",
|
Name: "test-asrs",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingRunnerSetSpec{
|
Spec: v1alpha1.AutoscalingRunnerSetSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
GitHubServerTLS: &actionsv1alpha1.GitHubServerTLSConfig{
|
GitHubServerTLS: &v1alpha1.GitHubServerTLSConfig{
|
||||||
CertificateFrom: &actionsv1alpha1.TLSCertificateSource{
|
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||||
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
||||||
LocalObjectReference: corev1.LocalObjectReference{
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
Name: rootCAConfigMap.Name,
|
Name: rootCAConfigMap.Name,
|
||||||
@@ -991,16 +1139,16 @@ var _ = Describe("Test GitHub Server TLS configuration", func() {
|
|||||||
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
err = k8sClient.Create(ctx, autoscalingRunnerSet)
|
||||||
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
Expect(err).NotTo(HaveOccurred(), "failed to create AutoScalingRunnerSet")
|
||||||
|
|
||||||
autoscalingListener = &actionsv1alpha1.AutoscalingListener{
|
autoscalingListener = &v1alpha1.AutoscalingListener{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-asl",
|
Name: "test-asl",
|
||||||
Namespace: autoscalingNS.Name,
|
Namespace: autoscalingNS.Name,
|
||||||
},
|
},
|
||||||
Spec: actionsv1alpha1.AutoscalingListenerSpec{
|
Spec: v1alpha1.AutoscalingListenerSpec{
|
||||||
GitHubConfigUrl: "https://github.com/owner/repo",
|
GitHubConfigUrl: "https://github.com/owner/repo",
|
||||||
GitHubConfigSecret: configSecret.Name,
|
GitHubConfigSecret: configSecret.Name,
|
||||||
GitHubServerTLS: &actionsv1alpha1.GitHubServerTLSConfig{
|
GitHubServerTLS: &v1alpha1.GitHubServerTLSConfig{
|
||||||
CertificateFrom: &actionsv1alpha1.TLSCertificateSource{
|
CertificateFrom: &v1alpha1.TLSCertificateSource{
|
||||||
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
||||||
LocalObjectReference: corev1.LocalObjectReference{
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
Name: rootCAConfigMap.Name,
|
Name: rootCAConfigMap.Name,
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
@@ -80,8 +79,7 @@ type AutoscalingRunnerSetReconciler struct {
|
|||||||
DefaultRunnerScaleSetListenerImagePullSecrets []string
|
DefaultRunnerScaleSetListenerImagePullSecrets []string
|
||||||
UpdateStrategy UpdateStrategy
|
UpdateStrategy UpdateStrategy
|
||||||
ActionsClient actions.MultiClient
|
ActionsClient actions.MultiClient
|
||||||
|
ResourceBuilder
|
||||||
resourceBuilder resourceBuilder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:rbac:groups=actions.github.com,resources=autoscalingrunnersets,verbs=get;list;watch;create;update;patch;delete
|
// +kubebuilder:rbac:groups=actions.github.com,resources=autoscalingrunnersets,verbs=get;list;watch;create;update;patch;delete
|
||||||
@@ -135,17 +133,11 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl
|
|||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
requeue, err := r.removeFinalizersFromDependentResources(ctx, autoscalingRunnerSet, log)
|
if err := r.removeFinalizersFromDependentResources(ctx, autoscalingRunnerSet, log); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Error(err, "Failed to remove finalizers on dependent resources")
|
log.Error(err, "Failed to remove finalizers on dependent resources")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if requeue {
|
|
||||||
log.Info("Waiting for dependent resources to be deleted")
|
|
||||||
return ctrl.Result{Requeue: true}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Removing finalizer")
|
log.Info("Removing finalizer")
|
||||||
err = patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) {
|
err = patch(ctx, r.Client, autoscalingRunnerSet, func(obj *v1alpha1.AutoscalingRunnerSet) {
|
||||||
controllerutil.RemoveFinalizer(obj, autoscalingRunnerSetFinalizerName)
|
controllerutil.RemoveFinalizer(obj, autoscalingRunnerSetFinalizerName)
|
||||||
@@ -277,6 +269,7 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl
|
|||||||
// need to scale down to 0
|
// need to scale down to 0
|
||||||
err := patch(ctx, r.Client, latestRunnerSet, func(obj *v1alpha1.EphemeralRunnerSet) {
|
err := patch(ctx, r.Client, latestRunnerSet, func(obj *v1alpha1.EphemeralRunnerSet) {
|
||||||
obj.Spec.Replicas = 0
|
obj.Spec.Replicas = 0
|
||||||
|
obj.Spec.PatchID = 0
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "Failed to patch runner set to set desired count to 0")
|
log.Error(err, "Failed to patch runner set to set desired count to 0")
|
||||||
@@ -389,7 +382,7 @@ func (r *AutoscalingRunnerSetReconciler) deleteEphemeralRunnerSets(ctx context.C
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoscalingRunnerSetReconciler) removeFinalizersFromDependentResources(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (requeue bool, err error) {
|
func (r *AutoscalingRunnerSetReconciler) removeFinalizersFromDependentResources(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) error {
|
||||||
c := autoscalingRunnerSetFinalizerDependencyCleaner{
|
c := autoscalingRunnerSetFinalizerDependencyCleaner{
|
||||||
client: r.Client,
|
client: r.Client,
|
||||||
autoscalingRunnerSet: autoscalingRunnerSet,
|
autoscalingRunnerSet: autoscalingRunnerSet,
|
||||||
@@ -404,12 +397,7 @@ func (r *AutoscalingRunnerSetReconciler) removeFinalizersFromDependentResources(
|
|||||||
c.removeManagerRoleBindingFinalizer(ctx)
|
c.removeManagerRoleBindingFinalizer(ctx)
|
||||||
c.removeManagerRoleFinalizer(ctx)
|
c.removeManagerRoleFinalizer(ctx)
|
||||||
|
|
||||||
requeue, err = c.result()
|
return c.Err()
|
||||||
if err != nil {
|
|
||||||
logger.Error(err, "Failed to cleanup finalizer from dependent resource")
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return requeue, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (ctrl.Result, error) {
|
func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, logger logr.Logger) (ctrl.Result, error) {
|
||||||
@@ -634,7 +622,7 @@ func (r *AutoscalingRunnerSetReconciler) deleteRunnerScaleSet(ctx context.Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *AutoscalingRunnerSetReconciler) createEphemeralRunnerSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, log logr.Logger) (ctrl.Result, error) {
|
func (r *AutoscalingRunnerSetReconciler) createEphemeralRunnerSet(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, log logr.Logger) (ctrl.Result, error) {
|
||||||
desiredRunnerSet, err := r.resourceBuilder.newEphemeralRunnerSet(autoscalingRunnerSet)
|
desiredRunnerSet, err := r.ResourceBuilder.newEphemeralRunnerSet(autoscalingRunnerSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "Could not create EphemeralRunnerSet")
|
log.Error(err, "Could not create EphemeralRunnerSet")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
@@ -663,7 +651,7 @@ func (r *AutoscalingRunnerSetReconciler) createAutoScalingListenerForRunnerSet(c
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
autoscalingListener, err := r.resourceBuilder.newAutoScalingListener(autoscalingRunnerSet, ephemeralRunnerSet, r.ControllerNamespace, r.DefaultRunnerScaleSetListenerImage, imagePullSecrets)
|
autoscalingListener, err := r.ResourceBuilder.newAutoScalingListener(autoscalingRunnerSet, ephemeralRunnerSet, r.ControllerNamespace, r.DefaultRunnerScaleSetListenerImage, imagePullSecrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "Could not create AutoscalingListener spec")
|
log.Error(err, "Could not create AutoscalingListener spec")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
@@ -758,26 +746,6 @@ func (r *AutoscalingRunnerSetReconciler) actionsClientOptionsFor(ctx context.Con
|
|||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
func (r *AutoscalingRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
func (r *AutoscalingRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
groupVersionIndexer := func(rawObj client.Object) []string {
|
|
||||||
groupVersion := v1alpha1.GroupVersion.String()
|
|
||||||
owner := metav1.GetControllerOf(rawObj)
|
|
||||||
if owner == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...make sure it is owned by this controller
|
|
||||||
if owner.APIVersion != groupVersion || owner.Kind != "AutoscalingRunnerSet" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and if so, return it
|
|
||||||
return []string{owner.Name}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &v1alpha1.EphemeralRunnerSet{}, resourceOwnerKey, groupVersionIndexer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctrl.NewControllerManagedBy(mgr).
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
For(&v1alpha1.AutoscalingRunnerSet{}).
|
For(&v1alpha1.AutoscalingRunnerSet{}).
|
||||||
Owns(&v1alpha1.EphemeralRunnerSet{}).
|
Owns(&v1alpha1.EphemeralRunnerSet{}).
|
||||||
@@ -804,17 +772,16 @@ type autoscalingRunnerSetFinalizerDependencyCleaner struct {
|
|||||||
autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
|
autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet
|
||||||
logger logr.Logger
|
logger logr.Logger
|
||||||
|
|
||||||
// fields to operate on
|
err error
|
||||||
requeue bool
|
|
||||||
err error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) result() (requeue bool, err error) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) Err() error {
|
||||||
return c.requeue, c.err
|
return c.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRoleBindingFinalizer(ctx context.Context) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRoleBindingFinalizer(ctx context.Context) {
|
||||||
if c.requeue || c.err != nil {
|
if c.err != nil {
|
||||||
|
c.logger.Info("Skipping cleaning up kubernetes mode service account")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -845,7 +812,6 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRol
|
|||||||
c.err = fmt.Errorf("failed to patch kubernetes mode role binding without finalizer: %w", err)
|
c.err = fmt.Errorf("failed to patch kubernetes mode role binding without finalizer: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.requeue = true
|
|
||||||
c.logger.Info("Removed finalizer from container mode kubernetes role binding", "name", roleBindingName)
|
c.logger.Info("Removed finalizer from container mode kubernetes role binding", "name", roleBindingName)
|
||||||
return
|
return
|
||||||
case err != nil && !kerrors.IsNotFound(err):
|
case err != nil && !kerrors.IsNotFound(err):
|
||||||
@@ -858,7 +824,7 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRol
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRoleFinalizer(ctx context.Context) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRoleFinalizer(ctx context.Context) {
|
||||||
if c.requeue || c.err != nil {
|
if c.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -888,7 +854,6 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRol
|
|||||||
c.err = fmt.Errorf("failed to patch kubernetes mode role without finalizer: %w", err)
|
c.err = fmt.Errorf("failed to patch kubernetes mode role without finalizer: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.requeue = true
|
|
||||||
c.logger.Info("Removed finalizer from container mode kubernetes role")
|
c.logger.Info("Removed finalizer from container mode kubernetes role")
|
||||||
return
|
return
|
||||||
case err != nil && !kerrors.IsNotFound(err):
|
case err != nil && !kerrors.IsNotFound(err):
|
||||||
@@ -901,7 +866,7 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeRol
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeServiceAccountFinalizer(ctx context.Context) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeServiceAccountFinalizer(ctx context.Context) {
|
||||||
if c.requeue || c.err != nil {
|
if c.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,7 +897,6 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeSer
|
|||||||
c.err = fmt.Errorf("failed to patch kubernetes mode service account without finalizer: %w", err)
|
c.err = fmt.Errorf("failed to patch kubernetes mode service account without finalizer: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.requeue = true
|
|
||||||
c.logger.Info("Removed finalizer from container mode kubernetes service account")
|
c.logger.Info("Removed finalizer from container mode kubernetes service account")
|
||||||
return
|
return
|
||||||
case err != nil && !kerrors.IsNotFound(err):
|
case err != nil && !kerrors.IsNotFound(err):
|
||||||
@@ -945,7 +909,7 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeKubernetesModeSer
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeNoPermissionServiceAccountFinalizer(ctx context.Context) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeNoPermissionServiceAccountFinalizer(ctx context.Context) {
|
||||||
if c.requeue || c.err != nil {
|
if c.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -976,7 +940,6 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeNoPermissionServi
|
|||||||
c.err = fmt.Errorf("failed to patch service account without finalizer: %w", err)
|
c.err = fmt.Errorf("failed to patch service account without finalizer: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.requeue = true
|
|
||||||
c.logger.Info("Removed finalizer from no permission service account", "name", serviceAccountName)
|
c.logger.Info("Removed finalizer from no permission service account", "name", serviceAccountName)
|
||||||
return
|
return
|
||||||
case err != nil && !kerrors.IsNotFound(err):
|
case err != nil && !kerrors.IsNotFound(err):
|
||||||
@@ -989,7 +952,7 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeNoPermissionServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeGitHubSecretFinalizer(ctx context.Context) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeGitHubSecretFinalizer(ctx context.Context) {
|
||||||
if c.requeue || c.err != nil {
|
if c.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1020,7 +983,6 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeGitHubSecretFinal
|
|||||||
c.err = fmt.Errorf("failed to patch GitHub secret without finalizer: %w", err)
|
c.err = fmt.Errorf("failed to patch GitHub secret without finalizer: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.requeue = true
|
|
||||||
c.logger.Info("Removed finalizer from GitHub secret", "name", githubSecretName)
|
c.logger.Info("Removed finalizer from GitHub secret", "name", githubSecretName)
|
||||||
return
|
return
|
||||||
case err != nil && !kerrors.IsNotFound(err) && !kerrors.IsForbidden(err):
|
case err != nil && !kerrors.IsNotFound(err) && !kerrors.IsForbidden(err):
|
||||||
@@ -1033,7 +995,7 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeGitHubSecretFinal
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeManagerRoleBindingFinalizer(ctx context.Context) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeManagerRoleBindingFinalizer(ctx context.Context) {
|
||||||
if c.requeue || c.err != nil {
|
if c.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1064,7 +1026,6 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeManagerRoleBindin
|
|||||||
c.err = fmt.Errorf("failed to patch manager role binding without finalizer: %w", err)
|
c.err = fmt.Errorf("failed to patch manager role binding without finalizer: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.requeue = true
|
|
||||||
c.logger.Info("Removed finalizer from manager role binding", "name", managerRoleBindingName)
|
c.logger.Info("Removed finalizer from manager role binding", "name", managerRoleBindingName)
|
||||||
return
|
return
|
||||||
case err != nil && !kerrors.IsNotFound(err):
|
case err != nil && !kerrors.IsNotFound(err):
|
||||||
@@ -1077,7 +1038,7 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeManagerRoleBindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeManagerRoleFinalizer(ctx context.Context) {
|
func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeManagerRoleFinalizer(ctx context.Context) {
|
||||||
if c.requeue || c.err != nil {
|
if c.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,7 +1069,6 @@ func (c *autoscalingRunnerSetFinalizerDependencyCleaner) removeManagerRoleFinali
|
|||||||
c.err = fmt.Errorf("failed to patch manager role without finalizer: %w", err)
|
c.err = fmt.Errorf("failed to patch manager role without finalizer: %w", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.requeue = true
|
|
||||||
c.logger.Info("Removed finalizer from manager role", "name", managerRoleName)
|
c.logger.Info("Removed finalizer from manager role", "name", managerRoleName)
|
||||||
return
|
return
|
||||||
case err != nil && !kerrors.IsNotFound(err):
|
case err != nil && !kerrors.IsNotFound(err):
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
autoscalingRunnerSetTestTimeout = time.Second * 5
|
autoscalingRunnerSetTestTimeout = time.Second * 20
|
||||||
autoscalingRunnerSetTestInterval = time.Millisecond * 250
|
autoscalingRunnerSetTestInterval = time.Millisecond * 250
|
||||||
autoscalingRunnerSetTestGitHubToken = "gh_token"
|
autoscalingRunnerSetTestGitHubToken = "gh_token"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||||
@@ -50,10 +49,10 @@ const (
|
|||||||
// EphemeralRunnerReconciler reconciles a EphemeralRunner object
|
// EphemeralRunnerReconciler reconciles a EphemeralRunner object
|
||||||
type EphemeralRunnerReconciler struct {
|
type EphemeralRunnerReconciler struct {
|
||||||
client.Client
|
client.Client
|
||||||
Log logr.Logger
|
Log logr.Logger
|
||||||
Scheme *runtime.Scheme
|
Scheme *runtime.Scheme
|
||||||
ActionsClient actions.MultiClient
|
ActionsClient actions.MultiClient
|
||||||
resourceBuilder resourceBuilder
|
ResourceBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:rbac:groups=actions.github.com,resources=ephemeralrunners,verbs=get;list;watch;create;update;patch;delete
|
// +kubebuilder:rbac:groups=actions.github.com,resources=ephemeralrunners,verbs=get;list;watch;create;update;patch;delete
|
||||||
@@ -150,19 +149,6 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !controllerutil.ContainsFinalizer(ephemeralRunner, ephemeralRunnerActionsFinalizerName) {
|
|
||||||
log.Info("Adding runner registration finalizer")
|
|
||||||
err := patch(ctx, r.Client, ephemeralRunner, func(obj *v1alpha1.EphemeralRunner) {
|
|
||||||
controllerutil.AddFinalizer(obj, ephemeralRunnerActionsFinalizerName)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err, "Failed to update with runner registration finalizer set")
|
|
||||||
return ctrl.Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Successfully added runner registration finalizer")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !controllerutil.ContainsFinalizer(ephemeralRunner, ephemeralRunnerFinalizerName) {
|
if !controllerutil.ContainsFinalizer(ephemeralRunner, ephemeralRunnerFinalizerName) {
|
||||||
log.Info("Adding finalizer")
|
log.Info("Adding finalizer")
|
||||||
if err := patch(ctx, r.Client, ephemeralRunner, func(obj *v1alpha1.EphemeralRunner) {
|
if err := patch(ctx, r.Client, ephemeralRunner, func(obj *v1alpha1.EphemeralRunner) {
|
||||||
@@ -176,9 +162,25 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !controllerutil.ContainsFinalizer(ephemeralRunner, ephemeralRunnerActionsFinalizerName) {
|
||||||
|
log.Info("Adding runner registration finalizer")
|
||||||
|
err := patch(ctx, r.Client, ephemeralRunner, func(obj *v1alpha1.EphemeralRunner) {
|
||||||
|
controllerutil.AddFinalizer(obj, ephemeralRunnerActionsFinalizerName)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err, "Failed to update with runner registration finalizer set")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Successfully added runner registration finalizer")
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
if ephemeralRunner.Status.RunnerId == 0 {
|
if ephemeralRunner.Status.RunnerId == 0 {
|
||||||
log.Info("Creating new ephemeral runner registration and updating status with runner config")
|
log.Info("Creating new ephemeral runner registration and updating status with runner config")
|
||||||
return r.updateStatusWithRunnerConfig(ctx, ephemeralRunner, log)
|
if r, err := r.updateStatusWithRunnerConfig(ctx, ephemeralRunner, log); r != nil {
|
||||||
|
return *r, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
secret := new(corev1.Secret)
|
secret := new(corev1.Secret)
|
||||||
@@ -189,7 +191,17 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
}
|
}
|
||||||
// create secret if not created
|
// create secret if not created
|
||||||
log.Info("Creating new ephemeral runner secret for jitconfig.")
|
log.Info("Creating new ephemeral runner secret for jitconfig.")
|
||||||
return r.createSecret(ctx, ephemeralRunner, log)
|
if r, err := r.createSecret(ctx, ephemeralRunner, log); r != nil {
|
||||||
|
return *r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry to get the secret that was just created.
|
||||||
|
// Otherwise, even though we want to continue to create the pod,
|
||||||
|
// it fails due to the missing secret resulting in an invalid pod spec.
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, secret); err != nil {
|
||||||
|
log.Error(err, "Failed to fetch secret")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pod := new(corev1.Pod)
|
pod := new(corev1.Pod)
|
||||||
@@ -295,14 +307,17 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *EphemeralRunnerReconciler) cleanupRunnerFromService(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (ctrl.Result, error) {
|
func (r *EphemeralRunnerReconciler) cleanupRunnerFromService(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (ctrl.Result, error) {
|
||||||
actionsError := &actions.ActionsError{}
|
if err := r.deleteRunnerFromService(ctx, ephemeralRunner, log); err != nil {
|
||||||
err := r.deleteRunnerFromService(ctx, ephemeralRunner, log)
|
actionsError := &actions.ActionsError{}
|
||||||
if err != nil {
|
if !errors.As(err, &actionsError) {
|
||||||
if errors.As(err, &actionsError) &&
|
log.Error(err, "Failed to clean up runner from the service (not an ActionsError)")
|
||||||
actionsError.StatusCode == http.StatusBadRequest &&
|
return ctrl.Result{}, err
|
||||||
strings.Contains(actionsError.ExceptionName, "JobStillRunningException") {
|
}
|
||||||
|
|
||||||
|
if actionsError.StatusCode == http.StatusBadRequest && actionsError.IsException("JobStillRunningException") {
|
||||||
log.Info("Runner is still running the job. Re-queue in 30 seconds")
|
log.Info("Runner is still running the job. Re-queue in 30 seconds")
|
||||||
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
|
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Error(err, "Failed clean up runner from the service")
|
log.Error(err, "Failed clean up runner from the service")
|
||||||
@@ -310,10 +325,9 @@ func (r *EphemeralRunnerReconciler) cleanupRunnerFromService(ctx context.Context
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Successfully removed runner registration from service")
|
log.Info("Successfully removed runner registration from service")
|
||||||
err = patch(ctx, r.Client, ephemeralRunner, func(obj *v1alpha1.EphemeralRunner) {
|
if err := patch(ctx, r.Client, ephemeralRunner, func(obj *v1alpha1.EphemeralRunner) {
|
||||||
controllerutil.RemoveFinalizer(obj, ephemeralRunnerActionsFinalizerName)
|
controllerutil.RemoveFinalizer(obj, ephemeralRunnerActionsFinalizerName)
|
||||||
})
|
}); err != nil {
|
||||||
if err != nil {
|
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,27 +523,35 @@ func (r *EphemeralRunnerReconciler) deletePodAsFailed(ctx context.Context, ephem
|
|||||||
|
|
||||||
// updateStatusWithRunnerConfig fetches runtime configuration needed by the runner
|
// updateStatusWithRunnerConfig fetches runtime configuration needed by the runner
|
||||||
// This method should always set .status.runnerId and .status.runnerJITConfig
|
// This method should always set .status.runnerId and .status.runnerJITConfig
|
||||||
func (r *EphemeralRunnerReconciler) updateStatusWithRunnerConfig(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (ctrl.Result, error) {
|
func (r *EphemeralRunnerReconciler) updateStatusWithRunnerConfig(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) (*ctrl.Result, error) {
|
||||||
// Runner is not registered with the service. We need to register it first
|
// Runner is not registered with the service. We need to register it first
|
||||||
log.Info("Creating ephemeral runner JIT config")
|
log.Info("Creating ephemeral runner JIT config")
|
||||||
actionsClient, err := r.actionsClientFor(ctx, ephemeralRunner)
|
actionsClient, err := r.actionsClientFor(ctx, ephemeralRunner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to get actions client for generating JIT config: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to get actions client for generating JIT config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
jitSettings := &actions.RunnerScaleSetJitRunnerSetting{
|
jitSettings := &actions.RunnerScaleSetJitRunnerSetting{
|
||||||
Name: ephemeralRunner.Name,
|
Name: ephemeralRunner.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := range ephemeralRunner.Spec.Spec.Containers {
|
||||||
|
if ephemeralRunner.Spec.Spec.Containers[i].Name == EphemeralRunnerContainerName &&
|
||||||
|
ephemeralRunner.Spec.Spec.Containers[i].WorkingDir != "" {
|
||||||
|
jitSettings.WorkFolder = ephemeralRunner.Spec.Spec.Containers[i].WorkingDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jitConfig, err := actionsClient.GenerateJitRunnerConfig(ctx, jitSettings, ephemeralRunner.Spec.RunnerScaleSetId)
|
jitConfig, err := actionsClient.GenerateJitRunnerConfig(ctx, jitSettings, ephemeralRunner.Spec.RunnerScaleSetId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
actionsError := &actions.ActionsError{}
|
actionsError := &actions.ActionsError{}
|
||||||
if !errors.As(err, &actionsError) {
|
if !errors.As(err, &actionsError) {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to generate JIT config with generic error: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to generate JIT config with generic error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if actionsError.StatusCode != http.StatusConflict ||
|
if actionsError.StatusCode != http.StatusConflict ||
|
||||||
!strings.Contains(actionsError.ExceptionName, "AgentExistsException") {
|
!actionsError.IsException("AgentExistsException") {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to generate JIT config with Actions service error: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to generate JIT config with Actions service error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the runner with the name we want already exists it means:
|
// If the runner with the name we want already exists it means:
|
||||||
@@ -542,12 +564,12 @@ func (r *EphemeralRunnerReconciler) updateStatusWithRunnerConfig(ctx context.Con
|
|||||||
log.Info("Getting runner jit config failed with conflict error, trying to get the runner by name", "runnerName", ephemeralRunner.Name)
|
log.Info("Getting runner jit config failed with conflict error, trying to get the runner by name", "runnerName", ephemeralRunner.Name)
|
||||||
existingRunner, err := actionsClient.GetRunnerByName(ctx, ephemeralRunner.Name)
|
existingRunner, err := actionsClient.GetRunnerByName(ctx, ephemeralRunner.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to get runner by name: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to get runner by name: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if existingRunner == nil {
|
if existingRunner == nil {
|
||||||
log.Info("Runner with the same name does not exist, re-queuing the reconciliation")
|
log.Info("Runner with the same name does not exist, re-queuing the reconciliation")
|
||||||
return ctrl.Result{Requeue: true}, nil
|
return &ctrl.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Found the runner with the same name", "runnerId", existingRunner.Id, "runnerScaleSetId", existingRunner.RunnerScaleSetId)
|
log.Info("Found the runner with the same name", "runnerId", existingRunner.Id, "runnerScaleSetId", existingRunner.RunnerScaleSetId)
|
||||||
@@ -555,16 +577,16 @@ func (r *EphemeralRunnerReconciler) updateStatusWithRunnerConfig(ctx context.Con
|
|||||||
log.Info("Removing the runner with the same name")
|
log.Info("Removing the runner with the same name")
|
||||||
err := actionsClient.RemoveRunner(ctx, int64(existingRunner.Id))
|
err := actionsClient.RemoveRunner(ctx, int64(existingRunner.Id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to remove runner from the service: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to remove runner from the service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Removed the runner with the same name, re-queuing the reconciliation")
|
log.Info("Removed the runner with the same name, re-queuing the reconciliation")
|
||||||
return ctrl.Result{Requeue: true}, nil
|
return &ctrl.Result{Requeue: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Do we want to mark the ephemeral runner as failed, and let EphemeralRunnerSet to clean it up, so we can recover from this situation?
|
// TODO: Do we want to mark the ephemeral runner as failed, and let EphemeralRunnerSet to clean it up, so we can recover from this situation?
|
||||||
// The situation is that the EphemeralRunner's name is already used by something else to register a runner, and we can't take the control back.
|
// The situation is that the EphemeralRunner's name is already used by something else to register a runner, and we can't take the control back.
|
||||||
return ctrl.Result{}, fmt.Errorf("runner with the same name but doesn't belong to this RunnerScaleSet: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("runner with the same name but doesn't belong to this RunnerScaleSet: %v", err)
|
||||||
}
|
}
|
||||||
log.Info("Created ephemeral runner JIT config", "runnerId", jitConfig.Runner.Id)
|
log.Info("Created ephemeral runner JIT config", "runnerId", jitConfig.Runner.Id)
|
||||||
|
|
||||||
@@ -575,11 +597,20 @@ func (r *EphemeralRunnerReconciler) updateStatusWithRunnerConfig(ctx context.Con
|
|||||||
obj.Status.RunnerJITConfig = jitConfig.EncodedJITConfig
|
obj.Status.RunnerJITConfig = jitConfig.EncodedJITConfig
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to update runner status for RunnerId/RunnerName/RunnerJITConfig: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to update runner status for RunnerId/RunnerName/RunnerJITConfig: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We want to continue without a requeue for faster pod creation.
|
||||||
|
//
|
||||||
|
// To do so, we update the status in-place, so that both continuing the loop and
|
||||||
|
// and requeuing and skipping updateStatusWithRunnerConfig in the next loop, will
|
||||||
|
// have the same effect.
|
||||||
|
ephemeralRunner.Status.RunnerId = jitConfig.Runner.Id
|
||||||
|
ephemeralRunner.Status.RunnerName = jitConfig.Runner.Name
|
||||||
|
ephemeralRunner.Status.RunnerJITConfig = jitConfig.EncodedJITConfig
|
||||||
|
|
||||||
log.Info("Updated ephemeral runner status with runnerId and runnerJITConfig")
|
log.Info("Updated ephemeral runner status with runnerId and runnerJITConfig")
|
||||||
return ctrl.Result{}, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alpha1.EphemeralRunner, secret *corev1.Secret, log logr.Logger) (ctrl.Result, error) {
|
func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alpha1.EphemeralRunner, secret *corev1.Secret, log logr.Logger) (ctrl.Result, error) {
|
||||||
@@ -632,7 +663,7 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Creating new pod for ephemeral runner")
|
log.Info("Creating new pod for ephemeral runner")
|
||||||
newPod := r.resourceBuilder.newEphemeralRunnerPod(ctx, runner, secret, envs...)
|
newPod := r.ResourceBuilder.newEphemeralRunnerPod(ctx, runner, secret, envs...)
|
||||||
|
|
||||||
if err := ctrl.SetControllerReference(runner, newPod, r.Scheme); err != nil {
|
if err := ctrl.SetControllerReference(runner, newPod, r.Scheme); err != nil {
|
||||||
log.Error(err, "Failed to set controller reference to a new pod")
|
log.Error(err, "Failed to set controller reference to a new pod")
|
||||||
@@ -655,21 +686,21 @@ func (r *EphemeralRunnerReconciler) createPod(ctx context.Context, runner *v1alp
|
|||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *EphemeralRunnerReconciler) createSecret(ctx context.Context, runner *v1alpha1.EphemeralRunner, log logr.Logger) (ctrl.Result, error) {
|
func (r *EphemeralRunnerReconciler) createSecret(ctx context.Context, runner *v1alpha1.EphemeralRunner, log logr.Logger) (*ctrl.Result, error) {
|
||||||
log.Info("Creating new secret for ephemeral runner")
|
log.Info("Creating new secret for ephemeral runner")
|
||||||
jitSecret := r.resourceBuilder.newEphemeralRunnerJitSecret(runner)
|
jitSecret := r.ResourceBuilder.newEphemeralRunnerJitSecret(runner)
|
||||||
|
|
||||||
if err := ctrl.SetControllerReference(runner, jitSecret, r.Scheme); err != nil {
|
if err := ctrl.SetControllerReference(runner, jitSecret, r.Scheme); err != nil {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to set controller reference: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to set controller reference: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Created new secret spec for ephemeral runner")
|
log.Info("Created new secret spec for ephemeral runner")
|
||||||
if err := r.Create(ctx, jitSecret); err != nil {
|
if err := r.Create(ctx, jitSecret); err != nil {
|
||||||
return ctrl.Result{}, fmt.Errorf("failed to create jit secret: %v", err)
|
return &ctrl.Result{}, fmt.Errorf("failed to create jit secret: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Created ephemeral runner secret", "secretName", jitSecret.Name)
|
log.Info("Created ephemeral runner secret", "secretName", jitSecret.Name)
|
||||||
return ctrl.Result{Requeue: true}, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateRunStatusFromPod is responsible for updating non-exiting statuses.
|
// updateRunStatusFromPod is responsible for updating non-exiting statuses.
|
||||||
@@ -784,7 +815,7 @@ func (r EphemeralRunnerReconciler) runnerRegisteredWithService(ctx context.Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if actionsError.StatusCode != http.StatusNotFound ||
|
if actionsError.StatusCode != http.StatusNotFound ||
|
||||||
!strings.Contains(actionsError.ExceptionName, "AgentNotFoundException") {
|
!actionsError.IsException("AgentNotFoundException") {
|
||||||
return false, fmt.Errorf("failed to check if runner exists in GitHub service: %v", err)
|
return false, fmt.Errorf("failed to check if runner exists in GitHub service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,14 +844,14 @@ func (r *EphemeralRunnerReconciler) deleteRunnerFromService(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
func (r *EphemeralRunnerReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
func (r *EphemeralRunnerReconciler) SetupWithManager(mgr ctrl.Manager, opts ...Option) error {
|
||||||
// TODO(nikola-jokic): Add indexing and filtering fields on corev1.Pod{}
|
return builderWithOptions(
|
||||||
return ctrl.NewControllerManagedBy(mgr).
|
ctrl.NewControllerManagedBy(mgr).
|
||||||
For(&v1alpha1.EphemeralRunner{}).
|
For(&v1alpha1.EphemeralRunner{}).
|
||||||
Owns(&corev1.Pod{}).
|
Owns(&corev1.Pod{}).
|
||||||
WithEventFilter(predicate.ResourceVersionChangedPredicate{}).
|
WithEventFilter(predicate.ResourceVersionChangedPredicate{}),
|
||||||
Named("ephemeral-runner-controller").
|
opts,
|
||||||
Complete(r)
|
).Complete(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runnerContainerStatus(pod *corev1.Pod) *corev1.ContainerStatus {
|
func runnerContainerStatus(pod *corev1.Pod) *corev1.ContainerStatus {
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
timeout = time.Second * 10
|
ephemeralRunnerTimeout = time.Second * 20
|
||||||
interval = time.Millisecond * 250
|
ephemeralRunnerInterval = time.Millisecond * 250
|
||||||
runnerImage = "ghcr.io/actions/actions-runner:latest"
|
runnerImage = "ghcr.io/actions/actions-runner:latest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newExampleRunner(name, namespace, configSecretName string) *v1alpha1.EphemeralRunner {
|
func newExampleRunner(name, namespace, configSecretName string) *v1alpha1.EphemeralRunner {
|
||||||
@@ -133,9 +133,9 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
n := len(created.Finalizers) // avoid capacity mismatch
|
n := len(created.Finalizers) // avoid capacity mismatch
|
||||||
return created.Finalizers[:n:n], nil
|
return created.Finalizers[:n:n], nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo([]string{ephemeralRunnerActionsFinalizerName, ephemeralRunnerFinalizerName}))
|
).Should(BeEquivalentTo([]string{ephemeralRunnerFinalizerName, ephemeralRunnerActionsFinalizerName}))
|
||||||
|
|
||||||
Eventually(
|
Eventually(
|
||||||
func() (bool, error) {
|
func() (bool, error) {
|
||||||
@@ -147,8 +147,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
_, ok := secret.Data[jitTokenKey]
|
_, ok := secret.Data[jitTokenKey]
|
||||||
return ok, nil
|
return ok, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
Eventually(
|
Eventually(
|
||||||
@@ -160,8 +160,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
|
|
||||||
return pod.Name, nil
|
return pod.Name, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(ephemeralRunner.Name))
|
).Should(BeEquivalentTo(ephemeralRunner.Name))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -184,8 +184,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return updated.Status.Phase, nil
|
return updated.Status.Phase, nil
|
||||||
}, timeout, interval).Should(BeEquivalentTo(corev1.PodFailed))
|
}, ephemeralRunnerTimeout, ephemeralRunnerInterval).Should(BeEquivalentTo(corev1.PodFailed))
|
||||||
Expect(updated.Status.Reason).Should(Equal("InvalidPod"))
|
Expect(updated.Status.Reason).Should(Equal("InvalidPod"))
|
||||||
Expect(updated.Status.Message).Should(Equal("Failed to create the pod: pods \"invalid-ephemeral-runner\" is forbidden: no PriorityClass with name notexist was found"))
|
Expect(updated.Status.Message).Should(Equal("Failed to create the pod: pods \"invalid-ephemeral-runner\" is forbidden: no PriorityClass with name notexist was found"))
|
||||||
})
|
})
|
||||||
@@ -247,8 +247,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
// create runner linked secret
|
// create runner linked secret
|
||||||
@@ -273,8 +273,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
err = k8sClient.Delete(ctx, ephemeralRunner)
|
err = k8sClient.Delete(ctx, ephemeralRunner)
|
||||||
@@ -289,8 +289,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return kerrors.IsNotFound(err), nil
|
return kerrors.IsNotFound(err), nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
Eventually(
|
Eventually(
|
||||||
@@ -302,8 +302,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return kerrors.IsNotFound(err), nil
|
return kerrors.IsNotFound(err), nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
Eventually(
|
Eventually(
|
||||||
@@ -315,8 +315,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return kerrors.IsNotFound(err), nil
|
return kerrors.IsNotFound(err), nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
Eventually(
|
Eventually(
|
||||||
@@ -328,8 +328,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return kerrors.IsNotFound(err), nil
|
return kerrors.IsNotFound(err), nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
Eventually(
|
Eventually(
|
||||||
@@ -341,8 +341,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return kerrors.IsNotFound(err), nil
|
return kerrors.IsNotFound(err), nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -356,8 +356,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return updatedEphemeralRunner.Status.RunnerId, nil
|
return updatedEphemeralRunner.Status.RunnerId, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeNumerically(">", 0))
|
).Should(BeNumerically(">", 0))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -371,8 +371,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
for _, phase := range []corev1.PodPhase{corev1.PodRunning, corev1.PodPending} {
|
for _, phase := range []corev1.PodPhase{corev1.PodRunning, corev1.PodPending} {
|
||||||
@@ -395,8 +395,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return updated.Status.Phase, nil
|
return updated.Status.Phase, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(phase))
|
).Should(BeEquivalentTo(phase))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -411,8 +411,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
pod.Status.Phase = corev1.PodRunning
|
pod.Status.Phase = corev1.PodRunning
|
||||||
@@ -427,7 +427,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return updated.Status.Phase, nil
|
return updated.Status.Phase, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
).Should(BeEquivalentTo(""))
|
).Should(BeEquivalentTo(""))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -462,8 +462,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
Expect(err).To(BeNil(), "Failed to update pod status")
|
Expect(err).To(BeNil(), "Failed to update pod status")
|
||||||
return false, fmt.Errorf("pod haven't failed for 5 times.")
|
return false, fmt.Errorf("pod haven't failed for 5 times.")
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true), "we should stop creating pod after 5 failures")
|
).Should(BeEquivalentTo(true), "we should stop creating pod after 5 failures")
|
||||||
|
|
||||||
// In case we still have pod created due to controller-runtime cache delay, mark the container as exited
|
// In case we still have pod created due to controller-runtime cache delay, mark the container as exited
|
||||||
@@ -488,7 +488,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return updated.Status.Reason, nil
|
return updated.Status.Reason, nil
|
||||||
}, timeout, interval).Should(BeEquivalentTo("TooManyPodFailures"), "Reason should be TooManyPodFailures")
|
}, ephemeralRunnerTimeout, ephemeralRunnerInterval).Should(BeEquivalentTo("TooManyPodFailures"), "Reason should be TooManyPodFailures")
|
||||||
|
|
||||||
// EphemeralRunner should not have any pod
|
// EphemeralRunner should not have any pod
|
||||||
Eventually(func() (bool, error) {
|
Eventually(func() (bool, error) {
|
||||||
@@ -497,7 +497,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return kerrors.IsNotFound(err), nil
|
return kerrors.IsNotFound(err), nil
|
||||||
}, timeout, interval).Should(BeEquivalentTo(true))
|
}, ephemeralRunnerTimeout, ephemeralRunnerInterval).Should(BeEquivalentTo(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("It should re-create pod on eviction", func() {
|
It("It should re-create pod on eviction", func() {
|
||||||
@@ -510,8 +510,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
pod.Status.Phase = corev1.PodFailed
|
pod.Status.Phase = corev1.PodFailed
|
||||||
@@ -530,7 +530,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return len(updated.Status.Failures) == 1, nil
|
return len(updated.Status.Failures) == 1, nil
|
||||||
}, timeout, interval).Should(BeEquivalentTo(true))
|
}, ephemeralRunnerTimeout, ephemeralRunnerInterval).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
// should re-create after failure
|
// should re-create after failure
|
||||||
Eventually(
|
Eventually(
|
||||||
@@ -541,8 +541,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -555,8 +555,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
||||||
@@ -577,7 +577,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return len(updated.Status.Failures) == 1, nil
|
return len(updated.Status.Failures) == 1, nil
|
||||||
}, timeout, interval).Should(BeEquivalentTo(true))
|
}, ephemeralRunnerTimeout, ephemeralRunnerInterval).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
// should re-create after failure
|
// should re-create after failure
|
||||||
Eventually(
|
Eventually(
|
||||||
@@ -588,8 +588,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -602,8 +602,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
// first set phase to running
|
// first set phase to running
|
||||||
@@ -627,8 +627,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return updated.Status.Phase, nil
|
return updated.Status.Phase, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(corev1.PodRunning))
|
).Should(BeEquivalentTo(corev1.PodRunning))
|
||||||
|
|
||||||
// set phase to succeeded
|
// set phase to succeeded
|
||||||
@@ -644,7 +644,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
}
|
}
|
||||||
return updated.Status.Phase, nil
|
return updated.Status.Phase, nil
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
).Should(BeEquivalentTo(corev1.PodRunning))
|
).Should(BeEquivalentTo(corev1.PodRunning))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -671,8 +671,10 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
fake.WithGetRunner(
|
fake.WithGetRunner(
|
||||||
nil,
|
nil,
|
||||||
&actions.ActionsError{
|
&actions.ActionsError{
|
||||||
StatusCode: http.StatusNotFound,
|
StatusCode: http.StatusNotFound,
|
||||||
ExceptionName: "AgentNotFoundException",
|
Err: &actions.ActionsExceptionError{
|
||||||
|
ExceptionName: "AgentNotFoundException",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -698,7 +700,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}, timeout, interval).Should(BeEquivalentTo(true))
|
}, ephemeralRunnerTimeout, ephemeralRunnerInterval).Should(BeEquivalentTo(true))
|
||||||
|
|
||||||
pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
pod.Status.ContainerStatuses = append(pod.Status.ContainerStatuses, corev1.ContainerStatus{
|
||||||
Name: EphemeralRunnerContainerName,
|
Name: EphemeralRunnerContainerName,
|
||||||
@@ -718,7 +720,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
return updated.Status.Phase, nil
|
return updated.Status.Phase, nil
|
||||||
}, timeout, interval).Should(BeEquivalentTo(corev1.PodSucceeded))
|
}, ephemeralRunnerTimeout, ephemeralRunnerInterval).Should(BeEquivalentTo(corev1.PodSucceeded))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -798,7 +800,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return proxySuccessfulllyCalled
|
return proxySuccessfulllyCalled
|
||||||
},
|
},
|
||||||
2*time.Second,
|
2*time.Second,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeEquivalentTo(true))
|
).Should(BeEquivalentTo(true))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -823,8 +825,8 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod)
|
||||||
g.Expect(err).To(BeNil(), "failed to get ephemeral runner pod")
|
g.Expect(err).To(BeNil(), "failed to get ephemeral runner pod")
|
||||||
},
|
},
|
||||||
timeout,
|
ephemeralRunnerTimeout,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(Succeed(), "failed to get ephemeral runner pod")
|
).Should(Succeed(), "failed to get ephemeral runner pod")
|
||||||
|
|
||||||
Expect(pod.Spec.Containers[0].Env).To(ContainElement(corev1.EnvVar{
|
Expect(pod.Spec.Containers[0].Env).To(ContainElement(corev1.EnvVar{
|
||||||
@@ -956,7 +958,7 @@ var _ = Describe("EphemeralRunner", func() {
|
|||||||
return serverSuccessfullyCalled
|
return serverSuccessfullyCalled
|
||||||
},
|
},
|
||||||
2*time.Second,
|
2*time.Second,
|
||||||
interval,
|
ephemeralRunnerInterval,
|
||||||
).Should(BeTrue(), "failed to contact server")
|
).Should(BeTrue(), "failed to contact server")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||||
"github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics"
|
"github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics"
|
||||||
@@ -54,7 +53,7 @@ type EphemeralRunnerSetReconciler struct {
|
|||||||
|
|
||||||
PublishMetrics bool
|
PublishMetrics bool
|
||||||
|
|
||||||
resourceBuilder resourceBuilder
|
ResourceBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
//+kubebuilder:rbac:groups=actions.github.com,resources=ephemeralrunnersets,verbs=get;list;watch;create;update;patch;delete
|
//+kubebuilder:rbac:groups=actions.github.com,resources=ephemeralrunnersets,verbs=get;list;watch;create;update;patch;delete
|
||||||
@@ -197,7 +196,6 @@ func (r *EphemeralRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl.R
|
|||||||
log.Error(err, "failed to cleanup finished ephemeral runners")
|
log.Error(err, "failed to cleanup finished ephemeral runners")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Info("Scaling comparison", "current", total, "desired", ephemeralRunnerSet.Spec.Replicas)
|
log.Info("Scaling comparison", "current", total, "desired", ephemeralRunnerSet.Spec.Replicas)
|
||||||
switch {
|
switch {
|
||||||
case total < ephemeralRunnerSet.Spec.Replicas: // Handle scale up
|
case total < ephemeralRunnerSet.Spec.Replicas: // Handle scale up
|
||||||
@@ -208,7 +206,12 @@ func (r *EphemeralRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl.R
|
|||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case total > ephemeralRunnerSet.Spec.Replicas: // Handle scale down scenario.
|
case ephemeralRunnerSet.Spec.PatchID > 0 && total >= ephemeralRunnerSet.Spec.Replicas: // Handle scale down scenario.
|
||||||
|
// If ephemeral runner did not yet update the phase to succeeded, but the scale down
|
||||||
|
// request is issued, we should ignore the scale down request.
|
||||||
|
// Eventually, the ephemeral runner will be cleaned up on the next patch request, which happens
|
||||||
|
// on the next batch
|
||||||
|
case ephemeralRunnerSet.Spec.PatchID == 0 && total > ephemeralRunnerSet.Spec.Replicas:
|
||||||
count := total - ephemeralRunnerSet.Spec.Replicas
|
count := total - ephemeralRunnerSet.Spec.Replicas
|
||||||
log.Info("Deleting ephemeral runners (scale down)", "count", count)
|
log.Info("Deleting ephemeral runners (scale down)", "count", count)
|
||||||
if err := r.deleteIdleEphemeralRunners(
|
if err := r.deleteIdleEphemeralRunners(
|
||||||
@@ -357,7 +360,7 @@ func (r *EphemeralRunnerSetReconciler) createEphemeralRunners(ctx context.Contex
|
|||||||
// Track multiple errors at once and return the bundle.
|
// Track multiple errors at once and return the bundle.
|
||||||
errs := make([]error, 0)
|
errs := make([]error, 0)
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
ephemeralRunner := r.resourceBuilder.newEphemeralRunner(runnerSet)
|
ephemeralRunner := r.ResourceBuilder.newEphemeralRunner(runnerSet)
|
||||||
if runnerSet.Spec.EphemeralRunnerSpec.Proxy != nil {
|
if runnerSet.Spec.EphemeralRunnerSpec.Proxy != nil {
|
||||||
ephemeralRunner.Spec.ProxySecretRef = proxyEphemeralRunnerSetSecretName(runnerSet)
|
ephemeralRunner.Spec.ProxySecretRef = proxyEphemeralRunnerSetSecretName(runnerSet)
|
||||||
}
|
}
|
||||||
@@ -428,6 +431,9 @@ func (r *EphemeralRunnerSetReconciler) createProxySecret(ctx context.Context, ep
|
|||||||
// When this happens, the next reconcile loop will try to delete the remaining ephemeral runners
|
// When this happens, the next reconcile loop will try to delete the remaining ephemeral runners
|
||||||
// after we get notified by any of the `v1alpha1.EphemeralRunner.Status` updates.
|
// after we get notified by any of the `v1alpha1.EphemeralRunner.Status` updates.
|
||||||
func (r *EphemeralRunnerSetReconciler) deleteIdleEphemeralRunners(ctx context.Context, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, pendingEphemeralRunners, runningEphemeralRunners []*v1alpha1.EphemeralRunner, count int, log logr.Logger) error {
|
func (r *EphemeralRunnerSetReconciler) deleteIdleEphemeralRunners(ctx context.Context, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, pendingEphemeralRunners, runningEphemeralRunners []*v1alpha1.EphemeralRunner, count int, log logr.Logger) error {
|
||||||
|
if count <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
runners := newEphemeralRunnerStepper(pendingEphemeralRunners, runningEphemeralRunners)
|
runners := newEphemeralRunnerStepper(pendingEphemeralRunners, runningEphemeralRunners)
|
||||||
if runners.len() == 0 {
|
if runners.len() == 0 {
|
||||||
log.Info("No pending or running ephemeral runners running at this time for scale down")
|
log.Info("No pending or running ephemeral runners running at this time for scale down")
|
||||||
@@ -473,10 +479,14 @@ func (r *EphemeralRunnerSetReconciler) deleteIdleEphemeralRunners(ctx context.Co
|
|||||||
func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, actionsClient actions.ActionsService, log logr.Logger) (bool, error) {
|
func (r *EphemeralRunnerSetReconciler) deleteEphemeralRunnerWithActionsClient(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, actionsClient actions.ActionsService, log logr.Logger) (bool, error) {
|
||||||
if err := actionsClient.RemoveRunner(ctx, int64(ephemeralRunner.Status.RunnerId)); err != nil {
|
if err := actionsClient.RemoveRunner(ctx, int64(ephemeralRunner.Status.RunnerId)); err != nil {
|
||||||
actionsError := &actions.ActionsError{}
|
actionsError := &actions.ActionsError{}
|
||||||
if errors.As(err, &actionsError) &&
|
if !errors.As(err, &actionsError) {
|
||||||
actionsError.StatusCode == http.StatusBadRequest &&
|
log.Error(err, "failed to remove runner from the service", "name", ephemeralRunner.Name, "runnerId", ephemeralRunner.Status.RunnerId)
|
||||||
strings.Contains(actionsError.ExceptionName, "JobStillRunningException") {
|
return false, err
|
||||||
// Runner is still running a job, proceed with the next one
|
}
|
||||||
|
|
||||||
|
if actionsError.StatusCode == http.StatusBadRequest &&
|
||||||
|
actionsError.IsException("JobStillRunningException") {
|
||||||
|
log.Info("Runner is still running a job, skipping deletion", "name", ephemeralRunner.Name, "runnerId", ephemeralRunner.Status.RunnerId)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,28 +571,6 @@ func (r *EphemeralRunnerSetReconciler) actionsClientOptionsFor(ctx context.Conte
|
|||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
func (r *EphemeralRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
func (r *EphemeralRunnerSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
// Index EphemeralRunner owned by EphemeralRunnerSet so we can perform faster look ups.
|
|
||||||
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &v1alpha1.EphemeralRunner{}, resourceOwnerKey, func(rawObj client.Object) []string {
|
|
||||||
groupVersion := v1alpha1.GroupVersion.String()
|
|
||||||
|
|
||||||
// grab the job object, extract the owner...
|
|
||||||
ephemeralRunner := rawObj.(*v1alpha1.EphemeralRunner)
|
|
||||||
owner := metav1.GetControllerOf(ephemeralRunner)
|
|
||||||
if owner == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...make sure it is owned by this controller
|
|
||||||
if owner.APIVersion != groupVersion || owner.Kind != "EphemeralRunnerSet" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and if so, return it
|
|
||||||
return []string{owner.Name}
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctrl.NewControllerManagedBy(mgr).
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
For(&v1alpha1.EphemeralRunnerSet{}).
|
For(&v1alpha1.EphemeralRunnerSet{}).
|
||||||
Owns(&v1alpha1.EphemeralRunner{}).
|
Owns(&v1alpha1.EphemeralRunner{}).
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,9 @@ const defaultGitHubToken = "gh_token"
|
|||||||
|
|
||||||
func startManagers(t ginkgo.GinkgoTInterface, first manager.Manager, others ...manager.Manager) {
|
func startManagers(t ginkgo.GinkgoTInterface, first manager.Manager, others ...manager.Manager) {
|
||||||
for _, mgr := range append([]manager.Manager{first}, others...) {
|
for _, mgr := range append([]manager.Manager{first}, others...) {
|
||||||
|
if err := SetupIndexers(mgr); err != nil {
|
||||||
|
t.Fatalf("failed to setup indexers: %v", err)
|
||||||
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
g, ctx := errgroup.WithContext(ctx)
|
g, ctx := errgroup.WithContext(ctx)
|
||||||
|
|||||||
71
controllers/actions.github.com/indexer.go
Normal file
71
controllers/actions.github.com/indexer.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package actionsgithubcom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
v1alpha1 "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupIndexers(mgr ctrl.Manager) error {
|
||||||
|
if err := mgr.GetFieldIndexer().IndexField(
|
||||||
|
context.Background(),
|
||||||
|
&corev1.Pod{},
|
||||||
|
resourceOwnerKey,
|
||||||
|
newGroupVersionOwnerKindIndexer("AutoscalingListener", "EphemeralRunner"),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.GetFieldIndexer().IndexField(
|
||||||
|
context.Background(),
|
||||||
|
&corev1.ServiceAccount{},
|
||||||
|
resourceOwnerKey,
|
||||||
|
newGroupVersionOwnerKindIndexer("AutoscalingListener"),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.GetFieldIndexer().IndexField(
|
||||||
|
context.Background(),
|
||||||
|
&v1alpha1.EphemeralRunnerSet{},
|
||||||
|
resourceOwnerKey,
|
||||||
|
newGroupVersionOwnerKindIndexer("AutoscalingRunnerSet"),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.GetFieldIndexer().IndexField(
|
||||||
|
context.Background(),
|
||||||
|
&v1alpha1.EphemeralRunner{},
|
||||||
|
resourceOwnerKey,
|
||||||
|
newGroupVersionOwnerKindIndexer("EphemeralRunnerSet"),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGroupVersionOwnerKindIndexer(ownerKind string, otherOwnerKinds ...string) client.IndexerFunc {
|
||||||
|
owners := append([]string{ownerKind}, otherOwnerKinds...)
|
||||||
|
return func(o client.Object) []string {
|
||||||
|
groupVersion := v1alpha1.GroupVersion.String()
|
||||||
|
owner := metav1.GetControllerOfNoCopy(o)
|
||||||
|
if owner == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...make sure it is owned by this controller
|
||||||
|
if owner.APIVersion != groupVersion || !slices.Contains(owners, owner.Kind) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and if so, return it
|
||||||
|
return []string{owner.Name}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
controllers/actions.github.com/options.go
Normal file
90
controllers/actions.github.com/options.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package actionsgithubcom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options is the optional configuration for the controllers, which can be
|
||||||
|
// set via command-line flags or environment variables.
|
||||||
|
type Options struct {
|
||||||
|
// RunnerMaxConcuncurrentReconciles is the maximum number of concurrent Reconciles which can be run
|
||||||
|
// by the EphemeralRunnerController.
|
||||||
|
RunnerMaxConcuncurrentReconciles int
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionsWithDefault returns the default options.
|
||||||
|
// This is here to maintain the options and their default values in one place,
|
||||||
|
// rather than having to correlate those in multiple places.
|
||||||
|
func OptionsWithDefault() Options {
|
||||||
|
return Options{
|
||||||
|
RunnerMaxConcuncurrentReconciles: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadEnv loads the options from the environment variables.
|
||||||
|
// This updates the option value only if the environment variable is set.
|
||||||
|
// If the option is already set (via a command-line flag), the value from the environment variable takes precedence.
|
||||||
|
func (o *Options) LoadEnv() error {
|
||||||
|
v, err := o.getEnvInt("RUNNER_MAX_CONCURRENT_RECONCILES")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if v != nil {
|
||||||
|
o.RunnerMaxConcuncurrentReconciles = *v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) getEnvInt(name string) (*int, error) {
|
||||||
|
s := os.Getenv(name)
|
||||||
|
if s == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert %s=%s to int: %w", name, s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*controller.Options)
|
||||||
|
|
||||||
|
// WithMaxConcurrentReconciles sets the maximum number of concurrent Reconciles which can be run.
|
||||||
|
//
|
||||||
|
// This is useful to improve the throughput of the controller, but it may also increase the load on the API server and
|
||||||
|
// the external service (e.g. GitHub API). The default value is 1, as defined by the controller-runtime.
|
||||||
|
//
|
||||||
|
// See https://github.com/actions/actions-runner-controller/issues/3021 for more information
|
||||||
|
// on real-world use cases and the potential impact of this option.
|
||||||
|
func WithMaxConcurrentReconciles(n int) Option {
|
||||||
|
return func(b *controller.Options) {
|
||||||
|
b.MaxConcurrentReconciles = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// builderWithOptions applies the given options to the provided builder, if any.
|
||||||
|
// This is a helper function to avoid the need to import the controller-runtime package in every reconciler source file
|
||||||
|
// and the command package that creates the controller.
|
||||||
|
// This is also useful for reducing code duplication around setting controller options in
|
||||||
|
// multiple reconcilers.
|
||||||
|
func builderWithOptions(b *builder.Builder, opts []Option) *builder.Builder {
|
||||||
|
if len(opts) == 0 {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
var controllerOpts controller.Options
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&controllerOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.WithOptions(controllerOpts)
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
"github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1"
|
||||||
"github.com/actions/actions-runner-controller/build"
|
"github.com/actions/actions-runner-controller/build"
|
||||||
@@ -68,9 +69,11 @@ func SetListenerEntrypoint(entrypoint string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type resourceBuilder struct{}
|
type ResourceBuilder struct {
|
||||||
|
ExcludeLabelPropagationPrefixes []string
|
||||||
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) {
|
func (b *ResourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) {
|
||||||
runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey])
|
runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -85,13 +88,13 @@ func (b *resourceBuilder) newAutoScalingListener(autoscalingRunnerSet *v1alpha1.
|
|||||||
effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners
|
effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners
|
||||||
}
|
}
|
||||||
|
|
||||||
labels := map[string]string{
|
labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{
|
||||||
LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace,
|
LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace,
|
||||||
LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name,
|
LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name,
|
||||||
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
||||||
LabelKeyKubernetesComponent: "runner-scale-set-listener",
|
LabelKeyKubernetesComponent: "runner-scale-set-listener",
|
||||||
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
||||||
}
|
})
|
||||||
|
|
||||||
annotations := map[string]string{
|
annotations := map[string]string{
|
||||||
annotationKeyRunnerSpecHash: autoscalingRunnerSet.ListenerSpecHash(),
|
annotationKeyRunnerSpecHash: autoscalingRunnerSet.ListenerSpecHash(),
|
||||||
@@ -150,7 +153,7 @@ func (lm *listenerMetricsServerConfig) containerPort() (corev1.ContainerPort, er
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, cert string) (*corev1.Secret, error) {
|
func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, cert string) (*corev1.Secret, error) {
|
||||||
var (
|
var (
|
||||||
metricsAddr = ""
|
metricsAddr = ""
|
||||||
metricsEndpoint = ""
|
metricsEndpoint = ""
|
||||||
@@ -213,7 +216,7 @@ func (b *resourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.AutoscalingListener, podConfig *corev1.Secret, serviceAccount *corev1.ServiceAccount, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, envs ...corev1.EnvVar) (*corev1.Pod, error) {
|
func (b *ResourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.AutoscalingListener, podConfig *corev1.Secret, serviceAccount *corev1.ServiceAccount, secret *corev1.Secret, metricsConfig *listenerMetricsServerConfig, envs ...corev1.EnvVar) (*corev1.Pod, error) {
|
||||||
listenerEnv := []corev1.EnvVar{
|
listenerEnv := []corev1.EnvVar{
|
||||||
{
|
{
|
||||||
Name: "LISTENER_CONFIG_PATH",
|
Name: "LISTENER_CONFIG_PATH",
|
||||||
@@ -406,33 +409,33 @@ func mergeListenerContainer(base, from *corev1.Container) {
|
|||||||
base.TTY = from.TTY
|
base.TTY = from.TTY
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener *v1alpha1.AutoscalingListener) *corev1.ServiceAccount {
|
func (b *ResourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener *v1alpha1.AutoscalingListener) *corev1.ServiceAccount {
|
||||||
return &corev1.ServiceAccount{
|
return &corev1.ServiceAccount{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: scaleSetListenerServiceAccountName(autoscalingListener),
|
Name: scaleSetListenerServiceAccountName(autoscalingListener),
|
||||||
Namespace: autoscalingListener.Namespace,
|
Namespace: autoscalingListener.Namespace,
|
||||||
Labels: map[string]string{
|
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
|
||||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1.AutoscalingListener) *rbacv1.Role {
|
func (b *ResourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1.AutoscalingListener) *rbacv1.Role {
|
||||||
rules := rulesForListenerRole([]string{autoscalingListener.Spec.EphemeralRunnerSetName})
|
rules := rulesForListenerRole([]string{autoscalingListener.Spec.EphemeralRunnerSetName})
|
||||||
rulesHash := hash.ComputeTemplateHash(&rules)
|
rulesHash := hash.ComputeTemplateHash(&rules)
|
||||||
newRole := &rbacv1.Role{
|
newRole := &rbacv1.Role{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: scaleSetListenerRoleName(autoscalingListener),
|
Name: scaleSetListenerRoleName(autoscalingListener),
|
||||||
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||||
Labels: map[string]string{
|
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
|
||||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||||
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
||||||
labelKeyListenerName: autoscalingListener.Name,
|
labelKeyListenerName: autoscalingListener.Name,
|
||||||
"role-policy-rules-hash": rulesHash,
|
"role-policy-rules-hash": rulesHash,
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
Rules: rules,
|
Rules: rules,
|
||||||
}
|
}
|
||||||
@@ -440,7 +443,7 @@ func (b *resourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1.
|
|||||||
return newRole
|
return newRole
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1alpha1.AutoscalingListener, listenerRole *rbacv1.Role, serviceAccount *corev1.ServiceAccount) *rbacv1.RoleBinding {
|
func (b *ResourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1alpha1.AutoscalingListener, listenerRole *rbacv1.Role, serviceAccount *corev1.ServiceAccount) *rbacv1.RoleBinding {
|
||||||
roleRef := rbacv1.RoleRef{
|
roleRef := rbacv1.RoleRef{
|
||||||
Kind: "Role",
|
Kind: "Role",
|
||||||
Name: listenerRole.Name,
|
Name: listenerRole.Name,
|
||||||
@@ -460,14 +463,14 @@ func (b *resourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: scaleSetListenerRoleName(autoscalingListener),
|
Name: scaleSetListenerRoleName(autoscalingListener),
|
||||||
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
Namespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||||
Labels: map[string]string{
|
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
|
||||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||||
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
labelKeyListenerNamespace: autoscalingListener.Namespace,
|
||||||
labelKeyListenerName: autoscalingListener.Name,
|
labelKeyListenerName: autoscalingListener.Name,
|
||||||
"role-binding-role-ref-hash": roleRefHash,
|
"role-binding-role-ref-hash": roleRefHash,
|
||||||
"role-binding-subject-hash": subjectHash,
|
"role-binding-subject-hash": subjectHash,
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
RoleRef: roleRef,
|
RoleRef: roleRef,
|
||||||
Subjects: subjects,
|
Subjects: subjects,
|
||||||
@@ -476,18 +479,18 @@ func (b *resourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1
|
|||||||
return newRoleBinding
|
return newRoleBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret) *corev1.Secret {
|
func (b *ResourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v1alpha1.AutoscalingListener, secret *corev1.Secret) *corev1.Secret {
|
||||||
dataHash := hash.ComputeTemplateHash(&secret.Data)
|
dataHash := hash.ComputeTemplateHash(&secret.Data)
|
||||||
|
|
||||||
newListenerSecret := &corev1.Secret{
|
newListenerSecret := &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: scaleSetListenerSecretMirrorName(autoscalingListener),
|
Name: scaleSetListenerSecretMirrorName(autoscalingListener),
|
||||||
Namespace: autoscalingListener.Namespace,
|
Namespace: autoscalingListener.Namespace,
|
||||||
Labels: map[string]string{
|
Labels: b.mergeLabels(autoscalingListener.Labels, map[string]string{
|
||||||
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
LabelKeyGitHubScaleSetNamespace: autoscalingListener.Spec.AutoscalingRunnerSetNamespace,
|
||||||
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
LabelKeyGitHubScaleSetName: autoscalingListener.Spec.AutoscalingRunnerSetName,
|
||||||
"secret-data-hash": dataHash,
|
"secret-data-hash": dataHash,
|
||||||
},
|
}),
|
||||||
},
|
},
|
||||||
Data: secret.DeepCopy().Data,
|
Data: secret.DeepCopy().Data,
|
||||||
}
|
}
|
||||||
@@ -495,27 +498,26 @@ func (b *resourceBuilder) newScaleSetListenerSecretMirror(autoscalingListener *v
|
|||||||
return newListenerSecret
|
return newListenerSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) (*v1alpha1.EphemeralRunnerSet, error) {
|
func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet) (*v1alpha1.EphemeralRunnerSet, error) {
|
||||||
runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey])
|
runnerScaleSetId, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIdAnnotationKey])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
runnerSpecHash := autoscalingRunnerSet.RunnerSetSpecHash()
|
runnerSpecHash := autoscalingRunnerSet.RunnerSetSpecHash()
|
||||||
|
|
||||||
labels := map[string]string{
|
labels := b.mergeLabels(autoscalingRunnerSet.Labels, map[string]string{
|
||||||
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
||||||
LabelKeyKubernetesComponent: "runner-set",
|
LabelKeyKubernetesComponent: "runner-set",
|
||||||
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
LabelKeyKubernetesVersion: autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion],
|
||||||
LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name,
|
LabelKeyGitHubScaleSetName: autoscalingRunnerSet.Name,
|
||||||
LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace,
|
LabelKeyGitHubScaleSetNamespace: autoscalingRunnerSet.Namespace,
|
||||||
}
|
})
|
||||||
|
|
||||||
if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, labels); err != nil {
|
if err := applyGitHubURLLabels(autoscalingRunnerSet.Spec.GitHubConfigUrl, labels); err != nil {
|
||||||
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
|
return nil, fmt.Errorf("failed to apply GitHub URL labels: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newAnnotations := map[string]string{
|
newAnnotations := map[string]string{
|
||||||
|
|
||||||
AnnotationKeyGitHubRunnerGroupName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName],
|
AnnotationKeyGitHubRunnerGroupName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName],
|
||||||
AnnotationKeyGitHubRunnerScaleSetName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName],
|
AnnotationKeyGitHubRunnerScaleSetName: autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName],
|
||||||
annotationKeyRunnerSpecHash: runnerSpecHash,
|
annotationKeyRunnerSpecHash: runnerSpecHash,
|
||||||
@@ -545,20 +547,16 @@ func (b *resourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A
|
|||||||
return newEphemeralRunnerSet, nil
|
return newEphemeralRunnerSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet) *v1alpha1.EphemeralRunner {
|
func (b *ResourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet) *v1alpha1.EphemeralRunner {
|
||||||
labels := make(map[string]string)
|
labels := make(map[string]string)
|
||||||
for _, key := range commonLabelKeys {
|
for k, v := range ephemeralRunnerSet.Labels {
|
||||||
switch key {
|
if k == LabelKeyKubernetesComponent {
|
||||||
case LabelKeyKubernetesComponent:
|
labels[k] = "runner"
|
||||||
labels[key] = "runner"
|
} else {
|
||||||
default:
|
labels[k] = v
|
||||||
v, ok := ephemeralRunnerSet.Labels[key]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
labels[key] = v
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
for key, val := range ephemeralRunnerSet.Annotations {
|
for key, val := range ephemeralRunnerSet.Annotations {
|
||||||
annotations[key] = val
|
annotations[key] = val
|
||||||
@@ -576,7 +574,7 @@ func (b *resourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.Epheme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1alpha1.EphemeralRunner, secret *corev1.Secret, envs ...corev1.EnvVar) *corev1.Pod {
|
func (b *ResourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1alpha1.EphemeralRunner, secret *corev1.Secret, envs ...corev1.EnvVar) *corev1.Pod {
|
||||||
var newPod corev1.Pod
|
var newPod corev1.Pod
|
||||||
|
|
||||||
labels := map[string]string{}
|
labels := map[string]string{}
|
||||||
@@ -644,7 +642,7 @@ func (b *resourceBuilder) newEphemeralRunnerPod(ctx context.Context, runner *v1a
|
|||||||
return &newPod
|
return &newPod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *resourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner) *corev1.Secret {
|
func (b *ResourceBuilder) newEphemeralRunnerJitSecret(ephemeralRunner *v1alpha1.EphemeralRunner) *corev1.Secret {
|
||||||
return &corev1.Secret{
|
return &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: ephemeralRunner.Name,
|
Name: ephemeralRunner.Name,
|
||||||
@@ -751,3 +749,29 @@ func trimLabelValue(val string) string {
|
|||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *ResourceBuilder) mergeLabels(base, overwrite map[string]string) map[string]string {
|
||||||
|
mergedLabels := make(map[string]string, len(base))
|
||||||
|
|
||||||
|
base:
|
||||||
|
for k, v := range base {
|
||||||
|
for _, prefix := range b.ExcludeLabelPropagationPrefixes {
|
||||||
|
if strings.HasPrefix(k, prefix) {
|
||||||
|
continue base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mergedLabels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
overwrite:
|
||||||
|
for k, v := range overwrite {
|
||||||
|
for _, prefix := range b.ExcludeLabelPropagationPrefixes {
|
||||||
|
if strings.HasPrefix(k, prefix) {
|
||||||
|
continue overwrite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mergedLabels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedLabels
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,8 +19,13 @@ func TestLabelPropagation(t *testing.T) {
|
|||||||
Name: "test-scale-set",
|
Name: "test-scale-set",
|
||||||
Namespace: "test-ns",
|
Namespace: "test-ns",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
LabelKeyKubernetesPartOf: labelValueKubernetesPartOf,
|
||||||
LabelKeyKubernetesVersion: "0.2.0",
|
LabelKeyKubernetesVersion: "0.2.0",
|
||||||
|
"arbitrary-label": "random-value",
|
||||||
|
"example.com/label": "example-value",
|
||||||
|
"example.com/example": "example-value",
|
||||||
|
"directly.excluded.org/label": "excluded-value",
|
||||||
|
"directly.excluded.org/arbitrary": "not-excluded-value",
|
||||||
},
|
},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
runnerScaleSetIdAnnotationKey: "1",
|
runnerScaleSetIdAnnotationKey: "1",
|
||||||
@@ -33,7 +38,12 @@ func TestLabelPropagation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var b resourceBuilder
|
b := ResourceBuilder{
|
||||||
|
ExcludeLabelPropagationPrefixes: []string{
|
||||||
|
"example.com/",
|
||||||
|
"directly.excluded.org/label",
|
||||||
|
},
|
||||||
|
}
|
||||||
ephemeralRunnerSet, err := b.newEphemeralRunnerSet(&autoscalingRunnerSet)
|
ephemeralRunnerSet, err := b.newEphemeralRunnerSet(&autoscalingRunnerSet)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, labelValueKubernetesPartOf, ephemeralRunnerSet.Labels[LabelKeyKubernetesPartOf])
|
assert.Equal(t, labelValueKubernetesPartOf, ephemeralRunnerSet.Labels[LabelKeyKubernetesPartOf])
|
||||||
@@ -47,6 +57,7 @@ func TestLabelPropagation(t *testing.T) {
|
|||||||
assert.Equal(t, "repo", ephemeralRunnerSet.Labels[LabelKeyGitHubRepository])
|
assert.Equal(t, "repo", ephemeralRunnerSet.Labels[LabelKeyGitHubRepository])
|
||||||
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName])
|
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerGroupName])
|
||||||
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName])
|
assert.Equal(t, autoscalingRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName], ephemeralRunnerSet.Annotations[AnnotationKeyGitHubRunnerScaleSetName])
|
||||||
|
assert.Equal(t, autoscalingRunnerSet.Labels["arbitrary-label"], ephemeralRunnerSet.Labels["arbitrary-label"])
|
||||||
|
|
||||||
listener, err := b.newAutoScalingListener(&autoscalingRunnerSet, ephemeralRunnerSet, autoscalingRunnerSet.Namespace, "test:latest", nil)
|
listener, err := b.newAutoScalingListener(&autoscalingRunnerSet, ephemeralRunnerSet, autoscalingRunnerSet.Namespace, "test:latest", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -59,6 +70,12 @@ func TestLabelPropagation(t *testing.T) {
|
|||||||
assert.Equal(t, "", listener.Labels[LabelKeyGitHubEnterprise])
|
assert.Equal(t, "", listener.Labels[LabelKeyGitHubEnterprise])
|
||||||
assert.Equal(t, "org", listener.Labels[LabelKeyGitHubOrganization])
|
assert.Equal(t, "org", listener.Labels[LabelKeyGitHubOrganization])
|
||||||
assert.Equal(t, "repo", listener.Labels[LabelKeyGitHubRepository])
|
assert.Equal(t, "repo", listener.Labels[LabelKeyGitHubRepository])
|
||||||
|
assert.Equal(t, autoscalingRunnerSet.Labels["arbitrary-label"], listener.Labels["arbitrary-label"])
|
||||||
|
|
||||||
|
assert.NotContains(t, listener.Labels, "example.com/label")
|
||||||
|
assert.NotContains(t, listener.Labels, "example.com/example")
|
||||||
|
assert.NotContains(t, listener.Labels, "directly.excluded.org/label")
|
||||||
|
assert.Equal(t, "not-excluded-value", listener.Labels["directly.excluded.org/arbitrary"])
|
||||||
|
|
||||||
listenerServiceAccount := &corev1.ServiceAccount{
|
listenerServiceAccount := &corev1.ServiceAccount{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -125,7 +142,7 @@ func TestGitHubURLTrimLabelValues(t *testing.T) {
|
|||||||
GitHubConfigUrl: fmt.Sprintf("https://github.com/%s/%s", organization, repository),
|
GitHubConfigUrl: fmt.Sprintf("https://github.com/%s/%s", organization, repository),
|
||||||
}
|
}
|
||||||
|
|
||||||
var b resourceBuilder
|
var b ResourceBuilder
|
||||||
ephemeralRunnerSet, err := b.newEphemeralRunnerSet(autoscalingRunnerSet)
|
ephemeralRunnerSet, err := b.newEphemeralRunnerSet(autoscalingRunnerSet)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise], 0)
|
assert.Len(t, ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise], 0)
|
||||||
@@ -149,7 +166,7 @@ func TestGitHubURLTrimLabelValues(t *testing.T) {
|
|||||||
GitHubConfigUrl: fmt.Sprintf("https://github.com/enterprises/%s", enterprise),
|
GitHubConfigUrl: fmt.Sprintf("https://github.com/enterprises/%s", enterprise),
|
||||||
}
|
}
|
||||||
|
|
||||||
var b resourceBuilder
|
var b ResourceBuilder
|
||||||
ephemeralRunnerSet, err := b.newEphemeralRunnerSet(autoscalingRunnerSet)
|
ephemeralRunnerSet, err := b.newEphemeralRunnerSet(autoscalingRunnerSet)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise], 63)
|
assert.Len(t, ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise], 63)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ eliminate some duplication:
|
|||||||
`-coverprofile` flags: while `-short` is used to skip [old ARC E2E
|
`-coverprofile` flags: while `-short` is used to skip [old ARC E2E
|
||||||
tests](https://github.com/actions/actions-runner-controller/blob/master/test/e2e/e2e_test.go#L85-L87),
|
tests](https://github.com/actions/actions-runner-controller/blob/master/test/e2e/e2e_test.go#L85-L87),
|
||||||
`-coverprofile` is adding to the test time without really giving us any value
|
`-coverprofile` is adding to the test time without really giving us any value
|
||||||
in return. We should also start using `actions/setup-go@v4` to take advantage
|
in return. We should also start using `actions/setup-go@v5` to take advantage
|
||||||
of caching (it would speed up our tests by a lot) or enable it on `v3` if we
|
of caching (it would speed up our tests by a lot) or enable it on `v3` if we
|
||||||
have a strong reason not to upgrade. We should keep ignoring our E2E tests too
|
have a strong reason not to upgrade. We should keep ignoring our E2E tests too
|
||||||
as those will be run elsewhere (either use `Short` there too or ignoring the
|
as those will be run elsewhere (either use `Short` there too or ignoring the
|
||||||
|
|||||||
@@ -43,6 +43,34 @@ You can follow [this troubleshooting guide](https://docs.github.com/en/actions/h
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v0.9.3
|
||||||
|
|
||||||
|
1. AutoscalingListener controller: Inspect listener container state instead of pod phase [#3548](https://github.com/actions/actions-runner-controller/pull/3548)
|
||||||
|
1. Exclude label prefix propagation [#3607](https://github.com/actions/actions-runner-controller/pull/3607)
|
||||||
|
1. Check status code of fetch access token for github app [#3568](https://github.com/actions/actions-runner-controller/pull/3568)
|
||||||
|
1. Remove .Named() from the ephemeral runner controller [#3596](https://github.com/actions/actions-runner-controller/pull/3596)
|
||||||
|
1. Customize work directory [#3477](https://github.com/actions/actions-runner-controller/pull/3477)
|
||||||
|
1. Fix problem with ephemeralRunner Succeeded state before build executed [#3528](https://github.com/actions/actions-runner-controller/pull/3528)
|
||||||
|
1. Remove finalizers in one pass to speed up cleanups AutoscalingRunnerSet [#3536](https://github.com/actions/actions-runner-controller/pull/3536)
|
||||||
|
|
||||||
|
### v0.9.2
|
||||||
|
|
||||||
|
1. Refresh session if token expires during delete message [#3529](https://github.com/actions/actions-runner-controller/pull/3529)
|
||||||
|
1. Re-use the last desired patch on empty batch [#3453](https://github.com/actions/actions-runner-controller/pull/3453)
|
||||||
|
1. Extract single place to set up indexers [#3454](https://github.com/actions/actions-runner-controller/pull/3454)
|
||||||
|
1. Include controller version in logs [#3473](https://github.com/actions/actions-runner-controller/pull/3473)
|
||||||
|
1. Propogate arbitrary labels from runnersets to all created resources [#3157](https://github.com/actions/actions-runner-controller/pull/3157)
|
||||||
|
|
||||||
|
### v0.9.1
|
||||||
|
|
||||||
|
#### Major changes
|
||||||
|
|
||||||
|
1. Shutdown metrics server when listener exits [#3445](https://github.com/actions/actions-runner-controller/pull/3445)
|
||||||
|
1. Propagate max capacity information to the actions back-end [#3431](https://github.com/actions/actions-runner-controller/pull/3431)
|
||||||
|
1. Refactor actions client error to include request id [#3430](https://github.com/actions/actions-runner-controller/pull/3430)
|
||||||
|
1. Include self correction on empty batch and avoid removing pending runners when cluster is busy [#3426](https://github.com/actions/actions-runner-controller/pull/3426)
|
||||||
|
1. Add topologySpreadConstraint to gha-runner-scale-set-controller chart [#3405](https://github.com/actions/actions-runner-controller/pull/3405)
|
||||||
|
|
||||||
### v0.9.0
|
### v0.9.0
|
||||||
|
|
||||||
#### ⚠️ Warning
|
#### ⚠️ Warning
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -29,6 +30,9 @@ const (
|
|||||||
apiVersionQueryParam = "api-version=6.0-preview"
|
apiVersionQueryParam = "api-version=6.0-preview"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Header used to propagate capacity information to the back-end
|
||||||
|
const HeaderScaleSetMaxCapacity = "X-ScaleSetMaxCapacity"
|
||||||
|
|
||||||
//go:generate mockery --inpackage --name=ActionsService
|
//go:generate mockery --inpackage --name=ActionsService
|
||||||
type ActionsService interface {
|
type ActionsService interface {
|
||||||
GetRunnerScaleSet(ctx context.Context, runnerGroupId int, runnerScaleSetName string) (*RunnerScaleSet, error)
|
GetRunnerScaleSet(ctx context.Context, runnerGroupId int, runnerScaleSetName string) (*RunnerScaleSet, error)
|
||||||
@@ -45,7 +49,7 @@ type ActionsService interface {
|
|||||||
AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error)
|
AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error)
|
||||||
GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*AcquirableJobList, error)
|
GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*AcquirableJobList, error)
|
||||||
|
|
||||||
GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64) (*RunnerScaleSetMessage, error)
|
GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64, maxCapacity int) (*RunnerScaleSetMessage, error)
|
||||||
DeleteMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, messageId int64) error
|
DeleteMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, messageId int64) error
|
||||||
|
|
||||||
GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *RunnerScaleSetJitRunnerSetting, scaleSetId int) (*RunnerScaleSetJitRunnerConfig, error)
|
GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *RunnerScaleSetJitRunnerSetting, scaleSetId int) (*RunnerScaleSetJitRunnerConfig, error)
|
||||||
@@ -104,6 +108,8 @@ type Client struct {
|
|||||||
proxyFunc ProxyFunc
|
proxyFunc ProxyFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ ActionsService = &Client{}
|
||||||
|
|
||||||
type ProxyFunc func(req *http.Request) (*url.URL, error)
|
type ProxyFunc func(req *http.Request) (*url.URL, error)
|
||||||
|
|
||||||
type ClientOption func(*Client)
|
type ClientOption func(*Client)
|
||||||
@@ -355,15 +361,22 @@ func (c *Client) GetRunnerScaleSet(ctx context.Context, runnerGroupId int, runne
|
|||||||
}
|
}
|
||||||
|
|
||||||
var runnerScaleSetList *runnerScaleSetsResponse
|
var runnerScaleSetList *runnerScaleSetsResponse
|
||||||
err = json.NewDecoder(resp.Body).Decode(&runnerScaleSetList)
|
if err := json.NewDecoder(resp.Body).Decode(&runnerScaleSetList); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if runnerScaleSetList.Count == 0 {
|
if runnerScaleSetList.Count == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if runnerScaleSetList.Count > 1 {
|
if runnerScaleSetList.Count > 1 {
|
||||||
return nil, fmt.Errorf("multiple runner scale sets found with name %s", runnerScaleSetName)
|
return nil, &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: fmt.Errorf("multiple runner scale sets found with name %q", runnerScaleSetName),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &runnerScaleSetList.RunnerScaleSets[0], nil
|
return &runnerScaleSetList.RunnerScaleSets[0], nil
|
||||||
@@ -386,9 +399,12 @@ func (c *Client) GetRunnerScaleSetById(ctx context.Context, runnerScaleSetId int
|
|||||||
}
|
}
|
||||||
|
|
||||||
var runnerScaleSet *RunnerScaleSet
|
var runnerScaleSet *RunnerScaleSet
|
||||||
err = json.NewDecoder(resp.Body).Decode(&runnerScaleSet)
|
if err := json.NewDecoder(resp.Body).Decode(&runnerScaleSet); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return runnerScaleSet, nil
|
return runnerScaleSet, nil
|
||||||
}
|
}
|
||||||
@@ -408,23 +424,43 @@ func (c *Client) GetRunnerGroupByName(ctx context.Context, runnerGroup string) (
|
|||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unexpected status code: %d - body: %s", resp.StatusCode, string(body))
|
return nil, fmt.Errorf("unexpected status code: %w", &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: errors.New(string(body)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var runnerGroupList *RunnerGroupList
|
var runnerGroupList *RunnerGroupList
|
||||||
err = json.NewDecoder(resp.Body).Decode(&runnerGroupList)
|
err = json.NewDecoder(resp.Body).Decode(&runnerGroupList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if runnerGroupList.Count == 0 {
|
if runnerGroupList.Count == 0 {
|
||||||
return nil, fmt.Errorf("no runner group found with name '%s'", runnerGroup)
|
return nil, &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: fmt.Errorf("no runner group found with name %q", runnerGroup),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if runnerGroupList.Count > 1 {
|
if runnerGroupList.Count > 1 {
|
||||||
return nil, fmt.Errorf("multiple runner group found with name %s", runnerGroup)
|
return nil, &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: fmt.Errorf("multiple runner group found with name %q", runnerGroup),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &runnerGroupList.RunnerGroups[0], nil
|
return &runnerGroupList.RunnerGroups[0], nil
|
||||||
@@ -450,9 +486,12 @@ func (c *Client) CreateRunnerScaleSet(ctx context.Context, runnerScaleSet *Runne
|
|||||||
return nil, ParseActionsErrorFromResponse(resp)
|
return nil, ParseActionsErrorFromResponse(resp)
|
||||||
}
|
}
|
||||||
var createdRunnerScaleSet *RunnerScaleSet
|
var createdRunnerScaleSet *RunnerScaleSet
|
||||||
err = json.NewDecoder(resp.Body).Decode(&createdRunnerScaleSet)
|
if err := json.NewDecoder(resp.Body).Decode(&createdRunnerScaleSet); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return createdRunnerScaleSet, nil
|
return createdRunnerScaleSet, nil
|
||||||
}
|
}
|
||||||
@@ -480,9 +519,12 @@ func (c *Client) UpdateRunnerScaleSet(ctx context.Context, runnerScaleSetId int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var updatedRunnerScaleSet *RunnerScaleSet
|
var updatedRunnerScaleSet *RunnerScaleSet
|
||||||
err = json.NewDecoder(resp.Body).Decode(&updatedRunnerScaleSet)
|
if err := json.NewDecoder(resp.Body).Decode(&updatedRunnerScaleSet); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return updatedRunnerScaleSet, nil
|
return updatedRunnerScaleSet, nil
|
||||||
}
|
}
|
||||||
@@ -507,7 +549,7 @@ func (c *Client) DeleteRunnerScaleSet(ctx context.Context, runnerScaleSetId int)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64) (*RunnerScaleSetMessage, error) {
|
func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64, maxCapacity int) (*RunnerScaleSetMessage, error) {
|
||||||
u, err := url.Parse(messageQueueUrl)
|
u, err := url.Parse(messageQueueUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -519,6 +561,10 @@ func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAc
|
|||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if maxCapacity < 0 {
|
||||||
|
return nil, fmt.Errorf("maxCapacity must be greater than or equal to 0")
|
||||||
|
}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -527,6 +573,7 @@ func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAc
|
|||||||
req.Header.Set("Accept", "application/json; api-version=6.0-preview")
|
req.Header.Set("Accept", "application/json; api-version=6.0-preview")
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", messageQueueAccessToken))
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", messageQueueAccessToken))
|
||||||
req.Header.Set("User-Agent", c.userAgent.String())
|
req.Header.Set("User-Agent", c.userAgent.String())
|
||||||
|
req.Header.Set(HeaderScaleSetMaxCapacity, strconv.Itoa(maxCapacity))
|
||||||
|
|
||||||
resp, err := c.Do(req)
|
resp, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -547,15 +594,26 @@ func (c *Client) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAc
|
|||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
body = trimByteOrderMark(body)
|
body = trimByteOrderMark(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ActionsError{
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, &MessageQueueTokenExpiredError{
|
||||||
|
activityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
statusCode: resp.StatusCode,
|
||||||
|
msg: string(body),
|
||||||
}
|
}
|
||||||
return nil, &MessageQueueTokenExpiredError{msg: string(body)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var message *RunnerScaleSetMessage
|
var message *RunnerScaleSetMessage
|
||||||
err = json.NewDecoder(resp.Body).Decode(&message)
|
if err := json.NewDecoder(resp.Body).Decode(&message); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return message, nil
|
return message, nil
|
||||||
}
|
}
|
||||||
@@ -591,9 +649,17 @@ func (c *Client) DeleteMessage(ctx context.Context, messageQueueUrl, messageQueu
|
|||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
body = trimByteOrderMark(body)
|
body = trimByteOrderMark(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return &ActionsError{
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &MessageQueueTokenExpiredError{
|
||||||
|
activityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
statusCode: resp.StatusCode,
|
||||||
|
msg: string(body),
|
||||||
}
|
}
|
||||||
return &MessageQueueTokenExpiredError{msg: string(body)}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -641,9 +707,18 @@ func (c *Client) doSessionRequest(ctx context.Context, method, path string, requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode == expectedResponseStatusCode {
|
if resp.StatusCode == expectedResponseStatusCode {
|
||||||
if responseUnmarshalTarget != nil {
|
if responseUnmarshalTarget == nil {
|
||||||
return json.NewDecoder(resp.Body).Decode(responseUnmarshalTarget)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(responseUnmarshalTarget); err != nil {
|
||||||
|
return &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,10 +730,18 @@ func (c *Client) doSessionRequest(ctx context.Context, method, path string, requ
|
|||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
body = trimByteOrderMark(body)
|
body = trimByteOrderMark(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("unexpected status code: %d - body: %s", resp.StatusCode, string(body))
|
return fmt.Errorf("unexpected status code: %w", &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: errors.New(string(body)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error) {
|
func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error) {
|
||||||
@@ -692,16 +775,28 @@ func (c *Client) AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQ
|
|||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
body = trimByteOrderMark(body)
|
body = trimByteOrderMark(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ActionsError{
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, &MessageQueueTokenExpiredError{msg: string(body)}
|
return nil, &MessageQueueTokenExpiredError{
|
||||||
|
activityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
statusCode: resp.StatusCode,
|
||||||
|
msg: string(body),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var acquiredJobs *Int64List
|
var acquiredJobs *Int64List
|
||||||
err = json.NewDecoder(resp.Body).Decode(&acquiredJobs)
|
err = json.NewDecoder(resp.Body).Decode(&acquiredJobs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ActionsError{
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return acquiredJobs.Value, nil
|
return acquiredJobs.Value, nil
|
||||||
@@ -732,7 +827,11 @@ func (c *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*
|
|||||||
var acquirableJobList *AcquirableJobList
|
var acquirableJobList *AcquirableJobList
|
||||||
err = json.NewDecoder(resp.Body).Decode(&acquirableJobList)
|
err = json.NewDecoder(resp.Body).Decode(&acquirableJobList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return acquirableJobList, nil
|
return acquirableJobList, nil
|
||||||
@@ -761,9 +860,12 @@ func (c *Client) GenerateJitRunnerConfig(ctx context.Context, jitRunnerSetting *
|
|||||||
}
|
}
|
||||||
|
|
||||||
var runnerJitConfig *RunnerScaleSetJitRunnerConfig
|
var runnerJitConfig *RunnerScaleSetJitRunnerConfig
|
||||||
err = json.NewDecoder(resp.Body).Decode(&runnerJitConfig)
|
if err := json.NewDecoder(resp.Body).Decode(&runnerJitConfig); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return runnerJitConfig, nil
|
return runnerJitConfig, nil
|
||||||
}
|
}
|
||||||
@@ -786,9 +888,12 @@ func (c *Client) GetRunner(ctx context.Context, runnerId int64) (*RunnerReferenc
|
|||||||
}
|
}
|
||||||
|
|
||||||
var runnerReference *RunnerReference
|
var runnerReference *RunnerReference
|
||||||
err = json.NewDecoder(resp.Body).Decode(&runnerReference)
|
if err := json.NewDecoder(resp.Body).Decode(&runnerReference); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return runnerReference, nil
|
return runnerReference, nil
|
||||||
@@ -812,9 +917,12 @@ func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*Runne
|
|||||||
}
|
}
|
||||||
|
|
||||||
var runnerList *RunnerReferenceList
|
var runnerList *RunnerReferenceList
|
||||||
err = json.NewDecoder(resp.Body).Decode(&runnerList)
|
if err := json.NewDecoder(resp.Body).Decode(&runnerList); err != nil {
|
||||||
if err != nil {
|
return nil, &ActionsError{
|
||||||
return nil, err
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if runnerList.Count == 0 {
|
if runnerList.Count == 0 {
|
||||||
@@ -822,7 +930,11 @@ func (c *Client) GetRunnerByName(ctx context.Context, runnerName string) (*Runne
|
|||||||
}
|
}
|
||||||
|
|
||||||
if runnerList.Count > 1 {
|
if runnerList.Count > 1 {
|
||||||
return nil, fmt.Errorf("multiple runner found with name %s", runnerName)
|
return nil, &ActionsError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ActivityID: resp.Header.Get(HeaderActionsActivityID),
|
||||||
|
Err: fmt.Errorf("multiple runner found with name %s", runnerName),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &runnerList.RunnerReferences[0], nil
|
return &runnerList.RunnerReferences[0], nil
|
||||||
@@ -895,12 +1007,20 @@ func (c *Client) getRunnerRegistrationToken(ctx context.Context) (*registrationT
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unexpected response from Actions service during registration token call: %v - %v", resp.StatusCode, string(body))
|
return nil, &GitHubAPIError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
RequestID: resp.Header.Get(HeaderGitHubRequestID),
|
||||||
|
Err: errors.New(string(body)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var registrationToken *registrationToken
|
var registrationToken *registrationToken
|
||||||
if err := json.NewDecoder(resp.Body).Decode(®istrationToken); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(®istrationToken); err != nil {
|
||||||
return nil, err
|
return nil, &GitHubAPIError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
RequestID: resp.Header.Get(HeaderGitHubRequestID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return registrationToken, nil
|
return registrationToken, nil
|
||||||
@@ -935,10 +1055,24 @@ func (c *Client) fetchAccessToken(ctx context.Context, gitHubConfigURL string, c
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusCreated {
|
||||||
|
return nil, &GitHubAPIError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
RequestID: resp.Header.Get(HeaderGitHubRequestID),
|
||||||
|
Err: fmt.Errorf("failed to get access token for GitHub App auth: %v", resp.Status),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Format: https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app
|
// Format: https://docs.github.com/en/rest/apps/apps#create-an-installation-access-token-for-an-app
|
||||||
var accessToken *accessToken
|
var accessToken *accessToken
|
||||||
err = json.NewDecoder(resp.Body).Decode(&accessToken)
|
if err = json.NewDecoder(resp.Body).Decode(&accessToken); err != nil {
|
||||||
return accessToken, err
|
return nil, &GitHubAPIError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
RequestID: resp.Header.Get(HeaderGitHubRequestID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accessToken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionsServiceAdminConnection struct {
|
type ActionsServiceAdminConnection struct {
|
||||||
@@ -989,29 +1123,56 @@ func (c *Client) getActionsServiceAdminConnection(ctx context.Context, rt *regis
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
errStr := fmt.Sprintf("unexpected response from Actions service during registration call: %v", resp.StatusCode)
|
var innerErr error
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("%s - %w", errStr, err)
|
innerErr = err
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("%s - %v", errStr, string(body))
|
innerErr = errors.New(string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusUnauthorized && resp.StatusCode != http.StatusForbidden {
|
if resp.StatusCode != http.StatusUnauthorized && resp.StatusCode != http.StatusForbidden {
|
||||||
return nil, err
|
return nil, &GitHubAPIError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
RequestID: resp.Header.Get(HeaderGitHubRequestID),
|
||||||
|
Err: innerErr,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retry++
|
retry++
|
||||||
if retry > 3 {
|
if retry > 5 {
|
||||||
return nil, fmt.Errorf("unable to register runner after 3 retries: %v", err)
|
return nil, fmt.Errorf("unable to register runner after 3 retries: %w", &GitHubAPIError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
RequestID: resp.Header.Get(HeaderGitHubRequestID),
|
||||||
|
Err: innerErr,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(500 * int(time.Millisecond) * (retry + 1)))
|
// Add exponential backoff + jitter to avoid thundering herd
|
||||||
|
// This will generate a backoff schedule:
|
||||||
|
// 1: 1s
|
||||||
|
// 2: 3s
|
||||||
|
// 3: 4s
|
||||||
|
// 4: 8s
|
||||||
|
// 5: 17s
|
||||||
|
baseDelay := 500 * time.Millisecond
|
||||||
|
jitter := time.Duration(rand.Intn(1000))
|
||||||
|
maxDelay := 20 * time.Second
|
||||||
|
delay := baseDelay*(1<<retry) + jitter
|
||||||
|
|
||||||
|
if delay > maxDelay {
|
||||||
|
delay = maxDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionsServiceAdminConnection *ActionsServiceAdminConnection
|
var actionsServiceAdminConnection *ActionsServiceAdminConnection
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&actionsServiceAdminConnection); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&actionsServiceAdminConnection); err != nil {
|
||||||
return nil, err
|
return nil, &GitHubAPIError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
RequestID: resp.Header.Get(HeaderGitHubRequestID),
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return actionsServiceAdminConnection, nil
|
return actionsServiceAdminConnection, nil
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ func TestGetMessage(t *testing.T) {
|
|||||||
client, err := actions.NewClient(s.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(s.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetMessage(ctx, s.URL, token, 0)
|
got, err := client.GetMessage(ctx, s.URL, token, 0, 10)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, want, got)
|
assert.Equal(t, want, got)
|
||||||
})
|
})
|
||||||
@@ -52,7 +53,7 @@ func TestGetMessage(t *testing.T) {
|
|||||||
client, err := actions.NewClient(s.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(s.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := client.GetMessage(ctx, s.URL, token, 1)
|
got, err := client.GetMessage(ctx, s.URL, token, 1, 10)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, want, got)
|
assert.Equal(t, want, got)
|
||||||
})
|
})
|
||||||
@@ -76,7 +77,7 @@ func TestGetMessage(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
|
assert.Equalf(t, actualRetry, expectedRetry, "A retry was expected after the first request but got: %v", actualRetry)
|
||||||
})
|
})
|
||||||
@@ -89,7 +90,7 @@ func TestGetMessage(t *testing.T) {
|
|||||||
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
var expectedErr *actions.MessageQueueTokenExpiredError
|
var expectedErr *actions.MessageQueueTokenExpiredError
|
||||||
@@ -98,7 +99,7 @@ func TestGetMessage(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Status code not found", func(t *testing.T) {
|
t.Run("Status code not found", func(t *testing.T) {
|
||||||
want := actions.ActionsError{
|
want := actions.ActionsError{
|
||||||
Message: "Request returned status: 404 Not Found",
|
Err: errors.New("unknown exception"),
|
||||||
StatusCode: 404,
|
StatusCode: 404,
|
||||||
}
|
}
|
||||||
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
@@ -108,7 +109,7 @@ func TestGetMessage(t *testing.T) {
|
|||||||
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
assert.Equal(t, want.Error(), err.Error())
|
assert.Equal(t, want.Error(), err.Error())
|
||||||
})
|
})
|
||||||
@@ -122,9 +123,35 @@ func TestGetMessage(t *testing.T) {
|
|||||||
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = client.GetMessage(ctx, server.URL, token, 0)
|
_, err = client.GetMessage(ctx, server.URL, token, 0, 10)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Capacity error handling", func(t *testing.T) {
|
||||||
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
hc := r.Header.Get(actions.HeaderScaleSetMaxCapacity)
|
||||||
|
c, err := strconv.Atoi(hc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.GreaterOrEqual(t, c, 0)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
}))
|
||||||
|
|
||||||
|
client, err := actions.NewClient(server.configURLForOrg("my-org"), auth)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = client.GetMessage(ctx, server.URL, token, 0, -1)
|
||||||
|
require.Error(t, err)
|
||||||
|
// Ensure we don't send requests with negative capacity
|
||||||
|
assert.False(t, errors.Is(err, &actions.ActionsError{}))
|
||||||
|
|
||||||
|
_, err = client.GetMessage(ctx, server.URL, token, 0, 0)
|
||||||
|
assert.Error(t, err)
|
||||||
|
var expectedErr *actions.ActionsError
|
||||||
|
assert.ErrorAs(t, err, &expectedErr)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, expectedErr.StatusCode)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteMessage(t *testing.T) {
|
func TestDeleteMessage(t *testing.T) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const exampleRequestID = "5ddf2050-dae0-013c-9159-04421ad31b68"
|
||||||
|
|
||||||
func TestCreateMessageSession(t *testing.T) {
|
func TestCreateMessageSession(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
auth := &actions.ActionsAuth{
|
auth := &actions.ActionsAuth{
|
||||||
@@ -69,13 +71,17 @@ func TestCreateMessageSession(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
want := &actions.ActionsError{
|
want := &actions.ActionsError{
|
||||||
ExceptionName: "CSharpExceptionNameHere",
|
ActivityID: exampleRequestID,
|
||||||
Message: "could not do something",
|
StatusCode: http.StatusBadRequest,
|
||||||
StatusCode: http.StatusBadRequest,
|
Err: &actions.ActionsExceptionError{
|
||||||
|
ExceptionName: "CSharpExceptionNameHere",
|
||||||
|
Message: "could not do something",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set(actions.HeaderActionsActivityID, exampleRequestID)
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
resp := []byte(`{"typeName": "CSharpExceptionNameHere","message": "could not do something"}`)
|
resp := []byte(`{"typeName": "CSharpExceptionNameHere","message": "could not do something"}`)
|
||||||
w.Write(resp)
|
w.Write(resp)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/actions/actions-runner-controller/github/actions"
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -124,9 +125,15 @@ func TestGetRunnerScaleSet(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Multiple runner scale sets found", func(t *testing.T) {
|
t.Run("Multiple runner scale sets found", func(t *testing.T) {
|
||||||
wantErr := fmt.Errorf("multiple runner scale sets found with name %s", scaleSetName)
|
reqID := uuid.NewString()
|
||||||
|
wantErr := &actions.ActionsError{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
ActivityID: reqID,
|
||||||
|
Err: fmt.Errorf("multiple runner scale sets found with name %q", scaleSetName),
|
||||||
|
}
|
||||||
runnerScaleSetsResp := []byte(`{"count":2,"value":[{"id":1,"name":"ScaleSet"}]}`)
|
runnerScaleSetsResp := []byte(`{"count":2,"value":[{"id":1,"name":"ScaleSet"}]}`)
|
||||||
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
server := newActionsServer(t, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set(actions.HeaderActionsActivityID, reqID)
|
||||||
w.Write(runnerScaleSetsResp)
|
w.Write(runnerScaleSetsResp)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@@ -2,63 +2,117 @@ package actions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Header names for request IDs
|
||||||
|
const (
|
||||||
|
HeaderActionsActivityID = "ActivityId"
|
||||||
|
HeaderGitHubRequestID = "X-GitHub-Request-Id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GitHubAPIError struct {
|
||||||
|
StatusCode int
|
||||||
|
RequestID string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GitHubAPIError) Error() string {
|
||||||
|
return fmt.Sprintf("github api error: StatusCode %d, RequestID %q: %v", e.StatusCode, e.RequestID, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GitHubAPIError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
type ActionsError struct {
|
type ActionsError struct {
|
||||||
ExceptionName string `json:"typeName,omitempty"`
|
ActivityID string
|
||||||
Message string `json:"message,omitempty"`
|
StatusCode int
|
||||||
StatusCode int
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ActionsError) Error() string {
|
func (e *ActionsError) Error() string {
|
||||||
return fmt.Sprintf("%v - had issue communicating with Actions backend: %v", e.StatusCode, e.Message)
|
return fmt.Sprintf("actions error: StatusCode %d, AcivityId %q: %v", e.StatusCode, e.ActivityID, e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ActionsError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ActionsError) IsException(target string) bool {
|
||||||
|
if ex, ok := e.Err.(*ActionsExceptionError); ok {
|
||||||
|
return strings.Contains(ex.ExceptionName, target)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionsExceptionError struct {
|
||||||
|
ExceptionName string `json:"typeName,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ActionsExceptionError) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %s", e.ExceptionName, e.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseActionsErrorFromResponse(response *http.Response) error {
|
func ParseActionsErrorFromResponse(response *http.Response) error {
|
||||||
if response.ContentLength == 0 {
|
if response.ContentLength == 0 {
|
||||||
message := "Request returned status: " + response.Status
|
|
||||||
return &ActionsError{
|
return &ActionsError{
|
||||||
ExceptionName: "unknown",
|
ActivityID: response.Header.Get(HeaderActionsActivityID),
|
||||||
Message: message,
|
StatusCode: response.StatusCode,
|
||||||
StatusCode: response.StatusCode,
|
Err: errors.New("unknown exception"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return &ActionsError{
|
||||||
|
ActivityID: response.Header.Get(HeaderActionsActivityID),
|
||||||
|
StatusCode: response.StatusCode,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body = trimByteOrderMark(body)
|
body = trimByteOrderMark(body)
|
||||||
contentType, ok := response.Header["Content-Type"]
|
contentType, ok := response.Header["Content-Type"]
|
||||||
if ok && len(contentType) > 0 && strings.Contains(contentType[0], "text/plain") {
|
if ok && len(contentType) > 0 && strings.Contains(contentType[0], "text/plain") {
|
||||||
message := string(body)
|
message := string(body)
|
||||||
statusCode := response.StatusCode
|
|
||||||
return &ActionsError{
|
return &ActionsError{
|
||||||
Message: message,
|
ActivityID: response.Header.Get(HeaderActionsActivityID),
|
||||||
StatusCode: statusCode,
|
StatusCode: response.StatusCode,
|
||||||
|
Err: errors.New(message),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actionsError := &ActionsError{StatusCode: response.StatusCode}
|
var exception ActionsExceptionError
|
||||||
if err := json.Unmarshal(body, &actionsError); err != nil {
|
if err := json.Unmarshal(body, &exception); err != nil {
|
||||||
return err
|
return &ActionsError{
|
||||||
|
ActivityID: response.Header.Get(HeaderActionsActivityID),
|
||||||
|
StatusCode: response.StatusCode,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return actionsError
|
return &ActionsError{
|
||||||
|
ActivityID: response.Header.Get(HeaderActionsActivityID),
|
||||||
|
StatusCode: response.StatusCode,
|
||||||
|
Err: &exception,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageQueueTokenExpiredError struct {
|
type MessageQueueTokenExpiredError struct {
|
||||||
msg string
|
activityID string
|
||||||
|
statusCode int
|
||||||
|
msg string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *MessageQueueTokenExpiredError) Error() string {
|
func (e *MessageQueueTokenExpiredError) Error() string {
|
||||||
return e.msg
|
return fmt.Sprintf("MessageQueueTokenExpiredError: AcivityId %q, StatusCode %d: %s", e.activityID, e.statusCode, e.msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpClientSideError struct {
|
type HttpClientSideError struct {
|
||||||
|
|||||||
206
github/actions/errors_test.go
Normal file
206
github/actions/errors_test.go
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
package actions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/actions/actions-runner-controller/github/actions"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestActionsError(t *testing.T) {
|
||||||
|
t.Run("contains the status code, activity ID, and error", func(t *testing.T) {
|
||||||
|
err := &actions.ActionsError{
|
||||||
|
ActivityID: "activity-id",
|
||||||
|
StatusCode: 404,
|
||||||
|
Err: errors.New("example error description"),
|
||||||
|
}
|
||||||
|
|
||||||
|
s := err.Error()
|
||||||
|
assert.Contains(t, s, "StatusCode 404")
|
||||||
|
assert.Contains(t, s, "AcivityId \"activity-id\"")
|
||||||
|
assert.Contains(t, s, "example error description")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unwraps the error", func(t *testing.T) {
|
||||||
|
err := &actions.ActionsError{
|
||||||
|
ActivityID: "activity-id",
|
||||||
|
StatusCode: 404,
|
||||||
|
Err: &actions.ActionsExceptionError{
|
||||||
|
ExceptionName: "exception-name",
|
||||||
|
Message: "example error message",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, err.Unwrap(), err.Err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("is exception is ok", func(t *testing.T) {
|
||||||
|
err := &actions.ActionsError{
|
||||||
|
ActivityID: "activity-id",
|
||||||
|
StatusCode: 404,
|
||||||
|
Err: &actions.ActionsExceptionError{
|
||||||
|
ExceptionName: "exception-name",
|
||||||
|
Message: "example error message",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var exception *actions.ActionsExceptionError
|
||||||
|
assert.True(t, errors.As(err, &exception))
|
||||||
|
|
||||||
|
assert.True(t, err.IsException("exception-name"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("is exception is not ok", func(t *testing.T) {
|
||||||
|
tt := map[string]*actions.ActionsError{
|
||||||
|
"not an exception": {
|
||||||
|
ActivityID: "activity-id",
|
||||||
|
StatusCode: 404,
|
||||||
|
Err: errors.New("example error description"),
|
||||||
|
},
|
||||||
|
"not target exception": {
|
||||||
|
ActivityID: "activity-id",
|
||||||
|
StatusCode: 404,
|
||||||
|
Err: &actions.ActionsExceptionError{
|
||||||
|
ExceptionName: "exception-name",
|
||||||
|
Message: "example error message",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
targetException := "target-exception"
|
||||||
|
for name, err := range tt {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert.False(t, err.IsException(targetException))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionsExceptionError(t *testing.T) {
|
||||||
|
t.Run("contains the exception name and message", func(t *testing.T) {
|
||||||
|
err := &actions.ActionsExceptionError{
|
||||||
|
ExceptionName: "exception-name",
|
||||||
|
Message: "example error message",
|
||||||
|
}
|
||||||
|
|
||||||
|
s := err.Error()
|
||||||
|
assert.Contains(t, s, "exception-name")
|
||||||
|
assert.Contains(t, s, "example error message")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitHubAPIError(t *testing.T) {
|
||||||
|
t.Run("contains the status code, request ID, and error", func(t *testing.T) {
|
||||||
|
err := &actions.GitHubAPIError{
|
||||||
|
StatusCode: 404,
|
||||||
|
RequestID: "request-id",
|
||||||
|
Err: errors.New("example error description"),
|
||||||
|
}
|
||||||
|
|
||||||
|
s := err.Error()
|
||||||
|
assert.Contains(t, s, "StatusCode 404")
|
||||||
|
assert.Contains(t, s, "RequestID \"request-id\"")
|
||||||
|
assert.Contains(t, s, "example error description")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unwraps the error", func(t *testing.T) {
|
||||||
|
err := &actions.GitHubAPIError{
|
||||||
|
StatusCode: 404,
|
||||||
|
RequestID: "request-id",
|
||||||
|
Err: errors.New("example error description"),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, err.Unwrap(), err.Err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseActionsErrorFromResponse(t *testing.T) {
|
||||||
|
t.Run("empty content length", func(t *testing.T) {
|
||||||
|
response := &http.Response{
|
||||||
|
ContentLength: 0,
|
||||||
|
Header: http.Header{
|
||||||
|
actions.HeaderActionsActivityID: []string{"activity-id"},
|
||||||
|
},
|
||||||
|
StatusCode: 404,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := actions.ParseActionsErrorFromResponse(response)
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.Equal(t, err.(*actions.ActionsError).ActivityID, "activity-id")
|
||||||
|
assert.Equal(t, err.(*actions.ActionsError).StatusCode, 404)
|
||||||
|
assert.Equal(t, err.(*actions.ActionsError).Err.Error(), "unknown exception")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("contains text plain error", func(t *testing.T) {
|
||||||
|
errorMessage := "example error message"
|
||||||
|
response := &http.Response{
|
||||||
|
ContentLength: int64(len(errorMessage)),
|
||||||
|
Header: http.Header{
|
||||||
|
actions.HeaderActionsActivityID: []string{"activity-id"},
|
||||||
|
"Content-Type": []string{"text/plain"},
|
||||||
|
},
|
||||||
|
StatusCode: 404,
|
||||||
|
Body: io.NopCloser(strings.NewReader(errorMessage)),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := actions.ParseActionsErrorFromResponse(response)
|
||||||
|
require.Error(t, err)
|
||||||
|
var actionsError *actions.ActionsError
|
||||||
|
assert.ErrorAs(t, err, &actionsError)
|
||||||
|
assert.Equal(t, actionsError.ActivityID, "activity-id")
|
||||||
|
assert.Equal(t, actionsError.StatusCode, 404)
|
||||||
|
assert.Equal(t, actionsError.Err.Error(), errorMessage)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("contains json error", func(t *testing.T) {
|
||||||
|
errorMessage := `{"typeName":"exception-name","message":"example error message"}`
|
||||||
|
response := &http.Response{
|
||||||
|
ContentLength: int64(len(errorMessage)),
|
||||||
|
Header: http.Header{
|
||||||
|
actions.HeaderActionsActivityID: []string{"activity-id"},
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
},
|
||||||
|
StatusCode: 404,
|
||||||
|
Body: io.NopCloser(strings.NewReader(errorMessage)),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := actions.ParseActionsErrorFromResponse(response)
|
||||||
|
require.Error(t, err)
|
||||||
|
var actionsError *actions.ActionsError
|
||||||
|
assert.ErrorAs(t, err, &actionsError)
|
||||||
|
assert.Equal(t, actionsError.ActivityID, "activity-id")
|
||||||
|
assert.Equal(t, actionsError.StatusCode, 404)
|
||||||
|
|
||||||
|
inner, ok := actionsError.Err.(*actions.ActionsExceptionError)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, inner.ExceptionName, "exception-name")
|
||||||
|
assert.Equal(t, inner.Message, "example error message")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("wrapped exception error", func(t *testing.T) {
|
||||||
|
errorMessage := `{"typeName":"exception-name","message":"example error message"}`
|
||||||
|
response := &http.Response{
|
||||||
|
ContentLength: int64(len(errorMessage)),
|
||||||
|
Header: http.Header{
|
||||||
|
actions.HeaderActionsActivityID: []string{"activity-id"},
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
},
|
||||||
|
StatusCode: 404,
|
||||||
|
Body: io.NopCloser(strings.NewReader(errorMessage)),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := actions.ParseActionsErrorFromResponse(response)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
var actionsExceptionError *actions.ActionsExceptionError
|
||||||
|
assert.ErrorAs(t, err, &actionsExceptionError)
|
||||||
|
|
||||||
|
assert.Equal(t, actionsExceptionError.ExceptionName, "exception-name")
|
||||||
|
assert.Equal(t, actionsExceptionError.Message, "example error message")
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -259,7 +259,7 @@ func (f *FakeClient) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int
|
|||||||
return f.getAcquirableJobsResult.AcquirableJobList, f.getAcquirableJobsResult.err
|
return f.getAcquirableJobsResult.AcquirableJobList, f.getAcquirableJobsResult.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeClient) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64) (*actions.RunnerScaleSetMessage, error) {
|
func (f *FakeClient) GetMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, lastMessageId int64, maxCapacity int) (*actions.RunnerScaleSetMessage, error) {
|
||||||
return f.getMessageResult.RunnerScaleSetMessage, f.getMessageResult.err
|
return f.getMessageResult.RunnerScaleSetMessage, f.getMessageResult.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,13 @@ func TestNewActionsServiceRequest(t *testing.T) {
|
|||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write([]byte(errMessage))
|
w.Write([]byte(errMessage))
|
||||||
}
|
}
|
||||||
server := testserver.New(t, nil, testserver.WithActionsToken("random-token"), testserver.WithActionsToken(newToken), testserver.WithActionsRegistrationTokenHandler(unauthorizedHandler))
|
server := testserver.New(
|
||||||
|
t,
|
||||||
|
nil,
|
||||||
|
testserver.WithActionsToken("random-token"),
|
||||||
|
testserver.WithActionsToken(newToken),
|
||||||
|
testserver.WithActionsRegistrationTokenHandler(unauthorizedHandler),
|
||||||
|
)
|
||||||
client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
|
client, err := actions.NewClient(server.ConfigURLForOrg("my-org"), defaultCreds)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expiringToken := "expiring-token"
|
expiringToken := "expiring-token"
|
||||||
@@ -164,7 +170,7 @@ func TestNewActionsServiceRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
failures := 0
|
failures := 0
|
||||||
unauthorizedHandler := func(w http.ResponseWriter, r *http.Request) {
|
unauthorizedHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
if failures < 2 {
|
if failures < 5 {
|
||||||
failures++
|
failures++
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
|||||||
@@ -186,25 +186,25 @@ func (_m *MockActionsService) GetAcquirableJobs(ctx context.Context, runnerScale
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMessage provides a mock function with given fields: ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId
|
// GetMessage provides a mock function with given fields: ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity
|
||||||
func (_m *MockActionsService) GetMessage(ctx context.Context, messageQueueUrl string, messageQueueAccessToken string, lastMessageId int64) (*RunnerScaleSetMessage, error) {
|
func (_m *MockActionsService) GetMessage(ctx context.Context, messageQueueUrl string, messageQueueAccessToken string, lastMessageId int64, maxCapacity int) (*RunnerScaleSetMessage, error) {
|
||||||
ret := _m.Called(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
ret := _m.Called(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
|
|
||||||
var r0 *RunnerScaleSetMessage
|
var r0 *RunnerScaleSetMessage
|
||||||
var r1 error
|
var r1 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (*RunnerScaleSetMessage, error)); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64, int) (*RunnerScaleSetMessage, error)); ok {
|
||||||
return rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
return rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
}
|
}
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) *RunnerScaleSetMessage); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, int64, int) *RunnerScaleSetMessage); ok {
|
||||||
r0 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
r0 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(*RunnerScaleSetMessage)
|
r0 = ret.Get(0).(*RunnerScaleSetMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64) error); ok {
|
if rf, ok := ret.Get(1).(func(context.Context, string, string, int64, int) error); ok {
|
||||||
r1 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId)
|
r1 = rf(ctx, messageQueueUrl, messageQueueAccessToken, lastMessageId, maxCapacity)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Error(1)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,25 +67,25 @@ func (_m *MockSessionService) DeleteMessage(ctx context.Context, messageId int64
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMessage provides a mock function with given fields: ctx, lastMessageId
|
// GetMessage provides a mock function with given fields: ctx, lastMessageId, maxCapacity
|
||||||
func (_m *MockSessionService) GetMessage(ctx context.Context, lastMessageId int64) (*RunnerScaleSetMessage, error) {
|
func (_m *MockSessionService) GetMessage(ctx context.Context, lastMessageId int64, maxCapacity int) (*RunnerScaleSetMessage, error) {
|
||||||
ret := _m.Called(ctx, lastMessageId)
|
ret := _m.Called(ctx, lastMessageId, maxCapacity)
|
||||||
|
|
||||||
var r0 *RunnerScaleSetMessage
|
var r0 *RunnerScaleSetMessage
|
||||||
var r1 error
|
var r1 error
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*RunnerScaleSetMessage, error)); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, int64, int) (*RunnerScaleSetMessage, error)); ok {
|
||||||
return rf(ctx, lastMessageId)
|
return rf(ctx, lastMessageId, maxCapacity)
|
||||||
}
|
}
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *RunnerScaleSetMessage); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, int64, int) *RunnerScaleSetMessage); ok {
|
||||||
r0 = rf(ctx, lastMessageId)
|
r0 = rf(ctx, lastMessageId, maxCapacity)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(*RunnerScaleSetMessage)
|
r0 = ret.Get(0).(*RunnerScaleSetMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
if rf, ok := ret.Get(1).(func(context.Context, int64, int) error); ok {
|
||||||
r1 = rf(ctx, lastMessageId)
|
r1 = rf(ctx, lastMessageId, maxCapacity)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Error(1)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
//go:generate mockery --inpackage --name=SessionService
|
//go:generate mockery --inpackage --name=SessionService
|
||||||
type SessionService interface {
|
type SessionService interface {
|
||||||
GetMessage(ctx context.Context, lastMessageId int64) (*RunnerScaleSetMessage, error)
|
GetMessage(ctx context.Context, lastMessageId int64, maxCapacity int) (*RunnerScaleSetMessage, error)
|
||||||
DeleteMessage(ctx context.Context, messageId int64) error
|
DeleteMessage(ctx context.Context, messageId int64) error
|
||||||
AcquireJobs(ctx context.Context, requestIds []int64) ([]int64, error)
|
AcquireJobs(ctx context.Context, requestIds []int64) ([]int64, error)
|
||||||
io.Closer
|
io.Closer
|
||||||
|
|||||||
32
go.mod
32
go.mod
@@ -1,11 +1,11 @@
|
|||||||
module github.com/actions/actions-runner-controller
|
module github.com/actions/actions-runner-controller
|
||||||
|
|
||||||
go 1.22.1
|
go 1.22.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bradleyfalzon/ghinstallation/v2 v2.8.0
|
github.com/bradleyfalzon/ghinstallation/v2 v2.8.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/evanphx/json-patch v5.7.0+incompatible
|
github.com/evanphx/json-patch v5.9.0+incompatible
|
||||||
github.com/go-logr/logr v1.4.1
|
github.com/go-logr/logr v1.4.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
@@ -14,20 +14,20 @@ require (
|
|||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
|
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
|
||||||
github.com/gruntwork-io/terratest v0.46.7
|
github.com/gruntwork-io/terratest v0.46.7
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5
|
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/ginkgo/v2 v2.13.1
|
github.com/onsi/ginkgo/v2 v2.17.1
|
||||||
github.com/onsi/gomega v1.30.0
|
github.com/onsi/gomega v1.33.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.17.0
|
github.com/prometheus/client_golang v1.17.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/teambition/rrule-go v1.8.2
|
github.com/teambition/rrule-go v1.8.2
|
||||||
go.uber.org/multierr v1.11.0
|
go.uber.org/multierr v1.11.0
|
||||||
go.uber.org/zap v1.26.0
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/net v0.19.0
|
golang.org/x/net v0.24.0
|
||||||
golang.org/x/oauth2 v0.15.0
|
golang.org/x/oauth2 v0.19.0
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/sync v0.7.0
|
||||||
gomodules.xyz/jsonpatch/v2 v2.4.0
|
gomodules.xyz/jsonpatch/v2 v2.4.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
k8s.io/api v0.28.4
|
k8s.io/api v0.28.4
|
||||||
@@ -43,7 +43,7 @@ require (
|
|||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1 // indirect
|
github.com/boombuler/barcode v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.6 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
|
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
|
||||||
@@ -87,15 +87,15 @@ require (
|
|||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/objx v0.5.1 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/urfave/cli v1.22.2 // indirect
|
github.com/urfave/cli v1.22.2 // indirect
|
||||||
golang.org/x/crypto v0.17.0 // indirect
|
golang.org/x/crypto v0.22.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
golang.org/x/term v0.15.0 // indirect
|
golang.org/x/term v0.19.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/time v0.4.0 // indirect
|
golang.org/x/time v0.4.0 // indirect
|
||||||
golang.org/x/tools v0.15.0 // indirect
|
golang.org/x/tools v0.17.0 // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
|||||||
77
go.sum
77
go.sum
@@ -19,8 +19,8 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
|
|||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
@@ -30,11 +30,13 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||||
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
|
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
||||||
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc=
|
github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc=
|
||||||
github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||||
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
@@ -113,12 +115,12 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
|
|||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||||
@@ -145,8 +147,12 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||||
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg=
|
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg=
|
||||||
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||||
@@ -169,12 +175,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU=
|
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
||||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
@@ -202,8 +208,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@@ -212,9 +218,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8=
|
github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8=
|
||||||
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
|
github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4=
|
||||||
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||||
@@ -222,28 +227,26 @@ github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
|
|||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -256,18 +259,18 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
|
||||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -289,15 +292,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
@@ -316,8 +319,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||||
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
57
main.go
57
main.go
@@ -94,13 +94,16 @@ func main() {
|
|||||||
runnerImagePullSecrets stringSlice
|
runnerImagePullSecrets stringSlice
|
||||||
runnerPodDefaults actionssummerwindnet.RunnerPodDefaults
|
runnerPodDefaults actionssummerwindnet.RunnerPodDefaults
|
||||||
|
|
||||||
namespace string
|
namespace string
|
||||||
logLevel string
|
logLevel string
|
||||||
logFormat string
|
logFormat string
|
||||||
watchSingleNamespace string
|
watchSingleNamespace string
|
||||||
|
excludeLabelPropagationPrefixes stringSlice
|
||||||
|
|
||||||
autoScalerImagePullSecrets stringSlice
|
autoScalerImagePullSecrets stringSlice
|
||||||
|
|
||||||
|
opts = actionsgithubcom.OptionsWithDefault()
|
||||||
|
|
||||||
commonRunnerLabels commaSeparatedStringSlice
|
commonRunnerLabels commaSeparatedStringSlice
|
||||||
)
|
)
|
||||||
var c github.Config
|
var c github.Config
|
||||||
@@ -135,9 +138,11 @@ func main() {
|
|||||||
flag.DurationVar(&defaultScaleDownDelay, "default-scale-down-delay", actionssummerwindnet.DefaultScaleDownDelay, "The approximate delay for a scale down followed by a scale up, used to prevent flapping (down->up->down->... loop)")
|
flag.DurationVar(&defaultScaleDownDelay, "default-scale-down-delay", actionssummerwindnet.DefaultScaleDownDelay, "The approximate delay for a scale down followed by a scale up, used to prevent flapping (down->up->down->... loop)")
|
||||||
flag.IntVar(&port, "port", 9443, "The port to which the admission webhook endpoint should bind")
|
flag.IntVar(&port, "port", 9443, "The port to which the admission webhook endpoint should bind")
|
||||||
flag.DurationVar(&syncPeriod, "sync-period", 1*time.Minute, "Determines the minimum frequency at which K8s resources managed by this controller are reconciled.")
|
flag.DurationVar(&syncPeriod, "sync-period", 1*time.Minute, "Determines the minimum frequency at which K8s resources managed by this controller are reconciled.")
|
||||||
|
flag.IntVar(&opts.RunnerMaxConcuncurrentReconciles, "runner-max-concurrent-reconciles", opts.RunnerMaxConcuncurrentReconciles, "The maximum number of concurrent reconciles which can be run by the EphemeralRunner controller. Increase this value to improve the throughput of the controller, but it may also increase the load on the API server and the external service (e.g. GitHub API).")
|
||||||
flag.Var(&commonRunnerLabels, "common-runner-labels", "Runner labels in the K1=V1,K2=V2,... format that are inherited all the runners created by the controller. See https://github.com/actions/actions-runner-controller/issues/321 for more information")
|
flag.Var(&commonRunnerLabels, "common-runner-labels", "Runner labels in the K1=V1,K2=V2,... format that are inherited all the runners created by the controller. See https://github.com/actions/actions-runner-controller/issues/321 for more information")
|
||||||
flag.StringVar(&namespace, "watch-namespace", "", "The namespace to watch for custom resources. Set to empty for letting it watch for all namespaces.")
|
flag.StringVar(&namespace, "watch-namespace", "", "The namespace to watch for custom resources. Set to empty for letting it watch for all namespaces.")
|
||||||
flag.StringVar(&watchSingleNamespace, "watch-single-namespace", "", "Restrict to watch for custom resources in a single namespace.")
|
flag.StringVar(&watchSingleNamespace, "watch-single-namespace", "", "Restrict to watch for custom resources in a single namespace.")
|
||||||
|
flag.Var(&excludeLabelPropagationPrefixes, "exclude-label-propagation-prefix", "The list of prefixes that should be excluded from label propagation")
|
||||||
flag.StringVar(&logLevel, "log-level", logging.LogLevelDebug, `The verbosity of the logging. Valid values are "debug", "info", "warn", "error". Defaults to "debug".`)
|
flag.StringVar(&logLevel, "log-level", logging.LogLevelDebug, `The verbosity of the logging. Valid values are "debug", "info", "warn", "error". Defaults to "debug".`)
|
||||||
flag.StringVar(&logFormat, "log-format", "text", `The log format. Valid options are "text" and "json". Defaults to "text"`)
|
flag.StringVar(&logFormat, "log-format", "text", `The log format. Valid options are "text" and "json". Defaults to "text"`)
|
||||||
flag.BoolVar(&autoScalingRunnerSetOnly, "auto-scaling-runner-set-only", false, "Make controller only reconcile AutoRunnerScaleSet object.")
|
flag.BoolVar(&autoScalingRunnerSetOnly, "auto-scaling-runner-set-only", false, "Make controller only reconcile AutoRunnerScaleSet object.")
|
||||||
@@ -154,6 +159,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
c.Log = &log
|
c.Log = &log
|
||||||
|
|
||||||
|
if err := opts.LoadEnv(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: loading environment variables: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
log.Info("Using options", "runner-max-concurrent-reconciles", opts.RunnerMaxConcuncurrentReconciles)
|
||||||
|
|
||||||
if !autoScalingRunnerSetOnly {
|
if !autoScalingRunnerSetOnly {
|
||||||
ghClient, err = c.NewClient()
|
ghClient, err = c.NewClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -239,6 +250,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if autoScalingRunnerSetOnly {
|
if autoScalingRunnerSetOnly {
|
||||||
|
if err := actionsgithubcom.SetupIndexers(mgr); err != nil {
|
||||||
|
log.Error(err, "unable to setup indexers")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
managerImage := os.Getenv("CONTROLLER_MANAGER_CONTAINER_IMAGE")
|
managerImage := os.Getenv("CONTROLLER_MANAGER_CONTAINER_IMAGE")
|
||||||
if managerImage == "" {
|
if managerImage == "" {
|
||||||
log.Error(err, "unable to obtain listener image")
|
log.Error(err, "unable to obtain listener image")
|
||||||
@@ -254,36 +269,43 @@ func main() {
|
|||||||
log.WithName("actions-clients"),
|
log.WithName("actions-clients"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rb := actionsgithubcom.ResourceBuilder{
|
||||||
|
ExcludeLabelPropagationPrefixes: excludeLabelPropagationPrefixes,
|
||||||
|
}
|
||||||
|
|
||||||
if err = (&actionsgithubcom.AutoscalingRunnerSetReconciler{
|
if err = (&actionsgithubcom.AutoscalingRunnerSetReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: log.WithName("AutoscalingRunnerSet"),
|
Log: log.WithName("AutoscalingRunnerSet").WithValues("version", build.Version),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
ControllerNamespace: managerNamespace,
|
ControllerNamespace: managerNamespace,
|
||||||
DefaultRunnerScaleSetListenerImage: managerImage,
|
DefaultRunnerScaleSetListenerImage: managerImage,
|
||||||
ActionsClient: actionsMultiClient,
|
ActionsClient: actionsMultiClient,
|
||||||
UpdateStrategy: actionsgithubcom.UpdateStrategy(updateStrategy),
|
UpdateStrategy: actionsgithubcom.UpdateStrategy(updateStrategy),
|
||||||
DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets,
|
DefaultRunnerScaleSetListenerImagePullSecrets: autoScalerImagePullSecrets,
|
||||||
|
ResourceBuilder: rb,
|
||||||
}).SetupWithManager(mgr); err != nil {
|
}).SetupWithManager(mgr); err != nil {
|
||||||
log.Error(err, "unable to create controller", "controller", "AutoscalingRunnerSet")
|
log.Error(err, "unable to create controller", "controller", "AutoscalingRunnerSet")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = (&actionsgithubcom.EphemeralRunnerReconciler{
|
if err = (&actionsgithubcom.EphemeralRunnerReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: log.WithName("EphemeralRunner"),
|
Log: log.WithName("EphemeralRunner").WithValues("version", build.Version),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
ActionsClient: actionsMultiClient,
|
ActionsClient: actionsMultiClient,
|
||||||
}).SetupWithManager(mgr); err != nil {
|
ResourceBuilder: rb,
|
||||||
|
}).SetupWithManager(mgr, actionsgithubcom.WithMaxConcurrentReconciles(opts.RunnerMaxConcuncurrentReconciles)); err != nil {
|
||||||
log.Error(err, "unable to create controller", "controller", "EphemeralRunner")
|
log.Error(err, "unable to create controller", "controller", "EphemeralRunner")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = (&actionsgithubcom.EphemeralRunnerSetReconciler{
|
if err = (&actionsgithubcom.EphemeralRunnerSetReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: log.WithName("EphemeralRunnerSet"),
|
Log: log.WithName("EphemeralRunnerSet").WithValues("version", build.Version),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
ActionsClient: actionsMultiClient,
|
ActionsClient: actionsMultiClient,
|
||||||
PublishMetrics: metricsAddr != "0",
|
PublishMetrics: metricsAddr != "0",
|
||||||
|
ResourceBuilder: rb,
|
||||||
}).SetupWithManager(mgr); err != nil {
|
}).SetupWithManager(mgr); err != nil {
|
||||||
log.Error(err, "unable to create controller", "controller", "EphemeralRunnerSet")
|
log.Error(err, "unable to create controller", "controller", "EphemeralRunnerSet")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -291,10 +313,11 @@ func main() {
|
|||||||
|
|
||||||
if err = (&actionsgithubcom.AutoscalingListenerReconciler{
|
if err = (&actionsgithubcom.AutoscalingListenerReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Log: log.WithName("AutoscalingListener"),
|
Log: log.WithName("AutoscalingListener").WithValues("version", build.Version),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
ListenerMetricsAddr: listenerMetricsAddr,
|
ListenerMetricsAddr: listenerMetricsAddr,
|
||||||
ListenerMetricsEndpoint: listenerMetricsEndpoint,
|
ListenerMetricsEndpoint: listenerMetricsEndpoint,
|
||||||
|
ResourceBuilder: rb,
|
||||||
}).SetupWithManager(mgr); err != nil {
|
}).SetupWithManager(mgr); err != nil {
|
||||||
log.Error(err, "unable to create controller", "controller", "AutoscalingListener")
|
log.Error(err, "unable to create controller", "controller", "AutoscalingListener")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -441,7 +464,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("starting manager")
|
log.Info("starting manager", "version", build.Version)
|
||||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||||
log.Error(err, "problem running manager")
|
log.Error(err, "problem running manager")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ DIND_ROOTLESS_RUNNER_NAME ?= ${DOCKER_USER}/actions-runner-dind-rootless
|
|||||||
OS_IMAGE ?= ubuntu-22.04
|
OS_IMAGE ?= ubuntu-22.04
|
||||||
TARGETPLATFORM ?= $(shell arch)
|
TARGETPLATFORM ?= $(shell arch)
|
||||||
|
|
||||||
RUNNER_VERSION ?= 2.314.1
|
RUNNER_VERSION ?= 2.319.1
|
||||||
RUNNER_CONTAINER_HOOKS_VERSION ?= 0.5.1
|
RUNNER_CONTAINER_HOOKS_VERSION ?= 0.6.1
|
||||||
DOCKER_VERSION ?= 24.0.7
|
DOCKER_VERSION ?= 24.0.7
|
||||||
|
|
||||||
# default list of platforms for which multiarch image is built
|
# default list of platforms for which multiarch image is built
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
RUNNER_VERSION=2.314.1
|
RUNNER_VERSION=2.319.1
|
||||||
RUNNER_CONTAINER_HOOKS_VERSION=0.5.1
|
RUNNER_CONTAINER_HOOKS_VERSION=0.6.1
|
||||||
@@ -36,8 +36,8 @@ var (
|
|||||||
|
|
||||||
testResultCMNamePrefix = "test-result-"
|
testResultCMNamePrefix = "test-result-"
|
||||||
|
|
||||||
RunnerVersion = "2.314.1"
|
RunnerVersion = "2.319.1"
|
||||||
RunnerContainerHooksVersion = "0.5.1"
|
RunnerContainerHooksVersion = "0.6.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// If you're willing to run this test via VS Code "run test" or "debug test",
|
// If you're willing to run this test via VS Code "run test" or "debug test",
|
||||||
@@ -1106,7 +1106,7 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||||||
testing.Step{
|
testing.Step{
|
||||||
Uses: "actions/setup-go@v3",
|
Uses: "actions/setup-go@v3",
|
||||||
With: &testing.With{
|
With: &testing.With{
|
||||||
GoVersion: "1.22.1",
|
GoVersion: "1.22.4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -1236,7 +1236,7 @@ func installActionsWorkflow(t *testing.T, testName, runnerLabel, testResultCMNam
|
|||||||
testing.Step{
|
testing.Step{
|
||||||
Uses: "azure/setup-kubectl@v1",
|
Uses: "azure/setup-kubectl@v1",
|
||||||
With: &testing.With{
|
With: &testing.With{
|
||||||
Version: "v1.22.1",
|
Version: "v1.22.4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
testing.Step{
|
testing.Step{
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ nodes:
|
|||||||
image: %s
|
image: %s
|
||||||
`, k.Name, image, image))
|
`, k.Name, image, image))
|
||||||
|
|
||||||
if err := os.WriteFile(f.Name(), kindConfig, 0644); err != nil {
|
if err := os.WriteFile(f.Name(), kindConfig, 0o644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +385,7 @@ func (k *Kind) LoadImages(ctx context.Context, images []ContainerImage) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmpDir := filepath.Join(wd, ".testing", k.Name)
|
tmpDir := filepath.Join(wd, ".testing", k.Name)
|
||||||
if err := os.MkdirAll(tmpDir, 0755); err != nil {
|
if err := os.MkdirAll(tmpDir, 0o755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package testing
|
package testing
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ActionsCheckout = "actions/checkout@v3"
|
ActionsCheckout = "actions/checkout@v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Workflow struct {
|
type Workflow struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user