Skip to content

Commit 7bcdada

Browse files
authored
feat(api,ui): websocket server + client for admin (#5128)
1 parent 1e25af6 commit 7bcdada

24 files changed

+1169
-58
lines changed

engine/api/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ type API struct {
249249
StartupTime time.Time
250250
Maintenance bool
251251
eventsBroker *eventsBroker
252+
websocketBroker *websocketBroker
252253
Cache cache.Store
253254
Metrics struct {
254255
WorkflowRunFailed *stats.Int64Measure

engine/api/api_routes.go

+12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ func (api *API) InitRouter() {
4040
}
4141
api.eventsBroker.Init(r.Background, api.PanicDump())
4242

43+
api.websocketBroker = &websocketBroker{
44+
router: api.Router,
45+
cache: api.Cache,
46+
dbFunc: api.mustDB,
47+
clients: make(map[string]*websocketClient),
48+
messages: make(chan sdk.Event),
49+
chanAddClient: make(chan *websocketClient),
50+
chanRemoveClient: make(chan string),
51+
}
52+
api.websocketBroker.Init(r.Background, api.PanicDump())
53+
4354
// Auth
4455
r.Handle("/auth/driver", ScopeNone(), r.GET(api.getAuthDriversHandler, Auth(false)))
4556
r.Handle("/auth/me", Scope(sdk.AuthConsumerScopeAction), r.GET(api.getAuthMe))
@@ -405,6 +416,7 @@ func (api *API) InitRouter() {
405416

406417
// SSE
407418
r.Handle("/events", ScopeNone(), r.GET(api.eventsBroker.ServeHTTP))
419+
r.Handle("/ws", ScopeNone(), r.GET(api.websocketBroker.ServeHTTP))
408420

409421
// Feature
410422
r.Handle("/feature/clean", ScopeNone(), r.POST(api.cleanFeatureHandler, NeedToken("X-Izanami-Token", api.Config.Features.Izanami.Token)))

engine/api/event/elasticsearch.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func PushInElasticSearch(ctx context.Context, db gorp.SqlExecutor, store cache.S
3737
}
3838

3939
switch e.EventType {
40-
case "sdk.EventJob", "sdk.EventEngine":
40+
case "sdk.EventEngine":
4141
continue
4242
}
4343
e.Payload = nil

engine/api/event/publish_workflow_run.go

+69-22
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,27 @@ import (
66
"fmt"
77
"time"
88

9-
"github.com/go-gorp/gorp"
10-
119
"github.com/ovh/cds/sdk"
1210
"github.com/ovh/cds/sdk/log"
1311
)
1412

15-
func publishRunWorkflow(ctx context.Context, payload interface{}, key, workflowName, appName, pipName, envName string, num int64, sub int64, status string, tags []sdk.WorkflowRunTag, eventIntegrations []sdk.ProjectIntegration) {
16-
eventIntegrationsID := make([]int64, len(eventIntegrations))
17-
for i, eventIntegration := range eventIntegrations {
13+
type publishWorkflowRunData struct {
14+
projectKey string
15+
workflowName string
16+
applicationName string
17+
pipelineName string
18+
environmentName string
19+
workflowRunNum int64
20+
workflowRunSubNum int64
21+
status string
22+
workflowRunTags []sdk.WorkflowRunTag
23+
eventIntegrations []sdk.ProjectIntegration
24+
workflowNodeRunID int64
25+
}
26+
27+
func publishRunWorkflow(ctx context.Context, payload interface{}, data publishWorkflowRunData) {
28+
eventIntegrationsID := make([]int64, len(data.eventIntegrations))
29+
for i, eventIntegration := range data.eventIntegrations {
1830
eventIntegrationsID[i] = eventIntegration.ID
1931
}
2032

@@ -25,18 +37,18 @@ func publishRunWorkflow(ctx context.Context, payload interface{}, key, workflowN
2537
CDSName: cdsname,
2638
EventType: fmt.Sprintf("%T", payload),
2739
Payload: bts,
28-
ProjectKey: key,
29-
ApplicationName: appName,
30-
PipelineName: pipName,
31-
WorkflowName: workflowName,
32-
EnvironmentName: envName,
33-
WorkflowRunNum: num,
34-
WorkflowRunNumSub: sub,
35-
Status: status,
36-
Tags: tags,
40+
ProjectKey: data.projectKey,
41+
ApplicationName: data.applicationName,
42+
PipelineName: data.pipelineName,
43+
WorkflowName: data.workflowName,
44+
EnvironmentName: data.environmentName,
45+
WorkflowRunNum: data.workflowRunNum,
46+
WorkflowRunNumSub: data.workflowRunSubNum,
47+
Status: data.status,
48+
Tags: data.workflowRunTags,
3749
EventIntegrationsID: eventIntegrationsID,
3850
}
39-
publishEvent(ctx, event)
51+
_ = publishEvent(ctx, event)
4052
}
4153

4254
// PublishWorkflowRun publish event on a workflow run
@@ -51,7 +63,16 @@ func PublishWorkflowRun(ctx context.Context, wr sdk.WorkflowRun, projectKey stri
5163
LastModifiedNano: wr.LastModified.UnixNano(),
5264
Tags: wr.Tags,
5365
}
54-
publishRunWorkflow(ctx, e, projectKey, wr.Workflow.Name, "", "", "", wr.Number, wr.LastSubNumber, wr.Status, wr.Tags, wr.Workflow.EventIntegrations)
66+
data := publishWorkflowRunData{
67+
projectKey: projectKey,
68+
workflowName: wr.Workflow.Name,
69+
workflowRunNum: wr.Number,
70+
workflowRunSubNum: wr.LastSubNumber,
71+
status: wr.Status,
72+
workflowRunTags: wr.Tags,
73+
eventIntegrations: wr.Workflow.EventIntegrations,
74+
}
75+
publishRunWorkflow(ctx, e, data)
5576
}
5677

5778
// PublishWorkflowNodeRun publish event on a workflow node run
@@ -157,19 +178,45 @@ func PublishWorkflowNodeRun(ctx context.Context, nr sdk.WorkflowNodeRun, w sdk.W
157178
if sdk.StatusIsTerminated(nr.Status) {
158179
e.Done = nr.Done.Unix()
159180
}
160-
publishRunWorkflow(ctx, e, w.ProjectKey, w.Name, appName, pipName, envName, nr.Number, nr.SubNumber, nr.Status, nil, w.EventIntegrations)
181+
data := publishWorkflowRunData{
182+
projectKey: w.ProjectKey,
183+
workflowName: w.Name,
184+
applicationName: appName,
185+
pipelineName: pipName,
186+
environmentName: envName,
187+
workflowRunNum: nr.Number,
188+
workflowRunSubNum: nr.SubNumber,
189+
status: nr.Status,
190+
eventIntegrations: w.EventIntegrations,
191+
workflowNodeRunID: nr.ID,
192+
}
193+
publishRunWorkflow(ctx, e, data)
161194
}
162195

163196
// PublishWorkflowNodeJobRun publish a WorkflowNodeJobRun
164-
func PublishWorkflowNodeJobRun(ctx context.Context, db gorp.SqlExecutor, pkey string, wr sdk.WorkflowRun, jr sdk.WorkflowNodeJobRun) {
197+
func PublishWorkflowNodeJobRun(ctx context.Context, pkey string, wr sdk.WorkflowRun, jr sdk.WorkflowNodeJobRun) {
165198
e := sdk.EventRunWorkflowJob{
166-
ID: jr.ID,
167-
Status: jr.Status,
168-
Start: jr.Start.Unix(),
199+
ID: jr.ID,
200+
Status: jr.Status,
201+
Start: jr.Start.Unix(),
202+
Requirements: jr.Job.Action.Requirements,
203+
WorkerName: jr.Job.WorkerName,
204+
BookByName: jr.BookedBy.Name,
205+
Parameters: jr.Parameters,
169206
}
170207

171208
if sdk.StatusIsTerminated(jr.Status) {
172209
e.Done = jr.Done.Unix()
173210
}
174-
publishRunWorkflow(ctx, e, pkey, wr.Workflow.Name, "", "", "", 0, 0, jr.Status, nil, wr.Workflow.EventIntegrations)
211+
data := publishWorkflowRunData{
212+
projectKey: pkey,
213+
workflowName: wr.Workflow.Name,
214+
workflowRunNum: wr.Number,
215+
workflowRunSubNum: wr.LastSubNumber,
216+
status: jr.Status,
217+
workflowRunTags: wr.Tags,
218+
eventIntegrations: wr.Workflow.EventIntegrations,
219+
workflowNodeRunID: jr.WorkflowNodeRunID,
220+
}
221+
publishRunWorkflow(ctx, e, data)
175222
}

engine/api/events.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,6 @@ func (b *eventsBroker) cacheSubscribe(c context.Context, cacheMsgChan chan<- sdk
9494
continue
9595
}
9696

97-
switch e.EventType {
98-
case "sdk.EventJob":
99-
continue
100-
}
10197
observability.Record(b.router.Background, SSEEvents, 1)
10298
cacheMsgChan <- e
10399
}
@@ -239,7 +235,7 @@ func (client *eventsBrokerSubscribe) manageEvent(db gorp.SqlExecutor, event sdk.
239235
var isHatcheryWithGroups = isHatchery && len(client.consumer.GroupIDs) > 0
240236

241237
switch {
242-
case strings.HasPrefix(event.EventType, "sdk.EventProject") || strings.HasPrefix(event.EventType, "sdk.EventAsCodeEvent"):
238+
case strings.HasPrefix(event.EventType, "sdk.EventProject") || strings.HasPrefix(event.EventType, "sdk.EventAsCodeEvent"):
243239
if client.consumer.Maintainer() && !isHatcheryWithGroups {
244240
return true, nil
245241
}

engine/api/router.go

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ var (
4141
Hits *stats.Int64Measure
4242
SSEClients *stats.Int64Measure
4343
SSEEvents *stats.Int64Measure
44+
WebSocketClients *stats.Int64Measure
45+
WebSocketEvents *stats.Int64Measure
4446
ServerRequestCount *stats.Int64Measure
4547
ServerRequestBytes *stats.Int64Measure
4648
ServerResponseBytes *stats.Int64Measure

engine/api/router_metrics.go

+10
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ func InitRouterMetrics(s service.NamedService) error {
4949
"cds/sse_events",
5050
"number of sse events",
5151
stats.UnitDimensionless)
52+
WebSocketClients = stats.Int64(
53+
"cds/websocket_clients",
54+
"number of websocket clients",
55+
stats.UnitDimensionless)
56+
WebSocketEvents = stats.Int64(
57+
"cds/websocket_events",
58+
"number of websocket events",
59+
stats.UnitDimensionless)
5260
ServerRequestCount = stats.Int64(
5361
"cds/http/server/request_count",
5462
"Number of HTTP requests started",
@@ -122,6 +130,8 @@ func InitRouterMetrics(s service.NamedService) error {
122130
observability.NewViewCount("cds/http/router/router_hits", Hits, []tag.Key{tagServiceType, tagServiceName}),
123131
observability.NewViewLast("cds/http/router/sse_clients", SSEClients, []tag.Key{tagServiceType, tagServiceName}),
124132
observability.NewViewCount("cds/http/router/sse_events", SSEEvents, []tag.Key{tagServiceType, tagServiceName}),
133+
observability.NewViewLast("cds/http/router/websocket_clients", WebSocketClients, []tag.Key{tagServiceType, tagServiceName}),
134+
observability.NewViewCount("cds/http/router/websocket_events", WebSocketEvents, []tag.Key{tagServiceType, tagServiceName}),
125135
ServerRequestCountView,
126136
ServerRequestBytesView,
127137
ServerResponseBytesView,

0 commit comments

Comments
 (0)