2020-06-27 17:26:46 +09:00
package controllers
import (
2021-06-23 20:25:03 +09:00
"context"
2020-06-27 17:26:46 +09:00
"fmt"
2020-10-07 17:00:44 -07:00
"net/http/httptest"
"net/url"
"testing"
2021-06-22 17:55:06 +09:00
"github.com/actions-runner-controller/actions-runner-controller/api/v1alpha1"
"github.com/actions-runner-controller/actions-runner-controller/github"
"github.com/actions-runner-controller/actions-runner-controller/github/fake"
2020-06-27 17:26:46 +09:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
func newGithubClient ( server * httptest . Server ) * github . Client {
2020-10-28 15:15:53 +02:00
c := github . Config {
Token : "token" ,
}
client , err := c . NewClient ( )
2020-06-27 17:26:46 +09:00
if err != nil {
panic ( err )
}
baseURL , err := url . Parse ( server . URL + "/" )
if err != nil {
panic ( err )
}
client . Client . BaseURL = baseURL
return client
}
func TestDetermineDesiredReplicas_RepositoryRunner ( t * testing . T ) {
intPtr := func ( v int ) * int {
return & v
}
metav1Now := metav1 . Now ( )
testcases := [ ] struct {
2022-04-24 14:41:34 +09:00
description string
repo string
org string
labels [ ] string
2021-02-16 18:55:55 +09:00
fixed * int
max * int
min * int
sReplicas * int
sTime * metav1 . Time
workflowRuns string
workflowRuns_queued string
workflowRuns_in_progress string
2020-10-07 17:00:44 -07:00
workflowJobs map [ int ] string
2020-06-27 17:26:46 +09:00
want int
err string
} {
2020-10-07 17:00:44 -07:00
// Legacy functionality
2020-06-27 17:26:46 +09:00
// 3 demanded, max at 3
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 3 ,
2020-06-27 17:26:46 +09:00
} ,
2022-04-24 14:41:34 +09:00
// Explicitly speified the default `self-hosted` label which is ignored by the simulator,
// as we assume that GitHub Actions automatically associates the `self-hosted` label to every self-hosted runner.
// 3 demanded, max at 3
{
repo : "test/valid" ,
labels : [ ] string { "self-hosted" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 3 ,
} ,
2020-06-27 17:26:46 +09:00
// 2 demanded, max at 3, currently 3, delay scaling down due to grace period
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
sReplicas : intPtr ( 3 ) ,
sTime : & metav1Now ,
workflowRuns : ` { "total_count": 3, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 3 ,
2020-06-27 17:26:46 +09:00
} ,
// 3 demanded, max at 2
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 2 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 2 ,
2020-06-27 17:26:46 +09:00
} ,
// 2 demanded, min at 2
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 3, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 2 ,
2020-06-27 17:26:46 +09:00
} ,
// 1 demanded, min at 2
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"queued"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 0, "workflow_runs":[]}" ` ,
want : 2 ,
2020-06-27 17:26:46 +09:00
} ,
// 1 demanded, min at 2
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 2 ,
2020-06-27 17:26:46 +09:00
} ,
// 1 demanded, min at 1
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"queued"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 0, "workflow_runs":[]}" ` ,
want : 1 ,
2020-06-27 17:26:46 +09:00
} ,
// 1 demanded, min at 1
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 1 ,
2020-06-27 17:26:46 +09:00
} ,
// fixed at 3
{
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
fixed : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 3, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 3 ,
2020-06-27 17:26:46 +09:00
} ,
2020-10-07 17:00:44 -07:00
{
2022-04-28 00:24:21 +09:00
description : "Job-level autoscaling with no explicit runner label (runners have implicit self-hosted, requested self-hosted, 5 jobs from 3 workflows)" ,
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted"]}, { "status":"queued", "labels":["self-hosted"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted"]}, { "status":"completed", "labels":["self-hosted"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted"]}, { "status":"queued", "labels":["self-hosted"]}]} ` ,
} ,
want : 5 ,
} ,
{
description : "Skipped job-level autoscaling with no explicit runner label (runners have implicit self-hosted, requested self-hosted+custom, 0 jobs from 3 workflows)" ,
2022-04-24 14:41:34 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
2022-04-28 00:24:21 +09:00
want : 2 ,
2022-04-24 14:41:34 +09:00
} ,
{
2022-04-28 00:24:21 +09:00
description : "Skipped job-level autoscaling with no label (runners have implicit self-hosted, jobs had no labels, 0 jobs from 3 workflows)" ,
2021-02-16 18:55:55 +09:00
repo : "test/valid" ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
2020-10-07 17:00:44 -07:00
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued"}, { "status":"queued"}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress"}, { "status":"completed"}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress"}, { "status":"queued"}]} ` ,
} ,
2022-04-24 14:41:34 +09:00
want : 2 ,
} ,
{
2022-04-28 00:24:21 +09:00
description : "Skipped job-level autoscaling with default runner label (runners have self-hosted only, requested self-hosted+custom, 0 jobs from 3 workflows)" ,
2022-04-24 14:41:34 +09:00
repo : "test/valid" ,
labels : [ ] string { "self-hosted" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
2022-04-28 00:24:21 +09:00
want : 2 ,
2022-04-24 14:41:34 +09:00
} ,
{
2022-04-28 00:24:21 +09:00
description : "Skipped job-level autoscaling with custom runner label (runners have custom2, requested self-hosted+custom, 0 jobs from 5 workflows" ,
2022-04-24 14:41:34 +09:00
repo : "test/valid" ,
labels : [ ] string { "custom2" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
want : 2 ,
} ,
{
2022-04-28 00:24:21 +09:00
description : "Skipped job-level autoscaling with default runner label (runners have self-hosted, requested managed-runner-label, 0 jobs from 3 runs)" ,
2022-04-24 14:41:34 +09:00
repo : "test/valid" ,
labels : [ ] string { "self-hosted" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["managed-runner-label"]}, { "status":"queued", "labels":["managed-runner-label"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["managed-runner-label"]}, { "status":"completed", "labels":["managed-runner-label"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["managed-runner-label"]}, { "status":"queued", "labels":["managed-runner-label"]}]} ` ,
} ,
want : 2 ,
} ,
{
2022-04-28 00:24:21 +09:00
description : "Job-level autoscaling with default + custom runner label (runners have self-hosted+custom, requested self-hosted+custom, 5 jobs from 3 workflows)" ,
2022-04-24 14:41:34 +09:00
repo : "test/valid" ,
labels : [ ] string { "self-hosted" , "custom" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
want : 5 ,
} ,
{
2022-04-28 00:24:21 +09:00
description : "Job-level autoscaling with custom runner label (runners have custom, requested self-hosted+custom, 5 jobs from 3 workflows)" ,
2022-04-24 14:41:34 +09:00
repo : "test/valid" ,
labels : [ ] string { "custom" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
2020-10-07 17:00:44 -07:00
want : 5 ,
} ,
2020-07-03 09:05:46 +09:00
}
for i := range testcases {
tc := testcases [ i ]
log := zap . New ( func ( o * zap . Options ) {
o . Development = true
} )
scheme := runtime . NewScheme ( )
_ = clientgoscheme . AddToScheme ( scheme )
_ = v1alpha1 . AddToScheme ( scheme )
2022-04-24 14:41:34 +09:00
testName := fmt . Sprintf ( "case %d" , i )
if tc . description != "" {
testName = tc . description
}
t . Run ( testName , func ( t * testing . T ) {
2021-02-07 17:37:27 +09:00
server := fake . NewServer (
2021-02-16 18:55:55 +09:00
fake . WithListRepositoryWorkflowRunsResponse ( 200 , tc . workflowRuns , tc . workflowRuns_queued , tc . workflowRuns_in_progress ) ,
2021-02-07 17:37:27 +09:00
fake . WithListWorkflowJobsResponse ( 200 , tc . workflowJobs ) ,
fake . WithListRunnersResponse ( 200 , fake . RunnersListBody ) ,
)
2020-07-03 09:05:46 +09:00
defer server . Close ( )
client := newGithubClient ( server )
2020-07-19 18:42:06 +09:00
h := & HorizontalRunnerAutoscalerReconciler {
2022-04-20 03:09:09 +01:00
Log : log ,
Scheme : scheme ,
DefaultScaleDownDelay : DefaultScaleDownDelay ,
2020-07-03 09:05:46 +09:00
}
rd := v1alpha1 . RunnerDeployment {
TypeMeta : metav1 . TypeMeta { } ,
2020-07-19 18:42:06 +09:00
ObjectMeta : metav1 . ObjectMeta {
Name : "testrd" ,
} ,
2020-07-03 09:05:46 +09:00
Spec : v1alpha1 . RunnerDeploymentSpec {
Template : v1alpha1 . RunnerTemplate {
Spec : v1alpha1 . RunnerSpec {
2021-06-22 17:10:09 +09:00
RunnerConfig : v1alpha1 . RunnerConfig {
Repository : tc . repo ,
2022-04-24 14:41:34 +09:00
Labels : tc . labels ,
2021-06-22 17:10:09 +09:00
} ,
2020-07-03 09:05:46 +09:00
} ,
} ,
2020-07-19 18:42:06 +09:00
Replicas : tc . fixed ,
} ,
Status : v1alpha1 . RunnerDeploymentStatus {
2021-05-22 08:29:53 +09:00
DesiredReplicas : tc . sReplicas ,
2020-07-19 18:42:06 +09:00
} ,
}
hra := v1alpha1 . HorizontalRunnerAutoscaler {
Spec : v1alpha1 . HorizontalRunnerAutoscalerSpec {
2020-07-03 09:05:46 +09:00
MaxReplicas : tc . max ,
MinReplicas : tc . min ,
2022-04-24 13:36:42 +09:00
Metrics : [ ] v1alpha1 . MetricSpec {
{
Type : "TotalNumberOfQueuedAndInProgressWorkflowRuns" ,
} ,
} ,
2020-07-03 09:05:46 +09:00
} ,
2020-07-19 18:42:06 +09:00
Status : v1alpha1 . HorizontalRunnerAutoscalerStatus {
DesiredReplicas : tc . sReplicas ,
2020-07-03 09:05:46 +09:00
LastSuccessfulScaleOutTime : tc . sTime ,
} ,
}
2021-05-22 08:29:53 +09:00
minReplicas , _ , _ , err := h . getMinReplicas ( log , metav1Now . Time , hra )
if err != nil {
t . Fatalf ( "unexpected error: %v" , err )
}
2021-06-23 20:25:03 +09:00
st := h . scaleTargetFromRD ( context . Background ( ) , rd )
2022-07-12 09:45:00 +09:00
got , err := h . computeReplicasWithCache ( client , log , metav1Now . Time , st , hra , minReplicas )
2020-07-03 09:05:46 +09:00
if err != nil {
if tc . err == "" {
t . Fatalf ( "unexpected error: expected none, got %v" , err )
} else if err . Error ( ) != tc . err {
t . Fatalf ( "unexpected error: expected %v, got %v" , tc . err , err )
}
return
}
Do not delay min/maxReplicas propagation from HRA to RD due to caching (#406)
As part of #282, I have introduced some caching mechanism to avoid excessive GitHub API calls due to the autoscaling calculation involving GitHub API calls is executed on each Webhook event.
Apparently, it was saving the wrong value in the cache- The value was one after applying `HRA.Spec.{Max,Min}Replicas` so manual changes to {Max,Min}Replicas doesn't affect RunnerDeployment.Spec.Replicas until the cache expires. This isn't what I had wanted.
This patch fixes that, by changing the value being cached to one before applying {Min,Max}Replicas.
Additionally, I've also updated logging so that you observe which number was fetched from cache, and what number was suggested by either TotalNumberOfQueuedAndInProgressWorkflowRuns or PercentageRunnersBusy, and what was the final number used as the desired-replicas(after applying {Min,Max}Replicas).
Follow-up for #282
2021-03-19 12:58:02 +09:00
if got != tc . want {
t . Errorf ( "%d: incorrect desired replicas: want %d, got %d" , i , tc . want , got )
2020-07-03 09:05:46 +09:00
}
} )
}
}
func TestDetermineDesiredReplicas_OrganizationalRunner ( t * testing . T ) {
intPtr := func ( v int ) * int {
return & v
}
metav1Now := metav1 . Now ( )
testcases := [ ] struct {
2022-04-24 14:41:34 +09:00
description string
repos [ ] string
org string
labels [ ] string
2021-02-16 18:55:55 +09:00
fixed * int
max * int
min * int
sReplicas * int
sTime * metav1 . Time
workflowRuns string
workflowRuns_queued string
workflowRuns_in_progress string
2020-10-07 17:00:44 -07:00
workflowJobs map [ int ] string
2020-07-03 09:05:46 +09:00
want int
err string
} {
// 3 demanded, max at 3
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 3 ,
2020-07-03 09:05:46 +09:00
} ,
// 2 demanded, max at 3, currently 3, delay scaling down due to grace period
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
sReplicas : intPtr ( 3 ) ,
sTime : & metav1Now ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 3 ,
2020-07-03 09:05:46 +09:00
} ,
// 3 demanded, max at 2
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 2 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 2 ,
2020-07-03 09:05:46 +09:00
} ,
// 2 demanded, min at 2
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 3, "workflow_runs":[ { "status":"queued"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 2 ,
2020-07-03 09:05:46 +09:00
} ,
// 1 demanded, min at 2
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"queued"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 0, "workflow_runs":[]}" ` ,
want : 2 ,
2020-07-03 09:05:46 +09:00
} ,
// 1 demanded, min at 2
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 2 ,
2020-07-03 09:05:46 +09:00
} ,
// 1 demanded, min at 1
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"queued"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 0, "workflow_runs":[]}" ` ,
want : 1 ,
2020-07-03 09:05:46 +09:00
} ,
// 1 demanded, min at 1
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
want : 1 ,
2020-07-03 09:05:46 +09:00
} ,
// fixed at 3
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
fixed : intPtr ( 1 ) ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 3, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 3 ,
2020-07-03 09:05:46 +09:00
} ,
2020-06-27 17:26:46 +09:00
// org runner, fixed at 3
{
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
fixed : intPtr ( 1 ) ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}, { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 3, "workflow_runs":[ { "status":"in_progress"}, { "status":"in_progress"}, { "status":"in_progress"}]}" ` ,
want : 3 ,
2020-06-27 17:26:46 +09:00
} ,
2020-07-03 09:05:46 +09:00
// org runner, 1 demanded, min at 1, no repos
2020-06-27 17:26:46 +09:00
{
2021-02-16 18:55:55 +09:00
org : "test" ,
min : intPtr ( 1 ) ,
max : intPtr ( 3 ) ,
workflowRuns : ` { "total_count": 2, "workflow_runs":[ { "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 0, "workflow_runs":[]}" ` ,
workflowRuns_in_progress : ` { "total_count": 1, "workflow_runs":[ { "status":"in_progress"}]}" ` ,
err : "validating autoscaling metrics: spec.autoscaling.metrics[].repositoryNames is required and must have one more more entries for organizational runner deployment" ,
2020-06-27 17:26:46 +09:00
} ,
2020-10-07 17:00:44 -07:00
{
2022-04-28 00:24:21 +09:00
description : "Job-level autoscaling (runners have implicit self-hosted, requested self-hosted, 5 jobs from 3 runs)" ,
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted"]}, { "status":"queued", "labels":["self-hosted"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted"]}, { "status":"completed", "labels":["self-hosted"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted"]}, { "status":"queued", "labels":["self-hosted"]}]} ` ,
} ,
want : 5 ,
} ,
{
description : "Job-level autoscaling (runners have explicit self-hosted, requested self-hosted, 5 jobs from 3 runs)" ,
org : "test" ,
repos : [ ] string { "valid" } ,
labels : [ ] string { "self-hosted" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted"]}, { "status":"queued", "labels":["self-hosted"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted"]}, { "status":"completed", "labels":["self-hosted"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted"]}, { "status":"queued", "labels":["self-hosted"]}]} ` ,
} ,
want : 5 ,
} ,
{
description : "Skipped job-level autoscaling (jobs lack labels, 0 requested from 3 workflows)" ,
2021-02-16 18:55:55 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
2020-10-07 17:00:44 -07:00
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued"}, { "status":"queued"}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress"}, { "status":"completed"}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress"}, { "status":"queued"}]} ` ,
} ,
2022-04-24 14:41:34 +09:00
want : 2 ,
} ,
{
2022-04-28 00:24:21 +09:00
description : "Skipped job-level autoscaling (runners have valid and implicit self-hosted, requested self-hosted+custom, 0 jobs from 3 runs)" ,
2022-04-24 14:41:34 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
2022-04-28 00:24:21 +09:00
want : 2 ,
2020-10-07 17:00:44 -07:00
} ,
2022-04-24 14:41:34 +09:00
{
2022-04-28 00:24:21 +09:00
description : "Skipped job-level autoscaling (runners have self-hosted, requested self-hosted+custom, 0 jobs from 3 workflows)" ,
2022-04-24 14:41:34 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
labels : [ ] string { "self-hosted" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
2022-04-28 00:24:21 +09:00
want : 2 ,
2022-04-24 14:41:34 +09:00
} ,
{
2022-04-28 00:24:21 +09:00
description : "Job-level autoscaling (runners have custom, requested self-hosted+custom, 5 requested from 3 workflows)" ,
2022-04-24 14:41:34 +09:00
org : "test" ,
repos : [ ] string { "valid" } ,
labels : [ ] string { "custom" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
want : 5 ,
} ,
2022-04-28 00:24:21 +09:00
{
description : "Job-level autoscaling (runners have custom, requested custom, 5 requested from 3 workflows)" ,
org : "test" ,
repos : [ ] string { "valid" } ,
labels : [ ] string { "custom" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["custom"]}, { "status":"queued", "labels":["custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["custom"]}, { "status":"completed", "labels":["custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["custom"]}, { "status":"queued", "labels":["custom"]}]} ` ,
} ,
want : 5 ,
} ,
2022-04-24 14:41:34 +09:00
{
description : "Skipped job-level autoscaling (specified custom2, 0 requested from 3 workflows)" ,
org : "test" ,
repos : [ ] string { "valid" } ,
labels : [ ] string { "custom2" } ,
min : intPtr ( 2 ) ,
max : intPtr ( 10 ) ,
workflowRuns : ` { "total_count": 4, "workflow_runs":[ { "id": 1, "status":"queued"}, { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowRuns_queued : ` { "total_count": 1, "workflow_runs":[ { "id": 1, "status":"queued"}]}" ` ,
workflowRuns_in_progress : ` { "total_count": 2, "workflow_runs":[ { "id": 2, "status":"in_progress"}, { "id": 3, "status":"in_progress"}, { "status":"completed"}]}" ` ,
workflowJobs : map [ int ] string {
1 : ` { "jobs": [ { "status":"queued", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
2 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"completed", "labels":["self-hosted", "custom"]}]} ` ,
3 : ` { "jobs": [ { "status": "in_progress", "labels":["self-hosted", "custom"]}, { "status":"queued", "labels":["self-hosted", "custom"]}]} ` ,
} ,
want : 2 ,
} ,
2020-06-27 17:26:46 +09:00
}
for i := range testcases {
tc := testcases [ i ]
log := zap . New ( func ( o * zap . Options ) {
o . Development = true
} )
scheme := runtime . NewScheme ( )
_ = clientgoscheme . AddToScheme ( scheme )
_ = v1alpha1 . AddToScheme ( scheme )
2022-04-24 14:41:34 +09:00
testName := fmt . Sprintf ( "case %d" , i )
if tc . description != "" {
testName = tc . description
}
t . Run ( testName , func ( t * testing . T ) {
Do not delay min/maxReplicas propagation from HRA to RD due to caching (#406)
As part of #282, I have introduced some caching mechanism to avoid excessive GitHub API calls due to the autoscaling calculation involving GitHub API calls is executed on each Webhook event.
Apparently, it was saving the wrong value in the cache- The value was one after applying `HRA.Spec.{Max,Min}Replicas` so manual changes to {Max,Min}Replicas doesn't affect RunnerDeployment.Spec.Replicas until the cache expires. This isn't what I had wanted.
This patch fixes that, by changing the value being cached to one before applying {Min,Max}Replicas.
Additionally, I've also updated logging so that you observe which number was fetched from cache, and what number was suggested by either TotalNumberOfQueuedAndInProgressWorkflowRuns or PercentageRunnersBusy, and what was the final number used as the desired-replicas(after applying {Min,Max}Replicas).
Follow-up for #282
2021-03-19 12:58:02 +09:00
t . Helper ( )
2021-02-07 17:37:27 +09:00
server := fake . NewServer (
2021-02-16 18:55:55 +09:00
fake . WithListRepositoryWorkflowRunsResponse ( 200 , tc . workflowRuns , tc . workflowRuns_queued , tc . workflowRuns_in_progress ) ,
2021-02-07 17:37:27 +09:00
fake . WithListWorkflowJobsResponse ( 200 , tc . workflowJobs ) ,
fake . WithListRunnersResponse ( 200 , fake . RunnersListBody ) ,
)
2020-06-27 17:26:46 +09:00
defer server . Close ( )
client := newGithubClient ( server )
2020-07-19 18:42:06 +09:00
h := & HorizontalRunnerAutoscalerReconciler {
2022-04-20 03:09:09 +01:00
Log : log ,
Scheme : scheme ,
DefaultScaleDownDelay : DefaultScaleDownDelay ,
2020-06-27 17:26:46 +09:00
}
rd := v1alpha1 . RunnerDeployment {
2020-07-19 18:42:06 +09:00
ObjectMeta : metav1 . ObjectMeta {
Name : "testrd" ,
} ,
2020-06-27 17:26:46 +09:00
Spec : v1alpha1 . RunnerDeploymentSpec {
2021-03-05 10:15:39 +09:00
Selector : & metav1 . LabelSelector {
MatchLabels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
2020-06-27 17:26:46 +09:00
Template : v1alpha1 . RunnerTemplate {
2021-03-05 10:15:39 +09:00
ObjectMeta : metav1 . ObjectMeta {
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
2020-06-27 17:26:46 +09:00
Spec : v1alpha1 . RunnerSpec {
2021-06-22 17:10:09 +09:00
RunnerConfig : v1alpha1 . RunnerConfig {
Organization : tc . org ,
2022-04-24 14:41:34 +09:00
Labels : tc . labels ,
2021-06-22 17:10:09 +09:00
} ,
2020-07-03 09:05:46 +09:00
} ,
} ,
2020-07-19 18:42:06 +09:00
Replicas : tc . fixed ,
} ,
Status : v1alpha1 . RunnerDeploymentStatus {
2021-05-22 08:29:53 +09:00
DesiredReplicas : tc . sReplicas ,
2020-07-19 18:42:06 +09:00
} ,
}
hra := v1alpha1 . HorizontalRunnerAutoscaler {
Spec : v1alpha1 . HorizontalRunnerAutoscalerSpec {
ScaleTargetRef : v1alpha1 . ScaleTargetRef {
Name : "testrd" ,
2020-06-27 17:26:46 +09:00
} ,
MaxReplicas : tc . max ,
MinReplicas : tc . min ,
2020-07-19 18:42:06 +09:00
Metrics : [ ] v1alpha1 . MetricSpec {
{
Type : v1alpha1 . AutoscalingMetricTypeTotalNumberOfQueuedAndInProgressWorkflowRuns ,
RepositoryNames : tc . repos ,
} ,
} ,
2020-06-27 17:26:46 +09:00
} ,
2020-07-19 18:42:06 +09:00
Status : v1alpha1 . HorizontalRunnerAutoscalerStatus {
DesiredReplicas : tc . sReplicas ,
2020-06-27 17:26:46 +09:00
LastSuccessfulScaleOutTime : tc . sTime ,
} ,
}
2021-05-22 08:29:53 +09:00
minReplicas , _ , _ , err := h . getMinReplicas ( log , metav1Now . Time , hra )
if err != nil {
t . Fatalf ( "unexpected error: %v" , err )
}
2021-06-23 20:25:03 +09:00
st := h . scaleTargetFromRD ( context . Background ( ) , rd )
2022-07-12 09:45:00 +09:00
got , err := h . computeReplicasWithCache ( client , log , metav1Now . Time , st , hra , minReplicas )
2020-06-27 17:26:46 +09:00
if err != nil {
if tc . err == "" {
t . Fatalf ( "unexpected error: expected none, got %v" , err )
} else if err . Error ( ) != tc . err {
t . Fatalf ( "unexpected error: expected %v, got %v" , tc . err , err )
}
return
}
Do not delay min/maxReplicas propagation from HRA to RD due to caching (#406)
As part of #282, I have introduced some caching mechanism to avoid excessive GitHub API calls due to the autoscaling calculation involving GitHub API calls is executed on each Webhook event.
Apparently, it was saving the wrong value in the cache- The value was one after applying `HRA.Spec.{Max,Min}Replicas` so manual changes to {Max,Min}Replicas doesn't affect RunnerDeployment.Spec.Replicas until the cache expires. This isn't what I had wanted.
This patch fixes that, by changing the value being cached to one before applying {Min,Max}Replicas.
Additionally, I've also updated logging so that you observe which number was fetched from cache, and what number was suggested by either TotalNumberOfQueuedAndInProgressWorkflowRuns or PercentageRunnersBusy, and what was the final number used as the desired-replicas(after applying {Min,Max}Replicas).
Follow-up for #282
2021-03-19 12:58:02 +09:00
if got != tc . want {
t . Errorf ( "%d: incorrect desired replicas: want %d, got %d" , i , tc . want , got )
2020-06-27 17:26:46 +09:00
}
} )
}
}