From 1e904162468908924ac9344e4af58829fa3086ec Mon Sep 17 00:00:00 2001 From: Leevi Lehtonen Date: Tue, 5 Nov 2024 22:34:01 +0200 Subject: [PATCH] feat: add support to not scale GitHub runner on default runner labels (#6251) Signed-off-by: Leevi Lehtonen --- CHANGELOG.md | 1 + pkg/scalers/github_runner_scaler.go | 41 +++++-- pkg/scalers/github_runner_scaler_test.go | 138 ++++++++++++++++++++--- 3 files changed, 155 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 297a4e6d7ab..5b974ff4bd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ Here is an overview of all new **experimental** features: - **Elasticsearch Scaler**: Support Query at the Elasticsearch scaler ([#6216](https://github.com/kedacore/keda/issues/6216)) - **Etcd Scaler**: Add username and password support for etcd ([#6199](https://github.com/kedacore/keda/pull/6199)) - **GCP Scalers**: Added custom time horizon in GCP scalers ([#5778](https://github.com/kedacore/keda/issues/5778)) +- **GitHub Scaler**: Add support to not scale on default runner labels ([#6127](https://github.com/kedacore/keda/issues/6127)) - **GitHub Scaler**: Fixed pagination, fetching repository list ([#5738](https://github.com/kedacore/keda/issues/5738)) - **Grafana dashboard**: Fix dashboard to handle wildcard scaledObject variables ([#6214](https://github.com/kedacore/keda/issues/6214)) - **Kafka**: Allow disabling FAST negotation when using Kerberos ([#6188](https://github.com/kedacore/keda/issues/6188)) diff --git a/pkg/scalers/github_runner_scaler.go b/pkg/scalers/github_runner_scaler.go index c9c93c501b1..026c363709e 100644 --- a/pkg/scalers/github_runner_scaler.go +++ b/pkg/scalers/github_runner_scaler.go @@ -43,6 +43,7 @@ type githubRunnerMetadata struct { personalAccessToken *string repos []string labels []string + noDefaultLabels bool targetWorkflowQueueLength int64 triggerIndex int applicationID *int64 @@ -372,8 +373,8 @@ func getValueFromMetaOrEnv(key string, metadata map[string]string, env map[strin } // getInt64ValueFromMetaOrEnv returns the value of the given key from the metadata or the environment variables -func getInt64ValueFromMetaOrEnv(key string, config *scalersconfig.ScalerConfig) (int64, error) { - sInt, err := getValueFromMetaOrEnv(key, config.TriggerMetadata, config.ResolvedEnv) +func getInt64ValueFromMetaOrEnv(key string, metadata map[string]string, env map[string]string) (int64, error) { + sInt, err := getValueFromMetaOrEnv(key, metadata, env) if err != nil { return -1, fmt.Errorf("error parsing %s: %w", key, err) } @@ -385,6 +386,20 @@ func getInt64ValueFromMetaOrEnv(key string, config *scalersconfig.ScalerConfig) return goodInt, nil } +// getInt64ValueFromMetaOrEnv returns the value of the given key from the metadata or the environment variables +func getBoolValueFromMetaOrEnv(key string, metadata map[string]string, env map[string]string) (bool, error) { + sBool, err := getValueFromMetaOrEnv(key, metadata, env) + if err != nil { + return false, fmt.Errorf("error parsing %s: %w", key, err) + } + + goodBool, err := strconv.ParseBool(sBool) + if err != nil { + return false, fmt.Errorf("error parsing %s: %w", key, err) + } + return goodBool, nil +} + func parseGitHubRunnerMetadata(config *scalersconfig.ScalerConfig) (*githubRunnerMetadata, error) { meta := &githubRunnerMetadata{} meta.targetWorkflowQueueLength = defaultTargetWorkflowQueueLength @@ -401,7 +416,7 @@ func parseGitHubRunnerMetadata(config *scalersconfig.ScalerConfig) (*githubRunne return nil, err } - if val, err := getInt64ValueFromMetaOrEnv("targetWorkflowQueueLength", config); err == nil && val != -1 { + if val, err := getInt64ValueFromMetaOrEnv("targetWorkflowQueueLength", config.TriggerMetadata, config.ResolvedEnv); err == nil && val != -1 { meta.targetWorkflowQueueLength = val } else { meta.targetWorkflowQueueLength = defaultTargetWorkflowQueueLength @@ -411,6 +426,12 @@ func parseGitHubRunnerMetadata(config *scalersconfig.ScalerConfig) (*githubRunne meta.labels = strings.Split(val, ",") } + if val, err := getBoolValueFromMetaOrEnv("noDefaultLabels", config.TriggerMetadata, config.ResolvedEnv); err == nil { + meta.noDefaultLabels = val + } else { + meta.noDefaultLabels = false + } + if val, err := getValueFromMetaOrEnv("repos", config.TriggerMetadata, config.ResolvedEnv); err == nil && val != "" { meta.repos = strings.Split(val, ",") } @@ -448,12 +469,12 @@ func setupGitHubApp(config *scalersconfig.ScalerConfig) (*int64, *int64, *string var instID *int64 var appKey *string - appIDVal, appIDErr := getInt64ValueFromMetaOrEnv("applicationID", config) + appIDVal, appIDErr := getInt64ValueFromMetaOrEnv("applicationID", config.TriggerMetadata, config.ResolvedEnv) if appIDErr == nil && appIDVal != -1 { appID = &appIDVal } - instIDVal, instIDErr := getInt64ValueFromMetaOrEnv("installationID", config) + instIDVal, instIDErr := getInt64ValueFromMetaOrEnv("installationID", config.TriggerMetadata, config.ResolvedEnv) if instIDErr == nil && instIDVal != -1 { instID = &instIDVal } @@ -625,9 +646,13 @@ func contains(s []string, e string) bool { } // canRunnerMatchLabels check Agent Label array will match runner label array -func canRunnerMatchLabels(jobLabels []string, runnerLabels []string) bool { +func canRunnerMatchLabels(jobLabels []string, runnerLabels []string, noDefaultLabels bool) bool { + allLabels := runnerLabels + if !noDefaultLabels { + allLabels = append(allLabels, reservedLabels...) + } for _, jobLabel := range jobLabels { - if !contains(runnerLabels, jobLabel) && !contains(reservedLabels, jobLabel) { + if !contains(allLabels, jobLabel) { return false } } @@ -665,7 +690,7 @@ func (s *githubRunnerScaler) GetWorkflowQueueLength(ctx context.Context) (int64, return -1, err } for _, job := range jobs { - if (job.Status == "queued" || job.Status == "in_progress") && canRunnerMatchLabels(job.Labels, s.metadata.labels) { + if (job.Status == "queued" || job.Status == "in_progress") && canRunnerMatchLabels(job.Labels, s.metadata.labels, s.metadata.noDefaultLabels) { queueCount++ } } diff --git a/pkg/scalers/github_runner_scaler_test.go b/pkg/scalers/github_runner_scaler_test.go index 808ac78562c..0f88a330c87 100644 --- a/pkg/scalers/github_runner_scaler_test.go +++ b/pkg/scalers/github_runner_scaler_test.go @@ -22,6 +22,7 @@ var testGhWorkflowResponse = `{"total_count":1,"workflow_runs":[{"id":30433642," var ghDeadJob = `{"id":30433642,"name":"Build","node_id":"MDEyOldvcmtmbG93IFJ1bjI2OTI4OQ==","check_suite_id":42,"check_suite_node_id":"MDEwOkNoZWNrU3VpdGU0Mg==","head_branch":"master","head_sha":"acb5820ced9479c074f688cc328bf03f341a511d","run_number":562,"event":"push","status":"completed","conclusion":null,"workflow_id":159038,"url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642","html_url":"https://github.com/octo-org/octo-repo/actions/runs/30433642","pull_requests":[],"created_at":"2020-01-22T19:33:08Z","updated_at":"2020-01-22T19:33:08Z","actor":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","html_url":"https://github.com/octocat","followers_url":"https://api.github.com/users/octocat/followers","following_url":"https://api.github.com/users/octocat/following{/other_user}","gists_url":"https://api.github.com/users/octocat/gists{/gist_id}","starred_url":"https://api.github.com/users/octocat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/octocat/subscriptions","organizations_url":"https://api.github.com/users/octocat/orgs","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"run_attempt":1,"run_started_at":"2020-01-22T19:33:08Z","triggering_actor":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","html_url":"https://github.com/octocat","followers_url":"https://api.github.com/users/octocat/followers","following_url":"https://api.github.com/users/octocat/following{/other_user}","gists_url":"https://api.github.com/users/octocat/gists{/gist_id}","starred_url":"https://api.github.com/users/octocat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/octocat/subscriptions","organizations_url":"https://api.github.com/users/octocat/orgs","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"jobs_url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/jobs","logs_url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/logs","check_suite_url":"https://api.github.com/repos/octo-org/octo-repo/check-suites/414944374","artifacts_url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/artifacts","cancel_url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/cancel","rerun_url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/30433642/rerun","workflow_url":"https://api.github.com/repos/octo-org/octo-repo/actions/workflows/159038","head_commit":{"id":"acb5820ced9479c074f688cc328bf03f341a511d","tree_id":"d23f6eedb1e1b9610bbc754ddb5197bfe7271223","message":"Create linter.yaml","timestamp":"2020-01-22T19:33:05Z","author":{"name":"Octo Cat","email":"octocat@github.com"},"committer":{"name":"GitHub","email":"noreply@github.com"}},"repository":{"id":1296269,"node_id":"MDEwOlJlcG9zaXRvcnkxMjk2MjY5","name":"Hello-World","full_name":"octocat/Hello-World","owner":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","html_url":"https://github.com/octocat","followers_url":"https://api.github.com/users/octocat/followers","following_url":"https://api.github.com/users/octocat/following{/other_user}","gists_url":"https://api.github.com/users/octocat/gists{/gist_id}","starred_url":"https://api.github.com/users/octocat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/octocat/subscriptions","organizations_url":"https://api.github.com/users/octocat/orgs","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/octocat/Hello-World","description":"This your first repo!","fork":false,"url":"https://api.github.com/repos/octocat/Hello-World","archive_url":"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}","assignees_url":"https://api.github.com/repos/octocat/Hello-World/assignees{/user}","blobs_url":"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}","branches_url":"https://api.github.com/repos/octocat/Hello-World/branches{/branch}","collaborators_url":"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}","comments_url":"https://api.github.com/repos/octocat/Hello-World/comments{/number}","commits_url":"https://api.github.com/repos/octocat/Hello-World/commits{/sha}","compare_url":"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}","contents_url":"https://api.github.com/repos/octocat/Hello-World/contents/{+path}","contributors_url":"https://api.github.com/repos/octocat/Hello-World/contributors","deployments_url":"https://api.github.com/repos/octocat/Hello-World/deployments","downloads_url":"https://api.github.com/repos/octocat/Hello-World/downloads","events_url":"https://api.github.com/repos/octocat/Hello-World/events","forks_url":"https://api.github.com/repos/octocat/Hello-World/forks","git_commits_url":"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}","git_refs_url":"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}","git_tags_url":"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}","git_url":"git:github.com/octocat/Hello-World.git","issue_comment_url":"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}","issue_events_url":"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}","issues_url":"https://api.github.com/repos/octocat/Hello-World/issues{/number}","keys_url":"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}","labels_url":"https://api.github.com/repos/octocat/Hello-World/labels{/name}","languages_url":"https://api.github.com/repos/octocat/Hello-World/languages","merges_url":"https://api.github.com/repos/octocat/Hello-World/merges","milestones_url":"https://api.github.com/repos/octocat/Hello-World/milestones{/number}","notifications_url":"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}","pulls_url":"https://api.github.com/repos/octocat/Hello-World/pulls{/number}","releases_url":"https://api.github.com/repos/octocat/Hello-World/releases{/id}","ssh_url":"git@github.com:octocat/Hello-World.git","stargazers_url":"https://api.github.com/repos/octocat/Hello-World/stargazers","statuses_url":"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}","subscribers_url":"https://api.github.com/repos/octocat/Hello-World/subscribers","subscription_url":"https://api.github.com/repos/octocat/Hello-World/subscription","tags_url":"https://api.github.com/repos/octocat/Hello-World/tags","teams_url":"https://api.github.com/repos/octocat/Hello-World/teams","trees_url":"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}","hooks_url":"http://api.github.com/repos/octocat/Hello-World/hooks"},"head_repository":{"id":217723378,"node_id":"MDEwOlJlcG9zaXRvcnkyMTc3MjMzNzg=","name":"octo-repo","full_name":"octo-org/octo-repo","private":true,"owner":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","html_url":"https://github.com/octocat","followers_url":"https://api.github.com/users/octocat/followers","following_url":"https://api.github.com/users/octocat/following{/other_user}","gists_url":"https://api.github.com/users/octocat/gists{/gist_id}","starred_url":"https://api.github.com/users/octocat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/octocat/subscriptions","organizations_url":"https://api.github.com/users/octocat/orgs","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"html_url":"https://github.com/octo-org/octo-repo","description":null,"fork":false,"url":"https://api.github.com/repos/octo-org/octo-repo","forks_url":"https://api.github.com/repos/octo-org/octo-repo/forks","keys_url":"https://api.github.com/repos/octo-org/octo-repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/octo-org/octo-repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/octo-org/octo-repo/teams","hooks_url":"https://api.github.com/repos/octo-org/octo-repo/hooks","issue_events_url":"https://api.github.com/repos/octo-org/octo-repo/issues/events{/number}","events_url":"https://api.github.com/repos/octo-org/octo-repo/events","assignees_url":"https://api.github.com/repos/octo-org/octo-repo/assignees{/user}","branches_url":"https://api.github.com/repos/octo-org/octo-repo/branches{/branch}","tags_url":"https://api.github.com/repos/octo-org/octo-repo/tags","blobs_url":"https://api.github.com/repos/octo-org/octo-repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/octo-org/octo-repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/octo-org/octo-repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/octo-org/octo-repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/octo-org/octo-repo/statuses/{sha}","languages_url":"https://api.github.com/repos/octo-org/octo-repo/languages","stargazers_url":"https://api.github.com/repos/octo-org/octo-repo/stargazers","contributors_url":"https://api.github.com/repos/octo-org/octo-repo/contributors","subscribers_url":"https://api.github.com/repos/octo-org/octo-repo/subscribers","subscription_url":"https://api.github.com/repos/octo-org/octo-repo/subscription","commits_url":"https://api.github.com/repos/octo-org/octo-repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/octo-org/octo-repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/octo-org/octo-repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/octo-org/octo-repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/octo-org/octo-repo/contents/{+path}","compare_url":"https://api.github.com/repos/octo-org/octo-repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/octo-org/octo-repo/merges","archive_url":"https://api.github.com/repos/octo-org/octo-repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/octo-org/octo-repo/downloads","issues_url":"https://api.github.com/repos/octo-org/octo-repo/issues{/number}","pulls_url":"https://api.github.com/repos/octo-org/octo-repo/pulls{/number}","milestones_url":"https://api.github.com/repos/octo-org/octo-repo/milestones{/number}","notifications_url":"https://api.github.com/repos/octo-org/octo-repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/octo-org/octo-repo/labels{/name}","releases_url":"https://api.github.com/repos/octo-org/octo-repo/releases{/id}","deployments_url":"https://api.github.com/repos/octo-org/octo-repo/deployments"}}` var testGhUserReposResponse = `[{"id":1296269,"node_id":"MDEwOlJlcG9zaXRvcnkxMjk2MjY5","name":"Hello-World-2","full_name":"octocat/Hello-World","owner":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","html_url":"https://github.com/octocat","followers_url":"https://api.github.com/users/octocat/followers","following_url":"https://api.github.com/users/octocat/following{/other_user}","gists_url":"https://api.github.com/users/octocat/gists{/gist_id}","starred_url":"https://api.github.com/users/octocat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/octocat/subscriptions","organizations_url":"https://api.github.com/users/octocat/orgs","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/octocat/Hello-World","description":"This your first repo!","fork":false,"url":"https://api.github.com/repos/octocat/Hello-World","archive_url":"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}","assignees_url":"https://api.github.com/repos/octocat/Hello-World/assignees{/user}","blobs_url":"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}","branches_url":"https://api.github.com/repos/octocat/Hello-World/branches{/branch}","collaborators_url":"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}","comments_url":"https://api.github.com/repos/octocat/Hello-World/comments{/number}","commits_url":"https://api.github.com/repos/octocat/Hello-World/commits{/sha}","compare_url":"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}","contents_url":"https://api.github.com/repos/octocat/Hello-World/contents/{+path}","contributors_url":"https://api.github.com/repos/octocat/Hello-World/contributors","deployments_url":"https://api.github.com/repos/octocat/Hello-World/deployments","downloads_url":"https://api.github.com/repos/octocat/Hello-World/downloads","events_url":"https://api.github.com/repos/octocat/Hello-World/events","forks_url":"https://api.github.com/repos/octocat/Hello-World/forks","git_commits_url":"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}","git_refs_url":"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}","git_tags_url":"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}","git_url":"git:github.com/octocat/Hello-World.git","issue_comment_url":"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}","issue_events_url":"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}","issues_url":"https://api.github.com/repos/octocat/Hello-World/issues{/number}","keys_url":"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}","labels_url":"https://api.github.com/repos/octocat/Hello-World/labels{/name}","languages_url":"https://api.github.com/repos/octocat/Hello-World/languages","merges_url":"https://api.github.com/repos/octocat/Hello-World/merges","milestones_url":"https://api.github.com/repos/octocat/Hello-World/milestones{/number}","notifications_url":"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}","pulls_url":"https://api.github.com/repos/octocat/Hello-World/pulls{/number}","releases_url":"https://api.github.com/repos/octocat/Hello-World/releases{/id}","ssh_url":"git@github.com:octocat/Hello-World.git","stargazers_url":"https://api.github.com/repos/octocat/Hello-World/stargazers","statuses_url":"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}","subscribers_url":"https://api.github.com/repos/octocat/Hello-World/subscribers","subscription_url":"https://api.github.com/repos/octocat/Hello-World/subscription","tags_url":"https://api.github.com/repos/octocat/Hello-World/tags","teams_url":"https://api.github.com/repos/octocat/Hello-World/teams","trees_url":"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}","clone_url":"https://github.com/octocat/Hello-World.git","mirror_url":"git:git.example.com/octocat/Hello-World","hooks_url":"https://api.github.com/repos/octocat/Hello-World/hooks","svn_url":"https://svn.github.com/octocat/Hello-World","homepage":"https://github.com","language":null,"forks_count":9,"stargazers_count":80,"watchers_count":80,"size":108,"default_branch":"master","open_issues_count":0,"is_template":true,"topics":["octocat","atom","electron","api"],"has_issues":true,"has_projects":true,"has_wiki":true,"has_pages":false,"has_downloads":true,"archived":false,"disabled":false,"visibility":"public","pushed_at":"2011-01-26T19:06:43Z","created_at":"2011-01-26T19:01:12Z","updated_at":"2011-01-26T19:14:43Z","permissions":{"admin":false,"push":false,"pull":true},"allow_rebase_merge":true,"template_repository":null,"temp_clone_token":"ABTLWHOULUVAXGTRYU7OC2876QJ2O","allow_squash_merge":true,"allow_auto_merge":false,"delete_branch_on_merge":true,"allow_merge_commit":true,"subscribers_count":42,"network_count":0,"license":{"key":"mit","name":"MIT License","url":"https://api.github.com/licenses/mit","spdx_id":"MIT","node_id":"MDc6TGljZW5zZW1pdA==","html_url":"https://github.com/licenses/mit"},"forks":1,"open_issues":1,"watchers":1},{"id":1296269,"node_id":"MDEwOlJlcG9zaXRvcnkxMjk2MjY5","name":"Hello-World","full_name":"octocat/Hello-World","owner":{"login":"octocat","id":1,"node_id":"MDQ6VXNlcjE=","avatar_url":"https://github.com/images/error/octocat_happy.gif","gravatar_id":"","url":"https://api.github.com/users/octocat","html_url":"https://github.com/octocat","followers_url":"https://api.github.com/users/octocat/followers","following_url":"https://api.github.com/users/octocat/following{/other_user}","gists_url":"https://api.github.com/users/octocat/gists{/gist_id}","starred_url":"https://api.github.com/users/octocat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/octocat/subscriptions","organizations_url":"https://api.github.com/users/octocat/orgs","repos_url":"https://api.github.com/users/octocat/repos","events_url":"https://api.github.com/users/octocat/events{/privacy}","received_events_url":"https://api.github.com/users/octocat/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/octocat/Hello-World","description":"This your first repo!","fork":false,"url":"https://api.github.com/repos/octocat/Hello-World","archive_url":"https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}","assignees_url":"https://api.github.com/repos/octocat/Hello-World/assignees{/user}","blobs_url":"https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}","branches_url":"https://api.github.com/repos/octocat/Hello-World/branches{/branch}","collaborators_url":"https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}","comments_url":"https://api.github.com/repos/octocat/Hello-World/comments{/number}","commits_url":"https://api.github.com/repos/octocat/Hello-World/commits{/sha}","compare_url":"https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}","contents_url":"https://api.github.com/repos/octocat/Hello-World/contents/{+path}","contributors_url":"https://api.github.com/repos/octocat/Hello-World/contributors","deployments_url":"https://api.github.com/repos/octocat/Hello-World/deployments","downloads_url":"https://api.github.com/repos/octocat/Hello-World/downloads","events_url":"https://api.github.com/repos/octocat/Hello-World/events","forks_url":"https://api.github.com/repos/octocat/Hello-World/forks","git_commits_url":"https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}","git_refs_url":"https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}","git_tags_url":"https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}","git_url":"git:github.com/octocat/Hello-World.git","issue_comment_url":"https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}","issue_events_url":"https://api.github.com/repos/octocat/Hello-World/issues/events{/number}","issues_url":"https://api.github.com/repos/octocat/Hello-World/issues{/number}","keys_url":"https://api.github.com/repos/octocat/Hello-World/keys{/key_id}","labels_url":"https://api.github.com/repos/octocat/Hello-World/labels{/name}","languages_url":"https://api.github.com/repos/octocat/Hello-World/languages","merges_url":"https://api.github.com/repos/octocat/Hello-World/merges","milestones_url":"https://api.github.com/repos/octocat/Hello-World/milestones{/number}","notifications_url":"https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}","pulls_url":"https://api.github.com/repos/octocat/Hello-World/pulls{/number}","releases_url":"https://api.github.com/repos/octocat/Hello-World/releases{/id}","ssh_url":"git@github.com:octocat/Hello-World.git","stargazers_url":"https://api.github.com/repos/octocat/Hello-World/stargazers","statuses_url":"https://api.github.com/repos/octocat/Hello-World/statuses/{sha}","subscribers_url":"https://api.github.com/repos/octocat/Hello-World/subscribers","subscription_url":"https://api.github.com/repos/octocat/Hello-World/subscription","tags_url":"https://api.github.com/repos/octocat/Hello-World/tags","teams_url":"https://api.github.com/repos/octocat/Hello-World/teams","trees_url":"https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}","clone_url":"https://github.com/octocat/Hello-World.git","mirror_url":"git:git.example.com/octocat/Hello-World","hooks_url":"https://api.github.com/repos/octocat/Hello-World/hooks","svn_url":"https://svn.github.com/octocat/Hello-World","homepage":"https://github.com","language":null,"forks_count":9,"stargazers_count":80,"watchers_count":80,"size":108,"default_branch":"master","open_issues_count":0,"is_template":true,"topics":["octocat","atom","electron","api"],"has_issues":true,"has_projects":true,"has_wiki":true,"has_pages":false,"has_downloads":true,"archived":false,"disabled":false,"visibility":"public","pushed_at":"2011-01-26T19:06:43Z","created_at":"2011-01-26T19:01:12Z","updated_at":"2011-01-26T19:14:43Z","permissions":{"admin":false,"push":false,"pull":true},"allow_rebase_merge":true,"template_repository":null,"temp_clone_token":"ABTLWHOULUVAXGTRYU7OC2876QJ2O","allow_squash_merge":true,"allow_auto_merge":false,"delete_branch_on_merge":true,"allow_merge_commit":true,"subscribers_count":42,"network_count":0,"license":{"key":"mit","name":"MIT License","url":"https://api.github.com/licenses/mit","spdx_id":"MIT","node_id":"MDc6TGljZW5zZW1pdA==","html_url":"https://github.com/licenses/mit"},"forks":1,"open_issues":1,"watchers":1}]` var testGhWFJobResponse = `{"total_count":1,"jobs":[{"id":399444496,"run_id":29679449,"run_url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/29679449","node_id":"MDEyOldvcmtmbG93IEpvYjM5OTQ0NDQ5Ng==","head_sha":"f83a356604ae3c5d03e1b46ef4d1ca77d64a90b0","url":"https://api.github.com/repos/octo-org/octo-repo/actions/jobs/399444496","html_url":"https://github.com/octo-org/octo-repo/runs/399444496","status":"queued","conclusion":"success","started_at":"2020-01-20T17:42:40Z","completed_at":"2020-01-20T17:44:39Z","name":"build","steps":[{"name":"Set up job","status":"completed","conclusion":"success","number":1,"started_at":"2020-01-20T09:42:40.000-08:00","completed_at":"2020-01-20T09:42:41.000-08:00"},{"name":"Run actions/checkout@v2","status":"queued","conclusion":"success","number":2,"started_at":"2020-01-20T09:42:41.000-08:00","completed_at":"2020-01-20T09:42:45.000-08:00"},{"name":"Set up Ruby","status":"completed","conclusion":"success","number":3,"started_at":"2020-01-20T09:42:45.000-08:00","completed_at":"2020-01-20T09:42:45.000-08:00"},{"name":"Run actions/cache@v3","status":"completed","conclusion":"success","number":4,"started_at":"2020-01-20T09:42:45.000-08:00","completed_at":"2020-01-20T09:42:48.000-08:00"},{"name":"Install Bundler","status":"completed","conclusion":"success","number":5,"started_at":"2020-01-20T09:42:48.000-08:00","completed_at":"2020-01-20T09:42:52.000-08:00"},{"name":"Install Gems","status":"completed","conclusion":"success","number":6,"started_at":"2020-01-20T09:42:52.000-08:00","completed_at":"2020-01-20T09:42:53.000-08:00"},{"name":"Run Tests","status":"completed","conclusion":"success","number":7,"started_at":"2020-01-20T09:42:53.000-08:00","completed_at":"2020-01-20T09:42:59.000-08:00"},{"name":"Deploy to Heroku","status":"completed","conclusion":"success","number":8,"started_at":"2020-01-20T09:42:59.000-08:00","completed_at":"2020-01-20T09:44:39.000-08:00"},{"name":"Post actions/cache@v3","status":"completed","conclusion":"success","number":16,"started_at":"2020-01-20T09:44:39.000-08:00","completed_at":"2020-01-20T09:44:39.000-08:00"},{"name":"Complete job","status":"completed","conclusion":"success","number":17,"started_at":"2020-01-20T09:44:39.000-08:00","completed_at":"2020-01-20T09:44:39.000-08:00"}],"check_run_url":"https://api.github.com/repos/octo-org/octo-repo/check-runs/399444496","labels":["self-hosted","foo","bar"],"runner_id":1,"runner_name":"my runner","runner_group_id":2,"runner_group_name":"my runner group","workflow_name":"CI","head_branch":"main"}]}` +var testGhWFJobResponseOnlyCustomLabels = `{"total_count":1,"jobs":[{"id":399444496,"run_id":29679449,"run_url":"https://api.github.com/repos/octo-org/octo-repo/actions/runs/29679449","node_id":"MDEyOldvcmtmbG93IEpvYjM5OTQ0NDQ5Ng==","head_sha":"f83a356604ae3c5d03e1b46ef4d1ca77d64a90b0","url":"https://api.github.com/repos/octo-org/octo-repo/actions/jobs/399444496","html_url":"https://github.com/octo-org/octo-repo/runs/399444496","status":"queued","conclusion":"success","started_at":"2020-01-20T17:42:40Z","completed_at":"2020-01-20T17:44:39Z","name":"build","steps":[{"name":"Set up job","status":"completed","conclusion":"success","number":1,"started_at":"2020-01-20T09:42:40.000-08:00","completed_at":"2020-01-20T09:42:41.000-08:00"},{"name":"Run actions/checkout@v2","status":"queued","conclusion":"success","number":2,"started_at":"2020-01-20T09:42:41.000-08:00","completed_at":"2020-01-20T09:42:45.000-08:00"},{"name":"Set up Ruby","status":"completed","conclusion":"success","number":3,"started_at":"2020-01-20T09:42:45.000-08:00","completed_at":"2020-01-20T09:42:45.000-08:00"},{"name":"Run actions/cache@v3","status":"completed","conclusion":"success","number":4,"started_at":"2020-01-20T09:42:45.000-08:00","completed_at":"2020-01-20T09:42:48.000-08:00"},{"name":"Install Bundler","status":"completed","conclusion":"success","number":5,"started_at":"2020-01-20T09:42:48.000-08:00","completed_at":"2020-01-20T09:42:52.000-08:00"},{"name":"Install Gems","status":"completed","conclusion":"success","number":6,"started_at":"2020-01-20T09:42:52.000-08:00","completed_at":"2020-01-20T09:42:53.000-08:00"},{"name":"Run Tests","status":"completed","conclusion":"success","number":7,"started_at":"2020-01-20T09:42:53.000-08:00","completed_at":"2020-01-20T09:42:59.000-08:00"},{"name":"Deploy to Heroku","status":"completed","conclusion":"success","number":8,"started_at":"2020-01-20T09:42:59.000-08:00","completed_at":"2020-01-20T09:44:39.000-08:00"},{"name":"Post actions/cache@v3","status":"completed","conclusion":"success","number":16,"started_at":"2020-01-20T09:44:39.000-08:00","completed_at":"2020-01-20T09:44:39.000-08:00"},{"name":"Complete job","status":"completed","conclusion":"success","number":17,"started_at":"2020-01-20T09:44:39.000-08:00","completed_at":"2020-01-20T09:44:39.000-08:00"}],"check_run_url":"https://api.github.com/repos/octo-org/octo-repo/check-runs/399444496","labels":["foo","bar"],"runner_id":1,"runner_name":"my runner","runner_group_id":2,"runner_group_name":"my runner group","workflow_name":"CI","head_branch":"main"}]}` type parseGitHubRunnerMetadataTestData struct { testName string @@ -179,6 +180,10 @@ func generateResponseExceed30Repos() []byte { } func apiStubHandler(hasRateLeft bool, exceeds30Repos bool) *httptest.Server { + return apiStubHandlerCustomJob(hasRateLeft, exceeds30Repos, testGhWFJobResponse) +} + +func apiStubHandlerCustomJob(hasRateLeft bool, exceeds30Repos bool, jobResponse string) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { futureReset := time.Now() futureReset = futureReset.Add(time.Minute * 30) @@ -190,7 +195,7 @@ func apiStubHandler(hasRateLeft bool, exceeds30Repos bool) *httptest.Server { w.WriteHeader(http.StatusForbidden) } if strings.HasSuffix(r.URL.String(), "jobs") { - _, _ = w.Write([]byte(testGhWFJobResponse)) + _, _ = w.Write([]byte(jobResponse)) w.WriteHeader(http.StatusOK) } if strings.HasSuffix(r.URL.String(), "runs") { @@ -243,7 +248,7 @@ func TestNewGitHubRunnerScaler_QueueLength_NoRateLeft(t *testing.T) { tRepo := []string{"test"} mockGitHubRunnerScaler.metadata.repos = tRepo - _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err == nil { t.Fail() @@ -267,7 +272,7 @@ func TestNewGitHubRunnerScaler_QueueLength_SingleRepo(t *testing.T) { mockGitHubRunnerScaler.metadata.repos = []string{"test"} mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { t.Fail() @@ -291,7 +296,7 @@ func TestNewGitHubRunnerScaler_QueueLength_SingleRepo_ExtraRunnerLabels(t *testi mockGitHubRunnerScaler.metadata.repos = []string{"test"} mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar", "other", "more"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { t.Fail() @@ -315,7 +320,7 @@ func TestNewGitHubRunnerScaler_QueueLength_SingleRepo_LessRunnerLabels(t *testin mockGitHubRunnerScaler.metadata.repos = []string{"test"} mockGitHubRunnerScaler.metadata.labels = []string{"foo"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { t.Fail() @@ -325,6 +330,105 @@ func TestNewGitHubRunnerScaler_QueueLength_SingleRepo_LessRunnerLabels(t *testin t.Fail() } } +func TestNewGitHubRunnerScaler_QueueLength_SingleRepo_WithScalerDefaultLabels_WithJobDefaultLabels(t *testing.T) { + var apiStub = apiStubHandler(true, false) + + meta := getGitHubTestMetaData(apiStub.URL) + + mockGitHubRunnerScaler := githubRunnerScaler{ + metadata: meta, + httpClient: http.DefaultClient, + } + + mockGitHubRunnerScaler.metadata.repos = []string{"test"} + mockGitHubRunnerScaler.metadata.noDefaultLabels = false + mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} + + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) + + if err != nil { + t.Fail() + } + + if queueLen != 1 { + t.Fail() + } +} + +func TestNewGitHubRunnerScaler_QueueLength_SingleRepo_WithScalerDefaultLabels_WithoutJobDefaultLabels(t *testing.T) { + var apiStub = apiStubHandlerCustomJob(true, false, testGhWFJobResponseOnlyCustomLabels) + + meta := getGitHubTestMetaData(apiStub.URL) + + mockGitHubRunnerScaler := githubRunnerScaler{ + metadata: meta, + httpClient: http.DefaultClient, + } + + mockGitHubRunnerScaler.metadata.repos = []string{"test"} + mockGitHubRunnerScaler.metadata.noDefaultLabels = false + mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} + + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) + + if err != nil { + t.Fail() + } + + if queueLen != 1 { + t.Fail() + } +} + +func TestNewGitHubRunnerScaler_QueueLength_SingleRepo_WithoutScalerDefaultLabels_WithJobDefaultLabels(t *testing.T) { + var apiStub = apiStubHandler(true, false) + + meta := getGitHubTestMetaData(apiStub.URL) + + mockGitHubRunnerScaler := githubRunnerScaler{ + metadata: meta, + httpClient: http.DefaultClient, + } + + mockGitHubRunnerScaler.metadata.repos = []string{"test"} + mockGitHubRunnerScaler.metadata.noDefaultLabels = true + mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} + + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) + + if err != nil { + t.Fail() + } + + if queueLen != 0 { + t.Fail() + } +} + +func TestNewGitHubRunnerScaler_QueueLength_SingleRepo_WithoutScalerDefaultLabels_WithoutJobDefaultLabels(t *testing.T) { + var apiStub = apiStubHandlerCustomJob(true, false, testGhWFJobResponseOnlyCustomLabels) + + meta := getGitHubTestMetaData(apiStub.URL) + + mockGitHubRunnerScaler := githubRunnerScaler{ + metadata: meta, + httpClient: http.DefaultClient, + } + + mockGitHubRunnerScaler.metadata.repos = []string{"test"} + mockGitHubRunnerScaler.metadata.noDefaultLabels = true + mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} + + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) + + if err != nil { + t.Fail() + } + + if queueLen != 1 { + t.Fail() + } +} func TestNewGitHubRunnerScaler_404(t *testing.T) { var apiStub = apiStubHandler404() @@ -338,7 +442,7 @@ func TestNewGitHubRunnerScaler_404(t *testing.T) { mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err == nil { t.Fail() @@ -360,7 +464,7 @@ func TestNewGitHubRunnerScaler_BadConnection(t *testing.T) { mockGitHubRunnerScaler.metadata.repos = []string{"test"} mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err == nil { t.Fail() @@ -383,7 +487,7 @@ func TestNewGitHubRunnerScaler_BadURL(t *testing.T) { mockGitHubRunnerScaler.metadata.repos = []string{"test"} mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err == nil { t.Fail() @@ -407,7 +511,7 @@ func TestNewGitHubRunnerScaler_QueueLength_NoRunnerLabels(t *testing.T) { mockGitHubRunnerScaler.metadata.repos = []string{"test"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { fmt.Println(err) @@ -434,7 +538,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_Assigned(t *testing.T) { mockGitHubRunnerScaler.metadata.runnerScope = ORG mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { fmt.Println(err) @@ -462,7 +566,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_Assigned_OneBad(t *testing. mockGitHubRunnerScaler.metadata.runnerScope = ORG mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { fmt.Println(err) @@ -487,7 +591,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_PulledUserRepos(t *testing. mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { fmt.Println(err) @@ -511,7 +615,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_PulledUserRepos_Exceeds30En mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { fmt.Println(err) t.Fail() @@ -535,7 +639,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_PulledOrgRepos(t *testing.T mockGitHubRunnerScaler.metadata.runnerScope = ORG mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { fmt.Println(err) @@ -560,7 +664,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_PulledEntRepos(t *testing.T mockGitHubRunnerScaler.metadata.runnerScope = ENT mockGitHubRunnerScaler.metadata.labels = []string{"foo", "bar"} - queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + queueLen, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err != nil { fmt.Println(err) @@ -584,7 +688,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_PulledBadRepos(t *testing.T mockGitHubRunnerScaler.metadata.runnerScope = "bad" - _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err == nil { t.Fail() @@ -605,7 +709,7 @@ func TestNewGitHubRunnerScaler_QueueLength_MultiRepo_PulledRepos_NoRate(t *testi httpClient: http.DefaultClient, } - _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.TODO()) + _, err := mockGitHubRunnerScaler.GetWorkflowQueueLength(context.Background()) if err == nil { t.Fail()