diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-31 16:09:12 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-31 16:09:12 -0600 |
| commit | 238b61113456ebad8bad880913dc315cd892a296 (patch) | |
| tree | f38db8e10c4b55aef21c96c30fc71278c6e3d5c6 /app | |
| parent | ebb003ef2beaeee61104d6b88a342c5c9fa73b51 (diff) | |
| parent | 311603d0c0b04d451e9fb8e5e8335dca8425e2c4 (diff) | |
Merge branch 'sparkle-visibility' into 'main'
Connect to postgresql
See merge request gitlab-org/software-supply-chain-security/authorization/sparkled!21
Diffstat (limited to 'app')
| -rw-r--r-- | app/app.go | 2 | ||||
| -rw-r--r-- | app/controllers/health/controller.go | 43 | ||||
| -rw-r--r-- | app/controllers/sparkles/controller.go | 17 | ||||
| -rw-r--r-- | app/db/connection.go | 54 | ||||
| -rw-r--r-- | app/db/url.go | 19 | ||||
| -rw-r--r-- | app/init.go | 11 |
6 files changed, 129 insertions, 17 deletions
@@ -9,6 +9,7 @@ import ( "github.com/xlgmokha/x/pkg/log" "github.com/xlgmokha/x/pkg/x" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/dashboard" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/health" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/sparkles" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/middleware" ) @@ -22,6 +23,7 @@ func New(rootDir string) http.Handler { mountable := []Mountable{ ioc.MustResolve[*dashboard.Controller](ioc.Default), + ioc.MustResolve[*health.Controller](ioc.Default), ioc.MustResolve[*sparkles.Controller](ioc.Default), } for _, m := range mountable { diff --git a/app/controllers/health/controller.go b/app/controllers/health/controller.go new file mode 100644 index 0000000..99ff4cd --- /dev/null +++ b/app/controllers/health/controller.go @@ -0,0 +1,43 @@ +package health + +import ( + "context" + "net/http" + "time" + + "github.com/xlgmokha/x/pkg/serde" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/db" +) + +type Controller struct { + dbConnection *db.Connection +} + +func New(dbConnection *db.Connection) *Controller { + return &Controller{ + dbConnection: dbConnection, + } +} + +func (c *Controller) MountTo(mux *http.ServeMux) { + mux.Handle("GET /-/health", http.HandlerFunc(c.Health)) + mux.Handle("GET /-/health/database", http.HandlerFunc(c.Database)) +} + +func (c *Controller) Health(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + serde.ToHTTP(w, r, map[string]string{"status": "ok"}) +} + +func (c *Controller) Database(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) + defer cancel() + + if c.dbConnection.IsHealthy(ctx) { + w.WriteHeader(http.StatusOK) + serde.ToHTTP(w, r, map[string]string{"database": "ok"}) + } else { + w.WriteHeader(http.StatusServiceUnavailable) + serde.ToHTTP(w, r, map[string]string{"database": "unavailable"}) + } +} diff --git a/app/controllers/sparkles/controller.go b/app/controllers/sparkles/controller.go index 90767b2..86610e1 100644 --- a/app/controllers/sparkles/controller.go +++ b/app/controllers/sparkles/controller.go @@ -3,7 +3,6 @@ package sparkles import ( "net/http" - "github.com/xlgmokha/x/pkg/log" "github.com/xlgmokha/x/pkg/mapper" "github.com/xlgmokha/x/pkg/serde" "github.com/xlgmokha/x/pkg/x" @@ -32,9 +31,6 @@ func (c *Controller) MountTo(mux *http.ServeMux) { middleware.RequireUser(), // middleware.RequirePermission("create", c.check), )) - - // This is a temporary endpoint to restore a backup - mux.HandleFunc("POST /sparkles/restore", c.Restore) } func (c *Controller) Index(w http.ResponseWriter, r *http.Request) { @@ -64,16 +60,3 @@ func (c *Controller) Create(w http.ResponseWriter, r *http.Request) { return } } - -// This is a temporary endpoint to restore a backup -// of sparkles and can be deleted once we have an actual database -func (c *Controller) Restore(w http.ResponseWriter, r *http.Request) { - sparkles, _ := serde.FromHTTP[[]*domain.Sparkle](r) - log.WithFields(r.Context(), log.Fields{"sparkles": sparkles}) - - x.Each(sparkles, func(sparkle *domain.Sparkle) { - if err := c.db.Save(r.Context(), sparkle); err != nil { - pls.LogError(r.Context(), err) - } - }) -} diff --git a/app/db/connection.go b/app/db/connection.go new file mode 100644 index 0000000..d494f6c --- /dev/null +++ b/app/db/connection.go @@ -0,0 +1,54 @@ +package db + +import ( + "context" + "database/sql" + "fmt" + + _ "github.com/lib/pq" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls" +) + +type Connection struct { + db *sql.DB +} + +func NewConnection(databaseURL string) (*Connection, error) { + db, err := sql.Open("postgres", databaseURL) + if err != nil { + return nil, fmt.Errorf("failed to open database connection: %w", err) + } + + return &Connection{ + db: db, + }, nil +} + +func (c *Connection) Ping(ctx context.Context) error { + if c.db == nil { + return fmt.Errorf("database connection not available") + } + + return c.db.PingContext(ctx) +} + +func (c *Connection) IsHealthy(ctx context.Context) bool { + if c.db == nil { + return false + } + + err := c.Ping(ctx) + if err != nil { + pls.LogError(ctx, err) + return false + } + + return true +} + +func (c *Connection) Close() error { + if c.db == nil { + return nil + } + return c.db.Close() +} diff --git a/app/db/url.go b/app/db/url.go new file mode 100644 index 0000000..b17c651 --- /dev/null +++ b/app/db/url.go @@ -0,0 +1,19 @@ +package db + +import ( + "fmt" + + "github.com/xlgmokha/x/pkg/env" +) + +func URL() string { + if url := env.Fetch("DATABASE_URL", ""); url != "" { + return url + } + + return fmt.Sprintf( + "postgresql://postgres:%s@localhost:5000/%s?sslmode=disable", + env.Fetch("RUNWAY_PG_USER_POSTGRES_PASSWORD_SPARKLE", ""), + env.Fetch("DATABASE_NAME", "sparkle"), + ) +} diff --git a/app/init.go b/app/init.go index 8e5e0e5..ab1d6f8 100644 --- a/app/init.go +++ b/app/init.go @@ -14,6 +14,7 @@ import ( "github.com/xlgmokha/x/pkg/mapper" "github.com/xlgmokha/x/pkg/x" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/dashboard" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/health" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/sparkles" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/db" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain" @@ -29,6 +30,13 @@ func init() { ioc.RegisterSingleton[*zerolog.Logger](c, func() *zerolog.Logger { return log.New(os.Stdout, log.Fields{"app": "sparkled"}) }) + ioc.RegisterSingleton[*db.Connection](c, func() *db.Connection { + conn, err := db.NewConnection(db.URL()) + if err != nil { + pls.LogErrorNow(context.Background(), err) + } + return conn + }) ioc.RegisterSingleton[*authzed.Client](c, func() *authzed.Client { return authz.NewSpiceDBClient( context.Background(), @@ -62,6 +70,9 @@ func init() { ioc.Register[*dashboard.Controller](c, func() *dashboard.Controller { return dashboard.New() }) + ioc.Register[*health.Controller](c, func() *health.Controller { + return health.New(ioc.MustResolve[*db.Connection](c)) + }) ioc.Register[*sparkles.Controller](c, func() *sparkles.Controller { return sparkles.New( ioc.MustResolve[domain.Repository[*domain.Sparkle]](c), |
