-
Notifications
You must be signed in to change notification settings - Fork 16
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
Add Datastore layer #73
Add Datastore layer #73
Conversation
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
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.
Great stuff! Did a first quick pass-through and got stuck thinking into two different things:
On a more high-level discussion, do you still see we having one interface to abstract the datastore layer, in which each of the different implementations (postgres, mysql, etc) will have to satisfy? I couldn't find that interface anymore, but the Datastore
object looks like it would implement this interface.
and the 2nd topic is related to using a connection pool, can be discussed in the comment below.
db/schema.go
Outdated
) | ||
|
||
//go:embed migrations/*.sql | ||
var fs embed.FS |
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 new to me :D
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.
Embed go feature allows embedding files in compile time. However, some errors can only be caught in runtime. (ie. If a SQL file is not well formatted or even when there is no SQL in an empty file). Another similar feature is go:generate
which allows running code at compile time. Take a look:
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.
The embedded SQLs (for migrations) are being exercised in the CRUD tests.
db/datastore.go
Outdated
return nil, fmt.Errorf("failed to parse Postgres Connection URL: %w", err) | ||
} | ||
|
||
db := stdlib.OpenDB(*c) |
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.
Just to confirm: this does not use a connection pool, does it?
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 does use a connection pool:
// DB is a database handle representing a pool of zero or more
// underlying connections. It's safe for concurrent use by multiple
// goroutines.
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.
Amazing work!!! See if my comments make sense
About the abstract datastore layer, I think that is this one: querier.go |
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now() | ||
); | ||
|
||
CREATE TABLE join_tokens |
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.
Will join tokens not be related to harvesters in any way? Thinking about HA, does it matter if we're able to validate if the join token was for a specific harvester?
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 know this has some impacts we would also have to think about, like how to deal when a harvester is deleted. This specifically is also something I was thinking for a bit and considering: wouldn't it be a better idea to instead of deleting harvesters to just disable them? is this data really not worth keeping?
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.
According to our data model, there is an implicit relation between join_token and harvesters through members relation, but not an explicit one. Would be enough for the validation to know that the join token is for a member that is related to the harvester, or you are thinking of a more restricted validation in which the token needs to be exactly issued for a harvester?
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.
Would be enough for the validation to know that the join token is for a member that is related to the harvester?
As I understand it so far, I think it would be enough for the validation knowing which member a token belongs to, whether it was used or not, and whether it didn't expire.
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.
According to our data model, there is an implicit relation between join_token and harvesters through members relation, but not an explicit one
my bad on the wording, that's what I actually meant 😅
We could still abstract the Datastore layer in an interface that uses entity objects from the API. I didn't think about that because this PR introduces a Datastore that just supports one dialect, and I still don't know if we'll need that abstraction anyway, even in the case of supporting multiple dialects. |
Right, it makes sense. Yes, the querier interface looks good enough, at least for now. Although this higher level abstraction will still be implemented in some form, (at the handlers, most likely?) by having an interface we're better segregating this logic for our handlers. Anyways, not something for right now |
Signed-off-by: Max Lambrecht <[email protected]>
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!! Btw, you're not planning on incorporating these changes into galadriel on this PR, right? If not, I'm ready to approve.
No need to approve just yet, I'm expecting more feedback from the rest of the team. |
Just realized my last question was a bit ambiguous, I wanted to ask about replacing the current In memory datastore to use this implementation. This should happen in a separate PR right? |
IMO migrating Galadriel to the new Datastore is not worth doing right now, since the whole implementation of Galadriel will be mostly re-visited while moving from the POC to the MVP. Each new (re)implemented functionality in Galadriel Server will need to use the new Datastore. |
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
e.handleError(w, errMsg) | ||
return | ||
} | ||
|
||
e.Logger.Printf("Created member for trust domain: %s", memberReq.TrustDomain) | ||
e.Logger.Printf("Created trustDomain for trust domain: %s", trustDomainReq.Name) |
Check failure
Code scanning / CodeQL
Log entries created from user input
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
@@ -215,11 +283,15 @@ | |||
e.Logger.Errorf(errMsg) | |||
|
|||
errBytes := []byte(errMsg) | |||
w.WriteHeader(len(errBytes)) | |||
w.WriteHeader(500) | |||
_, err := w.Write(errBytes) |
Check warning
Code scanning / CodeQL
Reflected cross-site scripting
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.
Amazing work @maxlambrecht ❤️
Did a code pass-through and it looks impeccable, I'm just not approving it now cause I didn't have time to test yet.
One topic that came to my mind though, is that I noticed we're not supporting TLS for the postgres connection. IDK if this is something needed for now, I don't think we've discussed this yet, but I'm pretty sure we'll have to think about this at some point. WDYT?
if receivedHarvesterState.TrustDomain.Compare(token.TrustDomain) != 0 { | ||
err := fmt.Errorf("authenticated trust domain {%s} does not match received trust domain {%s}", receivedHarvesterState.TrustDomain.String(), token.TrustDomain.String()) | ||
|
||
if harvesterReq.TrustDomainName != authenticatedTD.Name { |
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.
Just out of curiosity, was this change due to readability?
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, IMO it reads better that way.
return nil, nil, err | ||
} | ||
|
||
if b != 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.
Wondering if we might want to have a debug(?) log in case this is nil
Thanks Juliano! ❤️ The Postgres connection string allows TLS, something like:
|
Signed-off-by: Max Lambrecht <[email protected]>
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.
this is a number 10 work, right here. Nice job, Max 🏅 Ready to approve as soon as others take a look into this.
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.
Hey @maxlambrecht, great work here.
A lot of changes, most of my comments are about naming.
Don't feel obliged to accept it, but let me know what you think about it.
Feel free to ignore them as well.
Signed-off-by: Max Lambrecht <[email protected]>
64d4d07
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.
Just focused on the pkg/server/datastore. Thank you Maxl for this. Looks great!
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
Signed-off-by: Max Lambrecht <[email protected]>
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 looking great, Max! 💯
Signed-off-by: Max Lambrecht <[email protected]>
Kudos, SonarCloud Quality Gate passed!
|
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.
🌭
Signed-off-by: Max Lambrecht [email protected]
Pull request check list
Affected functionality
Added a new Datastore implementation that supports Postgres
Description of change
migrations
andqueries
as input.Datastore
struct was added, that defines CRUD methods for all the entities of Galadriel.Datastore
is created and it connects to the configured DB, the schema is validated and migrations applied if needed, using golang-migrate.Datastore
were implemented using dockertest, which starts a container from a Postgres image, runs the migrations, and then run the test.Which issue this pull requests fixes