diff --git a/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook.go b/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook.go index 0f37d0d3..629e026b 100644 --- a/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook.go +++ b/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook.go @@ -217,7 +217,8 @@ func (autoscaler *HorizontalRunnerAutoscalerGitHubWebhook) Handle(w http.Respons // But canceled events have runner_id == 0 and GetRunnerID() returns 0 when RunnerID == nil, // so we need to be more specific in filtering out the check runs. // See example check run completion at https://gist.github.com/nathanklick/268fea6496a4d7b14cecb2999747ef84 - if e.GetWorkflowJob().GetConclusion() == "success" && e.GetWorkflowJob().RunnerID == nil { + // Check runs appear to have no labels set, so use that in conjuction with the nil RunnerID to filter the event: + if len(e.GetWorkflowJob().Labels) == 0 && e.GetWorkflowJob().RunnerID == nil { log.V(1).Info("Ignoring workflow_job event because it does not relate to a self-hosted runner") } else { // A negative amount is processed in the tryScale func as a scale-down request, diff --git a/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook_test.go b/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook_test.go index 22ef62b8..38f023d4 100644 --- a/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook_test.go +++ b/controllers/actions.summerwind.net/horizontal_runner_autoscaler_webhook_test.go @@ -361,6 +361,127 @@ func TestWebhookWorkflowJobWithSelfHostedLabel(t *testing.T) { }) } +func TestWebhookWorkflowJobWithCompletedCheckRun(t *testing.T) { + setupTestSucceeded := func() github.WorkflowJobEvent { + f, err := os.Open("testdata/webhook_workflow_job_with_completed_success_check_run.json") + if err != nil { + t.Fatalf("could not open the fixture: %s", err) + } + defer f.Close() + var e github.WorkflowJobEvent + if err := json.NewDecoder(f).Decode(&e); err != nil { + t.Fatalf("invalid json: %s", err) + } + + return e + } + setupTestFailure := func() github.WorkflowJobEvent { + f, err := os.Open("testdata/webhook_workflow_job_with_completed_failure_check_run.json") + if err != nil { + t.Fatalf("could not open the fixture: %s", err) + } + defer f.Close() + var e github.WorkflowJobEvent + if err := json.NewDecoder(f).Decode(&e); err != nil { + t.Fatalf("invalid json: %s", err) + } + + return e + } + t.Run("Success", func(t *testing.T) { + e := setupTestSucceeded() + hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{ + ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{ + Name: "test-name", + }, + ScaleUpTriggers: []actionsv1alpha1.ScaleUpTrigger{ + { + GitHubEvent: &actionsv1alpha1.GitHubEventScaleUpTriggerSpec{ + WorkflowJob: &actionsv1alpha1.WorkflowJobSpec{}, + }, + }, + }, + }, + } + + rd := &actionsv1alpha1.RunnerDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: actionsv1alpha1.RunnerDeploymentSpec{ + Template: actionsv1alpha1.RunnerTemplate{ + Spec: actionsv1alpha1.RunnerSpec{ + RunnerConfig: actionsv1alpha1.RunnerConfig{ + Organization: "MYORG", + Labels: []string{"label1"}, + }, + }, + }, + }, + } + + initObjs := []runtime.Object{hra, rd} + + testServerWithInitObjs(t, + "workflow_job", + &e, + 200, + "", + initObjs, + ) + }) + t.Run("Failure", func(t *testing.T) { + e := setupTestFailure() + hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{ + ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{ + Name: "test-name", + }, + ScaleUpTriggers: []actionsv1alpha1.ScaleUpTrigger{ + { + GitHubEvent: &actionsv1alpha1.GitHubEventScaleUpTriggerSpec{ + WorkflowJob: &actionsv1alpha1.WorkflowJobSpec{}, + }, + }, + }, + }, + } + + rd := &actionsv1alpha1.RunnerDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + }, + Spec: actionsv1alpha1.RunnerDeploymentSpec{ + Template: actionsv1alpha1.RunnerTemplate{ + Spec: actionsv1alpha1.RunnerSpec{ + RunnerConfig: actionsv1alpha1.RunnerConfig{ + Organization: "MYORG", + Labels: []string{"label1"}, + }, + }, + }, + }, + } + + initObjs := []runtime.Object{hra, rd} + + testServerWithInitObjs(t, + "workflow_job", + &e, + 200, + "", + initObjs, + ) + }) +} + func TestGetRequest(t *testing.T) { hra := HorizontalRunnerAutoscalerGitHubWebhook{} request, _ := http.NewRequest(http.MethodGet, "/", nil) diff --git a/controllers/actions.summerwind.net/testdata/webhook_workflow_job_with_completed_failure_check_run.json b/controllers/actions.summerwind.net/testdata/webhook_workflow_job_with_completed_failure_check_run.json new file mode 100644 index 00000000..bd742bb4 --- /dev/null +++ b/controllers/actions.summerwind.net/testdata/webhook_workflow_job_with_completed_failure_check_run.json @@ -0,0 +1,167 @@ +{ + "action": "completed", + "workflow_job": { + "id": 10472913772, + "run_id": 3850387510, + "workflow_name": "Build Platform", + "head_branch": "develop", + "run_url": "https://api.github.com/repos/MYORG/MYREPO/actions/runs/3850387510", + "run_attempt": 1, + "node_id": "CR_kwDOA9pFCc8AAAACcDv7bA", + "head_sha": "c72f1c0a1b847a39a0414d408bd8aeb3f5e4945c", + "url": "https://api.github.com/repos/MYORG/MYREPO/actions/jobs/10472913772", + "html_url": "https://github.com/MYORG/MYREPO/runs/10472913772", + "status": "completed", + "conclusion": "failure", + "started_at": "2023-01-06T00:46:59Z", + "completed_at": "2023-01-06T00:46:59Z", + "name": "JUnit Test Report", + "steps": [], + "check_run_url": "https://api.github.com/repos/MYORG/MYREPO/check-runs/10472913772", + "labels": [], + "runner_id": null, + "runner_name": null, + "runner_group_id": null, + "runner_group_name": null + }, + "repository": { + "id": 64636169, + "node_id": "MDEwOlJlcG9zaXRvcnk2NDYzNjE2OQ==", + "name": "MYREPO", + "full_name": "MYORG/MYREPO", + "private": true, + "owner": { + "login": "MYORG", + "id": 29604235, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI5NjA0MjM1", + "avatar_url": "https://avatars.githubusercontent.com/u/29604235?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/MYORG", + "html_url": "https://github.com/MYORG", + "followers_url": "https://api.github.com/users/MYORG/followers", + "following_url": "https://api.github.com/users/MYORG/following{/other_user}", + "gists_url": "https://api.github.com/users/MYORG/gists{/gist_id}", + "starred_url": "https://api.github.com/users/MYORG/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/MYORG/subscriptions", + "organizations_url": "https://api.github.com/users/MYORG/orgs", + "repos_url": "https://api.github.com/users/MYORG/repos", + "events_url": "https://api.github.com/users/MYORG/events{/privacy}", + "received_events_url": "https://api.github.com/users/MYORG/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/MYORG/MYREPO", + "description": "Proprietary source code for the MYORG Platform. Please do not redistribute.", + "fork": false, + "url": "https://api.github.com/repos/MYORG/MYREPO", + "forks_url": "https://api.github.com/repos/MYORG/MYREPO/forks", + "keys_url": "https://api.github.com/repos/MYORG/MYREPO/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/MYORG/MYREPO/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/MYORG/MYREPO/teams", + "hooks_url": "https://api.github.com/repos/MYORG/MYREPO/hooks", + "issue_events_url": "https://api.github.com/repos/MYORG/MYREPO/issues/events{/number}", + "events_url": "https://api.github.com/repos/MYORG/MYREPO/events", + "assignees_url": "https://api.github.com/repos/MYORG/MYREPO/assignees{/user}", + "branches_url": "https://api.github.com/repos/MYORG/MYREPO/branches{/branch}", + "tags_url": "https://api.github.com/repos/MYORG/MYREPO/tags", + "blobs_url": "https://api.github.com/repos/MYORG/MYREPO/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/MYORG/MYREPO/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/MYORG/MYREPO/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/MYORG/MYREPO/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/MYORG/MYREPO/statuses/{sha}", + "languages_url": "https://api.github.com/repos/MYORG/MYREPO/languages", + "stargazers_url": "https://api.github.com/repos/MYORG/MYREPO/stargazers", + "contributors_url": "https://api.github.com/repos/MYORG/MYREPO/contributors", + "subscribers_url": "https://api.github.com/repos/MYORG/MYREPO/subscribers", + "subscription_url": "https://api.github.com/repos/MYORG/MYREPO/subscription", + "commits_url": "https://api.github.com/repos/MYORG/MYREPO/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/MYORG/MYREPO/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/MYORG/MYREPO/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/MYORG/MYREPO/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/MYORG/MYREPO/contents/{+path}", + "compare_url": "https://api.github.com/repos/MYORG/MYREPO/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/MYORG/MYREPO/merges", + "archive_url": "https://api.github.com/repos/MYORG/MYREPO/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/MYORG/MYREPO/downloads", + "issues_url": "https://api.github.com/repos/MYORG/MYREPO/issues{/number}", + "pulls_url": "https://api.github.com/repos/MYORG/MYREPO/pulls{/number}", + "milestones_url": "https://api.github.com/repos/MYORG/MYREPO/milestones{/number}", + "notifications_url": "https://api.github.com/repos/MYORG/MYREPO/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/MYORG/MYREPO/labels{/name}", + "releases_url": "https://api.github.com/repos/MYORG/MYREPO/releases{/id}", + "deployments_url": "https://api.github.com/repos/MYORG/MYREPO/deployments", + "created_at": "2016-08-01T04:36:52Z", + "updated_at": "2023-01-05T17:44:13Z", + "pushed_at": "2023-01-05T23:10:46Z", + "git_url": "git://github.com/MYORG/MYREPO.git", + "ssh_url": "git@github.com:MYORG/MYREPO.git", + "clone_url": "https://github.com/MYORG/MYREPO.git", + "svn_url": "https://github.com/MYORG/MYREPO", + "homepage": "", + "size": 724221, + "stargazers_count": 4, + "watchers_count": 4, + "language": "Java", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 532, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": true, + "topics": [], + "visibility": "private", + "forks": 0, + "open_issues": 532, + "watchers": 4, + "default_branch": "develop" + }, + "organization": { + "login": "MYORG", + "id": 29604235, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI5NjA0MjM1", + "url": "https://api.github.com/orgs/MYORG", + "repos_url": "https://api.github.com/orgs/MYORG/repos", + "events_url": "https://api.github.com/orgs/MYORG/events", + "hooks_url": "https://api.github.com/orgs/MYORG/hooks", + "issues_url": "https://api.github.com/orgs/MYORG/issues", + "members_url": "https://api.github.com/orgs/MYORG/members{/member}", + "public_members_url": "https://api.github.com/orgs/MYORG/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/29604235?v=4", + "description": "" + }, + "sender": { + "login": "poulok", + "id": 82919061, + "node_id": "MDQ6VXNlcjgyOTE5MDYx", + "avatar_url": "https://avatars.githubusercontent.com/u/82919061?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/poulok", + "html_url": "https://github.com/poulok", + "followers_url": "https://api.github.com/users/poulok/followers", + "following_url": "https://api.github.com/users/poulok/following{/other_user}", + "gists_url": "https://api.github.com/users/poulok/gists{/gist_id}", + "starred_url": "https://api.github.com/users/poulok/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/poulok/subscriptions", + "organizations_url": "https://api.github.com/users/poulok/orgs", + "repos_url": "https://api.github.com/users/poulok/repos", + "events_url": "https://api.github.com/users/poulok/events{/privacy}", + "received_events_url": "https://api.github.com/users/poulok/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/controllers/actions.summerwind.net/testdata/webhook_workflow_job_with_completed_success_check_run.json b/controllers/actions.summerwind.net/testdata/webhook_workflow_job_with_completed_success_check_run.json new file mode 100644 index 00000000..79061b8a --- /dev/null +++ b/controllers/actions.summerwind.net/testdata/webhook_workflow_job_with_completed_success_check_run.json @@ -0,0 +1,167 @@ +{ + "action": "completed", + "workflow_job": { + "id": 10472913772, + "run_id": 3850387510, + "workflow_name": "Build Platform", + "head_branch": "develop", + "run_url": "https://api.github.com/repos/MYORG/MYREPO/actions/runs/3850387510", + "run_attempt": 1, + "node_id": "CR_kwDOA9pFCc8AAAACcDv7bA", + "head_sha": "c72f1c0a1b847a39a0414d408bd8aeb3f5e4945c", + "url": "https://api.github.com/repos/MYORG/MYREPO/actions/jobs/10472913772", + "html_url": "https://github.com/MYORG/MYREPO/runs/10472913772", + "status": "completed", + "conclusion": "success", + "started_at": "2023-01-06T00:46:59Z", + "completed_at": "2023-01-06T00:46:59Z", + "name": "JUnit Test Report", + "steps": [], + "check_run_url": "https://api.github.com/repos/MYORG/MYREPO/check-runs/10472913772", + "labels": [], + "runner_id": null, + "runner_name": null, + "runner_group_id": null, + "runner_group_name": null + }, + "repository": { + "id": 64636169, + "node_id": "MDEwOlJlcG9zaXRvcnk2NDYzNjE2OQ==", + "name": "MYREPO", + "full_name": "MYORG/MYREPO", + "private": true, + "owner": { + "login": "MYORG", + "id": 29604235, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI5NjA0MjM1", + "avatar_url": "https://avatars.githubusercontent.com/u/29604235?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/MYORG", + "html_url": "https://github.com/MYORG", + "followers_url": "https://api.github.com/users/MYORG/followers", + "following_url": "https://api.github.com/users/MYORG/following{/other_user}", + "gists_url": "https://api.github.com/users/MYORG/gists{/gist_id}", + "starred_url": "https://api.github.com/users/MYORG/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/MYORG/subscriptions", + "organizations_url": "https://api.github.com/users/MYORG/orgs", + "repos_url": "https://api.github.com/users/MYORG/repos", + "events_url": "https://api.github.com/users/MYORG/events{/privacy}", + "received_events_url": "https://api.github.com/users/MYORG/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/MYORG/MYREPO", + "description": "Proprietary source code for the MYORG Platform. Please do not redistribute.", + "fork": false, + "url": "https://api.github.com/repos/MYORG/MYREPO", + "forks_url": "https://api.github.com/repos/MYORG/MYREPO/forks", + "keys_url": "https://api.github.com/repos/MYORG/MYREPO/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/MYORG/MYREPO/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/MYORG/MYREPO/teams", + "hooks_url": "https://api.github.com/repos/MYORG/MYREPO/hooks", + "issue_events_url": "https://api.github.com/repos/MYORG/MYREPO/issues/events{/number}", + "events_url": "https://api.github.com/repos/MYORG/MYREPO/events", + "assignees_url": "https://api.github.com/repos/MYORG/MYREPO/assignees{/user}", + "branches_url": "https://api.github.com/repos/MYORG/MYREPO/branches{/branch}", + "tags_url": "https://api.github.com/repos/MYORG/MYREPO/tags", + "blobs_url": "https://api.github.com/repos/MYORG/MYREPO/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/MYORG/MYREPO/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/MYORG/MYREPO/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/MYORG/MYREPO/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/MYORG/MYREPO/statuses/{sha}", + "languages_url": "https://api.github.com/repos/MYORG/MYREPO/languages", + "stargazers_url": "https://api.github.com/repos/MYORG/MYREPO/stargazers", + "contributors_url": "https://api.github.com/repos/MYORG/MYREPO/contributors", + "subscribers_url": "https://api.github.com/repos/MYORG/MYREPO/subscribers", + "subscription_url": "https://api.github.com/repos/MYORG/MYREPO/subscription", + "commits_url": "https://api.github.com/repos/MYORG/MYREPO/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/MYORG/MYREPO/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/MYORG/MYREPO/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/MYORG/MYREPO/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/MYORG/MYREPO/contents/{+path}", + "compare_url": "https://api.github.com/repos/MYORG/MYREPO/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/MYORG/MYREPO/merges", + "archive_url": "https://api.github.com/repos/MYORG/MYREPO/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/MYORG/MYREPO/downloads", + "issues_url": "https://api.github.com/repos/MYORG/MYREPO/issues{/number}", + "pulls_url": "https://api.github.com/repos/MYORG/MYREPO/pulls{/number}", + "milestones_url": "https://api.github.com/repos/MYORG/MYREPO/milestones{/number}", + "notifications_url": "https://api.github.com/repos/MYORG/MYREPO/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/MYORG/MYREPO/labels{/name}", + "releases_url": "https://api.github.com/repos/MYORG/MYREPO/releases{/id}", + "deployments_url": "https://api.github.com/repos/MYORG/MYREPO/deployments", + "created_at": "2016-08-01T04:36:52Z", + "updated_at": "2023-01-05T17:44:13Z", + "pushed_at": "2023-01-05T23:10:46Z", + "git_url": "git://github.com/MYORG/MYREPO.git", + "ssh_url": "git@github.com:MYORG/MYREPO.git", + "clone_url": "https://github.com/MYORG/MYREPO.git", + "svn_url": "https://github.com/MYORG/MYREPO", + "homepage": "", + "size": 724221, + "stargazers_count": 4, + "watchers_count": 4, + "language": "Java", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "has_discussions": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 532, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": true, + "topics": [], + "visibility": "private", + "forks": 0, + "open_issues": 532, + "watchers": 4, + "default_branch": "develop" + }, + "organization": { + "login": "MYORG", + "id": 29604235, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjI5NjA0MjM1", + "url": "https://api.github.com/orgs/MYORG", + "repos_url": "https://api.github.com/orgs/MYORG/repos", + "events_url": "https://api.github.com/orgs/MYORG/events", + "hooks_url": "https://api.github.com/orgs/MYORG/hooks", + "issues_url": "https://api.github.com/orgs/MYORG/issues", + "members_url": "https://api.github.com/orgs/MYORG/members{/member}", + "public_members_url": "https://api.github.com/orgs/MYORG/public_members{/member}", + "avatar_url": "https://avatars.githubusercontent.com/u/29604235?v=4", + "description": "" + }, + "sender": { + "login": "poulok", + "id": 82919061, + "node_id": "MDQ6VXNlcjgyOTE5MDYx", + "avatar_url": "https://avatars.githubusercontent.com/u/82919061?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/poulok", + "html_url": "https://github.com/poulok", + "followers_url": "https://api.github.com/users/poulok/followers", + "following_url": "https://api.github.com/users/poulok/following{/other_user}", + "gists_url": "https://api.github.com/users/poulok/gists{/gist_id}", + "starred_url": "https://api.github.com/users/poulok/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/poulok/subscriptions", + "organizations_url": "https://api.github.com/users/poulok/orgs", + "repos_url": "https://api.github.com/users/poulok/repos", + "events_url": "https://api.github.com/users/poulok/events{/privacy}", + "received_events_url": "https://api.github.com/users/poulok/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file