Skip to content

Commit 129aad6

Browse files
authored
Merge pull request #37 from persys-dev/Feat/Persys-gateway-wiring-new-services+catalog
Feat/persys gateway wiring new services+catalog
2 parents 6dfc35c + 46734b8 commit 129aad6

10 files changed

Lines changed: 715 additions & 116 deletions

File tree

persys-automation/internal/config/config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ func Load() (*Config, error) {
8080
),
8181
VaultPKIMount: envOr("AUTOMATION_VAULT_PKI_MOUNT", "pki"),
8282
VaultPKIRole: envOr("AUTOMATION_VAULT_PKI_ROLE", "persys-automation"),
83-
VaultCertTTL: envDurationOr("AUTOMATION_VAULT_CERT_TTL", 24*time.Hour),
83+
VaultCertTTL: envDurationOr("AUTOMATION_VAULT_CERT_TTL", 1*time.Hour),
8484
VaultServiceName: envOr("AUTOMATION_VAULT_SERVICE_NAME", "persys-automation"),
8585
VaultServiceDomain: strings.TrimSpace(os.Getenv("AUTOMATION_VAULT_SERVICE_DOMAIN")),
86-
VaultRetryInterval: envDurationOr("AUTOMATION_VAULT_RETRY_INTERVAL", time.Minute),
86+
VaultRetryInterval: envDurationOr("AUTOMATION_VAULT_RETRY_INTERVAL", 30*time.Second),
8787
ForgeryRedisEnabled: envBoolOr(
8888
"AUTOMATION_FORGERY_REDIS_ENABLED",
8989
false,

persys-forgery/utils/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func (c *Config) applyDefaults() {
100100
c.Vault.AuthMethod = "token"
101101
}
102102
if c.Vault.CertTTL == 0 {
103-
c.Vault.CertTTL = 24 * time.Hour
103+
c.Vault.CertTTL = 1 * time.Hour
104104
}
105105
if c.Vault.RetryInterval == 0 {
106106
c.Vault.RetryInterval = 30 * time.Second

persys-gateway/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ WORKDIR /app
2121

2222
COPY --from=build /src/persys-gateway/config.yaml .
2323
COPY --from=build /src/persys-gateway/cluster.yaml .
24+
COPY --from=build /src/persys-gateway/catalog.yaml .
2425
COPY --from=build /src/persys-gateway/cmd/persys-gateway .
2526

2627
RUN chmod +x ./persys-gateway

persys-gateway/catalog.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
services:
2+
- name: meter
3+
path_prefix: /meter
4+
upstream_addr: http://persys-meter:9092
5+
strip_prefix: true
6+
auth: none
7+
timeout: 10s
8+
enabled: true

persys-gateway/cmd/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type App struct {
5959
authController controllers.AuthController
6060
githubController controllers.GithubController
6161
clusterMetaController *controllers.ClusterMetaController
62+
eventsController *controllers.EventsController
6263
webhookController *controllers.WebhookController
6364
automationController *controllers.AutomationController
6465
}
@@ -190,6 +191,7 @@ func main() {
190191
)
191192
app.githubController = controllers.NewGithubController(app.authService, ctx, app.githubService, cnf)
192193
app.clusterMetaController = controllers.NewClusterMetaController(app.clusterControl, string(cnf.Deployment.Mode), cnf.Database.Enabled())
194+
app.eventsController = controllers.NewEventsController(app.clusterControl)
193195
app.webhookController = controllers.NewWebhookController(app.webhookService)
194196
app.automationController = controllers.NewAutomationController(app.automationService)
195197

@@ -229,6 +231,9 @@ func main() {
229231
authnMW := authn.New(jwtSecret)
230232
gwRouter := router.New(authnMW, cnf.Deployment.Mode)
231233

234+
// events/watch is registered on the mTLS router group
235+
app.eventsController.Register(mtlsGroup)
236+
232237
// ClusterMetaController: health/list-clusters/get-cluster — the only
233238
// handlers left that were never RPC-shaped.
234239
gwRouter.RegisterControllers(mtlsGroup, app.clusterMetaController)

persys-gateway/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ func (c *Config) applyDefaults() {
343343
c.Vault.ManagerAddr = "vault-manager:50069"
344344
}
345345
if c.Vault.CertTTL == time.Duration(0) {
346-
c.Vault.CertTTL = 24 * time.Hour
346+
c.Vault.CertTTL = 1 * time.Hour
347347
}
348348
if c.Vault.RetryInterval == time.Duration(0) {
349349
c.Vault.RetryInterval = 30 * time.Second
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package controllers
2+
3+
import (
4+
"context"
5+
"io"
6+
"strings"
7+
8+
"github.com/gin-gonic/gin"
9+
controlv1 "github.com/persys-dev/persys-cloud/persys-gateway/internal/controlv1"
10+
"github.com/persys-dev/persys-cloud/persys-gateway/services"
11+
)
12+
13+
// EventsController exposes cluster-wide scheduler events to browser-based
14+
// dashboard clients over Server-Sent Events. WatchEvents is a
15+
// server-streaming gRPC RPC, which grpcbridge explicitly does not
16+
// auto-bridge (see internal/grpcbridge/bridge.go) — this hand-written
17+
// endpoint is the browser-facing equivalent. A gRPC-native client (e.g.
18+
// persysctl) should call the scheduler's WatchEvents RPC directly instead
19+
// of going through this endpoint.
20+
type EventsController struct {
21+
clusterControl *services.ClusterControlService
22+
}
23+
24+
func NewEventsController(clusterControl *services.ClusterControlService) *EventsController {
25+
return &EventsController{clusterControl: clusterControl}
26+
}
27+
28+
// Register mounts /events/watch on rg, — this
29+
// is dashboard-user-facing mTLS-gated SSE endpoint for cluster-wide scheduler events. A gRPC-native client.
30+
func (c *EventsController) Register(rg *gin.RouterGroup) {
31+
rg.GET("/events/watch", c.WatchHandler())
32+
}
33+
34+
// WatchHandler streams cluster-wide scheduler events to the client as
35+
// Server-Sent Events, replaying recent history first (same semantics as
36+
// the underlying WatchEvents RPC). Supports the same optional filters as
37+
// query params: ?type=&workload_id=&node_id=&cluster_id=
38+
func (c *EventsController) WatchHandler() gin.HandlerFunc {
39+
return func(ctx *gin.Context) {
40+
clusterID := strings.TrimSpace(ctx.Query("cluster_id"))
41+
req := &controlv1.WatchEventsRequest{
42+
Type: strings.TrimSpace(ctx.Query("type")),
43+
WorkloadId: strings.TrimSpace(ctx.Query("workload_id")),
44+
NodeId: strings.TrimSpace(ctx.Query("node_id")),
45+
}
46+
47+
ctx.Header("Content-Type", "text/event-stream")
48+
ctx.Header("Cache-Control", "no-cache")
49+
ctx.Header("Connection", "keep-alive")
50+
// Disable response buffering if this gateway ever sits behind
51+
// nginx — otherwise SSE events sit in a buffer instead of
52+
// reaching the browser as they arrive.
53+
ctx.Header("X-Accel-Buffering", "no")
54+
55+
streamCtx, cancel := context.WithCancel(ctx.Request.Context())
56+
defer cancel()
57+
58+
events := make(chan *controlv1.SchedulerEventView, 16)
59+
go func() {
60+
defer close(events)
61+
_ = c.clusterControl.WatchEventsForClient(streamCtx, clusterID, req, func(event *controlv1.SchedulerEventView) error {
62+
select {
63+
case events <- event:
64+
return nil
65+
case <-streamCtx.Done():
66+
return streamCtx.Err()
67+
}
68+
})
69+
// Errors here (scheduler unreachable, stream ended, etc) are
70+
// intentionally swallowed rather than surfaced to the HTTP
71+
// response: by the time an error could occur, headers are
72+
// already flushed and the client is mid-stream — the
73+
// connection simply closes, and a well-behaved EventSource
74+
// client on the dashboard side reconnects on its own.
75+
}()
76+
77+
ctx.Stream(func(w io.Writer) bool {
78+
select {
79+
case event, ok := <-events:
80+
if !ok {
81+
return false
82+
}
83+
ctx.SSEvent("event", event)
84+
return true
85+
case <-streamCtx.Done():
86+
return false
87+
}
88+
})
89+
}
90+
}

0 commit comments

Comments
 (0)