Skip to content

Commit

Permalink
refactor(internal/jimm/jimm.go): introduces a jimm constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
alesstimec committed Nov 27, 2024
1 parent 9d36ed4 commit b910d95
Show file tree
Hide file tree
Showing 36 changed files with 667 additions and 646 deletions.
4 changes: 2 additions & 2 deletions cmd/jimmctl/cmd/relation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ func initializeEnvironment(c *gc.C, ctx context.Context, db *db.Database, u dbmo
}

func (s *relationSuite) TestListRelations(c *gc.C) {
env := initializeEnvironment(c, context.Background(), &s.JIMM.Database, *s.AdminUser)
env := initializeEnvironment(c, context.Background(), s.JIMM.Database, *s.AdminUser)
bClient := s.SetupCLIAccess(c, "alice") // alice is superuser

relations := []apiparams.RelationshipTuple{{
Expand Down Expand Up @@ -420,7 +420,7 @@ func (s *relationSuite) TestListRelations(c *gc.C) {
}

func (s *relationSuite) TestListRelationsWithError(c *gc.C) {
env := initializeEnvironment(c, context.Background(), &s.JIMM.Database, *s.AdminUser)
env := initializeEnvironment(c, context.Background(), s.JIMM.Database, *s.AdminUser)
// alice is superuser
bClient := s.SetupCLIAccess(c, "alice")

Expand Down
104 changes: 61 additions & 43 deletions cmd/jimmsrv/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ import (
"gorm.io/gorm"

"github.com/canonical/jimm/v3/internal/auth"
"github.com/canonical/jimm/v3/internal/db"
"github.com/canonical/jimm/v3/internal/dbmodel"
"github.com/canonical/jimm/v3/internal/discharger"
"github.com/canonical/jimm/v3/internal/errors"
"github.com/canonical/jimm/v3/internal/jimm"
jimmcreds "github.com/canonical/jimm/v3/internal/jimm/credentials"
"github.com/canonical/jimm/v3/internal/jimm/role"
"github.com/canonical/jimm/v3/internal/jimmhttp"
"github.com/canonical/jimm/v3/internal/jimmhttp/rebac_admin"
"github.com/canonical/jimm/v3/internal/jimmjwx"
Expand Down Expand Up @@ -194,14 +194,15 @@ type Params struct {

// A Service is the implementation of a JIMM server.
type Service struct {
jimm jimm.JIMM
jimmParameters jimm.Parameters
jimm *jimm.JIMM

mux *chi.Mux
cleanups []func() error
}

func (s *Service) JIMM() *jimm.JIMM {
return &s.jimm
return s.jimm
}

// ServeHTTP implements http.Handler.
Expand All @@ -213,6 +214,10 @@ func (s *Service) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// monitoring all changes to models. WatchControllers finishes when the
// given context is canceled, or there is a fatal error watching models.
func (s *Service) WatchControllers(ctx context.Context) error {
if s.jimm == nil {
zapctx.Error(ctx, "jimm not created")
return errors.E("jimm not created")
}
w := jimm.Watcher{
Database: s.jimm.Database,
Dialer: s.jimm.Dialer,
Expand All @@ -225,6 +230,10 @@ func (s *Service) WatchControllers(ctx context.Context) error {
// the given context is canceled, or there is a fatal error watching model
// summaries.
func (s *Service) WatchModelSummaries(ctx context.Context) error {
if s.jimm == nil {
zapctx.Error(ctx, "jimm not created")
return errors.E("jimm not created")
}
w := jimm.Watcher{
Database: s.jimm.Database,
Dialer: s.jimm.Dialer,
Expand All @@ -235,6 +244,10 @@ func (s *Service) WatchModelSummaries(ctx context.Context) error {

// StartJWKSRotator see internal/jimmjwx/jwks.go for details.
func (s *Service) StartJWKSRotator(ctx context.Context, checkRotateRequired <-chan time.Time, initialRotateRequiredTime time.Time) error {
if s.jimm == nil {
zapctx.Error(ctx, "jimm not created")
return errors.E("jimm not created")
}
if s.jimm.JWKService == nil {
zapctx.Warn(ctx, "not starting JWKS rotation")
return nil
Expand All @@ -244,6 +257,10 @@ func (s *Service) StartJWKSRotator(ctx context.Context, checkRotateRequired <-ch

// MonitorResources periodically updates metrics.
func (s *Service) MonitorResources(ctx context.Context) {
if s.jimm == nil {
zapctx.Error(ctx, "jimm not created")
return
}
s.jimm.UpdateMetrics(ctx)
ticker := time.NewTicker(5 * time.Minute)
for {
Expand All @@ -258,6 +275,10 @@ func (s *Service) MonitorResources(ctx context.Context) {

// OpenFGACleanup starts a goroutine that cleans up any orphaned tuples from OpenFGA.
func (s *Service) OpenFGACleanup(ctx context.Context, trigger <-chan time.Time) error {
if s.jimm == nil {
zapctx.Error(ctx, "jimm not created")
return errors.E("jimm not created")
}
for {
select {
case <-trigger:
Expand Down Expand Up @@ -298,29 +319,25 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
s.mux.Use(chimiddleware.RequestLogger(&logger.HTTPLogFormatter{}))
s.mux.Use(middleware.MeasureHTTPResponseTime)

s.jimmParameters = jimm.Parameters{
UUID: p.ControllerUUID,
Pubsub: &pubsub.Hub{MaxConcurrency: 50},
}
// Setup all dependency services

if p.ControllerUUID == "" {
controllerUUID, err := uuid.NewRandom()
if err != nil {
return nil, errors.E(op, err)
}
p.ControllerUUID = controllerUUID.String()
if s.jimmParameters.UUID == "" {
s.jimmParameters.UUID = uuid.NewString()
}
s.jimm.UUID = p.ControllerUUID
s.jimm.Pubsub = &pubsub.Hub{MaxConcurrency: 50}

if p.DSN == "" {
return nil, errors.E(op, "missing DSN")
}

var err error
s.jimm.Database.DB, err = openDB(ctx, p.DSN, p.LogSQL)
database, err := openDB(ctx, p.DSN, p.LogSQL)
if err != nil {
return nil, errors.E(op, err)
}
if err := s.jimm.Database.Migrate(ctx, false); err != nil {
return nil, errors.E(op, err)
s.jimmParameters.Database = &db.Database{
DB: database,
}

if p.AuditLogRetentionPeriodInDays != "" {
Expand All @@ -332,15 +349,15 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
return nil, errors.E(op, "retention period cannot be less than 0")
}
if period != 0 {
jimm.NewAuditLogCleanupService(s.jimm.Database, period).Start(ctx)
jimm.NewAuditLogCleanupService(s.jimmParameters.Database, period).Start(ctx)
}
}

openFGAclient, err := newOpenFGAClient(ctx, p.OpenFGAParams)
if err != nil {
return nil, errors.E(op, err)
}
s.jimm.OpenFGAClient = openFGAclient
s.jimmParameters.OpenFGAClient = openFGAclient
if err := ensureControllerAdministrators(ctx, openFGAclient, p.ControllerUUID, p.ControllerAdmins); err != nil {
return nil, errors.E(op, err, "failed to ensure controller admins")
}
Expand Down Expand Up @@ -373,12 +390,12 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
SessionCookieMaxAge: p.OAuthAuthenticatorParams.SessionCookieMaxAge,
JWTSessionKey: p.OAuthAuthenticatorParams.JWTSessionKey,
SecureCookies: p.OAuthAuthenticatorParams.SecureSessionCookies,
Store: &s.jimm.Database,
Store: s.jimmParameters.Database,
SessionStore: sessionStore,
RedirectURL: redirectUrl,
},
)
s.jimm.OAuthAuthenticator = authSvc
s.jimmParameters.OAuthAuthenticator = authSvc
if err != nil {
zapctx.Error(ctx, "failed to setup authentication service", zap.Error(err))
return nil, errors.E(op, err, "failed to setup authentication service")
Expand All @@ -388,32 +405,33 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
p.JWTExpiryDuration = 24 * time.Hour
}

s.jimm.JWKService = jimmjwx.NewJWKSService(s.jimm.CredentialStore)
s.jimm.JWTService = jimmjwx.NewJWTService(jimmjwx.JWTServiceParams{
s.jimmParameters.JWKService = jimmjwx.NewJWKSService(s.jimmParameters.CredentialStore)
s.jimmParameters.JWTService = jimmjwx.NewJWTService(jimmjwx.JWTServiceParams{
Host: p.PublicDNSName,
Store: s.jimm.CredentialStore,
Store: s.jimmParameters.CredentialStore,
Expiry: p.JWTExpiryDuration,
})
s.jimm.Dialer = &jujuclient.Dialer{
ControllerCredentialsStore: s.jimm.CredentialStore,
JWTService: s.jimm.JWTService,
}

roleManager, err := role.NewRoleManager(&s.jimm.Database, s.jimm.OpenFGAClient)
if err != nil {
return nil, errors.E(op, err, "failed to create RoleManager")
s.jimmParameters.Dialer = &jujuclient.Dialer{
ControllerCredentialsStore: s.jimmParameters.CredentialStore,
JWTService: s.jimmParameters.JWTService,
}
s.jimm.RoleManager = roleManager

if !p.DisableConnectionCache {
s.jimm.Dialer = jimm.CacheDialer(s.jimm.Dialer)
s.jimmParameters.Dialer = jimm.CacheDialer(s.jimmParameters.Dialer)
}

if _, err := url.Parse(p.DashboardFinalRedirectURL); err != nil {
return nil, errors.E(op, err, "failed to parse final redirect url for the dashboard")
}

rebacBackend, err := rebac_admin.SetupBackend(ctx, &s.jimm)
// instanciate jimm
j, err := jimm.New(s.jimmParameters)
if err != nil {
return nil, errors.E(op, err)
}
s.jimm = j

rebacBackend, err := rebac_admin.SetupBackend(ctx, s.jimm)
if err != nil {
return nil, errors.E(op, err)
}
Expand All @@ -433,7 +451,7 @@ func NewService(ctx context.Context, p Params) (*Service, error) {

s.mux.Mount("/metrics", promhttp.Handler())

s.mux.Mount("/rebac", middleware.AuthenticateRebac("/rebac", rebacBackend.Handler(""), &s.jimm))
s.mux.Mount("/rebac", middleware.AuthenticateRebac("/rebac", rebacBackend.Handler(""), s.jimm))

mountHandler(
"/debug",
Expand Down Expand Up @@ -479,11 +497,11 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
// Websockets require extra care when cookies are used for authentication
// to avoid CSRF attacks. https://portswigger.net/web-security/websockets/cross-site-websocket-hijacking
websocketCors := middleware.NewWebsocketCors(p.CorsAllowedOrigins)
s.mux.Handle("/api", websocketCors.Handler(jujuapi.APIHandler(ctx, &s.jimm, params)))
s.mux.Handle("/model/*", websocketCors.Handler(http.StripPrefix("/model", jujuapi.ModelHandler(ctx, &s.jimm, params))))
s.mux.Handle("/api", websocketCors.Handler(jujuapi.APIHandler(ctx, s.jimm, params)))
s.mux.Handle("/model/*", websocketCors.Handler(http.StripPrefix("/model", jujuapi.ModelHandler(ctx, s.jimm, params))))
mountHandler(
"/model/{uuid}/{type:charms|applications}",
jimmhttp.NewHTTPProxyHandler(&s.jimm),
jimmhttp.NewHTTPProxyHandler(s.jimm),
)

return s, nil
Expand All @@ -499,7 +517,7 @@ func (s *Service) setupDischarger(p Params) (*discharger.MacaroonDischarger, err
MacaroonExpiryDuration: p.MacaroonExpiryDuration,
ControllerUUID: p.ControllerUUID,
}
MacaroonDischarger, err := discharger.NewMacaroonDischarger(cfg, &s.jimm.Database, s.jimm.OpenFGAClient)
MacaroonDischarger, err := discharger.NewMacaroonDischarger(cfg, s.jimm.Database, s.jimm.OpenFGAClient)
if err != nil {
return nil, errors.E(err)
}
Expand All @@ -509,11 +527,11 @@ func (s *Service) setupDischarger(p Params) (*discharger.MacaroonDischarger, err
func (s *Service) setupSessionStore(ctx context.Context, sessionSecret []byte) (*pgstore.PGStore, error) {
const op = errors.Op("setupSessionStore")

if s.jimm.CredentialStore == nil {
if s.jimmParameters.CredentialStore == nil {
return nil, errors.E(op, "credential store is not configured")
}

sqlDb, err := s.jimm.Database.DB.DB()
sqlDb, err := s.jimmParameters.Database.DB.DB()
if err != nil {
return nil, errors.E(op, err)
}
Expand Down Expand Up @@ -560,7 +578,7 @@ func (s *Service) setupCredentialStore(ctx context.Context, p Params) error {
// Only enable Postgres storage for secrets if explicitly enabled.
if p.InsecureSecretStorage {
zapctx.Warn(ctx, "using plaintext postgres for secret storage")
s.jimm.CredentialStore = &s.jimm.Database
s.jimmParameters.CredentialStore = s.jimmParameters.Database
return nil
}

Expand All @@ -570,7 +588,7 @@ func (s *Service) setupCredentialStore(ctx context.Context, p Params) error {
return errors.E(op, err)
}
if vs != nil {
s.jimm.CredentialStore = vs
s.jimmParameters.CredentialStore = vs
return nil
}

Expand Down
Loading

0 comments on commit b910d95

Please sign in to comment.