-
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
EVEREST-1709: backup rbac tests #904
base: main
Are you sure you want to change the base?
Conversation
f9736bf
to
44920d9
Compare
if tt.proxyResponse != nil { | ||
kp.EXPECT().proxyKubernetes(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). | ||
Run(func( | ||
ctx echo.Context, namespace, kind, name string, |
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.
🚫 [golangci-lint] reported by reviewdog 🐶
unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
return ctx.JSON(http.StatusBadRequest, Error{ | ||
Message: pointer.ToString("Could not create REST transport"), | ||
}) | ||
} | ||
reverseProxy.Transport = transport | ||
reverseProxy.ErrorHandler = everestErrorHandler(e.l) | ||
reverseProxy.ErrorHandler = everestErrorHandler(k.l) | ||
reverseProxy.ModifyResponse = modifiersFn(k.l, respTransformers...) |
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.
🚫 [golangci-lint] reported by reviewdog 🐶
response body must be closed (bodyclose)
Header: make(http.Header), | ||
} | ||
|
||
modify := modifiersFn(l, respTransformers...) |
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.
🚫 [golangci-lint] reported by reviewdog 🐶
response body must be closed (bodyclose)
body = bytes.NewReader(b) | ||
} | ||
|
||
req, err := http.NewRequest(tt.httpMethod, "/", body) |
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.
🚫 [golangci-lint] reported by reviewdog 🐶
should rewrite http.NewRequestWithContext or add (*Request).WithContext (noctx)
body: &DatabaseClusterBackup{ | ||
Spec: &struct { | ||
BackupStorageName string "json:\"backupStorageName\"" | ||
DbClusterName string "json:\"dbClusterName\"" |
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.
🚫 [golangci-lint] reported by reviewdog 🐶
ST1003: struct field DbClusterName should be DBClusterName (stylecheck)
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.
Overall I like the unit test based approach, it was a good suggestion 👏 However, I have 2 concerns with the tight coupling with the actual implementation.
- We are checking for enforce calls but we are not asserting what was done with those enforcements. For example, if we had a bug here and called something other than
continue
the unit tests would pass because the enforce call was done but the end result would be wrong, the list wouldn't have been properly filtered. - The tight coupling with the implementation can make these tests brittle with refactors. I know that @mayankshah1607 had some plans to refactor the way we are handling the filtering functions and I'm concern that this testing approach will need many adjustments as part of that refactoring. Let's hear from @mayankshah1607
Spec: everestv1alpha1.DatabaseClusterBackupSpec{ | ||
DBClusterName: "db-cluster-name", | ||
}, | ||
}, | ||
}, | ||
}, | ||
|
||
expectedEnforce: []enforceStrings{ | ||
{"alice", rbac.ResourceBackupStorages, rbac.ActionRead, "ns/name1"}, |
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 these tests just helped us find a bug 😅
The enforce for ResourceBackupStorages shouldn't be ns/name1
. Instead, it should be ns/backup-storage-name
that is part of the Spec like so:
Spec: everestv1alpha1.DatabaseClusterBackupSpec{ | |
DBClusterName: "db-cluster-name", | |
}, | |
}, | |
}, | |
}, | |
expectedEnforce: []enforceStrings{ | |
{"alice", rbac.ResourceBackupStorages, rbac.ActionRead, "ns/name1"}, | |
Spec: everestv1alpha1.DatabaseClusterBackupSpec{ | |
DBClusterName: "db-cluster-name", | |
BackupStorageName: "backup-storage-name", | |
}, | |
}, | |
}, | |
}, | |
expectedEnforce: []enforceStrings{ | |
{"alice", rbac.ResourceBackupStorages, rbac.ActionRead, "ns/backup-storage-name"}, |
The bug is here. It should be dbbackup.Spec.BackupStorageName
instead of dbbackup.GetName()
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 other functions have the same bug 🙈
The unit-test like approach seems fine, but I share the same concerns as Diogo:
Agreed with this. Mocking the very component we aim to test—in this case, the RBAC enforcer—may not add much value. Checking if enforce is called with the correct arguments is somewhat useful, but it’s not enough for catching deeper issues such as:
An important part of testing the integration between the RBAC enforcer and the API logic is verifying that the code correctly respects the decisions made by the enforcer. But in the above cases, we risk missing regressions because the current tests only check if enforce was called with the right arguments, and not what was done with the result. When I created the ticket for 1709, I imagined a slightly different approach. I'd suggest we mock the Kubernetes API but use a real Everest API and RBAC enforcer to better test the integration:
I feel that with such an approach, we’re able to correctly test the integration between Everest API, RBAC enforcer and Kubernetes, and perform assertions on different kinds of RBAC policies.
Agreed with this again. And I feel this is true not only for the tests, but even with how the API code is currently implemented. Right now, the business logic is embedded directly within the HTTP handler methods. This works fine, but I think there are a few problems:
To address this, I’m working on a refactor to decouple the business logic from the API handler methods. The core idea is to define an interface for the business logic, with multiple actors implementing this interface. Each actor would handle a specific responsibility, such as RBAC enforcements, request validation, Kubernetes integration, etc. All these actors will be executed serially, in a specific order, forming something similar to a chain of responsibility. This has many advantages:
Coming back to the point on testing, I feel like these kinds of unit tests will become a lot simpler once we introduce this kind of a modular structure to our code. I’ve worked with this design pattern at my previous work, and it proved invaluable for maintaining clean and testable code. I'm happy to provide a short PoC if needed. If it’s okay, I’d like to propose that we hold this PR until we review the refactoring and then re-think how we should proceed with the integration testing. There may not necessarily be overlapping work, but it would be good to ensure we’re not heading in a direction that might require rework later. Let me know what you think @recharte @michal-kralik 🙂 |
EVEREST-1709