From 5f23afaad311be44be1781b09541d0a01d7912a3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:17:31 -0500 Subject: [PATCH 01/13] Updates: runner to v2.312.0 (#3229) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- Makefile | 2 +- runner/Makefile | 2 +- runner/VERSION | 2 +- test/e2e/e2e_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 2d4a9a9a..7cd1b8fb 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ endif DOCKER_USER ?= $(shell echo ${DOCKER_IMAGE_NAME} | cut -d / -f1) VERSION ?= dev COMMIT_SHA = $(shell git rev-parse HEAD) -RUNNER_VERSION ?= 2.311.0 +RUNNER_VERSION ?= 2.312.0 TARGETPLATFORM ?= $(shell arch) RUNNER_NAME ?= ${DOCKER_USER}/actions-runner RUNNER_TAG ?= ${VERSION} diff --git a/runner/Makefile b/runner/Makefile index 9f7de076..98f33f8b 100644 --- a/runner/Makefile +++ b/runner/Makefile @@ -6,7 +6,7 @@ DIND_ROOTLESS_RUNNER_NAME ?= ${DOCKER_USER}/actions-runner-dind-rootless OS_IMAGE ?= ubuntu-22.04 TARGETPLATFORM ?= $(shell arch) -RUNNER_VERSION ?= 2.311.0 +RUNNER_VERSION ?= 2.312.0 RUNNER_CONTAINER_HOOKS_VERSION ?= 0.5.0 DOCKER_VERSION ?= 24.0.7 diff --git a/runner/VERSION b/runner/VERSION index 4aaf58c8..250829ce 100644 --- a/runner/VERSION +++ b/runner/VERSION @@ -1,2 +1,2 @@ -RUNNER_VERSION=2.311.0 +RUNNER_VERSION=2.312.0 RUNNER_CONTAINER_HOOKS_VERSION=0.5.0 \ No newline at end of file diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index a81a2de4..b3d16bb3 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -36,7 +36,7 @@ var ( testResultCMNamePrefix = "test-result-" - RunnerVersion = "2.311.0" + RunnerVersion = "2.312.0" RunnerContainerHooksVersion = "0.5.0" ) From c00465973e69b9bb33296ab45655405673c5813d Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Thu, 25 Jan 2024 14:46:42 +0100 Subject: [PATCH 02/13] Publish metrics in the new ghalistener (#3193) --- cmd/ghalistener/app/app.go | 2 +- cmd/ghalistener/app/mocks/worker.go | 26 +- cmd/ghalistener/listener/listener.go | 155 +++++---- cmd/ghalistener/listener/listener_test.go | 295 ++++++++++++++++-- cmd/ghalistener/listener/metrics_test.go | 204 ++++++++++++ cmd/ghalistener/listener/mocks/handler.go | 26 +- cmd/ghalistener/worker/worker.go | 14 +- .../actions.github.com/resourcebuilder.go | 6 +- 8 files changed, 613 insertions(+), 115 deletions(-) create mode 100644 cmd/ghalistener/listener/metrics_test.go diff --git a/cmd/ghalistener/app/app.go b/cmd/ghalistener/app/app.go index e8f64f21..2d903fa9 100644 --- a/cmd/ghalistener/app/app.go +++ b/cmd/ghalistener/app/app.go @@ -34,7 +34,7 @@ type Listener interface { //go:generate mockery --name Worker --output ./mocks --outpkg mocks --case underscore type Worker interface { HandleJobStarted(ctx context.Context, jobInfo *actions.JobStarted) error - HandleDesiredRunnerCount(ctx context.Context, desiredRunnerCount int) error + HandleDesiredRunnerCount(ctx context.Context, count int) (int, error) } func New(config config.Config) (*App, error) { diff --git a/cmd/ghalistener/app/mocks/worker.go b/cmd/ghalistener/app/mocks/worker.go index a2561adb..69828c38 100644 --- a/cmd/ghalistener/app/mocks/worker.go +++ b/cmd/ghalistener/app/mocks/worker.go @@ -15,18 +15,28 @@ type Worker struct { mock.Mock } -// HandleDesiredRunnerCount provides a mock function with given fields: ctx, desiredRunnerCount -func (_m *Worker) HandleDesiredRunnerCount(ctx context.Context, desiredRunnerCount int) error { - ret := _m.Called(ctx, desiredRunnerCount) +// HandleDesiredRunnerCount provides a mock function with given fields: ctx, count +func (_m *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) (int, error) { + ret := _m.Called(ctx, count) - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { - r0 = rf(ctx, desiredRunnerCount) + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, count) + } + if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { + r0 = rf(ctx, count) } else { - r0 = ret.Error(0) + r0 = ret.Get(0).(int) } - return r0 + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, count) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // HandleJobStarted provides a mock function with given fields: ctx, jobInfo diff --git a/cmd/ghalistener/listener/listener.go b/cmd/ghalistener/listener/listener.go index e90622a0..cb611239 100644 --- a/cmd/ghalistener/listener/listener.go +++ b/cmd/ghalistener/listener/listener.go @@ -113,7 +113,7 @@ func New(config Config) (*Listener, error) { //go:generate mockery --name Handler --output ./mocks --outpkg mocks --case underscore type Handler interface { HandleJobStarted(ctx context.Context, jobInfo *actions.JobStarted) error - HandleDesiredRunnerCount(ctx context.Context, desiredRunnerCount int) error + HandleDesiredRunnerCount(ctx context.Context, count int) (int, error) } // Listen listens for incoming messages and handles them using the provided handler. @@ -133,28 +133,21 @@ func (l *Listener) Listen(ctx context.Context, handler Handler) error { Body: "", } - if l.session.Statistics.TotalAvailableJobs > 0 || l.session.Statistics.TotalAssignedJobs > 0 { - acquirableJobs, err := l.client.GetAcquirableJobs(ctx, l.scaleSetID) - if err != nil { - return fmt.Errorf("failed to call GetAcquirableJobs: %w", err) - } - - acquirableJobsJson, err := json.Marshal(acquirableJobs) - if err != nil { - return fmt.Errorf("failed to marshal acquirable jobs: %w", err) - } - - initialMessage.Body = string(acquirableJobsJson) + if l.session.Statistics == nil { + return fmt.Errorf("session statistics is nil") } + l.metrics.PublishStatistics(initialMessage.Statistics) - if err := handler.HandleDesiredRunnerCount(ctx, initialMessage.Statistics.TotalAssignedJobs); err != nil { + desiredRunners, err := handler.HandleDesiredRunnerCount(ctx, initialMessage.Statistics.TotalAssignedJobs) + if err != nil { return fmt.Errorf("handling initial message failed: %w", err) } + l.metrics.PublishDesiredRunners(desiredRunners) for { select { case <-ctx.Done(): - return fmt.Errorf("context cancelled: %w", ctx.Err()) + return ctx.Err() default: } @@ -167,29 +160,54 @@ func (l *Listener) Listen(ctx context.Context, handler Handler) error { continue } - statistics, jobsStarted, err := l.parseMessage(ctx, msg) - if err != nil { - return fmt.Errorf("failed to parse message: %w", err) - } - - l.lastMessageID = msg.MessageId - - if err := l.deleteLastMessage(ctx); err != nil { - return fmt.Errorf("failed to delete message: %w", err) - } - - for _, jobStarted := range jobsStarted { - if err := handler.HandleJobStarted(ctx, jobStarted); err != nil { - return fmt.Errorf("failed to handle job started: %w", err) - } - } - - if err := handler.HandleDesiredRunnerCount(ctx, statistics.TotalAssignedJobs); err != nil { - return fmt.Errorf("failed to handle desired runner count: %w", err) + // New context is created to avoid cancelation during message handling. + if err := l.handleMessage(context.Background(), handler, msg); err != nil { + return fmt.Errorf("failed to handle message: %w", err) } } } +func (l *Listener) handleMessage(ctx context.Context, handler Handler, msg *actions.RunnerScaleSetMessage) error { + parsedMsg, err := l.parseMessage(ctx, msg) + if err != nil { + return fmt.Errorf("failed to parse message: %w", err) + } + l.metrics.PublishStatistics(parsedMsg.statistics) + + if len(parsedMsg.jobsAvailable) > 0 { + acquiredJobIDs, err := l.acquireAvailableJobs(ctx, parsedMsg.jobsAvailable) + if err != nil { + return fmt.Errorf("failed to acquire jobs: %w", err) + } + + l.logger.Info("Jobs are acquired", "count", len(acquiredJobIDs), "requestIds", fmt.Sprint(acquiredJobIDs)) + } + + for _, jobCompleted := range parsedMsg.jobsCompleted { + l.metrics.PublishJobCompleted(jobCompleted) + } + + l.lastMessageID = msg.MessageId + + if err := l.deleteLastMessage(ctx); err != nil { + return fmt.Errorf("failed to delete message: %w", err) + } + + for _, jobStarted := range parsedMsg.jobsStarted { + if err := handler.HandleJobStarted(ctx, jobStarted); err != nil { + return fmt.Errorf("failed to handle job started: %w", err) + } + l.metrics.PublishJobStarted(jobStarted) + } + + desiredRunners, err := handler.HandleDesiredRunnerCount(ctx, parsedMsg.statistics.TotalAssignedJobs) + if err != nil { + return fmt.Errorf("failed to handle desired runner count: %w", err) + } + l.metrics.PublishDesiredRunners(desiredRunners) + return nil +} + func (l *Listener) createSession(ctx context.Context) error { var session *actions.RunnerScaleSetSession var retries int @@ -271,48 +289,57 @@ func (l *Listener) deleteLastMessage(ctx context.Context) error { return nil } -func (l *Listener) parseMessage(ctx context.Context, msg *actions.RunnerScaleSetMessage) (*actions.RunnerScaleSetStatistic, []*actions.JobStarted, error) { +type parsedMessage struct { + statistics *actions.RunnerScaleSetStatistic + jobsStarted []*actions.JobStarted + jobsAvailable []*actions.JobAvailable + jobsCompleted []*actions.JobCompleted +} + +func (l *Listener) parseMessage(ctx context.Context, msg *actions.RunnerScaleSetMessage) (*parsedMessage, error) { + if msg.MessageType != "RunnerScaleSetJobMessages" { + l.logger.Info("Skipping message", "messageType", msg.MessageType) + return nil, fmt.Errorf("invalid message type: %s", msg.MessageType) + } + l.logger.Info("Processing message", "messageId", msg.MessageId, "messageType", msg.MessageType) if msg.Statistics == nil { - return nil, nil, fmt.Errorf("invalid message: statistics is nil") + return nil, fmt.Errorf("invalid message: statistics is nil") } l.logger.Info("New runner scale set statistics.", "statistics", msg.Statistics) - if msg.MessageType != "RunnerScaleSetJobMessages" { - l.logger.Info("Skipping message", "messageType", msg.MessageType) - return nil, nil, fmt.Errorf("invalid message type: %s", msg.MessageType) - } - var batchedMessages []json.RawMessage if len(msg.Body) > 0 { if err := json.Unmarshal([]byte(msg.Body), &batchedMessages); err != nil { - return nil, nil, fmt.Errorf("failed to unmarshal batched messages: %w", err) + return nil, fmt.Errorf("failed to unmarshal batched messages: %w", err) } } - var availableJobs []int64 - var startedJobs []*actions.JobStarted + parsedMsg := &parsedMessage{ + statistics: msg.Statistics, + } + for _, msg := range batchedMessages { var messageType actions.JobMessageType if err := json.Unmarshal(msg, &messageType); err != nil { - return nil, nil, fmt.Errorf("failed to decode job message type: %w", err) + return nil, fmt.Errorf("failed to decode job message type: %w", err) } switch messageType.MessageType { case messageTypeJobAvailable: var jobAvailable actions.JobAvailable if err := json.Unmarshal(msg, &jobAvailable); err != nil { - return nil, nil, fmt.Errorf("failed to decode job available: %w", err) + return nil, fmt.Errorf("failed to decode job available: %w", err) } l.logger.Info("Job available message received", "jobId", jobAvailable.RunnerRequestId) - availableJobs = append(availableJobs, jobAvailable.RunnerRequestId) + parsedMsg.jobsAvailable = append(parsedMsg.jobsAvailable, &jobAvailable) case messageTypeJobAssigned: var jobAssigned actions.JobAssigned if err := json.Unmarshal(msg, &jobAssigned); err != nil { - return nil, nil, fmt.Errorf("failed to decode job assigned: %w", err) + return nil, fmt.Errorf("failed to decode job assigned: %w", err) } l.logger.Info("Job assigned message received", "jobId", jobAssigned.RunnerRequestId) @@ -320,41 +347,37 @@ func (l *Listener) parseMessage(ctx context.Context, msg *actions.RunnerScaleSet case messageTypeJobStarted: var jobStarted actions.JobStarted if err := json.Unmarshal(msg, &jobStarted); err != nil { - return nil, nil, fmt.Errorf("could not decode job started message. %w", err) + return nil, fmt.Errorf("could not decode job started message. %w", err) } l.logger.Info("Job started message received.", "RequestId", jobStarted.RunnerRequestId, "RunnerId", jobStarted.RunnerId) - startedJobs = append(startedJobs, &jobStarted) + parsedMsg.jobsStarted = append(parsedMsg.jobsStarted, &jobStarted) case messageTypeJobCompleted: var jobCompleted actions.JobCompleted if err := json.Unmarshal(msg, &jobCompleted); err != nil { - return nil, nil, fmt.Errorf("failed to decode job completed: %w", err) + return nil, fmt.Errorf("failed to decode job completed: %w", err) } l.logger.Info("Job completed message received.", "RequestId", jobCompleted.RunnerRequestId, "Result", jobCompleted.Result, "RunnerId", jobCompleted.RunnerId, "RunnerName", jobCompleted.RunnerName) + parsedMsg.jobsCompleted = append(parsedMsg.jobsCompleted, &jobCompleted) default: l.logger.Info("unknown job message type.", "messageType", messageType.MessageType) } } - l.logger.Info("Available jobs.", "count", len(availableJobs), "requestIds", fmt.Sprint(availableJobs)) - if len(availableJobs) > 0 { - acquired, err := l.acquireAvailableJobs(ctx, availableJobs) - if err != nil { - return nil, nil, err - } - - l.logger.Info("Jobs are acquired", "count", len(acquired), "requestIds", fmt.Sprint(acquired)) - } - - return msg.Statistics, startedJobs, nil + return parsedMsg, nil } -func (l *Listener) acquireAvailableJobs(ctx context.Context, availableJobs []int64) ([]int64, error) { - l.logger.Info("Acquiring jobs") +func (l *Listener) acquireAvailableJobs(ctx context.Context, jobsAvailable []*actions.JobAvailable) ([]int64, error) { + ids := make([]int64, 0, len(jobsAvailable)) + for _, job := range jobsAvailable { + ids = append(ids, job.RunnerRequestId) + } - ids, err := l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, availableJobs) + l.logger.Info("Acquiring jobs", "count", len(ids), "requestIds", fmt.Sprint(ids)) + + ids, err := l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, ids) if err == nil { // if NO errors return ids, nil } @@ -368,7 +391,7 @@ func (l *Listener) acquireAvailableJobs(ctx context.Context, availableJobs []int return nil, err } - ids, err = l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, availableJobs) + ids, err = l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, ids) if err != nil { return nil, fmt.Errorf("failed to acquire jobs after session refresh: %w", err) } diff --git a/cmd/ghalistener/listener/listener_test.go b/cmd/ghalistener/listener/listener_test.go index 86b69b83..df81ee94 100644 --- a/cmd/ghalistener/listener/listener_test.go +++ b/cmd/ghalistener/listener/listener_test.go @@ -2,6 +2,7 @@ package listener import ( "context" + "encoding/json" "errors" "net/http" "testing" @@ -9,7 +10,6 @@ import ( listenermocks "github.com/actions/actions-runner-controller/cmd/ghalistener/listener/mocks" "github.com/actions/actions-runner-controller/cmd/ghalistener/metrics" - metricsmocks "github.com/actions/actions-runner-controller/cmd/ghalistener/metrics/mocks" "github.com/actions/actions-runner-controller/github/actions" "github.com/google/uuid" "github.com/stretchr/testify/assert" @@ -38,22 +38,6 @@ func TestNew(t *testing.T) { assert.NotNil(t, l) }) - t.Run("SetStaticMetrics", func(t *testing.T) { - t.Parallel() - - metrics := metricsmocks.NewPublisher(t) - - metrics.On("PublishStatic", mock.Anything, mock.Anything).Once() - - config := Config{ - Client: listenermocks.NewClient(t), - ScaleSetID: 1, - Metrics: metrics, - } - l, err := New(config) - assert.Nil(t, err) - assert.NotNil(t, l) - }) } func TestListener_createSession(t *testing.T) { @@ -443,7 +427,7 @@ func TestListener_Listen(t *testing.T) { var called bool handler := listenermocks.NewHandler(t) handler.On("HandleDesiredRunnerCount", mock.Anything, mock.Anything). - Return(nil). + Return(0, nil). Run( func(mock.Arguments) { called = true @@ -456,6 +440,63 @@ func TestListener_Listen(t *testing.T) { assert.True(t, errors.Is(err, context.Canceled)) assert.True(t, called) }) + + t.Run("CancelContextAfterGetMessage", func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + + config := Config{ + ScaleSetID: 1, + Metrics: metrics.Discard, + } + + client := listenermocks.NewClient(t) + uuid := uuid.New() + session := &actions.RunnerScaleSetSession{ + SessionId: &uuid, + OwnerName: "example", + RunnerScaleSet: &actions.RunnerScaleSet{}, + MessageQueueUrl: "https://example.com", + MessageQueueAccessToken: "1234567890", + Statistics: &actions.RunnerScaleSetStatistic{}, + } + client.On("CreateMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once() + + msg := &actions.RunnerScaleSetMessage{ + MessageId: 1, + MessageType: "RunnerScaleSetJobMessages", + Statistics: &actions.RunnerScaleSetStatistic{}, + } + client.On("GetMessage", ctx, mock.Anything, mock.Anything, mock.Anything). + Return(msg, nil). + Run( + func(mock.Arguments) { + cancel() + }, + ). + Once() + + // Ensure delete message is called with background context + client.On("DeleteMessage", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + + config.Client = client + + handler := listenermocks.NewHandler(t) + handler.On("HandleDesiredRunnerCount", mock.Anything, mock.Anything). + Return(0, nil). + Once() + + handler.On("HandleDesiredRunnerCount", mock.Anything, mock.Anything). + Return(0, nil). + Once() + + l, err := New(config) + require.Nil(t, err) + + err = l.Listen(ctx, handler) + assert.ErrorIs(t, context.Canceled, err) + }) } func TestListener_acquireAvailableJobs(t *testing.T) { @@ -489,7 +530,24 @@ func TestListener_acquireAvailableJobs(t *testing.T) { Statistics: &actions.RunnerScaleSetStatistic{}, } - _, err = l.acquireAvailableJobs(ctx, []int64{1, 2, 3}) + availableJobs := []*actions.JobAvailable{ + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 1, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 2, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 3, + }, + }, + } + _, err = l.acquireAvailableJobs(ctx, availableJobs) assert.Error(t, err) }) @@ -523,9 +581,26 @@ func TestListener_acquireAvailableJobs(t *testing.T) { Statistics: &actions.RunnerScaleSetStatistic{}, } - acquiredJobIDs, err := l.acquireAvailableJobs(ctx, []int64{1, 2, 3}) + availableJobs := []*actions.JobAvailable{ + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 1, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 2, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 3, + }, + }, + } + acquiredJobIDs, err := l.acquireAvailableJobs(ctx, availableJobs) assert.NoError(t, err) - assert.Equal(t, jobIDs, acquiredJobIDs) + assert.Equal(t, []int64{1, 2, 3}, acquiredJobIDs) }) t.Run("RefreshAndSucceeds", func(t *testing.T) { @@ -555,6 +630,23 @@ func TestListener_acquireAvailableJobs(t *testing.T) { // Second call to AcquireJobs will succeed want := []int64{1, 2, 3} + availableJobs := []*actions.JobAvailable{ + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 1, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 2, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 3, + }, + }, + } client.On("AcquireJobs", ctx, mock.Anything, mock.Anything, mock.Anything).Return(want, nil).Once() config.Client = client @@ -567,7 +659,7 @@ func TestListener_acquireAvailableJobs(t *testing.T) { RunnerScaleSet: &actions.RunnerScaleSet{}, } - got, err := l.acquireAvailableJobs(ctx, want) + got, err := l.acquireAvailableJobs(ctx, availableJobs) assert.Nil(t, err) assert.Equal(t, want, got) }) @@ -606,8 +698,165 @@ func TestListener_acquireAvailableJobs(t *testing.T) { RunnerScaleSet: &actions.RunnerScaleSet{}, } - got, err := l.acquireAvailableJobs(ctx, []int64{1, 2, 3}) + availableJobs := []*actions.JobAvailable{ + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 1, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 2, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + RunnerRequestId: 3, + }, + }, + } + + got, err := l.acquireAvailableJobs(ctx, availableJobs) assert.NotNil(t, err) assert.Nil(t, got) }) } + +func TestListener_parseMessage(t *testing.T) { + t.Run("FailOnEmptyStatistics", func(t *testing.T) { + msg := &actions.RunnerScaleSetMessage{ + MessageId: 1, + MessageType: "RunnerScaleSetJobMessages", + Statistics: nil, + } + + l := &Listener{} + parsedMsg, err := l.parseMessage(context.Background(), msg) + assert.Error(t, err) + assert.Nil(t, parsedMsg) + }) + + t.Run("FailOnIncorrectMessageType", func(t *testing.T) { + msg := &actions.RunnerScaleSetMessage{ + MessageId: 1, + MessageType: "RunnerMessages", // arbitrary message type + Statistics: &actions.RunnerScaleSetStatistic{}, + } + + l := &Listener{} + parsedMsg, err := l.parseMessage(context.Background(), msg) + assert.Error(t, err) + assert.Nil(t, parsedMsg) + }) + + t.Run("ParseAll", func(t *testing.T) { + msg := &actions.RunnerScaleSetMessage{ + MessageId: 1, + MessageType: "RunnerScaleSetJobMessages", + Body: "", + Statistics: &actions.RunnerScaleSetStatistic{ + TotalAvailableJobs: 1, + TotalAcquiredJobs: 2, + TotalAssignedJobs: 3, + TotalRunningJobs: 4, + TotalRegisteredRunners: 5, + TotalBusyRunners: 6, + TotalIdleRunners: 7, + }, + } + + var batchedMessages []any + jobsAvailable := []*actions.JobAvailable{ + { + AcquireJobUrl: "https://github.com/example", + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobAvailable, + }, + RunnerRequestId: 1, + }, + }, + { + AcquireJobUrl: "https://github.com/example", + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobAvailable, + }, + RunnerRequestId: 2, + }, + }, + } + for _, msg := range jobsAvailable { + batchedMessages = append(batchedMessages, msg) + } + + jobsAssigned := []*actions.JobAssigned{ + { + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobAssigned, + }, + RunnerRequestId: 3, + }, + }, + { + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobAssigned, + }, + RunnerRequestId: 4, + }, + }, + } + for _, msg := range jobsAssigned { + batchedMessages = append(batchedMessages, msg) + } + + jobsStarted := []*actions.JobStarted{ + { + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobStarted, + }, + RunnerRequestId: 5, + }, + RunnerId: 2, + RunnerName: "runner2", + }, + } + for _, msg := range jobsStarted { + batchedMessages = append(batchedMessages, msg) + } + + jobsCompleted := []*actions.JobCompleted{ + { + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobCompleted, + }, + RunnerRequestId: 6, + }, + Result: "success", + RunnerId: 1, + RunnerName: "runner1", + }, + } + for _, msg := range jobsCompleted { + batchedMessages = append(batchedMessages, msg) + } + + b, err := json.Marshal(batchedMessages) + require.NoError(t, err) + + msg.Body = string(b) + + l := &Listener{} + parsedMsg, err := l.parseMessage(context.Background(), msg) + require.NoError(t, err) + + assert.Equal(t, msg.Statistics, parsedMsg.statistics) + assert.Equal(t, jobsAvailable, parsedMsg.jobsAvailable) + assert.Equal(t, jobsStarted, parsedMsg.jobsStarted) + assert.Equal(t, jobsCompleted, parsedMsg.jobsCompleted) + }) +} diff --git a/cmd/ghalistener/listener/metrics_test.go b/cmd/ghalistener/listener/metrics_test.go new file mode 100644 index 00000000..96ca6ac2 --- /dev/null +++ b/cmd/ghalistener/listener/metrics_test.go @@ -0,0 +1,204 @@ +package listener + +import ( + "context" + "encoding/json" + "testing" + + listenermocks "github.com/actions/actions-runner-controller/cmd/ghalistener/listener/mocks" + metricsmocks "github.com/actions/actions-runner-controller/cmd/ghalistener/metrics/mocks" + "github.com/actions/actions-runner-controller/github/actions" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestInitialMetrics(t *testing.T) { + t.Parallel() + + t.Run("SetStaticMetrics", func(t *testing.T) { + t.Parallel() + + metrics := metricsmocks.NewPublisher(t) + + minRunners := 5 + maxRunners := 10 + metrics.On("PublishStatic", minRunners, maxRunners).Once() + + config := Config{ + Client: listenermocks.NewClient(t), + ScaleSetID: 1, + Metrics: metrics, + MinRunners: minRunners, + MaxRunners: maxRunners, + } + l, err := New(config) + + assert.Nil(t, err) + assert.NotNil(t, l) + }) + + t.Run("InitialMessageStatistics", func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + + sessionStatistics := &actions.RunnerScaleSetStatistic{ + TotalAvailableJobs: 1, + TotalAcquiredJobs: 2, + TotalAssignedJobs: 3, + TotalRunningJobs: 4, + TotalRegisteredRunners: 5, + TotalBusyRunners: 6, + TotalIdleRunners: 7, + } + + uuid := uuid.New() + session := &actions.RunnerScaleSetSession{ + SessionId: &uuid, + OwnerName: "example", + RunnerScaleSet: &actions.RunnerScaleSet{}, + MessageQueueUrl: "https://example.com", + MessageQueueAccessToken: "1234567890", + Statistics: sessionStatistics, + } + + metrics := metricsmocks.NewPublisher(t) + metrics.On("PublishStatic", mock.Anything, mock.Anything).Once() + metrics.On("PublishStatistics", sessionStatistics).Once() + metrics.On("PublishDesiredRunners", sessionStatistics.TotalAssignedJobs). + Run( + func(mock.Arguments) { + cancel() + }, + ).Once() + + config := Config{ + Client: listenermocks.NewClient(t), + ScaleSetID: 1, + Metrics: metrics, + } + + client := listenermocks.NewClient(t) + client.On("CreateMessageSession", mock.Anything, mock.Anything, mock.Anything).Return(session, nil).Once() + config.Client = client + + handler := listenermocks.NewHandler(t) + handler.On("HandleDesiredRunnerCount", mock.Anything, sessionStatistics.TotalAssignedJobs). + Return(sessionStatistics.TotalAssignedJobs, nil). + Once() + + l, err := New(config) + assert.Nil(t, err) + assert.NotNil(t, l) + + assert.ErrorIs(t, context.Canceled, l.Listen(ctx, handler)) + }) +} + +func TestHandleMessageMetrics(t *testing.T) { + t.Parallel() + + msg := &actions.RunnerScaleSetMessage{ + MessageId: 1, + MessageType: "RunnerScaleSetJobMessages", + Body: "", + Statistics: &actions.RunnerScaleSetStatistic{ + TotalAvailableJobs: 1, + TotalAcquiredJobs: 2, + TotalAssignedJobs: 3, + TotalRunningJobs: 4, + TotalRegisteredRunners: 5, + TotalBusyRunners: 6, + TotalIdleRunners: 7, + }, + } + + var batchedMessages []any + jobsStarted := []*actions.JobStarted{ + { + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobStarted, + }, + RunnerRequestId: 8, + }, + RunnerId: 3, + RunnerName: "runner3", + }, + } + for _, msg := range jobsStarted { + batchedMessages = append(batchedMessages, msg) + } + + jobsCompleted := []*actions.JobCompleted{ + { + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobCompleted, + }, + RunnerRequestId: 6, + }, + Result: "success", + RunnerId: 1, + RunnerName: "runner1", + }, + { + JobMessageBase: actions.JobMessageBase{ + JobMessageType: actions.JobMessageType{ + MessageType: messageTypeJobCompleted, + }, + RunnerRequestId: 7, + }, + Result: "success", + RunnerId: 2, + RunnerName: "runner2", + }, + } + for _, msg := range jobsCompleted { + batchedMessages = append(batchedMessages, msg) + } + + b, err := json.Marshal(batchedMessages) + require.NoError(t, err) + + msg.Body = string(b) + + desiredResult := 4 + + metrics := metricsmocks.NewPublisher(t) + metrics.On("PublishStatic", 0, 0).Once() + metrics.On("PublishStatistics", msg.Statistics).Once() + metrics.On("PublishJobCompleted", jobsCompleted[0]).Once() + metrics.On("PublishJobCompleted", jobsCompleted[1]).Once() + metrics.On("PublishJobStarted", jobsStarted[0]).Once() + metrics.On("PublishDesiredRunners", desiredResult).Once() + + handler := listenermocks.NewHandler(t) + handler.On("HandleJobStarted", mock.Anything, jobsStarted[0]).Return(nil).Once() + handler.On("HandleDesiredRunnerCount", mock.Anything, mock.Anything).Return(desiredResult, nil).Once() + + client := listenermocks.NewClient(t) + client.On("DeleteMessage", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() + + config := Config{ + Client: listenermocks.NewClient(t), + ScaleSetID: 1, + Metrics: metrics, + } + + l, err := New(config) + require.NoError(t, err) + l.client = client + l.session = &actions.RunnerScaleSetSession{ + OwnerName: "", + RunnerScaleSet: &actions.RunnerScaleSet{}, + MessageQueueUrl: "", + MessageQueueAccessToken: "", + Statistics: &actions.RunnerScaleSetStatistic{}, + } + + err = l.handleMessage(context.Background(), handler, msg) + require.NoError(t, err) +} diff --git a/cmd/ghalistener/listener/mocks/handler.go b/cmd/ghalistener/listener/mocks/handler.go index c78fe250..edc1b30b 100644 --- a/cmd/ghalistener/listener/mocks/handler.go +++ b/cmd/ghalistener/listener/mocks/handler.go @@ -15,18 +15,28 @@ type Handler struct { mock.Mock } -// HandleDesiredRunnerCount provides a mock function with given fields: ctx, desiredRunnerCount -func (_m *Handler) HandleDesiredRunnerCount(ctx context.Context, desiredRunnerCount int) error { - ret := _m.Called(ctx, desiredRunnerCount) +// HandleDesiredRunnerCount provides a mock function with given fields: ctx, count +func (_m *Handler) HandleDesiredRunnerCount(ctx context.Context, count int) (int, error) { + ret := _m.Called(ctx, count) - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { - r0 = rf(ctx, desiredRunnerCount) + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) (int, error)); ok { + return rf(ctx, count) + } + if rf, ok := ret.Get(0).(func(context.Context, int) int); ok { + r0 = rf(ctx, count) } else { - r0 = ret.Error(0) + r0 = ret.Get(0).(int) } - return r0 + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, count) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // HandleJobStarted provides a mock function with given fields: ctx, jobInfo diff --git a/cmd/ghalistener/worker/worker.go b/cmd/ghalistener/worker/worker.go index f9d7b7db..169f0251 100644 --- a/cmd/ghalistener/worker/worker.go +++ b/cmd/ghalistener/worker/worker.go @@ -156,7 +156,7 @@ func (w *Worker) HandleJobStarted(ctx context.Context, jobInfo *actions.JobStart // The function then scales the ephemeral runner set by applying the merge patch. // Finally, it logs the scaled ephemeral runner set details and returns nil if successful. // If any error occurs during the process, it returns an error with a descriptive message. -func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) error { +func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) (int, error) { // Max runners should always be set by the resource builder either to the configured value, // or the maximum int32 (resourcebuilder.newAutoScalingListener()). targetRunnerCount := min(w.config.MinRunners+count, w.config.MaxRunners) @@ -171,7 +171,7 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) error if targetRunnerCount == w.lastPatch { w.logger.Info("Skipping patching of EphemeralRunnerSet as the desired count has not changed", logValues...) - return nil + return targetRunnerCount, nil } original, err := json.Marshal( @@ -182,7 +182,7 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) error }, ) if err != nil { - return fmt.Errorf("failed to marshal empty ephemeral runner set: %w", err) + return 0, fmt.Errorf("failed to marshal empty ephemeral runner set: %w", err) } patch, err := json.Marshal( @@ -194,12 +194,12 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) error ) if err != nil { w.logger.Error(err, "could not marshal patch ephemeral runner set") - return err + return 0, err } mergePatch, err := jsonpatch.CreateMergePatch(original, patch) if err != nil { - return fmt.Errorf("failed to create merge patch json for ephemeral runner set: %w", err) + return 0, fmt.Errorf("failed to create merge patch json for ephemeral runner set: %w", err) } w.logger.Info("Created merge patch json for EphemeralRunnerSet update", "json", string(mergePatch)) @@ -217,7 +217,7 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) error Do(ctx). Into(patchedEphemeralRunnerSet) if err != nil { - return fmt.Errorf("could not patch ephemeral runner set , patch JSON: %s, error: %w", string(mergePatch), err) + return 0, fmt.Errorf("could not patch ephemeral runner set , patch JSON: %s, error: %w", string(mergePatch), err) } w.logger.Info("Ephemeral runner set scaled.", @@ -225,5 +225,5 @@ func (w *Worker) HandleDesiredRunnerCount(ctx context.Context, count int) error "name", w.config.EphemeralRunnerSetName, "replicas", patchedEphemeralRunnerSet.Spec.Replicas, ) - return nil + return targetRunnerCount, nil } diff --git a/controllers/actions.github.com/resourcebuilder.go b/controllers/actions.github.com/resourcebuilder.go index 18f58840..0ee48326 100644 --- a/controllers/actions.github.com/resourcebuilder.go +++ b/controllers/actions.github.com/resourcebuilder.go @@ -226,6 +226,7 @@ func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.A ports = append(ports, port) } + terminationGracePeriodSeconds := int64(60) podSpec := corev1.PodSpec{ ServiceAccountName: serviceAccount.Name, Containers: []corev1.Container{ @@ -256,8 +257,9 @@ func (b *resourceBuilder) newScaleSetListenerPod(autoscalingListener *v1alpha1.A }, }, }, - ImagePullSecrets: autoscalingListener.Spec.ImagePullSecrets, - RestartPolicy: corev1.RestartPolicyNever, + ImagePullSecrets: autoscalingListener.Spec.ImagePullSecrets, + RestartPolicy: corev1.RestartPolicyNever, + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, } labels := make(map[string]string, len(autoscalingListener.Labels)) From 728f05c8447278cf53b2669b1947a870f98b4e25 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Thu, 25 Jan 2024 15:12:19 +0100 Subject: [PATCH 03/13] Delete message session when `listener.Listen` returns (#3240) --- cmd/ghalistener/listener/listener.go | 20 +++++++++ cmd/ghalistener/listener/listener_test.go | 1 + cmd/ghalistener/listener/mocks/client.go | 14 ++++++ cmd/ghalistener/worker/worker.go | 5 +++ github/actions/client.go | 7 ++- github/actions/testdata/rootCA.crt | 34 +++++++------- github/actions/testdata/server.crt | 41 ++++++++--------- github/actions/testdata/server.key | 55 ++++++++++++----------- 8 files changed, 112 insertions(+), 65 deletions(-) diff --git a/cmd/ghalistener/listener/listener.go b/cmd/ghalistener/listener/listener.go index cb611239..1ec8cb9c 100644 --- a/cmd/ghalistener/listener/listener.go +++ b/cmd/ghalistener/listener/listener.go @@ -35,6 +35,7 @@ type Client interface { DeleteMessage(ctx context.Context, messageQueueUrl, messageQueueAccessToken string, messageId int64) error AcquireJobs(ctx context.Context, runnerScaleSetId int, messageQueueAccessToken string, requestIds []int64) ([]int64, error) RefreshMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) (*actions.RunnerScaleSetSession, error) + DeleteMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) error } type Config struct { @@ -126,6 +127,12 @@ func (l *Listener) Listen(ctx context.Context, handler Handler) error { return fmt.Errorf("createSession failed: %w", err) } + defer func() { + if err := l.deleteMessageSession(); err != nil { + l.logger.Error(err, "failed to delete message session") + } + }() + initialMessage := &actions.RunnerScaleSetMessage{ MessageId: 0, MessageType: "RunnerScaleSetJobMessages", @@ -409,3 +416,16 @@ func (l *Listener) refreshSession(ctx context.Context) error { l.session = session return nil } + +func (l *Listener) deleteMessageSession() error { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + l.logger.Info("Deleting message session") + + if err := l.client.DeleteMessageSession(ctx, l.session.RunnerScaleSet.Id, l.session.SessionId); err != nil { + return fmt.Errorf("failed to delete message session: %w", err) + } + + return nil +} diff --git a/cmd/ghalistener/listener/listener_test.go b/cmd/ghalistener/listener/listener_test.go index df81ee94..9e0ac1dc 100644 --- a/cmd/ghalistener/listener/listener_test.go +++ b/cmd/ghalistener/listener/listener_test.go @@ -419,6 +419,7 @@ func TestListener_Listen(t *testing.T) { Statistics: &actions.RunnerScaleSetStatistic{}, } client.On("CreateMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once() + client.On("DeleteMessageSession", mock.Anything, session.RunnerScaleSet.Id, session.SessionId).Return(nil).Once() config.Client = client l, err := New(config) diff --git a/cmd/ghalistener/listener/mocks/client.go b/cmd/ghalistener/listener/mocks/client.go index 4a2311ea..9c3d38fd 100644 --- a/cmd/ghalistener/listener/mocks/client.go +++ b/cmd/ghalistener/listener/mocks/client.go @@ -83,6 +83,20 @@ func (_m *Client) DeleteMessage(ctx context.Context, messageQueueUrl string, mes return r0 } +// DeleteMessageSession provides a mock function with given fields: ctx, runnerScaleSetId, sessionId +func (_m *Client) DeleteMessageSession(ctx context.Context, runnerScaleSetId int, sessionId *uuid.UUID) error { + ret := _m.Called(ctx, runnerScaleSetId, sessionId) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, int, *uuid.UUID) error); ok { + r0 = rf(ctx, runnerScaleSetId, sessionId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // GetAcquirableJobs provides a mock function with given fields: ctx, runnerScaleSetId func (_m *Client) GetAcquirableJobs(ctx context.Context, runnerScaleSetId int) (*actions.AcquirableJobList, error) { ret := _m.Called(ctx, runnerScaleSetId) diff --git a/cmd/ghalistener/worker/worker.go b/cmd/ghalistener/worker/worker.go index 169f0251..9387a565 100644 --- a/cmd/ghalistener/worker/worker.go +++ b/cmd/ghalistener/worker/worker.go @@ -11,6 +11,7 @@ import ( "github.com/actions/actions-runner-controller/logging" jsonpatch "github.com/evanphx/json-patch" "github.com/go-logr/logr" + kerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -141,6 +142,10 @@ func (w *Worker) HandleJobStarted(ctx context.Context, jobInfo *actions.JobStart Do(ctx). Into(patchedStatus) if err != nil { + if kerrors.IsNotFound(err) { + w.logger.Info("Ephemeral runner not found, skipping patching of ephemeral runner status", "runnerName", jobInfo.RunnerName) + return nil + } return fmt.Errorf("could not patch ephemeral runner status, patch JSON: %s, error: %w", string(mergePatch), err) } diff --git a/github/actions/client.go b/github/actions/client.go index 7113893e..c31eefc8 100644 --- a/github/actions/client.go +++ b/github/actions/client.go @@ -640,8 +640,11 @@ func (c *Client) doSessionRequest(ctx context.Context, method, path string, requ return err } - if resp.StatusCode == expectedResponseStatusCode && responseUnmarshalTarget != nil { - return json.NewDecoder(resp.Body).Decode(responseUnmarshalTarget) + if resp.StatusCode == expectedResponseStatusCode { + if responseUnmarshalTarget != nil { + return json.NewDecoder(resp.Body).Decode(responseUnmarshalTarget) + } + return nil } if resp.StatusCode >= 400 && resp.StatusCode < 500 { diff --git a/github/actions/testdata/rootCA.crt b/github/actions/testdata/rootCA.crt index 7c9c7bbc..96eb1680 100644 --- a/github/actions/testdata/rootCA.crt +++ b/github/actions/testdata/rootCA.crt @@ -1,18 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIC6jCCAdICCQCoZFduxPa/eDANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJV -UzEnMCUGA1UEAwweYWN0aW9ucy1ydW5uZXItY29udHJvbGxlci10ZXN0MCAXDTIz -MDExOTE2NTAwMVoYDzIwNTAwNjA1MTY1MDAxWjA2MQswCQYDVQQGEwJVUzEnMCUG -A1UEAwweYWN0aW9ucy1ydW5uZXItY29udHJvbGxlci10ZXN0MIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAykHCU0I/pdzhnQBwr2N+7so66LPq0cxc8JJL -S2mmk7gg+NWhTZzoci6aYXNRKCyH6B2Wmy7Qveku2wqT2+/4JBMYgTWH5bF7yt76 -LB+x9YruSgH/pBN2WI4vRU87NOAU8F0o0U/Lp5vAJoRo+ePPvcHu0OY1WF+QnEX+ -xtp6gJFGf5DT4U9upwEgQjKgvKFEoB5KNeH1qr2fS2yA2vhm6Uhm+1i/KUQUZ49K -GvFK8TQQT4HXft8rPLP5M9OitdqVU8SX0dQoXZ4M41/qydycHOvApj0LlH/XsicZ -x0mkF90hD+9VRqeYFe562NI4NHR7FGP7HKPWibNjXKC2w+z+aQIDAQABMA0GCSqG -SIb3DQEBCwUAA4IBAQBxaOCnmakd1PPp+pH40OjUktKG1nqM2tGqP0o3Bk7huB2y -jXIDi9ETuTeqqHONwwgsKOVY3J+Zt5R+teBSC0qUnypODzu+9v8Xa4Is9G9GyT5S -erjpPcJjQnvZyMHLH9DGGWE9UCyqKIqmaEc9bwr2oz1+a0rsaS3ZdIFlQibBHij5 -tdJcnzXfN4T4GIbYXKMCOYDy/5CiNJ26l/pQNpO9JCzsEmngw0ooS0Bi8EcTCgB6 -dsHl0w8va3l1kvxWWIlNTGwrAEpRbXmL01hAqx2yCiaFPVZ/eRNWmBWO4LpW4ouK -YOaA+X7geM6XVFlZE3cP58AxYKWHGAThxkZbD5cu +MIIDVTCCAj2gAwIBAgIUOo9VGKll71GYjunZhdMQhS5rP+gwDQYJKoZIhvcNAQEL +BQAwOTESMBAGA1UEAwwJbG9jYWxob3N0MQswCQYDVQQGEwJVUzEWMBQGA1UEBwwN +U2FuIEZyYW5zaXNjbzAgFw0yNDAxMjIxMjUyNTdaGA8yMDUxMDYwODEyNTI1N1ow +OTESMBAGA1UEAwwJbG9jYWxob3N0MQswCQYDVQQGEwJVUzEWMBQGA1UEBwwNU2Fu +IEZyYW5zaXNjbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALmyQRuC +S13Iat5jMun5zg8tn4E3RZ4x5KWPvRiR9RRX4zo5f/ytmnFVGkSnDhXJkuHRzwWl +KjtdW23uUaBfNbJR55O0qUnZWAMNKO1Afm68Tfg+91a5X+KpwGiHfIGZs7UCERYg +6O2iqHQMLCOL/Ytpd6NBF+QFK9klRbfncBJmCR6FEpw1/bGr7HwlldfkPkpHNWUG +cIqytYBvzo2T2cUyrTysKtATcRg/4Fp0DAZocYfzT6/gL2yWhLwnmxqU7Gbxvrd2 +6ejFitgxwoM/3rKWuXds7tFMeiKUu2RovGkvDkMEieJWwTufPBJjkIklW5S4iMMi +hJnDIn+Ag1nbVHcCAwEAAaNTMFEwHQYDVR0OBBYEFK33e+IWho6FKn4GaxRb2cmv +mmxjMB8GA1UdIwQYMBaAFK33e+IWho6FKn4GaxRb2cmvmmxjMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHZ/Z3CSrPoWb02+iu1cUN8nlQBtAsxI +oR3nqhUSEA/9oyyXJt8NIIXauACyYzmNXG87aKQZvVzUEQM0aK4MBq+Pg0Zdnvns +8QtBvdro7jInHhfn4uS8X21Fa1gYZ0d0C6UHIXUeD9KSEOAX1JT+3VP/7FNIDzns +2ddSxzcji3eVFkDR4/1vRMTng/kiP5vFz1St1op2EYDT+v6PVr9ew3NWUf/w7fgP +sRRyx3qi7m8SRHc7FwDLk+6/zc1/14YIiX9PrvVmnJj0yULSHiBu4cQccKE2ibos +ZeUPfZL8Kl+hs/MtXG/XlYBbApm69eo7EEGHAS/2DIq2yPgsQrGMYkA= -----END CERTIFICATE----- diff --git a/github/actions/testdata/server.crt b/github/actions/testdata/server.crt index be71271a..59bf791f 100644 --- a/github/actions/testdata/server.crt +++ b/github/actions/testdata/server.crt @@ -1,22 +1,23 @@ -----BEGIN CERTIFICATE----- -MIIDnTCCAoWgAwIBAgIJAJskDVhiEY6fMA0GCSqGSIb3DQEBCwUAMDYxCzAJBgNV -BAYTAlVTMScwJQYDVQQDDB5hY3Rpb25zLXJ1bm5lci1jb250cm9sbGVyLXRlc3Qw -HhcNMjMwMTE5MTY1MTE0WhcNMjQwMTE5MTY1MTE0WjBaMQswCQYDVQQGEwJVUzEi -MCAGA1UECgwZYWN0aW9ucy1ydW5uZXItY29udHJvbGxlcjEnMCUGA1UECwweYWN0 -aW9ucy1ydW5uZXItY29udHJvbGxlciB0ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAzOTt1/VjuaHzn+b7jLeufW3rxLHFKQV+LiUiT389rbFGY+DN -CC+Nzx+DbFBpKcX/scseVhFzlXlrESWWZ4h7LGMXRsTDKs91F1RMuFCd8eIEwbuV -civR44IqT5r/0hlMOWemd3Fh/c8KF+9dWQ0q0T3tvlVzEbWNRTVAXTT4JzizqNd1 -1hhnuV/KjhiptPC/8jQ4D9ocZKM8a1pM9O2z3bnmH7VTQJkhjxE7gefQTPQRmvKk -C7uqvfk2NHTTnKiLfkE10JhLTa0VND2aofNWCybGTyHNNCNlepakoP3KyFC2LjPR -oR5iwSnCRDu1z8tDWW+rIa3pfxdQ8LnH4J4CDwIDAQABo4GJMIGGMFAGA1UdIwRJ -MEehOqQ4MDYxCzAJBgNVBAYTAlVTMScwJQYDVQQDDB5hY3Rpb25zLXJ1bm5lci1j -b250cm9sbGVyLXRlc3SCCQCoZFduxPa/eDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE -8DAaBgNVHREEEzARhwR/AAABgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEB -ALdl0ytjellmhtjbXkUZKAl/R2ZXMAVxIOtb4qiN6OOwOMK4p2Wt26p34bQa2JD0 -t0qvesI7spQzQObNMdT6NZJl8Ul0ABuzti/Esvmby+VfsFPasCQVXx+jqGhERqXc -SeZFIVWVACyfAc1dkqfGwehSrY62eBlY2PJ1JezagW6aLAnV6Si+96++mkALJDdX -MZhhSqjxM+Nnmhpy4My6oHVrdYWHcuVhzlEmNaMtmJCYuihIyD2Usn32xJK1k89d -WgEOPCk+ZDAligPlGZS201fsznJk5uIjmxPjjFlJLXotBs8H7j0cQ2JkV5YHsHCk -EYf5EJ0ZKtZbwRFeRC1Ajxg= +MIIDyDCCArCgAwIBAgIUKCU/uCdz/9EcfzL6wd7ubSPrsxIwDQYJKoZIhvcNAQEL +BQAwOTESMBAGA1UEAwwJbG9jYWxob3N0MQswCQYDVQQGEwJVUzEWMBQGA1UEBwwN +U2FuIEZyYW5zaXNjbzAgFw0yNDAxMjIxMjU0MTRaGA8yMDUxMDYwODEyNTQxNFow +gYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1T +YW4gRnJhbnNpc2NvMRMwEQYDVQQKDApHaXRIdWJUZXN0MSMwIQYDVQQLDBpHaXRI +dWJUZXN0IEFjdGlvbnMgUnVudGltZTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVQ7yHHAxehcsOW8NNEplrEF/48n +9+XCc4ZWu0LdPdKAjcwMSAddHvLZVp5OUNRTUKgwWfL5DyGFnAhSZ31Ag3FHyoOB +C5BQSBEd+xsO1Gflt8Pm0A7TN2jzlVx7rq1j7kZ25AZY9oJ6ipK4Hf4mYbfSR5cl +M2WKBPGk9JbYmI7l0t3IYLm954xxfNtPxr1tEAwk75UAKNWXBwqkR31+madOaFsU +9LJT4aeFJoFs+95tQzvAymGwlE+w6aWiz0WecLSzf8ZgXcRqmQkh1EcP6/2cu5MA +CMRJcNly421DYUEbofgoZ8OetkqtFcYk+RyjUBhkQWi8AAQLKJ4q7VZKqwIDAQAB +o3YwdDAfBgNVHSMEGDAWgBSt93viFoaOhSp+BmsUW9nJr5psYzAJBgNVHRMEAjAA +MAsGA1UdDwQEAwIE8DAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwHQYDVR0O +BBYEFM4ELRkBcflqUtQ/GQK86CjBqjTUMA0GCSqGSIb3DQEBCwUAA4IBAQCMkiid +7v2jsSWc8nGOM4Z6vEJ912mKpyyfpWSpM8SxCCxzUrbMrpFx8LB4rmeziy6hNEA0 +yv+h9qiu9l/vVzVc3Q9HA3linEPXqnlUEXd7PV/G/IFoYKFrXi/H+zda9G0Nqt1A +oOKM3t9fsff8KDaRQ2sdSUEjqtAlfg6bbBwO66CICXLU+VUH7hOVghT23UJVvwNY +Dvkha9TYR+aawRypLoTfT5ZtLp/0A9P+liqo6F5Xm0M89bYLXNPl1fPzY3Ihi5Jd +b6/mttpY9gxTfbw67m2Epfmt1NdOHkY7ac/Hr6pt/YyMBrPz9Z3eZxIXUIVDo/Nh +4O2g9RoFFN4m3A+d -----END CERTIFICATE----- diff --git a/github/actions/testdata/server.key b/github/actions/testdata/server.key index f7011fd4..52af52be 100644 --- a/github/actions/testdata/server.key +++ b/github/actions/testdata/server.key @@ -1,27 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAzOTt1/VjuaHzn+b7jLeufW3rxLHFKQV+LiUiT389rbFGY+DN -CC+Nzx+DbFBpKcX/scseVhFzlXlrESWWZ4h7LGMXRsTDKs91F1RMuFCd8eIEwbuV -civR44IqT5r/0hlMOWemd3Fh/c8KF+9dWQ0q0T3tvlVzEbWNRTVAXTT4JzizqNd1 -1hhnuV/KjhiptPC/8jQ4D9ocZKM8a1pM9O2z3bnmH7VTQJkhjxE7gefQTPQRmvKk -C7uqvfk2NHTTnKiLfkE10JhLTa0VND2aofNWCybGTyHNNCNlepakoP3KyFC2LjPR -oR5iwSnCRDu1z8tDWW+rIa3pfxdQ8LnH4J4CDwIDAQABAoIBAC5rr3c+IVntV0Tj -EBrRgrboMIJfxEuG8w+BWkSoj1DK2SfHxqwUGgzTFvNzRGAye7vMSRM24Pj8iUVZ -Pro2MbHcwWlHKvCID/85GiioGyCyFGHQHgu/4c2pr+xZMZxoHtzintRw28KlJaRG -lt+WHB1L6pE0yt04RMlpRyvW1GIODtEh1wya61Aa2xZMJxgbNWv89znLI2f3ForY -QR/he8hQtfJQeH+mv2SvJ1bopkJ58ZObKapuJAWCSxzVRj/yol1MqfUDBy4NrJfY -F5UP0BSmnED1EdIXeC0duo5RyiSfHqqJlcKR+zlepOb4pr4I1H8P6AIJ9iiunxUJ -h9i+YAECgYEA7JgrH5sjStcHdwUgEPN4E1MI3WRwDnnuVpzeSUH7tRqK4lsdIKmF -u/ss3TMwTgC8XR4JJbVp+6ea54zpVjtBGwShMSvn2+e7OHHg1YoVqBIgRpL+/C4m -wfon2EglQ0IjscUtKuAR/UyhU6vZtkYRUKeXRKisW4yoobdob0Y4lakCgYEA3bMl -BfszC5c0RXI5vsBUBvr9gXMY/8tacM7H8i3vT7JqoYJG6GGST0/HeK+La3w2zryx -Q8IL6uLy/HZvTHZ+BSp4KzwwgDUIk0jm/JcvzD2ZhJHoAo4aQTc6QI2ZNgjGVwCb -nJ0Niaxc4CdSUEAUHH1bCXk/e2stcnieFuiiPPcCgYAIxrA60OdjPEyzloYU+uMG -XHskszgQ4Wb84X7BWug6VIy4Tsbq0j76tRt57Q8qpY5XKekO9AbFZfcyBaEWKMaG -eQp9p3JHTvY75sV/Rkr9XAbEd2lr805OvbfCpxJyxz5JttWxFHS2X6RQVTyTLVAx -HLZYvqT+FF6g+QuvrPwmWQKBgAQspVvReQqU1EUie3feAzcGbtOLKUNXvuI04orq -1oC3qU5VN6SUgb7Aj87z7zoc4qNN5kCSXMsVbuHWEQ5thL3wKMcXoQoo9Xpgewjy -h9Herw9R9/5kUpY7xfsFL4dW7vUga82tH14iQrVtyBz+t+I5cgdhoxJd2EM5hjCE -PNnNAoGBALPjmvEZ1HJdCOxY/AisziVtOFc6Glk/KhpSIT7WE1me4qLQFmrsHIDQ -kZ8Sb1f3PQ4T4vHGrtl8qh144MJPI1Nb8klzdlD1xeypGpgXoQb5fsC17g1fgczp -TGzq3pvnlGnrgVmnfrWQCHXDLzXtLqM/Pu84guPFftJQ+++yy0np ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtVDvIccDF6Fyw +5bw00SmWsQX/jyf35cJzhla7Qt090oCNzAxIB10e8tlWnk5Q1FNQqDBZ8vkPIYWc +CFJnfUCDcUfKg4ELkFBIER37Gw7UZ+W3w+bQDtM3aPOVXHuurWPuRnbkBlj2gnqK +krgd/iZht9JHlyUzZYoE8aT0ltiYjuXS3chgub3njHF820/GvW0QDCTvlQAo1ZcH +CqRHfX6Zp05oWxT0slPhp4UmgWz73m1DO8DKYbCUT7DppaLPRZ5wtLN/xmBdxGqZ +CSHURw/r/Zy7kwAIxElw2XLjbUNhQRuh+Chnw562Sq0VxiT5HKNQGGRBaLwABAso +nirtVkqrAgMBAAECggEAR+/t4ANWPs1xqvmuYz1sRV6zXp3LuNdjHQ9kb9QQftgf +ArrtXfewbmfcTFbnqiR1b8ReTPbK57zB90B88vbJD8S0RxjNNj9vEnoIN2/Dd+Sn +Mt3brf55K0Yj0pnPu2+7Sel07q6zvZvpwBmk0M3qoCPq4kuY5Pv/jI2+KMVyn94A +Dc3J6xdKqLNsw7nhUDELHn8DrKQgqucTzi4goJo8Lwc9I8lanTfmbiXj1wYo3nhr +5DgVcPUceZnsrDNnfkwOaaXKAGUCTi3PWieKq6Cm22oh53s1WS5NJDuk/1NvvfV+ ++6dyhfmW/jkHHMelox91n1qmLMYnq+GhoK6szapqAQKBgQDLRWZH17zdTNALQzks +RbZU9abe+UQV1O5ywdL+4F444IPY2f3gxhEWyL+xAF66ZG0+NA/EO9n7FPqAbgyA +Atz0LT7W6o9/AveqBSNs73zxGo7OYlBDq81nCgMzU11nvfTmydJhaMC+6Zyh0Bbc +vzIbygpDOL7tg4AyyEcLUNA7BwKBgQDaSnmwMCEdcTENwzVd1mOZdnXRTBPz0u0t +aCK5voL99L0+8HyKjtUBtWbBgUxCz7/+mfoNCU+QUHCJksm9vN1m5Zq4r0aEHE36 +7lYAAeWnltg+OHWqGcSHRZ/zHHs8c/azemvRaTZnZ++meVkfd07jsd+yIYt/G3La +KV9t86V2PQKBgEfNdfm+vVo2ve6cil+XKHcOZymwR1qm4qvqx4t82guhUzGQn1t8 +26B+vSfbB5szylsErOUWd0N3/5zKQuQdHsuqB96G8LVe6PlH42GhnzLTvMoudEfT +MjVJliPVONNiiFXVyNjb1eoaP1fxV4IWj669Sa7BJsBjiS9nC6F1pHiVAoGBALBT +fFxPZFBuAFvHlTIJXUa3I5A+zdckSCVnerVjKFiO+tb+VvttSK4qo6gnEzzcp4+3 +PP6OyNAfyee2xHMZPhZB3WrVWjaYznylTJ6Q6bsn4+DOpm0Sh2dlXEB6fylj2qE7 +gCAVxrZchH6Kgu0h6H2QTsuKwS2ZNHr49HbSWpNZAoGBAMrEMiyKYWKgiejs69pj +idKifoCDI+Hu1WD/eViUm2OuOfdW9fIBHoeuKmOBKGYIqx5yEbFhXoJmTtJ1aSa1 ++N+0NBzv9+1W5EII0voELevxLvjeaejcUgLNabGIj1xIcPzaEKTS+Vv2Hn6nffWR +yKlIixoSTJ+oJShyT9DZyZAd +-----END PRIVATE KEY----- From f7b6ad901d7021046038f69e0d4d1055c41e9914 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Thu, 25 Jan 2024 15:45:07 +0100 Subject: [PATCH 04/13] Add listener graceful termination period and background context after the message is received (#3187) --- cmd/ghalistener/listener/listener_test.go | 2 ++ cmd/ghalistener/listener/metrics_test.go | 1 + 2 files changed, 3 insertions(+) diff --git a/cmd/ghalistener/listener/listener_test.go b/cmd/ghalistener/listener/listener_test.go index 9e0ac1dc..ff5208de 100644 --- a/cmd/ghalistener/listener/listener_test.go +++ b/cmd/ghalistener/listener/listener_test.go @@ -420,6 +420,7 @@ func TestListener_Listen(t *testing.T) { } client.On("CreateMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once() client.On("DeleteMessageSession", mock.Anything, session.RunnerScaleSet.Id, session.SessionId).Return(nil).Once() + config.Client = client l, err := New(config) @@ -463,6 +464,7 @@ func TestListener_Listen(t *testing.T) { Statistics: &actions.RunnerScaleSetStatistic{}, } client.On("CreateMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once() + client.On("DeleteMessageSession", mock.Anything, session.RunnerScaleSet.Id, session.SessionId).Return(nil).Once() msg := &actions.RunnerScaleSetMessage{ MessageId: 1, diff --git a/cmd/ghalistener/listener/metrics_test.go b/cmd/ghalistener/listener/metrics_test.go index 96ca6ac2..0f94c28d 100644 --- a/cmd/ghalistener/listener/metrics_test.go +++ b/cmd/ghalistener/listener/metrics_test.go @@ -82,6 +82,7 @@ func TestInitialMetrics(t *testing.T) { client := listenermocks.NewClient(t) client.On("CreateMessageSession", mock.Anything, mock.Anything, mock.Anything).Return(session, nil).Once() + client.On("DeleteMessageSession", mock.Anything, session.RunnerScaleSet.Id, session.SessionId).Return(nil).Once() config.Client = client handler := listenermocks.NewHandler(t) From d72774753c1ac24f927cac68b368f2abc9f65f40 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Fri, 26 Jan 2024 11:03:08 +0100 Subject: [PATCH 05/13] Prepare 0.8.2 release (#3249) --- .github/workflows/gha-e2e-tests.yaml | 2 +- charts/gha-runner-scale-set-controller/Chart.yaml | 4 ++-- charts/gha-runner-scale-set/Chart.yaml | 4 ++-- docs/gha-runner-scale-set-controller/README.md | 5 +++++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gha-e2e-tests.yaml b/.github/workflows/gha-e2e-tests.yaml index e08d6ecd..a241f0ad 100644 --- a/.github/workflows/gha-e2e-tests.yaml +++ b/.github/workflows/gha-e2e-tests.yaml @@ -16,7 +16,7 @@ env: TARGET_ORG: actions-runner-controller TARGET_REPO: arc_e2e_test_dummy IMAGE_NAME: "arc-test-image" - IMAGE_VERSION: "0.8.1" + IMAGE_VERSION: "0.8.2" concurrency: # This will make sure we only apply the concurrency limits on pull requests diff --git a/charts/gha-runner-scale-set-controller/Chart.yaml b/charts/gha-runner-scale-set-controller/Chart.yaml index ef7683c6..dd08c48c 100644 --- a/charts/gha-runner-scale-set-controller/Chart.yaml +++ b/charts/gha-runner-scale-set-controller/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.8.1 +version: 0.8.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.8.1" +appVersion: "0.8.2" home: https://github.com/actions/actions-runner-controller diff --git a/charts/gha-runner-scale-set/Chart.yaml b/charts/gha-runner-scale-set/Chart.yaml index 795e03fc..8ab56e30 100644 --- a/charts/gha-runner-scale-set/Chart.yaml +++ b/charts/gha-runner-scale-set/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.8.1 +version: 0.8.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.8.1" +appVersion: "0.8.2" home: https://github.com/actions/actions-runner-controller diff --git a/docs/gha-runner-scale-set-controller/README.md b/docs/gha-runner-scale-set-controller/README.md index 39a3c5a3..90a6009a 100644 --- a/docs/gha-runner-scale-set-controller/README.md +++ b/docs/gha-runner-scale-set-controller/README.md @@ -43,6 +43,11 @@ You can follow [this troubleshooting guide](https://docs.github.com/en/actions/h ## Changelog +### v0.8.2 +1. Add listener graceful termination period and background context after the message is received [#3187](https://github.com/actions/actions-runner-controller/pull/3187) +1. Publish metrics in the new ghalistener [#3193](https://github.com/actions/actions-runner-controller/pull/3193) +1. Delete message session when listener.Listen returns [#3240](https://github.com/actions/actions-runner-controller/pull/3240) + ### v0.8.1 1. Fix proxy issue in new listener client [#3181](https://github.com/actions/actions-runner-controller/pull/3181) From c03fac8fdd487e8934b3a45c89326f7615947c85 Mon Sep 17 00:00:00 2001 From: Nick McClorey <32378821+nickrmcclorey@users.noreply.github.com> Date: Fri, 2 Feb 2024 04:01:22 -0500 Subject: [PATCH 06/13] Remove Typo in Grafana docs (#3235) --- .../samples/grafana-dashboard/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gha-runner-scale-set-controller/samples/grafana-dashboard/README.md b/docs/gha-runner-scale-set-controller/samples/grafana-dashboard/README.md index 1cbd057f..3a484c24 100644 --- a/docs/gha-runner-scale-set-controller/samples/grafana-dashboard/README.md +++ b/docs/gha-runner-scale-set-controller/samples/grafana-dashboard/README.md @@ -12,4 +12,4 @@ We do not intend to provide a supported ARC dashboard. This is simply a referenc 1. Make sure to have [Grafana](https://grafana.com/docs/grafana/latest/installation/) and [Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) running in your cluster. 2. Make sure that Prometheus is properly scraping the metrics endpoints of the controller-manager and listeners. -3. Import the [dashboard](ARC-Autoscaling-Runner-Set-Monitoring_1692627561838.json.json) into Grafana. +3. Import the [dashboard](ARC-Autoscaling-Runner-Set-Monitoring_1692627561838.json) into Grafana. From 9b053102ed5bbc924118fa68d34115787c037c20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:05:09 +0100 Subject: [PATCH 07/13] Bump github.com/google/uuid from 1.4.0 to 1.6.0 (#3253) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c3292ab4..6181ea9e 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-cmp v0.6.0 github.com/google/go-github/v52 v52.0.0 - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/gruntwork-io/terratest v0.46.7 diff --git a/go.sum b/go.sum index d9ed1828..dc6eca12 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= From a68aa00bd8d3c30b2a8636e2eddaccb9aec07766 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:44:28 -0500 Subject: [PATCH 08/13] Updates: runner to v2.313.0 container-hooks to v0.5.1 (#3270) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- Makefile | 2 +- runner/Makefile | 4 ++-- runner/VERSION | 4 ++-- test/e2e/e2e_test.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 7cd1b8fb..c505a95f 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ endif DOCKER_USER ?= $(shell echo ${DOCKER_IMAGE_NAME} | cut -d / -f1) VERSION ?= dev COMMIT_SHA = $(shell git rev-parse HEAD) -RUNNER_VERSION ?= 2.312.0 +RUNNER_VERSION ?= 2.313.0 TARGETPLATFORM ?= $(shell arch) RUNNER_NAME ?= ${DOCKER_USER}/actions-runner RUNNER_TAG ?= ${VERSION} diff --git a/runner/Makefile b/runner/Makefile index 98f33f8b..f0510517 100644 --- a/runner/Makefile +++ b/runner/Makefile @@ -6,8 +6,8 @@ DIND_ROOTLESS_RUNNER_NAME ?= ${DOCKER_USER}/actions-runner-dind-rootless OS_IMAGE ?= ubuntu-22.04 TARGETPLATFORM ?= $(shell arch) -RUNNER_VERSION ?= 2.312.0 -RUNNER_CONTAINER_HOOKS_VERSION ?= 0.5.0 +RUNNER_VERSION ?= 2.313.0 +RUNNER_CONTAINER_HOOKS_VERSION ?= 0.5.1 DOCKER_VERSION ?= 24.0.7 # default list of platforms for which multiarch image is built diff --git a/runner/VERSION b/runner/VERSION index 250829ce..2d34443a 100644 --- a/runner/VERSION +++ b/runner/VERSION @@ -1,2 +1,2 @@ -RUNNER_VERSION=2.312.0 -RUNNER_CONTAINER_HOOKS_VERSION=0.5.0 \ No newline at end of file +RUNNER_VERSION=2.313.0 +RUNNER_CONTAINER_HOOKS_VERSION=0.5.1 \ No newline at end of file diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index b3d16bb3..ca2df6b7 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -36,8 +36,8 @@ var ( testResultCMNamePrefix = "test-result-" - RunnerVersion = "2.312.0" - RunnerContainerHooksVersion = "0.5.0" + RunnerVersion = "2.313.0" + RunnerContainerHooksVersion = "0.5.1" ) // If you're willing to run this test via VS Code "run test" or "debug test", From 9fba37540a5dab4bb20ce2d931fa298ad5f132d4 Mon Sep 17 00:00:00 2001 From: Talia Stocks <928827+taliastocks@users.noreply.github.com> Date: Mon, 12 Feb 2024 08:47:09 -0500 Subject: [PATCH 09/13] Expose volumeMounts and volumes in gha-runner-scale-set-controller (#3260) --- .../templates/deployment.yaml | 6 +++++ .../tests/template_test.go | 22 +++++++++++++------ .../values.yaml | 4 ++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/charts/gha-runner-scale-set-controller/templates/deployment.yaml b/charts/gha-runner-scale-set-controller/templates/deployment.yaml index 1b857b37..2a32f525 100644 --- a/charts/gha-runner-scale-set-controller/templates/deployment.yaml +++ b/charts/gha-runner-scale-set-controller/templates/deployment.yaml @@ -110,10 +110,16 @@ spec: volumeMounts: - mountPath: /tmp name: tmp + {{- range .Values.volumeMounts }} + - {{ toYaml . | nindent 10 }} + {{- end }} terminationGracePeriodSeconds: 10 volumes: - name: tmp emptyDir: {} + {{- range .Values.volumes }} + - {{ toYaml . | nindent 8 }} + {{- end }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/charts/gha-runner-scale-set-controller/tests/template_test.go b/charts/gha-runner-scale-set-controller/tests/template_test.go index d42c1d43..e72e0e37 100644 --- a/charts/gha-runner-scale-set-controller/tests/template_test.go +++ b/charts/gha-runner-scale-set-controller/tests/template_test.go @@ -424,10 +424,14 @@ func TestTemplate_ControllerDeployment_Customize(t *testing.T) { "tolerations[0].key": "foo", "affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key": "foo", "affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator": "bar", - "priorityClassName": "test-priority-class", - "flags.updateStrategy": "eventual", - "flags.logLevel": "info", - "flags.logFormat": "json", + "priorityClassName": "test-priority-class", + "flags.updateStrategy": "eventual", + "flags.logLevel": "info", + "flags.logFormat": "json", + "volumes[0].name": "customMount", + "volumes[0].configMap.name": "my-configmap", + "volumeMounts[0].name": "customMount", + "volumeMounts[0].mountPath": "/my/mount/path", }, KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), } @@ -470,9 +474,11 @@ func TestTemplate_ControllerDeployment_Customize(t *testing.T) { assert.Equal(t, int64(1000), *deployment.Spec.Template.Spec.SecurityContext.FSGroup) assert.Equal(t, "test-priority-class", deployment.Spec.Template.Spec.PriorityClassName) assert.Equal(t, int64(10), *deployment.Spec.Template.Spec.TerminationGracePeriodSeconds) - assert.Len(t, deployment.Spec.Template.Spec.Volumes, 1) + assert.Len(t, deployment.Spec.Template.Spec.Volumes, 2) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Volumes[0].Name) - assert.NotNil(t, 10, deployment.Spec.Template.Spec.Volumes[0].EmptyDir) + assert.NotNil(t, deployment.Spec.Template.Spec.Volumes[0].EmptyDir) + assert.Equal(t, "customMount", deployment.Spec.Template.Spec.Volumes[1].Name) + assert.Equal(t, "my-configmap", deployment.Spec.Template.Spec.Volumes[1].ConfigMap.Name) assert.Len(t, deployment.Spec.Template.Spec.NodeSelector, 1) assert.Equal(t, "bar", deployment.Spec.Template.Spec.NodeSelector["foo"]) @@ -521,9 +527,11 @@ func TestTemplate_ControllerDeployment_Customize(t *testing.T) { assert.True(t, *deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsNonRoot) assert.Equal(t, int64(1000), *deployment.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser) - assert.Len(t, deployment.Spec.Template.Spec.Containers[0].VolumeMounts, 1) + assert.Len(t, deployment.Spec.Template.Spec.Containers[0].VolumeMounts, 2) assert.Equal(t, "tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].Name) assert.Equal(t, "/tmp", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath) + assert.Equal(t, "customMount", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[1].Name) + assert.Equal(t, "/my/mount/path", deployment.Spec.Template.Spec.Containers[0].VolumeMounts[1].MountPath) } func TestTemplate_EnableLeaderElectionRole(t *testing.T) { diff --git a/charts/gha-runner-scale-set-controller/values.yaml b/charts/gha-runner-scale-set-controller/values.yaml index 3a63c829..43b79740 100644 --- a/charts/gha-runner-scale-set-controller/values.yaml +++ b/charts/gha-runner-scale-set-controller/values.yaml @@ -72,6 +72,10 @@ tolerations: [] affinity: {} +# Mount volumes in the container. +volumes: [] +volumeMounts: [] + # Leverage a PriorityClass to ensure your pods survive resource shortages # ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ # PriorityClass: system-cluster-critical From e06c7edc213b098729e49343b4c085615bf46671 Mon Sep 17 00:00:00 2001 From: Ivar Larsson Date: Mon, 26 Feb 2024 09:51:07 -0500 Subject: [PATCH 10/13] Refer to the correct variable in discovery error message (#3296) --- charts/gha-runner-scale-set/templates/_helpers.tpl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/charts/gha-runner-scale-set/templates/_helpers.tpl b/charts/gha-runner-scale-set/templates/_helpers.tpl index 764d923f..2bf24139 100644 --- a/charts/gha-runner-scale-set/templates/_helpers.tpl +++ b/charts/gha-runner-scale-set/templates/_helpers.tpl @@ -526,13 +526,13 @@ volumeMounts: {{- end }} {{- end }} {{- if and (eq $multiNamespacesCounter 0) (eq $singleNamespaceCounter 0) }} - {{- fail "No gha-rs-controller deployment found using label (app.kubernetes.io/part-of=gha-rs-controller). Consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- fail "No gha-rs-controller deployment found using label (app.kubernetes.io/part-of=gha-rs-controller). Consider setting controllerServiceAccount.namespace in values.yaml to be explicit if you think the discovery is wrong." }} {{- end }} {{- if and (gt $multiNamespacesCounter 0) (gt $singleNamespaceCounter 0) }} - {{- fail "Found both gha-rs-controller installed with flags.watchSingleNamespace set and unset in cluster, this is not supported. Consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- fail "Found both gha-rs-controller installed with flags.watchSingleNamespace set and unset in cluster, this is not supported. Consider setting controllerServiceAccount.namespace in values.yaml to be explicit if you think the discovery is wrong." }} {{- end }} {{- if gt $multiNamespacesCounter 1 }} - {{- fail "More than one gha-rs-controller deployment found using label (app.kubernetes.io/part-of=gha-rs-controller). Consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- fail "More than one gha-rs-controller deployment found using label (app.kubernetes.io/part-of=gha-rs-controller). Consider setting controllerServiceAccount.namespace in values.yaml to be explicit if you think the discovery is wrong." }} {{- end }} {{- if eq $multiNamespacesCounter 1 }} {{- with $controllerDeployment.metadata }} @@ -545,11 +545,11 @@ volumeMounts: {{- $managerServiceAccountNamespace = (get $controllerDeployment.metadata.labels "actions.github.com/controller-service-account-namespace") }} {{- end }} {{- else }} - {{- fail "No gha-rs-controller deployment that watch this namespace found using label (actions.github.com/controller-watch-single-namespace). Consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- fail "No gha-rs-controller deployment that watch this namespace found using label (actions.github.com/controller-watch-single-namespace). Consider setting controllerServiceAccount.namespace in values.yaml to be explicit if you think the discovery is wrong." }} {{- end }} {{- end }} {{- if eq $managerServiceAccountNamespace "" }} - {{- fail "No service account namespace found for gha-rs-controller deployment using label (actions.github.com/controller-service-account-namespace), consider setting controllerServiceAccount.name in values.yaml to be explicit if you think the discovery is wrong." }} + {{- fail "No service account namespace found for gha-rs-controller deployment using label (actions.github.com/controller-service-account-namespace), consider setting controllerServiceAccount.namespace in values.yaml to be explicit if you think the discovery is wrong." }} {{- end }} {{- $managerServiceAccountNamespace }} {{- end }} From 7da2d7f96ab342b8049c2f35b042f7b43c8cbf98 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Tue, 27 Feb 2024 17:37:42 +0100 Subject: [PATCH 11/13] Fix acquire jobs after session refresh ghalistener (#3307) --- cmd/ghalistener/listener/listener.go | 8 ++++---- cmd/ghalistener/listener/listener_test.go | 22 ++++++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/cmd/ghalistener/listener/listener.go b/cmd/ghalistener/listener/listener.go index 1ec8cb9c..37aeac0b 100644 --- a/cmd/ghalistener/listener/listener.go +++ b/cmd/ghalistener/listener/listener.go @@ -384,9 +384,9 @@ func (l *Listener) acquireAvailableJobs(ctx context.Context, jobsAvailable []*ac l.logger.Info("Acquiring jobs", "count", len(ids), "requestIds", fmt.Sprint(ids)) - ids, err := l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, ids) + idsAcquired, err := l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, ids) if err == nil { // if NO errors - return ids, nil + return idsAcquired, nil } expiredError := &actions.MessageQueueTokenExpiredError{} @@ -398,12 +398,12 @@ func (l *Listener) acquireAvailableJobs(ctx context.Context, jobsAvailable []*ac return nil, err } - ids, err = l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, ids) + idsAcquired, err = l.client.AcquireJobs(ctx, l.scaleSetID, l.session.MessageQueueAccessToken, ids) if err != nil { return nil, fmt.Errorf("failed to acquire jobs after session refresh: %w", err) } - return ids, nil + return idsAcquired, nil } func (l *Listener) refreshSession(ctx context.Context) error { diff --git a/cmd/ghalistener/listener/listener_test.go b/cmd/ghalistener/listener/listener_test.go index ff5208de..28e13ebf 100644 --- a/cmd/ghalistener/listener/listener_test.go +++ b/cmd/ghalistener/listener/listener_test.go @@ -628,9 +628,6 @@ func TestListener_acquireAvailableJobs(t *testing.T) { } client.On("RefreshMessageSession", ctx, mock.Anything, mock.Anything).Return(session, nil).Once() - // First call to AcquireJobs will fail with a token expired error - client.On("AcquireJobs", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, &actions.MessageQueueTokenExpiredError{}).Once() - // Second call to AcquireJobs will succeed want := []int64{1, 2, 3} availableJobs := []*actions.JobAvailable{ @@ -650,7 +647,24 @@ func TestListener_acquireAvailableJobs(t *testing.T) { }, }, } - client.On("AcquireJobs", ctx, mock.Anything, mock.Anything, mock.Anything).Return(want, nil).Once() + + // First call to AcquireJobs will fail with a token expired error + client.On("AcquireJobs", ctx, mock.Anything, mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + ids := args.Get(3).([]int64) + assert.Equal(t, want, ids) + }). + Return(nil, &actions.MessageQueueTokenExpiredError{}). + Once() + + // First call to AcquireJobs will fail with a token expired error + client.On("AcquireJobs", ctx, mock.Anything, mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + ids := args.Get(3).([]int64) + assert.Equal(t, want, ids) + }). + Return(want, nil). + Once() config.Client = client From 309b53143e55d4ff7b1777561c20a70bc09c8da1 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Wed, 28 Feb 2024 10:26:32 +0100 Subject: [PATCH 12/13] Prepare 0.8.3 release (#3309) --- .github/workflows/gha-e2e-tests.yaml | 2 +- charts/gha-runner-scale-set-controller/Chart.yaml | 4 ++-- charts/gha-runner-scale-set/Chart.yaml | 4 ++-- docs/gha-runner-scale-set-controller/README.md | 5 +++++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gha-e2e-tests.yaml b/.github/workflows/gha-e2e-tests.yaml index a241f0ad..f7161e6e 100644 --- a/.github/workflows/gha-e2e-tests.yaml +++ b/.github/workflows/gha-e2e-tests.yaml @@ -16,7 +16,7 @@ env: TARGET_ORG: actions-runner-controller TARGET_REPO: arc_e2e_test_dummy IMAGE_NAME: "arc-test-image" - IMAGE_VERSION: "0.8.2" + IMAGE_VERSION: "0.8.3" concurrency: # This will make sure we only apply the concurrency limits on pull requests diff --git a/charts/gha-runner-scale-set-controller/Chart.yaml b/charts/gha-runner-scale-set-controller/Chart.yaml index dd08c48c..26640b86 100644 --- a/charts/gha-runner-scale-set-controller/Chart.yaml +++ b/charts/gha-runner-scale-set-controller/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.8.2 +version: 0.8.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.8.2" +appVersion: "0.8.3" home: https://github.com/actions/actions-runner-controller diff --git a/charts/gha-runner-scale-set/Chart.yaml b/charts/gha-runner-scale-set/Chart.yaml index 8ab56e30..490bc484 100644 --- a/charts/gha-runner-scale-set/Chart.yaml +++ b/charts/gha-runner-scale-set/Chart.yaml @@ -15,13 +15,13 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.8.2 +version: 0.8.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.8.2" +appVersion: "0.8.3" home: https://github.com/actions/actions-runner-controller diff --git a/docs/gha-runner-scale-set-controller/README.md b/docs/gha-runner-scale-set-controller/README.md index 90a6009a..65a51f99 100644 --- a/docs/gha-runner-scale-set-controller/README.md +++ b/docs/gha-runner-scale-set-controller/README.md @@ -43,6 +43,11 @@ You can follow [this troubleshooting guide](https://docs.github.com/en/actions/h ## Changelog +### v0.8.3 +1. Expose volumeMounts and volumes in gha-runner-scale-set-controller [#3260](https://github.com/actions/actions-runner-controller/pull/3260) +1. Refer to the correct variable in discovery error message [#3296](https://github.com/actions/actions-runner-controller/pull/3296) +1. Fix acquire jobs after session refresh ghalistener [#3307](https://github.com/actions/actions-runner-controller/pull/3307) + ### v0.8.2 1. Add listener graceful termination period and background context after the message is received [#3187](https://github.com/actions/actions-runner-controller/pull/3187) 1. Publish metrics in the new ghalistener [#3193](https://github.com/actions/actions-runner-controller/pull/3193) From 753afb75b9dfd3ba7a402630d94a06491fcfcfdf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:43:14 -0500 Subject: [PATCH 13/13] Updates: runner to v2.314.1 (#3308) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Tingluo Huang --- Makefile | 2 +- runner/Makefile | 2 +- runner/VERSION | 2 +- test/e2e/e2e_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c505a95f..a945b48c 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ endif DOCKER_USER ?= $(shell echo ${DOCKER_IMAGE_NAME} | cut -d / -f1) VERSION ?= dev COMMIT_SHA = $(shell git rev-parse HEAD) -RUNNER_VERSION ?= 2.313.0 +RUNNER_VERSION ?= 2.314.1 TARGETPLATFORM ?= $(shell arch) RUNNER_NAME ?= ${DOCKER_USER}/actions-runner RUNNER_TAG ?= ${VERSION} diff --git a/runner/Makefile b/runner/Makefile index f0510517..d95a07e2 100644 --- a/runner/Makefile +++ b/runner/Makefile @@ -6,7 +6,7 @@ DIND_ROOTLESS_RUNNER_NAME ?= ${DOCKER_USER}/actions-runner-dind-rootless OS_IMAGE ?= ubuntu-22.04 TARGETPLATFORM ?= $(shell arch) -RUNNER_VERSION ?= 2.313.0 +RUNNER_VERSION ?= 2.314.1 RUNNER_CONTAINER_HOOKS_VERSION ?= 0.5.1 DOCKER_VERSION ?= 24.0.7 diff --git a/runner/VERSION b/runner/VERSION index 2d34443a..a830255c 100644 --- a/runner/VERSION +++ b/runner/VERSION @@ -1,2 +1,2 @@ -RUNNER_VERSION=2.313.0 +RUNNER_VERSION=2.314.1 RUNNER_CONTAINER_HOOKS_VERSION=0.5.1 \ No newline at end of file diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index ca2df6b7..64257b1d 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -36,7 +36,7 @@ var ( testResultCMNamePrefix = "test-result-" - RunnerVersion = "2.313.0" + RunnerVersion = "2.314.1" RunnerContainerHooksVersion = "0.5.1" )