2020-07-29 21:16:48 +09:00
package controllers
import (
"context"
2021-02-17 10:23:35 +09:00
"fmt"
2021-02-07 17:37:27 +09:00
"github.com/google/go-github/v33/github"
github2 "github.com/summerwind/actions-runner-controller/github"
2021-02-17 10:23:35 +09:00
"k8s.io/apimachinery/pkg/runtime"
2021-02-07 17:37:27 +09:00
"net/http"
"net/http/httptest"
2020-07-29 21:16:48 +09:00
"time"
2020-10-05 01:06:37 +01:00
"github.com/summerwind/actions-runner-controller/github/fake"
2020-07-29 21:16:48 +09:00
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
)
2020-08-02 11:49:25 +09:00
type testEnvironment struct {
Namespace * corev1 . Namespace
Responses * fake . FixedResponses
2021-02-19 10:33:04 +09:00
webhookServer * httptest . Server
ghClient * github2 . Client
fakeRunnerList * fake . RunnersList
fakeGithubServer * httptest . Server
2020-08-02 11:49:25 +09:00
}
var (
2021-02-16 18:55:55 +09:00
workflowRunsFor3Replicas = ` { "total_count": 5, "workflow_runs":[ { "status":"queued"}, { "status":"queued"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" `
workflowRunsFor3Replicas_queued = ` { "total_count": 2, "workflow_runs":[ { "status":"queued"}, { "status":"queued"}]}" `
2021-02-19 10:33:04 +09:00
workflowRunsFor3Replicas_in_progress = ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" `
2021-02-16 18:55:55 +09:00
workflowRunsFor1Replicas = ` { "total_count": 6, "workflow_runs":[ { "status":"queued"}, { "status":"completed"}, { "status":"completed"}, { "status":"completed"}, { "status":"completed"}]}" `
workflowRunsFor1Replicas_queued = ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" `
workflowRunsFor1Replicas_in_progress = ` { "total_count": 0, "workflow_runs":[]}" `
2020-08-02 11:49:25 +09:00
)
2020-07-29 21:16:48 +09:00
// SetupIntegrationTest will set up a testing environment.
// This includes:
// * creating a Namespace to be used during the test
// * starting all the reconcilers
// * stopping all the reconcilers after the test ends
// Call this function at the start of each of your tests.
2020-08-02 11:49:25 +09:00
func SetupIntegrationTest ( ctx context . Context ) * testEnvironment {
2020-07-29 21:16:48 +09:00
var stopCh chan struct { }
ns := & corev1 . Namespace { }
2021-02-19 10:33:04 +09:00
env := & testEnvironment {
Namespace : ns ,
webhookServer : nil ,
ghClient : nil ,
2020-08-02 11:49:25 +09:00
}
2020-08-02 10:34:58 +09:00
2020-07-29 21:16:48 +09:00
BeforeEach ( func ( ) {
stopCh = make ( chan struct { } )
* ns = corev1 . Namespace {
ObjectMeta : metav1 . ObjectMeta { Name : "testns-" + randStringRunes ( 5 ) } ,
}
err := k8sClient . Create ( ctx , ns )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to create test namespace" )
mgr , err := ctrl . NewManager ( cfg , ctrl . Options { } )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to create manager" )
2021-02-19 10:33:04 +09:00
responses := & fake . FixedResponses { }
responses . ListRunners = fake . DefaultListRunnersHandler ( )
responses . ListRepositoryWorkflowRuns = & fake . Handler {
Status : 200 ,
Body : workflowRunsFor3Replicas ,
Statuses : map [ string ] string {
"queued" : workflowRunsFor3Replicas_queued ,
"in_progress" : workflowRunsFor3Replicas_in_progress ,
} ,
}
fakeRunnerList := fake . NewRunnersList ( )
responses . ListRunners = fakeRunnerList . HandleList ( )
fakeGithubServer := fake . NewServer ( fake . WithFixedResponses ( responses ) )
env . Responses = responses
env . fakeRunnerList = fakeRunnerList
env . fakeGithubServer = fakeGithubServer
env . ghClient = newGithubClient ( fakeGithubServer )
controllerName := func ( name string ) string {
return fmt . Sprintf ( "%s%s" , ns . Name , name )
}
2020-10-05 01:06:37 +01:00
2020-07-29 21:16:48 +09:00
replicasetController := & RunnerReplicaSetReconciler {
2020-10-05 01:06:37 +01:00
Client : mgr . GetClient ( ) ,
Scheme : scheme . Scheme ,
Log : logf . Log ,
Recorder : mgr . GetEventRecorderFor ( "runnerreplicaset-controller" ) ,
2021-02-19 10:33:04 +09:00
GitHubClient : env . ghClient ,
Name : controllerName ( "runnerreplicaset" ) ,
2020-07-29 21:16:48 +09:00
}
err = replicasetController . SetupWithManager ( mgr )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to setup controller" )
deploymentsController := & RunnerDeploymentReconciler {
Client : mgr . GetClient ( ) ,
Scheme : scheme . Scheme ,
Log : logf . Log ,
Recorder : mgr . GetEventRecorderFor ( "runnerdeployment-controller" ) ,
2021-02-19 10:33:04 +09:00
Name : controllerName ( "runnnerdeployment" ) ,
2020-07-29 21:16:48 +09:00
}
err = deploymentsController . SetupWithManager ( mgr )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to setup controller" )
autoscalerController := & HorizontalRunnerAutoscalerReconciler {
2021-02-07 17:37:27 +09:00
Client : mgr . GetClient ( ) ,
Scheme : scheme . Scheme ,
Log : logf . Log ,
2021-02-19 10:33:04 +09:00
GitHubClient : env . ghClient ,
2021-02-07 17:37:27 +09:00
Recorder : mgr . GetEventRecorderFor ( "horizontalrunnerautoscaler-controller" ) ,
CacheDuration : 1 * time . Second ,
2021-02-19 10:33:04 +09:00
Name : controllerName ( "horizontalrunnerautoscaler" ) ,
2020-07-29 21:16:48 +09:00
}
err = autoscalerController . SetupWithManager ( mgr )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to setup controller" )
2021-02-07 17:37:27 +09:00
autoscalerWebhook := & HorizontalRunnerAutoscalerGitHubWebhook {
2021-02-23 08:05:25 +09:00
Client : mgr . GetClient ( ) ,
Scheme : scheme . Scheme ,
Log : logf . Log ,
Recorder : mgr . GetEventRecorderFor ( "horizontalrunnerautoscaler-controller" ) ,
Name : controllerName ( "horizontalrunnerautoscalergithubwebhook" ) ,
WatchNamespace : ns . Name ,
2021-02-07 17:37:27 +09:00
}
err = autoscalerWebhook . SetupWithManager ( mgr )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to setup autoscaler webhook" )
mux := http . NewServeMux ( )
mux . HandleFunc ( "/" , autoscalerWebhook . Handle )
2021-02-19 10:33:04 +09:00
env . webhookServer = httptest . NewServer ( mux )
2021-02-07 17:37:27 +09:00
2020-07-29 21:16:48 +09:00
go func ( ) {
defer GinkgoRecover ( )
err := mgr . Start ( stopCh )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to start manager" )
} ( )
} )
AfterEach ( func ( ) {
close ( stopCh )
2021-02-19 10:33:04 +09:00
env . fakeGithubServer . Close ( )
env . webhookServer . Close ( )
2020-08-02 10:34:58 +09:00
2020-07-29 21:16:48 +09:00
err := k8sClient . Delete ( ctx , ns )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to delete test namespace" )
} )
2021-02-19 10:33:04 +09:00
return env
2020-07-29 21:16:48 +09:00
}
2021-02-07 17:37:27 +09:00
var _ = Context ( "INTEGRATION: Inside of a new namespace" , func ( ) {
2020-07-29 21:16:48 +09:00
ctx := context . TODO ( )
2020-08-02 11:49:25 +09:00
env := SetupIntegrationTest ( ctx )
ns := env . Namespace
2020-07-29 21:16:48 +09:00
Describe ( "when no existing resources exist" , func ( ) {
2021-02-23 08:05:25 +09:00
It ( "should create and scale organization's repository runners on pull_request event" , func ( ) {
2020-07-29 21:16:48 +09:00
name := "example-runnerdeploy"
{
2021-02-17 10:23:35 +09:00
rd := & actionsv1alpha1 . RunnerDeployment {
2020-07-29 21:16:48 +09:00
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : ns . Name ,
} ,
Spec : actionsv1alpha1 . RunnerDeploymentSpec {
Replicas : intPtr ( 1 ) ,
Template : actionsv1alpha1 . RunnerTemplate {
Spec : actionsv1alpha1 . RunnerSpec {
2020-08-02 10:34:58 +09:00
Repository : "test/valid" ,
2020-07-29 21:16:48 +09:00
Image : "bar" ,
2020-11-10 08:15:54 +00:00
Group : "baz" ,
2020-07-29 21:16:48 +09:00
Env : [ ] corev1 . EnvVar {
{ Name : "FOO" , Value : "FOOVALUE" } ,
} ,
} ,
} ,
} ,
}
2021-02-17 10:23:35 +09:00
ExpectCreate ( ctx , rd , "test RunnerDeployment" )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 1 )
2020-07-29 21:16:48 +09:00
}
{
2021-02-17 10:23:35 +09:00
ExpectRunnerDeploymentEventuallyUpdates ( ctx , ns . Name , name , func ( rd * actionsv1alpha1 . RunnerDeployment ) {
2020-07-29 21:16:48 +09:00
rd . Spec . Replicas = intPtr ( 2 )
2021-02-17 10:23:35 +09:00
} )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
2021-02-19 10:33:04 +09:00
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 2 )
2020-07-29 21:16:48 +09:00
}
2020-08-02 11:49:25 +09:00
// Scale-up to 3 replicas
2020-07-29 21:16:48 +09:00
{
2020-08-02 11:49:25 +09:00
hra := & actionsv1alpha1 . HorizontalRunnerAutoscaler {
2020-07-29 21:16:48 +09:00
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : ns . Name ,
} ,
Spec : actionsv1alpha1 . HorizontalRunnerAutoscalerSpec {
ScaleTargetRef : actionsv1alpha1 . ScaleTargetRef {
Name : name ,
} ,
MinReplicas : intPtr ( 1 ) ,
2020-08-02 10:34:58 +09:00
MaxReplicas : intPtr ( 3 ) ,
2021-02-07 17:37:27 +09:00
ScaleDownDelaySecondsAfterScaleUp : intPtr ( 1 ) ,
2020-07-29 21:16:48 +09:00
Metrics : nil ,
2021-02-07 17:37:27 +09:00
ScaleUpTriggers : [ ] actionsv1alpha1 . ScaleUpTrigger {
{
GitHubEvent : & actionsv1alpha1 . GitHubEventScaleUpTriggerSpec {
PullRequest : & actionsv1alpha1 . PullRequestSpec {
Types : [ ] string { "created" } ,
Branches : [ ] string { "main" } ,
} ,
} ,
Amount : 1 ,
Duration : metav1 . Duration { Duration : time . Minute } ,
} ,
} ,
2020-07-29 21:16:48 +09:00
} ,
}
2021-02-17 10:23:35 +09:00
ExpectCreate ( ctx , hra , "test HorizontalRunnerAutoscaler" )
2020-07-29 21:16:48 +09:00
2021-02-17 10:23:35 +09:00
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 3 )
2021-02-26 10:17:09 +09:00
ExpectHRAStatusCacheEntryLengthEventuallyEquals ( ctx , ns . Name , name , 1 )
2020-07-29 21:16:48 +09:00
}
2020-08-02 11:49:25 +09:00
2021-02-07 17:37:27 +09:00
{
2021-02-25 09:30:32 +09:00
env . ExpectRegisteredNumberCountEventuallyEquals ( 3 , "count of fake runners after HRA creation" )
2021-02-07 17:37:27 +09:00
}
2020-08-02 11:49:25 +09:00
// Scale-down to 1 replica
{
2021-02-07 17:37:27 +09:00
time . Sleep ( time . Second )
2021-02-19 10:33:04 +09:00
env . Responses . ListRepositoryWorkflowRuns . Body = workflowRunsFor1Replicas
env . Responses . ListRepositoryWorkflowRuns . Statuses [ "queued" ] = workflowRunsFor1Replicas_queued
env . Responses . ListRepositoryWorkflowRuns . Statuses [ "in_progress" ] = workflowRunsFor1Replicas_in_progress
2020-08-02 11:49:25 +09:00
var hra actionsv1alpha1 . HorizontalRunnerAutoscaler
err := k8sClient . Get ( ctx , types . NamespacedName { Namespace : ns . Name , Name : name } , & hra )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to get test HorizontalRunnerAutoscaler resource" )
hra . Annotations = map [ string ] string {
"force-update" : "1" ,
}
err = k8sClient . Update ( ctx , & hra )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to get test HorizontalRunnerAutoscaler resource" )
2021-02-17 10:23:35 +09:00
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 1 , "runners after HRA force update for scale-down" )
}
2020-08-02 11:49:25 +09:00
2021-02-17 10:23:35 +09:00
// Scale-up to 2 replicas on first pull_request create webhook event
{
2021-02-23 08:05:25 +09:00
env . SendOrgPullRequestEvent ( "test" , "valid" , "main" , "created" )
2021-02-17 10:23:35 +09:00
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 , "runner sets after webhook" )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 2 , "runners after first webhook event" )
2021-02-07 17:37:27 +09:00
}
2021-02-17 10:23:35 +09:00
// Scale-up to 3 replicas on second pull_request create webhook event
2021-02-07 17:37:27 +09:00
{
2021-02-23 08:05:25 +09:00
env . SendOrgPullRequestEvent ( "test" , "valid" , "main" , "created" )
2021-02-17 10:23:35 +09:00
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 3 , "runners after second webhook event" )
}
} )
2021-02-19 10:33:04 +09:00
2021-02-23 08:05:25 +09:00
It ( "should create and scale organization's repository runners on check_run event" , func ( ) {
2021-02-19 10:33:04 +09:00
name := "example-runnerdeploy"
{
rd := & actionsv1alpha1 . RunnerDeployment {
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : ns . Name ,
} ,
Spec : actionsv1alpha1 . RunnerDeploymentSpec {
Replicas : intPtr ( 1 ) ,
Template : actionsv1alpha1 . RunnerTemplate {
Spec : actionsv1alpha1 . RunnerSpec {
Repository : "test/valid" ,
Image : "bar" ,
Group : "baz" ,
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 3 replicas by the default TotalNumberOfQueuedAndInProgressWorkflowRuns-based scaling
// See workflowRunsFor3Replicas_queued and workflowRunsFor3Replicas_in_progress for GitHub List-Runners API responses
// used while testing.
{
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 ) ,
Metrics : nil ,
ScaleUpTriggers : [ ] actionsv1alpha1 . ScaleUpTrigger {
{
GitHubEvent : & actionsv1alpha1 . GitHubEventScaleUpTriggerSpec {
CheckRun : & actionsv1alpha1 . CheckRunSpec {
Types : [ ] string { "created" } ,
Status : "pending" ,
} ,
} ,
Amount : 1 ,
Duration : metav1 . Duration { Duration : time . Minute } ,
} ,
} ,
} ,
}
ExpectCreate ( ctx , hra , "test HorizontalRunnerAutoscaler" )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 3 )
}
{
env . ExpectRegisteredNumberCountEventuallyEquals ( 3 , "count of fake list runners" )
}
// Scale-up to 4 replicas on first check_run create webhook event
{
2021-02-23 08:05:25 +09:00
env . SendOrgCheckRunEvent ( "test" , "valid" , "pending" , "created" )
2021-02-19 10:33:04 +09:00
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 , "runner sets after webhook" )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 4 , "runners after first webhook event" )
}
{
env . ExpectRegisteredNumberCountEventuallyEquals ( 4 , "count of fake list runners" )
}
// Scale-up to 5 replicas on second check_run create webhook event
{
2021-02-23 08:05:25 +09:00
env . SendOrgCheckRunEvent ( "test" , "valid" , "pending" , "created" )
2021-02-19 10:33:04 +09:00
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 5 , "runners after second webhook event" )
}
env . ExpectRegisteredNumberCountEventuallyEquals ( 5 , "count of fake list runners" )
} )
2021-02-23 08:05:25 +09:00
It ( "should create and scale user's repository runners on pull_request event" , func ( ) {
name := "example-runnerdeploy"
{
rd := & actionsv1alpha1 . RunnerDeployment {
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : ns . Name ,
} ,
Spec : actionsv1alpha1 . RunnerDeploymentSpec {
Replicas : intPtr ( 1 ) ,
Template : actionsv1alpha1 . RunnerTemplate {
Spec : actionsv1alpha1 . RunnerSpec {
Repository : "test/valid" ,
Image : "bar" ,
Group : "baz" ,
Env : [ ] corev1 . EnvVar {
{ Name : "FOO" , Value : "FOOVALUE" } ,
} ,
} ,
} ,
} ,
}
ExpectCreate ( ctx , rd , "test RunnerDeployment" )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 1 )
}
{
ExpectRunnerDeploymentEventuallyUpdates ( ctx , ns . Name , name , func ( rd * actionsv1alpha1 . RunnerDeployment ) {
rd . Spec . Replicas = intPtr ( 2 )
} )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 2 )
}
// Scale-up to 3 replicas
{
hra := & actionsv1alpha1 . HorizontalRunnerAutoscaler {
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : ns . Name ,
} ,
Spec : actionsv1alpha1 . HorizontalRunnerAutoscalerSpec {
ScaleTargetRef : actionsv1alpha1 . ScaleTargetRef {
Name : name ,
} ,
MinReplicas : intPtr ( 1 ) ,
MaxReplicas : intPtr ( 3 ) ,
ScaleDownDelaySecondsAfterScaleUp : intPtr ( 1 ) ,
Metrics : nil ,
ScaleUpTriggers : [ ] actionsv1alpha1 . ScaleUpTrigger {
{
GitHubEvent : & actionsv1alpha1 . GitHubEventScaleUpTriggerSpec {
PullRequest : & actionsv1alpha1 . PullRequestSpec {
Types : [ ] string { "created" } ,
Branches : [ ] string { "main" } ,
} ,
} ,
Amount : 1 ,
Duration : metav1 . Duration { Duration : time . Minute } ,
} ,
} ,
} ,
}
ExpectCreate ( ctx , hra , "test HorizontalRunnerAutoscaler" )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 3 )
}
{
2021-02-25 09:30:32 +09:00
env . ExpectRegisteredNumberCountEventuallyEquals ( 3 , "count of fake runners after HRA creation" )
2021-02-23 08:05:25 +09:00
}
// Scale-down to 1 replica
{
time . Sleep ( time . Second )
env . Responses . ListRepositoryWorkflowRuns . Body = workflowRunsFor1Replicas
env . Responses . ListRepositoryWorkflowRuns . Statuses [ "queued" ] = workflowRunsFor1Replicas_queued
env . Responses . ListRepositoryWorkflowRuns . Statuses [ "in_progress" ] = workflowRunsFor1Replicas_in_progress
var hra actionsv1alpha1 . HorizontalRunnerAutoscaler
err := k8sClient . Get ( ctx , types . NamespacedName { Namespace : ns . Name , Name : name } , & hra )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to get test HorizontalRunnerAutoscaler resource" )
hra . Annotations = map [ string ] string {
"force-update" : "1" ,
}
err = k8sClient . Update ( ctx , & hra )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to get test HorizontalRunnerAutoscaler resource" )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 1 , "runners after HRA force update for scale-down" )
2021-02-25 10:32:09 +09:00
ExpectHRADesiredReplicasEquals ( ctx , ns . Name , name , 1 , "runner deployment desired replicas" )
2021-02-23 08:05:25 +09:00
}
// Scale-up to 2 replicas on first pull_request create webhook event
{
env . SendUserPullRequestEvent ( "test" , "valid" , "main" , "created" )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 , "runner sets after webhook" )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 2 , "runners after first webhook event" )
2021-02-25 10:32:09 +09:00
ExpectHRADesiredReplicasEquals ( ctx , ns . Name , name , 2 , "runner deployment desired replicas" )
2021-02-23 08:05:25 +09:00
}
// Scale-up to 3 replicas on second pull_request create webhook event
{
env . SendUserPullRequestEvent ( "test" , "valid" , "main" , "created" )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 3 , "runners after second webhook event" )
2021-02-25 10:32:09 +09:00
ExpectHRADesiredReplicasEquals ( ctx , ns . Name , name , 3 , "runner deployment desired replicas" )
2021-02-23 08:05:25 +09:00
}
} )
It ( "should create and scale user's repository runners on check_run event" , func ( ) {
name := "example-runnerdeploy"
{
rd := & actionsv1alpha1 . RunnerDeployment {
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
Namespace : ns . Name ,
} ,
Spec : actionsv1alpha1 . RunnerDeploymentSpec {
Replicas : intPtr ( 1 ) ,
Template : actionsv1alpha1 . RunnerTemplate {
Spec : actionsv1alpha1 . RunnerSpec {
Repository : "test/valid" ,
Image : "bar" ,
Group : "baz" ,
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 3 replicas by the default TotalNumberOfQueuedAndInProgressWorkflowRuns-based scaling
// See workflowRunsFor3Replicas_queued and workflowRunsFor3Replicas_in_progress for GitHub List-Runners API responses
// used while testing.
{
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 ) ,
Metrics : nil ,
ScaleUpTriggers : [ ] actionsv1alpha1 . ScaleUpTrigger {
{
GitHubEvent : & actionsv1alpha1 . GitHubEventScaleUpTriggerSpec {
CheckRun : & actionsv1alpha1 . CheckRunSpec {
Types : [ ] string { "created" } ,
Status : "pending" ,
} ,
} ,
Amount : 1 ,
Duration : metav1 . Duration { Duration : time . Minute } ,
} ,
} ,
} ,
}
ExpectCreate ( ctx , hra , "test HorizontalRunnerAutoscaler" )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 3 )
}
{
env . ExpectRegisteredNumberCountEventuallyEquals ( 3 , "count of fake list runners" )
}
// Scale-up to 4 replicas on first check_run create webhook event
{
env . SendUserCheckRunEvent ( "test" , "valid" , "pending" , "created" )
ExpectRunnerSetsCountEventuallyEquals ( ctx , ns . Name , 1 , "runner sets after webhook" )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 4 , "runners after first webhook event" )
}
{
env . ExpectRegisteredNumberCountEventuallyEquals ( 4 , "count of fake list runners" )
}
// Scale-up to 5 replicas on second check_run create webhook event
{
env . SendUserCheckRunEvent ( "test" , "valid" , "pending" , "created" )
ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx , ns . Name , 5 , "runners after second webhook event" )
}
env . ExpectRegisteredNumberCountEventuallyEquals ( 5 , "count of fake list runners" )
} )
2021-02-17 10:23:35 +09:00
} )
} )
2021-02-07 17:37:27 +09:00
2021-02-26 10:17:09 +09:00
func ExpectHRAStatusCacheEntryLengthEventuallyEquals ( ctx context . Context , ns string , name string , value int , optionalDescriptions ... interface { } ) {
EventuallyWithOffset (
1 ,
func ( ) int {
var hra actionsv1alpha1 . HorizontalRunnerAutoscaler
err := k8sClient . Get ( ctx , types . NamespacedName { Namespace : ns , Name : name } , & hra )
ExpectWithOffset ( 1 , err ) . NotTo ( HaveOccurred ( ) , "failed to get test HRA resource" )
return len ( hra . Status . CacheEntries )
} ,
time . Second * 5 , time . Millisecond * 500 ) . Should ( Equal ( value ) , optionalDescriptions ... )
}
2021-02-25 10:32:09 +09:00
func ExpectHRADesiredReplicasEquals ( ctx context . Context , ns , name string , desired int , optionalDescriptions ... interface { } ) {
var rd actionsv1alpha1 . HorizontalRunnerAutoscaler
err := k8sClient . Get ( ctx , types . NamespacedName { Namespace : ns , Name : name } , & rd )
ExpectWithOffset ( 1 , err ) . NotTo ( HaveOccurred ( ) , "failed to get test HRA resource" )
replicas := rd . Status . DesiredReplicas
ExpectWithOffset ( 1 , * replicas ) . To ( Equal ( desired ) , optionalDescriptions ... )
}
2021-02-19 10:33:04 +09:00
func ( env * testEnvironment ) ExpectRegisteredNumberCountEventuallyEquals ( want int , optionalDescriptions ... interface { } ) {
EventuallyWithOffset (
1 ,
func ( ) int {
env . SyncRunnerRegistrations ( )
rs , err := env . ghClient . ListRunners ( context . Background ( ) , "" , "" , "test/valid" )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "verifying list fake runners response" )
return len ( rs )
} ,
2021-02-23 08:05:25 +09:00
time . Second * 5 , time . Millisecond * 500 ) . Should ( Equal ( want ) , optionalDescriptions ... )
2021-02-19 10:33:04 +09:00
}
2021-02-23 08:05:25 +09:00
func ( env * testEnvironment ) SendOrgPullRequestEvent ( org , repo , branch , action string ) {
2021-02-19 10:33:04 +09:00
resp , err := sendWebhook ( env . webhookServer , "pull_request" , & github . PullRequestEvent {
2021-02-17 10:23:35 +09:00
PullRequest : & github . PullRequest {
Base : & github . PullRequestBranch {
Ref : github . String ( branch ) ,
} ,
} ,
Repo : & github . Repository {
Name : github . String ( repo ) ,
2021-02-23 08:05:25 +09:00
Owner : & github . User {
Login : github . String ( org ) ,
Type : github . String ( "Organization" ) ,
2021-02-17 10:23:35 +09:00
} ,
} ,
Action : github . String ( action ) ,
} )
2021-02-07 17:37:27 +09:00
2021-02-17 10:23:35 +09:00
ExpectWithOffset ( 1 , err ) . NotTo ( HaveOccurred ( ) , "failed to send pull_request event" )
2021-02-07 17:37:27 +09:00
2021-02-17 10:23:35 +09:00
ExpectWithOffset ( 1 , resp . StatusCode ) . To ( Equal ( 200 ) )
}
2021-02-07 17:37:27 +09:00
2021-02-23 08:05:25 +09:00
func ( env * testEnvironment ) SendOrgCheckRunEvent ( org , repo , status , action string ) {
2021-02-19 10:33:04 +09:00
resp , err := sendWebhook ( env . webhookServer , "check_run" , & github . CheckRunEvent {
CheckRun : & github . CheckRun {
Status : github . String ( status ) ,
} ,
Org : & github . Organization {
Login : github . String ( org ) ,
} ,
Repo : & github . Repository {
Name : github . String ( repo ) ,
2021-02-23 08:05:25 +09:00
Owner : & github . User {
Login : github . String ( org ) ,
Type : github . String ( "Organization" ) ,
} ,
} ,
Action : github . String ( action ) ,
} )
ExpectWithOffset ( 1 , err ) . NotTo ( HaveOccurred ( ) , "failed to send check_run 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 {
Base : & github . PullRequestBranch {
Ref : github . String ( branch ) ,
} ,
} ,
Repo : & github . Repository {
Name : github . String ( repo ) ,
Owner : & github . User {
Login : github . String ( owner ) ,
Type : github . String ( "User" ) ,
} ,
} ,
Action : github . String ( action ) ,
} )
ExpectWithOffset ( 1 , err ) . NotTo ( HaveOccurred ( ) , "failed to send pull_request event" )
ExpectWithOffset ( 1 , resp . StatusCode ) . To ( Equal ( 200 ) )
}
func ( env * testEnvironment ) SendUserCheckRunEvent ( owner , repo , status , action string ) {
resp , err := sendWebhook ( env . webhookServer , "check_run" , & github . CheckRunEvent {
CheckRun : & github . CheckRun {
Status : github . String ( status ) ,
} ,
Repo : & github . Repository {
Name : github . String ( repo ) ,
Owner : & github . User {
Login : github . String ( owner ) ,
Type : github . String ( "User" ) ,
} ,
2021-02-19 10:33:04 +09:00
} ,
Action : github . String ( action ) ,
} )
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
err := k8sClient . List ( context . TODO ( ) , & runnerList , client . InNamespace ( env . Namespace . Name ) )
if err != nil {
logf . Log . Error ( err , "list runners" )
}
2021-02-25 09:30:32 +09:00
env . fakeRunnerList . Sync ( runnerList . Items )
2021-02-19 10:33:04 +09:00
}
2021-02-17 10:23:35 +09:00
func ExpectCreate ( ctx context . Context , rd runtime . Object , s string ) {
err := k8sClient . Create ( ctx , rd )
2021-02-07 17:37:27 +09:00
2021-02-17 10:23:35 +09:00
ExpectWithOffset ( 1 , err ) . NotTo ( HaveOccurred ( ) , fmt . Sprintf ( "failed to create %s resource" , s ) )
}
2021-02-07 17:37:27 +09:00
2021-02-17 10:23:35 +09:00
func ExpectRunnerDeploymentEventuallyUpdates ( ctx context . Context , ns string , name string , f func ( rd * actionsv1alpha1 . RunnerDeployment ) ) {
// We wrap the update in the Eventually block to avoid the below error that occurs due to concurrent modification
// made by the controller to update .Status.AvailableReplicas and .Status.ReadyReplicas
// Operation cannot be fulfilled on runnersets.actions.summerwind.dev "example-runnerset": the object has been modified; please apply your changes to the latest version and try again
EventuallyWithOffset (
1 ,
func ( ) error {
var rd actionsv1alpha1 . RunnerDeployment
2021-02-07 17:37:27 +09:00
2021-02-17 10:23:35 +09:00
err := k8sClient . Get ( ctx , types . NamespacedName { Namespace : ns , Name : name } , & rd )
Expect ( err ) . NotTo ( HaveOccurred ( ) , "failed to get test RunnerDeployment resource" )
f ( & rd )
return k8sClient . Update ( ctx , & rd )
} ,
time . Second * 1 , time . Millisecond * 500 ) . Should ( BeNil ( ) )
}
func ExpectRunnerSetsCountEventuallyEquals ( ctx context . Context , ns string , count int , optionalDescription ... interface { } ) {
runnerSets := actionsv1alpha1 . RunnerReplicaSetList { Items : [ ] actionsv1alpha1 . RunnerReplicaSet { } }
EventuallyWithOffset (
1 ,
func ( ) int {
err := k8sClient . List ( ctx , & runnerSets , client . InNamespace ( ns ) )
if err != nil {
logf . Log . Error ( err , "list runner sets" )
2020-08-02 11:49:25 +09:00
}
2021-02-17 10:23:35 +09:00
return len ( runnerSets . Items )
} ,
2021-02-19 10:33:04 +09:00
time . Second * 10 , time . Millisecond * 500 ) . Should ( BeEquivalentTo ( count ) , optionalDescription ... )
2021-02-17 10:23:35 +09:00
}
func ExpectRunnerSetsManagedReplicasCountEventuallyEquals ( ctx context . Context , ns string , count int , optionalDescription ... interface { } ) {
runnerSets := actionsv1alpha1 . RunnerReplicaSetList { Items : [ ] actionsv1alpha1 . RunnerReplicaSet { } }
EventuallyWithOffset (
1 ,
func ( ) int {
err := k8sClient . List ( ctx , & runnerSets , client . InNamespace ( ns ) )
if err != nil {
logf . Log . Error ( err , "list runner sets" )
}
if len ( runnerSets . Items ) == 0 {
logf . Log . Info ( "No runnerreplicasets exist yet" )
return - 1
}
2021-02-19 10:33:04 +09:00
if len ( runnerSets . Items ) != 1 {
logf . Log . Info ( "Too many runnerreplicasets exist" , "runnerSets" , runnerSets )
return - 1
}
2021-02-17 10:23:35 +09:00
return * runnerSets . Items [ 0 ] . Spec . Replicas
} ,
time . Second * 5 , time . Millisecond * 500 ) . Should ( BeEquivalentTo ( count ) , optionalDescription ... )
}