Skip to content
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 MySQL storage and tests #5

Merged

Conversation

lucasmrod
Copy link
Contributor

@lucasmrod lucasmrod commented Jul 21, 2022

Hi folks.

This PR implements a MySQL storage, issue #4.
I've added tests that can be run on any future storage (which caught a small bug in the FileStorage).

@lucasmrod lucasmrod force-pushed the issue-4-add-mysql-storage-and-tests branch from 0aa680c to cd04701 Compare July 21, 2022 18:07
Copy link

@zwass zwass left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stoked to see this up!

http/api/tokenpki.go Outdated Show resolved Hide resolved
storage/mysql/mysql.go Outdated Show resolved Hide resolved
storage/storagetest/storagetest.go Outdated Show resolved Hide resolved
Copy link
Member

@jessepeterson jessepeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an amazing contribution! Thanks so much for this! Here's some quick thoughts and I'll try to get time soon, hopefully early next week for more. :)

Bunch of comments inline. :)

I have some reservations about having an opinionated docker-compose front and center. Let me think more on that (maybe it's as simple as placing the compose file in storage/storagetest/?).

The cmd/cli.go -> parse/storage.go is interesting. What was the thinking there?

Also - don't forget to update the docs (docs/operations-guide.md) for the new storage backend. :)

go.mod Outdated Show resolved Hide resolved
parse/storage.go Outdated Show resolved Hide resolved
@@ -20,6 +21,8 @@ type FileStorage struct {
path string
}

var _ storage.AllStorage = (*FileStorage)(nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what's the reason for this?

Copy link
Contributor Author

@lucasmrod lucasmrod Jul 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convention that serves both as a "compile time check" and as a form of documentation for code readers/reviewers that FileStorage implements storage.AllStorage interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this creates an unnecessary coupling. How do you feel about removing it? As Go is statically typed an implementation must conform to it's use. AllStorage isn't particularly useful outside of main.go so I'd not like to couple these.

Let me know what you think. :)

return tokens, decodeJSONfile(s.tokensFilename(name), tokens)
err := decodeJSONfile(s.tokensFilename(name), tokens)
if err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any problem with returning a non-nil empty token — most callers are going to check for errors anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A convention I've seen many projects use is to try (when possible) to always return zero-values when error is non-nil. In this case, the zero-value of a pointer is nil.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I'm fine with this specific code change, although its a bit verbose. But in general I don't think its necessary to add a bunch of boilerplate to assure a zero-values for an error. Discussed here: https://macadmins.slack.com/archives/C07ME1284/p1663867171949419

storage/file/file.go Show resolved Hide resolved
storage/mysql/mysql.go Outdated Show resolved Hide resolved
storage/mysql/mysql.go Outdated Show resolved Hide resolved
storage/mysql/mysql_test.go Outdated Show resolved Hide resolved
storage/storagetest/storagetest.go Outdated Show resolved Hide resolved
storage/storagetest/storagetest.go Show resolved Hide resolved
@lucasmrod
Copy link
Contributor Author

lucasmrod commented Jul 21, 2022

I believe I've address all the comments.

I have some reservations about having an opinionated docker-compose front and center. Let me think more on that (maybe it's as simple as placing the compose file in storage/storagetest/?).

I've moved it to storage/mysql/. Let me know if that works.

The cmd/cli.go -> parse/storage.go is interesting. What was the thinking there?

Originally I didn't want mysql imports in storage/storage.go (where we define the abstract interface), thus I moved the one parsing method to its own package parse.

Also - don't forget to update the docs (docs/operations-guide.md) for the new storage backend. :)

Done.

I really appreciate the feedback!

@michalnicp michalnicp force-pushed the issue-4-add-mysql-storage-and-tests branch from 6952196 to bb4cb0f Compare September 19, 2022 17:56
Copy link
Member

@jessepeterson jessepeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for the unacceptably huge delay! just a few changes. would love your input on the general Go idiomatic/convention stuff — but it sounds like we might disagree on a few small things :). hopefully we can resolve!

parse/storage.go Outdated
@@ -0,0 +1,28 @@
package parse
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of moving this out of the cmd/ tree. But can we rename this package to just cli to match micromdm/nanomdm@ff8b7af?

@@ -20,6 +21,8 @@ type FileStorage struct {
path string
}

var _ storage.AllStorage = (*FileStorage)(nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this creates an unnecessary coupling. How do you feel about removing it? As Go is statically typed an implementation must conform to it's use. AllStorage isn't particularly useful outside of main.go so I'd not like to couple these.

Let me know what you think. :)

return tokens, decodeJSONfile(s.tokensFilename(name), tokens)
err := decodeJSONfile(s.tokensFilename(name), tokens)
if err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I'm fine with this specific code change, although its a bit verbose. But in general I don't think its necessary to add a bunch of boilerplate to assure a zero-values for an error. Discussed here: https://macadmins.slack.com/archives/C07ME1284/p1663867171949419

if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// an 'empty' config is valid
return &client.Config{}, nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, good call-out. I think the file-storage should be changed to match actually:

No config (missing) = nil
Present config (including an empty BaseURL) = returned.

They're treated the same in the config retriever interface implementation, as it happens. This should probably be documented there:

nanodep/client/client.go

Lines 23 to 25 in a7538c5

type ConfigRetriever interface {
RetrieveConfig(context.Context, string) (*Config, error)
}

@lucasmrod
Copy link
Contributor Author

Hmm, good call-out. I think the file-storage should be changed to match actually:
No config (missing) = nil
Present config (including an empty BaseURL) = returned.

I've amended the two storage implementations to behave this way, and added a test for DefaultConfigRetreiver.

@jessepeterson jessepeterson linked an issue Oct 3, 2022 that may be closed by this pull request
@lucasmrod
Copy link
Contributor Author

lucasmrod commented Oct 3, 2022

@jessepeterson One last change that can be added to this PR, or open a separate one if this one is merged-as is: mikermcneil#1

Your call.

@jessepeterson
Copy link
Member

@lucasmrod let's do a separate PR and keep this contained :)

also I think you need to 'request review' or whatever - it's showing as changes still requested :)

thanks again!

@lucasmrod
Copy link
Contributor Author

let's do a separate PR and keep this contained :)

Cool. I'll create another PR once this one is merged to main.

Copy link
Member

@jessepeterson jessepeterson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks much for this contribution @lucasmrod !

@@ -21,7 +21,11 @@ type Config struct {
}

type ConfigRetriever interface {
RetrieveConfig(context.Context, string) (*Config, error)
// RetrieveConfig reads the JSON DEP config of a DEP name.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

@jessepeterson jessepeterson merged commit 9288f28 into micromdm:main Oct 3, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement MySQL backend
3 participants