Skip to content

Commit 4733edc

Browse files
author
Yusuke Kuoka
committed
Add scaling-down scenario to integration test
1 parent 3818e58 commit 4733edc

File tree

3 files changed

+103
-50
lines changed

3 files changed

+103
-50
lines changed

controllers/integration_test.go

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,32 @@ import (
1919
actionsv1alpha1 "github.com/summerwind/actions-runner-controller/api/v1alpha1"
2020
)
2121

22+
type testEnvironment struct {
23+
Namespace *corev1.Namespace
24+
Responses *fake.FixedResponses
25+
}
26+
27+
var (
28+
workflowRunsFor3Replicas = `{"total_count": 5, "workflow_runs":[{"status":"queued"}, {"status":"queued"}, {"status":"in_progress"}, {"status":"in_progress"}, {"status":"completed"}]}"`
29+
workflowRunsFor1Replicas = `{"total_count": 6, "workflow_runs":[{"status":"queued"}, {"status":"completed"}, {"status":"completed"}, {"status":"completed"}, {"status":"completed"}]}"`
30+
)
31+
2232
// SetupIntegrationTest will set up a testing environment.
2333
// This includes:
2434
// * creating a Namespace to be used during the test
2535
// * starting all the reconcilers
2636
// * stopping all the reconcilers after the test ends
2737
// Call this function at the start of each of your tests.
28-
func SetupIntegrationTest(ctx context.Context) *corev1.Namespace {
38+
func SetupIntegrationTest(ctx context.Context) *testEnvironment {
2939
var stopCh chan struct{}
3040
ns := &corev1.Namespace{}
3141

32-
workflowRuns := `{"total_count": 5, "workflow_runs":[{"status":"queued"}, {"status":"queued"}, {"status":"in_progress"}, {"status":"in_progress"}, {"status":"completed"}]}"`
33-
server := fake.NewServer(fake.WithListRepositoryWorkflowRunsResponse(200, workflowRuns))
42+
responses := &fake.FixedResponses{}
43+
responses.ListRepositoryWorkflowRuns = &fake.Handler{
44+
Status: 200,
45+
Body: workflowRunsFor3Replicas,
46+
}
47+
server := fake.NewServer(fake.WithFixedResponses(responses))
3448

3549
BeforeEach(func() {
3650
stopCh = make(chan struct{})
@@ -91,12 +105,14 @@ func SetupIntegrationTest(ctx context.Context) *corev1.Namespace {
91105
Expect(err).NotTo(HaveOccurred(), "failed to delete test namespace")
92106
})
93107

94-
return ns
108+
return &testEnvironment{Namespace: ns, Responses: responses}
95109
}
96110

97111
var _ = Context("Inside of a new namespace", func() {
98112
ctx := context.TODO()
99-
ns := SetupIntegrationTest(ctx)
113+
env := SetupIntegrationTest(ctx)
114+
ns := env.Namespace
115+
responses := env.Responses
100116

101117
Describe("when no existing resources exist", func() {
102118

@@ -199,8 +215,9 @@ var _ = Context("Inside of a new namespace", func() {
199215
time.Second*5, time.Millisecond*500).Should(BeEquivalentTo(2))
200216
}
201217

218+
// Scale-up to 3 replicas
202219
{
203-
rs := &actionsv1alpha1.HorizontalRunnerAutoscaler{
220+
hra := &actionsv1alpha1.HorizontalRunnerAutoscaler{
204221
ObjectMeta: metav1.ObjectMeta{
205222
Name: name,
206223
Namespace: ns.Name,
@@ -216,9 +233,9 @@ var _ = Context("Inside of a new namespace", func() {
216233
},
217234
}
218235

219-
err := k8sClient.Create(ctx, rs)
236+
err := k8sClient.Create(ctx, hra)
220237

221-
Expect(err).NotTo(HaveOccurred(), "failed to create test RunnerDeployment resource")
238+
Expect(err).NotTo(HaveOccurred(), "failed to create test HorizontalRunnerAutoscaler resource")
222239

223240
runnerSets := actionsv1alpha1.RunnerReplicaSetList{Items: []actionsv1alpha1.RunnerReplicaSet{}}
224241

@@ -249,6 +266,43 @@ var _ = Context("Inside of a new namespace", func() {
249266
},
250267
time.Second*5, time.Millisecond*500).Should(BeEquivalentTo(3))
251268
}
269+
270+
// Scale-down to 1 replica
271+
{
272+
responses.ListRepositoryWorkflowRuns.Body = workflowRunsFor1Replicas
273+
274+
var hra actionsv1alpha1.HorizontalRunnerAutoscaler
275+
276+
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: ns.Name, Name: name}, &hra)
277+
278+
Expect(err).NotTo(HaveOccurred(), "failed to get test HorizontalRunnerAutoscaler resource")
279+
280+
hra.Annotations = map[string]string{
281+
"force-update": "1",
282+
}
283+
284+
err = k8sClient.Update(ctx, &hra)
285+
286+
Expect(err).NotTo(HaveOccurred(), "failed to get test HorizontalRunnerAutoscaler resource")
287+
288+
Eventually(
289+
func() int {
290+
var runnerSets actionsv1alpha1.RunnerReplicaSetList
291+
292+
err := k8sClient.List(ctx, &runnerSets, client.InNamespace(ns.Name))
293+
if err != nil {
294+
logf.Log.Error(err, "list runner sets")
295+
}
296+
297+
if len(runnerSets.Items) == 0 {
298+
logf.Log.Info("No runnerreplicasets exist yet")
299+
return -1
300+
}
301+
302+
return *runnerSets.Items[0].Spec.Replicas
303+
},
304+
time.Second*5, time.Millisecond*500).Should(BeEquivalentTo(1))
305+
}
252306
})
253307
})
254308
})

github/fake/fake.go

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,111 +21,116 @@ const (
2121
`
2222
)
2323

24-
type handler struct {
24+
type Handler struct {
2525
Status int
2626
Body string
2727
}
2828

29-
func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
29+
func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
3030
w.WriteHeader(h.Status)
3131
fmt.Fprintf(w, h.Body)
3232
}
3333

34+
type ServerConfig struct {
35+
*FixedResponses
36+
}
37+
3438
// NewServer creates a fake server for running unit tests
3539
func NewServer(opts ...Option) *httptest.Server {
36-
var responses FixedResponses
40+
config := ServerConfig{
41+
FixedResponses: &FixedResponses{},
42+
}
3743

3844
for _, o := range opts {
39-
o(&responses)
45+
o(&config)
4046
}
4147

42-
routes := map[string]handler{
48+
routes := map[string]*Handler{
4349
// For CreateRegistrationToken
44-
"/repos/test/valid/actions/runners/registration-token": handler{
50+
"/repos/test/valid/actions/runners/registration-token": &Handler{
4551
Status: http.StatusCreated,
4652
Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)),
4753
},
48-
"/repos/test/invalid/actions/runners/registration-token": handler{
54+
"/repos/test/invalid/actions/runners/registration-token": &Handler{
4955
Status: http.StatusOK,
5056
Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)),
5157
},
52-
"/repos/test/error/actions/runners/registration-token": handler{
58+
"/repos/test/error/actions/runners/registration-token": &Handler{
5359
Status: http.StatusBadRequest,
5460
Body: "",
5561
},
56-
"/orgs/test/actions/runners/registration-token": handler{
62+
"/orgs/test/actions/runners/registration-token": &Handler{
5763
Status: http.StatusCreated,
5864
Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)),
5965
},
60-
"/orgs/invalid/actions/runners/registration-token": handler{
66+
"/orgs/invalid/actions/runners/registration-token": &Handler{
6167
Status: http.StatusOK,
6268
Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)),
6369
},
64-
"/orgs/error/actions/runners/registration-token": handler{
70+
"/orgs/error/actions/runners/registration-token": &Handler{
6571
Status: http.StatusBadRequest,
6672
Body: "",
6773
},
6874

6975
// For ListRunners
70-
"/repos/test/valid/actions/runners": handler{
76+
"/repos/test/valid/actions/runners": &Handler{
7177
Status: http.StatusOK,
7278
Body: RunnersListBody,
7379
},
74-
"/repos/test/invalid/actions/runners": handler{
80+
"/repos/test/invalid/actions/runners": &Handler{
7581
Status: http.StatusNoContent,
7682
Body: "",
7783
},
78-
"/repos/test/error/actions/runners": handler{
84+
"/repos/test/error/actions/runners": &Handler{
7985
Status: http.StatusBadRequest,
8086
Body: "",
8187
},
82-
"/orgs/test/actions/runners": handler{
88+
"/orgs/test/actions/runners": &Handler{
8389
Status: http.StatusOK,
8490
Body: RunnersListBody,
8591
},
86-
"/orgs/invalid/actions/runners": handler{
92+
"/orgs/invalid/actions/runners": &Handler{
8793
Status: http.StatusNoContent,
8894
Body: "",
8995
},
90-
"/orgs/error/actions/runners": handler{
96+
"/orgs/error/actions/runners": &Handler{
9197
Status: http.StatusBadRequest,
9298
Body: "",
9399
},
94100

95101
// For RemoveRunner
96-
"/repos/test/valid/actions/runners/1": handler{
102+
"/repos/test/valid/actions/runners/1": &Handler{
97103
Status: http.StatusNoContent,
98104
Body: "",
99105
},
100-
"/repos/test/invalid/actions/runners/1": handler{
106+
"/repos/test/invalid/actions/runners/1": &Handler{
101107
Status: http.StatusOK,
102108
Body: "",
103109
},
104-
"/repos/test/error/actions/runners/1": handler{
110+
"/repos/test/error/actions/runners/1": &Handler{
105111
Status: http.StatusBadRequest,
106112
Body: "",
107113
},
108-
"/orgs/test/actions/runners/1": handler{
114+
"/orgs/test/actions/runners/1": &Handler{
109115
Status: http.StatusNoContent,
110116
Body: "",
111117
},
112-
"/orgs/invalid/actions/runners/1": handler{
118+
"/orgs/invalid/actions/runners/1": &Handler{
113119
Status: http.StatusOK,
114120
Body: "",
115121
},
116-
"/orgs/error/actions/runners/1": handler{
122+
"/orgs/error/actions/runners/1": &Handler{
117123
Status: http.StatusBadRequest,
118124
Body: "",
119125
},
120126

121127
// For auto-scaling based on the number of queued(pending) workflow runs
122-
"/repos/test/valid/actions/runs": responses.listRepositoryWorkflowRuns.handler(),
128+
"/repos/test/valid/actions/runs": config.FixedResponses.ListRepositoryWorkflowRuns,
123129
}
124130

125131
mux := http.NewServeMux()
126132
for path, handler := range routes {
127-
h := handler
128-
mux.Handle(path, &h)
133+
mux.Handle(path, handler)
129134
}
130135

131136
return httptest.NewServer(mux)

github/fake/options.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
11
package fake
22

33
type FixedResponses struct {
4-
listRepositoryWorkflowRuns FixedResponse
4+
ListRepositoryWorkflowRuns *Handler
55
}
66

7-
type FixedResponse struct {
8-
Status int
9-
Body string
10-
}
11-
12-
func (r FixedResponse) handler() handler {
13-
return handler{
14-
Status: r.Status,
15-
Body: r.Body,
16-
}
17-
}
18-
19-
type Option func(responses *FixedResponses)
7+
type Option func(*ServerConfig)
208

219
func WithListRepositoryWorkflowRunsResponse(status int, body string) Option {
22-
return func(r *FixedResponses) {
23-
r.listRepositoryWorkflowRuns = FixedResponse{
10+
return func(c *ServerConfig) {
11+
c.FixedResponses.ListRepositoryWorkflowRuns = &Handler{
2412
Status: status,
2513
Body: body,
2614
}
2715
}
2816
}
17+
18+
func WithFixedResponses(responses *FixedResponses) Option {
19+
return func(c *ServerConfig) {
20+
c.FixedResponses = responses
21+
}
22+
}

0 commit comments

Comments
 (0)