-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(internal/jimm/jimm.go): introduces a jimm constructor #1468
Conversation
a730c54
to
b910d95
Compare
// connections therefore the JIMM object itself does not contain any per- | ||
// request state. | ||
type JIMM struct { | ||
Parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we are embedding the struct, it's not Parameters
but more Services
. Because we don't start the service in the constructor, we rather take the service reference and store it in the JIMM struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constructors have parameters. and the constructor will then instantiate all the managers/service provided by JIMM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't embed it, params you pass into the func, and on JIMM it filles in another struct. Don't couple them please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're only embedded for the time being. Once we have the domain objects, we can delete this.
internal/jimm/jimm.go
Outdated
func New(p Parameters, options ...Option) (*JIMM, error) { | ||
j := &JIMM{ | ||
Parameters: p, | ||
} | ||
|
||
if err := j.Database.Migrate(context.Background(), false); err != nil { | ||
return nil, errors.E(err) | ||
} | ||
|
||
roleManager, err := role.NewRoleManager(j.Database, p.OpenFGAClient) | ||
if err != nil { | ||
return nil, err | ||
} | ||
j.RoleManager = roleManager | ||
for _, option := range options { | ||
option(j) | ||
} | ||
|
||
return j, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i thought this method would have been a method where you receive some parameters (ex. db connection string), and you create the Database, Openfgaclient
.
So we'll end up with a central point where we validate jimm's config, start the jimm services and return the well defined jimm struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's correct like this, the JIMM method sets up the business logic, so when we have more "managers", the JIMM struct won't need any of the fields in Params, it will just take the pieces of params like the DB, OpenFGA client, pubSub, etc and pass them to the relevant constructors for the managers. Whoever calls jimm.New() decides what they want to use as a DB, as an auth service, etc (even if you can't mock the DB or auth service today).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good with some comments
f0bc5ed
to
cd1f2a2
Compare
If you're new to commit signing, there are different ways to set it up: Sign commits with
|
abc2bf1
to
30ce643
Compare
} | ||
|
||
sqlDb, err := s.jimm.Database.DB.DB() | ||
sqlDb, err := db.DB.DB() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we improve this at the moment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
improve how?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a separate PR, and not for you per se, but I was just remarking that the naming isn't great. database.database.database() is crazy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
few comments then LGTM
|
||
jimmParameters := jimm.Parameters{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would still call the services. But I won't die on this hill
f0985ed
to
fbe23f4
Compare
return errors.E("missing jwks service") | ||
} | ||
|
||
if p.JWTService == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine this should only contain primitives? Not Serices?
// connections therefore the JIMM object itself does not contain any per- | ||
// request state. | ||
type JIMM struct { | ||
Parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't embed it, params you pass into the func, and on JIMM it filles in another struct. Don't couple them please?
40f3c7d
to
61fea0d
Compare
- adds jimm.New costructor - adds jimmtest.NewJIMM - refactors GetRoleManager and GetGroupManager
61fea0d
to
8b40a37
Compare
if p.Database == nil { | ||
p.Database = &db.Database{ | ||
DB: PostgresDB(t, func() time.Time { return now }), | ||
} | ||
} | ||
if p.CredentialStore == nil { | ||
p.CredentialStore = p.Database | ||
} | ||
if p.OpenFGAClient == nil { | ||
ofgaClient, _, _, err := SetupTestOFGAClient(t.Name()) | ||
if err != nil { | ||
t.Fatalf("setting up openfga client: %v", err) | ||
} | ||
p.OpenFGAClient = ofgaClient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, this is the default config for jimmtest now. Update the godoc please
} | ||
) | ||
|
||
func NewJIMM(t Tester, additionalParameters *jimm.Parameters, options ...Option) *jimm.JIMM { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
godoc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, but please new time let's split this in multiple pr, with a feature branch.
Too many mechanical changes in the same pr
type Option func(j *jimm.JIMM) | ||
|
||
var ( | ||
UnsetCredentialStore Option = func(j *jimm.JIMM) { | ||
j.CredentialStore = nil | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we give default options to choose from, do we need to have the Option
exported?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. we have a slight bug around cloud credentials. and there is a test that relies on the fact that the credential store is unset.. until we fix that bug, we need this option
@@ -47,7 +46,11 @@ func (t GocheckTester) Name() string { | |||
} | |||
|
|||
func (t GocheckTester) Cleanup(f func()) { | |||
t.C.Logf("warning: gocheck does not support Cleanup functions; make sure you're using suite's tear-down method") | |||
if t.AddCleanup != nil { | |||
t.AddCleanup(f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why bother with adding the cleanup? Since we don't have any cleanup with all the ŧesting.t
tests.
I think it's just enough to increase the conn limit on postgres
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw, where did the change go? Or now it's fine because we are not creating that many connections?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this link stops working when you force push btw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's almost okay. My biggest gripe is with all tests getting a DB and OpenFGA client.
} | ||
|
||
sqlDb, err := s.jimm.Database.DB.DB() | ||
sqlDb, err := db.DB.DB() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a separate PR, and not for you per se, but I was just remarking that the naming isn't great. database.database.database() is crazy.
@@ -70,7 +70,7 @@ services: | |||
POSTGRES_PASSWORD: jimm | |||
# Since it's mainly used for testing purposes, it's okay to set fsync=off for | |||
# improved performance. | |||
command: -c fsync=off -c full_page_writes=off | |||
command: -c fsync=off -c full_page_writes=off -c max_connections=200 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit concerning. I wonder if we're going over the default 100 connections because we're leaking connections or because of tests running in parallel. Perhaps one way to check would be to add a global test cleanup methods that checks the number of open connections to Postgres. Or run tests with -p 1 and print the number of connections to Postgres between each test incovation.
// connections therefore the JIMM object itself does not contain any per- | ||
// request state. | ||
type JIMM struct { | ||
Parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They're only embedded for the time being. Once we have the domain objects, we can delete this.
|
||
if p.Database == nil { | ||
p.Database = &db.Database{ | ||
DB: PostgresDB(t, func() time.Time { return now }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm currently not in favour of this approach where every test that calls jimmtest.NewJIMM
gets a whole database and OpenFGA store. Some tests need neither, some tests need one. That was nicely captured in the tests and when we move to managers we can use suites so that there is less duplication of that boilerplate test setup.
I think the default should be all mocks or all nil fields unless the test requests them.
@@ -172,14 +190,14 @@ func (s *JIMMSuite) TearDownTest(c *gc.C) { | |||
} | |||
} | |||
|
|||
func (s *JIMMSuite) setupMacaroonDischarger(c *gc.C) *discharger.MacaroonDischarger { | |||
func setupMacaroonDischarger(c *gc.C, uuid string, db *db.Database, ofgaClient *openfga.OFGAClient) *discharger.MacaroonDischarger { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this changed to a function?
} | ||
|
||
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) | ||
// instantiate jimm | ||
s.jimm, err = jimm.New(jimmParameters) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy with this as is, as we discussed, but we should definitely break this down further into primitives and application domains later on. I'd love to see primitive db, openfga, authsvc (as params) -> and within the constructor: rolemanager, identityservice, etc. all constructed returning a nicely complete jimm. And, if we want to override any application domain we pass an option.
) | ||
|
||
if p.AuditLogRetentionPeriodInDays != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we should have service params honestly, it's confusing / bit odd, to see params which re for both jimm and the service and they take effect when you start the service. @kian99 I think you mentioned this right?
return s, nil | ||
} | ||
|
||
func (s *Service) StartServices(ctx context.Context, svc *service.Service) { | ||
// on the leader unit we start additional routines |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do like how we start all services here, but in main.go we're running this and then we start the jimm service. Would be nice if jimm was also started here? Could even setup the on shutdown etc all here.
@@ -244,7 +244,7 @@ controllers: | |||
region: test-region | |||
priority: 1 | |||
`) | |||
env.PopulateDB(c, *s.Database) | |||
env.PopulateDB(c, s.Database) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting we could handle ** here. Good spot.
|
||
var now = (time.Time{}).UTC().Round(time.Millisecond) | ||
|
||
type Option func(j *jimm.JIMM) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, we use option pattern only in the context of tests... I think... It's more useful the have the pattern on the JIMM constructor for when we come to (or not to) initialise services. Kinda like juju's manifolds where they configure the binary in different ways - if we ever need to go down that path we can. It's also no harm I think rather than having the option pattern only apply to the jimmtest constructor, I think it's a lot clearer if we just use jimm.New() and pass test options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope.. in production we never expect to use options.. only for tests, so why pollute production code with options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, I've left some comments. Nothing too serious, but more about options and service. I'd much prefer we just use a single constructor for initialisng jimm in tests and in main.
Description
Introduces a constructor for jimm in internal/jimm. With the lightest touch possible atm.
drive-by fix: the db.Database type holds a
migrated
(uint32) field. however we were passing the database around by value and every time we copied it, whoever tried to use it would get aCodeUpgradeInProgress
error. In this PR we pass a pointer to the database around, hopefully avoiding further migration voes.Engineering checklist
Check only items that apply
Test instructions
Notes for code reviewers