Sync branch magefile with main #104308
@ -175,7 +175,7 @@ func runFlamencoManager() bool {
|
|||||||
shamanServer := buildShamanServer(configService, isFirstRun)
|
shamanServer := buildShamanServer(configService, isFirstRun)
|
||||||
jobDeleter := job_deleter.NewService(persist, localStorage, eventBroker, shamanServer)
|
jobDeleter := job_deleter.NewService(persist, localStorage, eventBroker, shamanServer)
|
||||||
|
|
||||||
farmStatus := farmstatus.NewService(persist)
|
farmStatus := farmstatus.NewService(persist, eventBroker)
|
||||||
|
|
||||||
flamenco := api_impl.NewFlamenco(
|
flamenco := api_impl.NewFlamenco(
|
||||||
compiler, persist, eventBroker, logStorage, configService,
|
compiler, persist, eventBroker, logStorage, configService,
|
||||||
|
17
internal/manager/eventbus/events_farmstatus.go
Normal file
17
internal/manager/eventbus/events_farmstatus.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package eventbus
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"projects.blender.org/studio/flamenco/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewFarmStatusEvent(farmstatus api.FarmStatusReport) api.EventFarmStatus {
|
||||||
|
return api.EventFarmStatus(farmstatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Broker) BroadcastFarmStatusEvent(event api.EventFarmStatus) {
|
||||||
|
log.Debug().Interface("event", event).Msg("eventbus: broadcasting FarmStatus event")
|
||||||
|
b.broadcast(TopicFarmStatus, event)
|
||||||
|
}
|
@ -7,6 +7,7 @@ import "fmt"
|
|||||||
const (
|
const (
|
||||||
// Topics on which events are published.
|
// Topics on which events are published.
|
||||||
TopicLifeCycle EventTopic = "/lifecycle" // sends api.EventLifeCycle
|
TopicLifeCycle EventTopic = "/lifecycle" // sends api.EventLifeCycle
|
||||||
|
TopicFarmStatus EventTopic = "/status" // sends api.EventFarmStatus
|
||||||
TopicJobUpdate EventTopic = "/jobs" // sends api.EventJobUpdate
|
TopicJobUpdate EventTopic = "/jobs" // sends api.EventJobUpdate
|
||||||
TopicLastRenderedImage EventTopic = "/last-rendered" // sends api.EventLastRenderedUpdate
|
TopicLastRenderedImage EventTopic = "/last-rendered" // sends api.EventLastRenderedUpdate
|
||||||
TopicTaskUpdate EventTopic = "/task" // sends api.EventTaskUpdate
|
TopicTaskUpdate EventTopic = "/task" // sends api.EventTaskUpdate
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"projects.blender.org/studio/flamenco/internal/manager/eventbus"
|
||||||
"projects.blender.org/studio/flamenco/pkg/api"
|
"projects.blender.org/studio/flamenco/pkg/api"
|
||||||
"projects.blender.org/studio/flamenco/pkg/website"
|
"projects.blender.org/studio/flamenco/pkg/website"
|
||||||
)
|
)
|
||||||
@ -25,14 +26,16 @@ const (
|
|||||||
// Service keeps track of the overall farm status.
|
// Service keeps track of the overall farm status.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
persist PersistenceService
|
persist PersistenceService
|
||||||
|
eventbus EventBus
|
||||||
|
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
lastReport api.FarmStatusReport
|
lastReport api.FarmStatusReport
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(persist PersistenceService) *Service {
|
func NewService(persist PersistenceService, eventbus EventBus) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
persist: persist,
|
persist: persist,
|
||||||
|
eventbus: eventbus,
|
||||||
mutex: sync.Mutex{},
|
mutex: sync.Mutex{},
|
||||||
lastReport: api.FarmStatusReport{
|
lastReport: api.FarmStatusReport{
|
||||||
Status: api.FarmStatusStarting,
|
Status: api.FarmStatusStarting,
|
||||||
@ -64,6 +67,18 @@ func (s *Service) Report() api.FarmStatusReport {
|
|||||||
return s.lastReport
|
return s.lastReport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateStatusReport updates the last status report in a thread-safe way.
|
||||||
|
// It returns whether the report changed.
|
||||||
|
func (s *Service) updateStatusReport(report api.FarmStatusReport) bool {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
reportChanged := s.lastReport != report
|
||||||
|
s.lastReport = report
|
||||||
|
|
||||||
|
return reportChanged
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) poll(ctx context.Context) {
|
func (s *Service) poll(ctx context.Context) {
|
||||||
report := s.checkFarmStatus(ctx)
|
report := s.checkFarmStatus(ctx)
|
||||||
if report == nil {
|
if report == nil {
|
||||||
@ -71,9 +86,11 @@ func (s *Service) poll(ctx context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mutex.Lock()
|
reportChanged := s.updateStatusReport(*report)
|
||||||
s.lastReport = *report
|
if reportChanged {
|
||||||
s.mutex.Unlock()
|
event := eventbus.NewFarmStatusEvent(s.lastReport)
|
||||||
|
s.eventbus.BroadcastFarmStatusEvent(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkFarmStatus checks the farm status by querying the peristence layer.
|
// checkFarmStatus checks the farm status by querying the peristence layer.
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
type Fixtures struct {
|
type Fixtures struct {
|
||||||
service *Service
|
service *Service
|
||||||
persist *mocks.MockPersistenceService
|
persist *mocks.MockPersistenceService
|
||||||
|
eventbus *mocks.MockEventBus
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +159,29 @@ func TestCheckFarmStatusAsleep(t *testing.T) {
|
|||||||
assert.Equal(t, api.FarmStatusAsleep, report.Status)
|
assert.Equal(t, api.FarmStatusAsleep, report.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFarmStatusEvent(t *testing.T) {
|
||||||
|
f := fixtures(t)
|
||||||
|
|
||||||
|
// "inoperative": no workers.
|
||||||
|
f.mockWorkerStatuses(persistence.WorkerStatusCount{})
|
||||||
|
f.eventbus.EXPECT().BroadcastFarmStatusEvent(api.EventFarmStatus{
|
||||||
|
Status: api.FarmStatusInoperative,
|
||||||
|
})
|
||||||
|
f.service.poll(f.ctx)
|
||||||
|
|
||||||
|
// Re-polling should not trigger any event, as the status doesn't change.
|
||||||
|
f.mockWorkerStatuses(persistence.WorkerStatusCount{})
|
||||||
|
f.service.poll(f.ctx)
|
||||||
|
|
||||||
|
// "active": Actively working on jobs.
|
||||||
|
f.mockWorkerStatuses(persistence.WorkerStatusCount{api.WorkerStatusAwake: 3})
|
||||||
|
f.mockJobStatuses(persistence.JobStatusCount{api.JobStatusActive: 1})
|
||||||
|
f.eventbus.EXPECT().BroadcastFarmStatusEvent(api.EventFarmStatus{
|
||||||
|
Status: api.FarmStatusActive,
|
||||||
|
})
|
||||||
|
f.service.poll(f.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func Test_allIn(t *testing.T) {
|
func Test_allIn(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
statuses map[api.WorkerStatus]int
|
statuses map[api.WorkerStatus]int
|
||||||
@ -196,10 +220,11 @@ func fixtures(t *testing.T) *Fixtures {
|
|||||||
|
|
||||||
f := Fixtures{
|
f := Fixtures{
|
||||||
persist: mocks.NewMockPersistenceService(mockCtrl),
|
persist: mocks.NewMockPersistenceService(mockCtrl),
|
||||||
|
eventbus: mocks.NewMockEventBus(mockCtrl),
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
}
|
}
|
||||||
|
|
||||||
f.service = NewService(f.persist)
|
f.service = NewService(f.persist, f.eventbus)
|
||||||
|
|
||||||
return &f
|
return &f
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,13 @@ package farmstatus
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"projects.blender.org/studio/flamenco/internal/manager/eventbus"
|
||||||
"projects.blender.org/studio/flamenco/internal/manager/persistence"
|
"projects.blender.org/studio/flamenco/internal/manager/persistence"
|
||||||
|
"projects.blender.org/studio/flamenco/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generate mock implementations of these interfaces.
|
// Generate mock implementations of these interfaces.
|
||||||
//go:generate go run github.com/golang/mock/mockgen -destination mocks/interfaces_mock.gen.go -package mocks projects.blender.org/studio/flamenco/internal/manager/farmstatus PersistenceService
|
//go:generate go run github.com/golang/mock/mockgen -destination mocks/interfaces_mock.gen.go -package mocks projects.blender.org/studio/flamenco/internal/manager/farmstatus PersistenceService,EventBus
|
||||||
|
|
||||||
type PersistenceService interface {
|
type PersistenceService interface {
|
||||||
SummarizeJobStatuses(ctx context.Context) (persistence.JobStatusCount, error)
|
SummarizeJobStatuses(ctx context.Context) (persistence.JobStatusCount, error)
|
||||||
@ -15,3 +17,9 @@ type PersistenceService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _ PersistenceService = (*persistence.DB)(nil)
|
var _ PersistenceService = (*persistence.DB)(nil)
|
||||||
|
|
||||||
|
type EventBus interface {
|
||||||
|
BroadcastFarmStatusEvent(event api.EventFarmStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ EventBus = (*eventbus.Broker)(nil)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
// Source: projects.blender.org/studio/flamenco/internal/manager/farmstatus (interfaces: PersistenceService)
|
// Source: projects.blender.org/studio/flamenco/internal/manager/farmstatus (interfaces: PersistenceService,EventBus)
|
||||||
|
|
||||||
// Package mocks is a generated GoMock package.
|
// Package mocks is a generated GoMock package.
|
||||||
package mocks
|
package mocks
|
||||||
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
persistence "projects.blender.org/studio/flamenco/internal/manager/persistence"
|
persistence "projects.blender.org/studio/flamenco/internal/manager/persistence"
|
||||||
|
api "projects.blender.org/studio/flamenco/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockPersistenceService is a mock of PersistenceService interface.
|
// MockPersistenceService is a mock of PersistenceService interface.
|
||||||
@ -64,3 +65,38 @@ func (mr *MockPersistenceServiceMockRecorder) SummarizeWorkerStatuses(arg0 inter
|
|||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SummarizeWorkerStatuses", reflect.TypeOf((*MockPersistenceService)(nil).SummarizeWorkerStatuses), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SummarizeWorkerStatuses", reflect.TypeOf((*MockPersistenceService)(nil).SummarizeWorkerStatuses), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MockEventBus is a mock of EventBus interface.
|
||||||
|
type MockEventBus struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockEventBusMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockEventBusMockRecorder is the mock recorder for MockEventBus.
|
||||||
|
type MockEventBusMockRecorder struct {
|
||||||
|
mock *MockEventBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockEventBus creates a new mock instance.
|
||||||
|
func NewMockEventBus(ctrl *gomock.Controller) *MockEventBus {
|
||||||
|
mock := &MockEventBus{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockEventBusMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockEventBus) EXPECT() *MockEventBusMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// BroadcastFarmStatusEvent mocks base method.
|
||||||
|
func (m *MockEventBus) BroadcastFarmStatusEvent(arg0 api.EventFarmStatus) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "BroadcastFarmStatusEvent", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BroadcastFarmStatusEvent indicates an expected call of BroadcastFarmStatusEvent.
|
||||||
|
func (mr *MockEventBusMockRecorder) BroadcastFarmStatusEvent(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BroadcastFarmStatusEvent", reflect.TypeOf((*MockEventBus)(nil).BroadcastFarmStatusEvent), arg0)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user