diff --git a/cmd/master/main.go b/cmd/master/main.go index 34e0de7..68d0287 100644 --- a/cmd/master/main.go +++ b/cmd/master/main.go @@ -76,7 +76,7 @@ func run() error { } auth := webauth.New(provider, st, cfg.AllowedGroup, strings.HasPrefix(cfg.PublicURL, "https://"), logger) - ui, err := web.New(st, agentHub, broker, web.Config{ + ui, err := web.New(st, agentHub, nil, broker, web.Config{ // jobs is nil: wired in W3-4 AgentURL: cfg.AgentURL, MasterKey: fingerprint, AgentImage: cfg.AgentImage, }, logger) if err != nil { diff --git a/internal/master/web/server.go b/internal/master/web/server.go index bf81f1d..a6433ea 100644 --- a/internal/master/web/server.go +++ b/internal/master/web/server.go @@ -28,6 +28,9 @@ var agentNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}$`) // Store is the storage the UI reads and writes. *store.Store satisfies it. type Store interface { ListServices(ctx context.Context) ([]model.Service, error) + GetService(ctx context.Context, id int64) (model.Service, error) + GetJob(ctx context.Context, id int64) (model.Job, error) + ListJobs(ctx context.Context, f model.JobFilter) ([]model.Job, error) ListAgents(ctx context.Context) ([]model.Agent, error) CreateAgent(ctx context.Context, name, tokenHash string) (model.Agent, error) RevokeAgent(ctx context.Context, id int64, at time.Time) error @@ -39,6 +42,13 @@ type Hub interface { Disconnect(agentID int64) } +// Jobs starts updates and explains why a service cannot be updated. +// *jobs.Service satisfies it. +type Jobs interface { + Updatable(svc model.Service) error + Trigger(ctx context.Context, serviceID int64, requestedBy string) (model.Job, error) +} + // Broker delivers change signals to open streams and accepts signals from // UI actions. *events.Broker satisfies it. type Broker interface { @@ -57,6 +67,7 @@ type Config struct { type Server struct { store Store hub Hub + jobs Jobs broker Broker cfg Config log *slog.Logger @@ -66,7 +77,7 @@ type Server struct { // New parses the templates. Each page is its own template set so every page // can define its own "content" block. -func New(store Store, hub Hub, broker Broker, cfg Config, log *slog.Logger) (*Server, error) { +func New(store Store, hub Hub, jobs Jobs, broker Broker, cfg Config, log *slog.Logger) (*Server, error) { funcs := template.FuncMap{"shortRef": shortRef} pages := map[string]*template.Template{} for _, page := range []string{"overview.html", "agents.html", "agent_created.html"} { @@ -76,7 +87,7 @@ func New(store Store, hub Hub, broker Broker, cfg Config, log *slog.Logger) (*Se } pages[page] = t } - return &Server{store: store, hub: hub, broker: broker, cfg: cfg, log: log, now: time.Now, pages: pages}, nil + return &Server{store: store, hub: hub, jobs: jobs, broker: broker, cfg: cfg, log: log, now: time.Now, pages: pages}, nil } // Register adds the UI routes to mux. protect wraps every route except diff --git a/internal/master/web/server_test.go b/internal/master/web/server_test.go index 8cec09d..d2f5494 100644 --- a/internal/master/web/server_test.go +++ b/internal/master/web/server_test.go @@ -34,7 +34,14 @@ type fakeStore struct { } func (f *fakeStore) ListServices(context.Context) ([]model.Service, error) { return f.services, nil } -func (f *fakeStore) ListAgents(context.Context) ([]model.Agent, error) { return f.agents, nil } +func (f *fakeStore) GetService(context.Context, int64) (model.Service, error) { + return model.Service{}, model.ErrNotFound +} +func (f *fakeStore) GetJob(context.Context, int64) (model.Job, error) { + return model.Job{}, model.ErrNotFound +} +func (f *fakeStore) ListJobs(context.Context, model.JobFilter) ([]model.Job, error) { return nil, nil } +func (f *fakeStore) ListAgents(context.Context) ([]model.Agent, error) { return f.agents, nil } func (f *fakeStore) CreateAgent(_ context.Context, name, hash string) (model.Agent, error) { f.mu.Lock() @@ -65,6 +72,13 @@ func (f *fakeStore) revoked() int64 { return f.revokedID } +type fakeJobs struct{} + +func (fakeJobs) Updatable(model.Service) error { return nil } +func (fakeJobs) Trigger(context.Context, int64, string) (model.Job, error) { + return model.Job{}, model.ErrNotFound +} + type fakeHub struct { mu sync.Mutex online map[int64]bool @@ -110,7 +124,7 @@ func newTestServer(t *testing.T) (*httptest.Server, *fakeStore, *fakeHub, *event } hub := &fakeHub{online: map[int64]bool{2: true}} broker := events.NewBroker() - s, err := New(store, hub, broker, Config{ + s, err := New(store, hub, fakeJobs{}, broker, Config{ AgentURL: "wss://10.0.0.5:8090/agent", MasterKey: "sha256:abc", AgentImage: "gitea.shcizo.se/shcizo/package-updater:latest", }, slog.New(slog.NewTextHandler(io.Discard, nil))) require.NoError(t, err)