From 7a5a6381c3deddb23ea640bc8a33b090a3b023d0 Mon Sep 17 00:00:00 2001 From: Patrick Ellis <319655+pje@users.noreply.github.com> Date: Tue, 19 Apr 2022 20:59:08 -0400 Subject: [PATCH] Add WorkflowJob to GitHubEventScaleUpTriggerSpec types (#922) --- .../horizontalrunnerautoscaler_types.go | 6 + api/v1alpha1/zz_generated.deepcopy.go | 20 +++ ...rwind.dev_horizontalrunnerautoscalers.yaml | 4 + ...rwind.dev_horizontalrunnerautoscalers.yaml | 4 + controllers/integration_test.go | 154 +++++++++++++++--- 5 files changed, 161 insertions(+), 27 deletions(-) diff --git a/api/v1alpha1/horizontalrunnerautoscaler_types.go b/api/v1alpha1/horizontalrunnerautoscaler_types.go index b5da8275..06c215d5 100644 --- a/api/v1alpha1/horizontalrunnerautoscaler_types.go +++ b/api/v1alpha1/horizontalrunnerautoscaler_types.go @@ -72,10 +72,12 @@ type GitHubEventScaleUpTriggerSpec struct { CheckRun *CheckRunSpec `json:"checkRun,omitempty"` PullRequest *PullRequestSpec `json:"pullRequest,omitempty"` Push *PushSpec `json:"push,omitempty"` + WorkflowJob *WorkflowJobSpec `json:"workflowJob,omitempty"` } // https://docs.github.com/en/actions/reference/events-that-trigger-workflows#check_run type CheckRunSpec struct { + // One of: created, rerequested, or completed Types []string `json:"types,omitempty"` Status string `json:"status,omitempty"` @@ -90,6 +92,10 @@ type CheckRunSpec struct { Repositories []string `json:"repositories,omitempty"` } +// https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_job +type WorkflowJobSpec struct { +} + // https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request type PullRequestSpec struct { Types []string `json:"types,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index d96b3076..fdca2e3a 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -108,6 +108,11 @@ func (in *GitHubEventScaleUpTriggerSpec) DeepCopyInto(out *GitHubEventScaleUpTri *out = new(PushSpec) **out = **in } + if in.WorkflowJob != nil { + in, out := &in.WorkflowJob, &out.WorkflowJob + *out = new(WorkflowJobSpec) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitHubEventScaleUpTriggerSpec. @@ -1122,3 +1127,18 @@ func (in *ScheduledOverride) DeepCopy() *ScheduledOverride { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkflowJobSpec) DeepCopyInto(out *WorkflowJobSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkflowJobSpec. +func (in *WorkflowJobSpec) DeepCopy() *WorkflowJobSpec { + if in == nil { + return nil + } + out := new(WorkflowJobSpec) + in.DeepCopyInto(out) + return out +} diff --git a/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml b/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml index 2f1719fd..e256dddd 100644 --- a/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml +++ b/charts/actions-runner-controller/crds/actions.summerwind.dev_horizontalrunnerautoscalers.yaml @@ -141,6 +141,7 @@ spec: status: type: string types: + description: 'One of: created, rerequested, or completed' items: type: string type: array @@ -160,6 +161,9 @@ spec: push: description: PushSpec is the condition for triggering scale-up on push event Also see https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push type: object + workflowJob: + description: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_job + type: object type: object type: object type: array diff --git a/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml b/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml index 2f1719fd..e256dddd 100644 --- a/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml +++ b/config/crd/bases/actions.summerwind.dev_horizontalrunnerautoscalers.yaml @@ -141,6 +141,7 @@ spec: status: type: string types: + description: 'One of: created, rerequested, or completed' items: type: string type: array @@ -160,6 +161,9 @@ spec: push: description: PushSpec is the condition for triggering scale-up on push event Also see https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push type: object + workflowJob: + description: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_job + type: object type: object type: object type: array diff --git a/controllers/integration_test.go b/controllers/integration_test.go index 5695b44d..f5c79fec 100644 --- a/controllers/integration_test.go +++ b/controllers/integration_test.go @@ -537,6 +537,106 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() { } }) + It("should create and scale organization's repository runners on workflow_job event", func() { + name := "example-runnerdeploy" + + { + rd := &actionsv1alpha1.RunnerDeployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns.Name, + }, + Spec: actionsv1alpha1.RunnerDeploymentSpec{ + Replicas: intPtr(1), + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + }, + }, + Template: actionsv1alpha1.RunnerTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "foo": "bar", + }, + }, + Spec: actionsv1alpha1.RunnerSpec{ + RunnerConfig: actionsv1alpha1.RunnerConfig{ + Repository: "test/valid", + Image: "bar", + Group: "baz", + }, + RunnerPodSpec: actionsv1alpha1.RunnerPodSpec{ + Env: []corev1.EnvVar{ + {Name: "FOO", Value: "FOOVALUE"}, + }, + }, + }, + }, + }, + } + + ExpectCreate(ctx, rd, "test RunnerDeployment") + ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1) + ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 1) + env.ExpectRegisteredNumberCountEventuallyEquals(1, "count of fake list runners") + } + + // Scale-up to 1 replica via ScaleUpTriggers.GitHubEvent.WorkflowJob based scaling + { + hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns.Name, + }, + Spec: actionsv1alpha1.HorizontalRunnerAutoscalerSpec{ + ScaleTargetRef: actionsv1alpha1.ScaleTargetRef{ + Name: name, + }, + MinReplicas: intPtr(1), + MaxReplicas: intPtr(5), + ScaleDownDelaySecondsAfterScaleUp: intPtr(1), + ScaleUpTriggers: []actionsv1alpha1.ScaleUpTrigger{ + { + GitHubEvent: &actionsv1alpha1.GitHubEventScaleUpTriggerSpec{ + WorkflowJob: &actionsv1alpha1.WorkflowJobSpec{}, + }, + Amount: 1, + Duration: metav1.Duration{Duration: time.Minute}, + }, + }, + }, + } + + ExpectCreate(ctx, hra, "test HorizontalRunnerAutoscaler") + + ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1) + ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 1) + env.ExpectRegisteredNumberCountEventuallyEquals(1, "count of fake list runners") + } + + // Scale-up to 2 replicas on first workflow_job.queued webhook event + { + env.SendWorkflowJobEvent("test", "valid", "queued", []string{"self-hosted"}) + ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 2, "runners after first webhook event") + env.ExpectRegisteredNumberCountEventuallyEquals(2, "count of fake list runners") + } + + // Scale-up to 3 replicas on second workflow_job.queued webhook event + { + env.SendWorkflowJobEvent("test", "valid", "queued", []string{"self-hosted"}) + ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 3, "runners after second webhook event") + env.ExpectRegisteredNumberCountEventuallyEquals(3, "count of fake list runners") + } + + // Do not scale-up on third workflow_job.queued webhook event + // repo "example" doesn't match our Spec + { + env.SendWorkflowJobEvent("test", "example", "queued", []string{"self-hosted"}) + ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 3, "runners after third webhook event") + env.ExpectRegisteredNumberCountEventuallyEquals(3, "count of fake list runners") + } + }) + It("should create and scale organization's repository runners only on check_run event", func() { name := "example-runnerdeploy" @@ -581,9 +681,7 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() { env.ExpectRegisteredNumberCountEventuallyEquals(1, "count of fake list runners") } - // Scale-up to 3 replicas by the default TotalNumberOfQueuedAndInProgressWorkflowRuns-based scaling - // See workflowRunsFor3Replicas_queued and workflowRunsFor3Replicas_in_progress for GitHub List-Runners API responses - // used while testing. + // Scale-up to 1 replica via ScaleUpTriggers.GitHubEvent.CheckRun based scaling { hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{ ObjectMeta: metav1.ObjectMeta{ @@ -1150,7 +1248,7 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() { // Scale-up to 2 replicas on first workflow_job webhook event { - env.SendWorkflowJobEvent("test", "valid", "pending", "created", []string{"self-hosted"}) + env.SendWorkflowJobEvent("test", "valid", "queued", []string{"self-hosted"}) ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1, "runner sets after webhook") ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 2, "runners after first webhook event") env.ExpectRegisteredNumberCountEventuallyEquals(2, "count of fake list runners") @@ -1232,7 +1330,7 @@ var _ = Context("INTEGRATION: Inside of a new namespace", func() { // Scale-up to 2 replicas on first workflow_job webhook event { - env.SendWorkflowJobEvent("test", "valid", "pending", "created", []string{"custom-label"}) + env.SendWorkflowJobEvent("test", "valid", "queued", []string{"custom-label"}) ExpectRunnerSetsCountEventuallyEquals(ctx, ns.Name, 1, "runner sets after webhook") ExpectRunnerSetsManagedReplicasCountEventuallyEquals(ctx, ns.Name, 2, "runners after first webhook event") env.ExpectRegisteredNumberCountEventuallyEquals(2, "count of fake list runners") @@ -1313,6 +1411,30 @@ func (env *testEnvironment) SendOrgCheckRunEvent(org, repo, status, action strin ExpectWithOffset(1, resp.StatusCode).To(Equal(200)) } +func (env *testEnvironment) SendWorkflowJobEvent(org, repo, statusAndAction string, labels []string) { + resp, err := sendWebhook(env.webhookServer, "workflow_job", &github.WorkflowJobEvent{ + WorkflowJob: &github.WorkflowJob{ + Status: &statusAndAction, + Labels: labels, + }, + Org: &github.Organization{ + Login: github.String(org), + }, + Repo: &github.Repository{ + Name: github.String(repo), + Owner: &github.User{ + Login: github.String(org), + Type: github.String("Organization"), + }, + }, + Action: github.String(statusAndAction), + }) + + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "failed to send workflow_job event") + + ExpectWithOffset(1, resp.StatusCode).To(Equal(200)) +} + func (env *testEnvironment) SendUserPullRequestEvent(owner, repo, branch, action string) { resp, err := sendWebhook(env.webhookServer, "pull_request", &github.PullRequestEvent{ PullRequest: &github.PullRequest{ @@ -1355,28 +1477,6 @@ func (env *testEnvironment) SendUserCheckRunEvent(owner, repo, status, action st ExpectWithOffset(1, resp.StatusCode).To(Equal(200)) } -func (env *testEnvironment) SendWorkflowJobEvent(owner, repo, status, action string, labels []string) { - resp, err := sendWebhook(env.webhookServer, "workflow_job", &github.WorkflowJobEvent{ - Org: &github.Organization{ - Name: github.String(owner), - }, - WorkflowJob: &github.WorkflowJob{ - Labels: labels, - }, - Action: github.String("queued"), - Repo: &github.Repository{ - Name: github.String(repo), - Owner: &github.User{ - Login: github.String(owner), - Type: github.String("Organization"), - }, - }, - }) - - ExpectWithOffset(1, err).NotTo(HaveOccurred(), "failed to send check_run event") - - ExpectWithOffset(1, resp.StatusCode).To(Equal(200)) -} func (env *testEnvironment) SyncRunnerRegistrations() { var runnerList actionsv1alpha1.RunnerList