-
Notifications
You must be signed in to change notification settings - Fork 65
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
feat: Implement SpaceBindingRequest controller main logic #848
Conversation
/retest update e2e PR |
toolchainv1alpha1.TypeLabelKey: "tenant", | ||
toolchainv1alpha1.ProviderLabelKey: toolchainv1alpha1.ProviderLabelValue, | ||
} | ||
if spacename != "nospace" { |
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.
What happens if == nospace
? Is it still a valid namespace? Do think this ever returns an invalid namespace? I mean should we return (*corev1.Namespace, error)
or not necessary?
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 function is used only for mocking namespace while unit testing. When is being called with "nospace" is done with the purpose of configuring (misconfiguring) the namespace, and verify the corner case in which a required label is missing, for example in this unit test: https://github.com/codeready-toolchain/host-operator/pull/848/files#diff-f15c73194e22112fd24e7e852200c395d2cddb8cb016fe76710f4c38bab782e1R232-R252
In short, I want the controller to throw this error here: https://github.com/codeready-toolchain/host-operator/pull/848/files#diff-e48d49741db80226348ae5ffd08100dd10afa2e83e5d18b2a94776dfdfb616f3R333-R335
Hope this makes sense.
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 it makes sense. Sorry I missed the test
part ... sigh...
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.
No worries! My pleasure getting more feedback/reviews and discussing alternative solutions! Thanks for checking!
|
||
func WithFinalizer() Option { | ||
return func(spaceBindingRequest *toolchainv1alpha1.SpaceBindingRequest) { | ||
spaceBindingRequest.Finalizers = append(spaceBindingRequest.Finalizers, toolchainv1alpha1.FinalizerName) |
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.
what if the spaceBindingRequest
object is not nil
before applying the finalizer? Would that be a problem? If so:
if spaceBindingRequest != nil {
spaceBindingRequest.Finalizers = append(spaceBindingRequest.Finalizers, toolchainv1alpha1.FinalizerName)
} else {
// Handle the case where the input spaceBindingRequest is nil, possibly with logging or an error return.
}
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.
Unless I'm missing something, it cannot be nil, since those Option
functions are being passed to the NewSpaceBindingRequest
function, which takes care of creating the SpaceBindingRequest object: https://github.com/codeready-toolchain/host-operator/blob/master/test/spacebindingrequest/spacebindingrequest.go#L13-L19
Of course if somehow misused you could break them, but those are "only" used for unit testing, and we might want to modify all our With...
helper in this case (also the ones in toolchain-common), since they all follow the same style, eg: https://github.com/codeready-toolchain/toolchain-common/blob/master/pkg/test/usersignup/usersignup.go
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.
same mistake from my part. I didn't see this is for testing purposes. Please ignore.
go.mod
Outdated
@@ -114,3 +114,5 @@ require ( | |||
) | |||
|
|||
go 1.19 | |||
|
|||
replace github.com/codeready-toolchain/api => github.com/mfrancisc/api v0.0.0-20230814083608-6d8633a3bcf3 |
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.
Please update the relevant api repo first and then update thus replicate statement, instead.
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 done only for testing (unit and e2e) the PR with the updated version of the API repo, once all the PRs are approved and passing all the tests/checks, we:
- merge the API pr
- update other PRs with the new version
- merge other repos with new master API version
we also have a CI check in place which prevents from merging if there is any replace
statement, see: https://github.com/codeready-toolchain/host-operator/actions/runs/5857146355/job/15878356586?pr=848
So this will be replaced once all the tests are passing and all the PRs are approved.
Hopefully this answers you question/concern. But plz let me know if I misunderstood.
@@ -853,7 +852,7 @@ func TestDeleteSpaceRequest(t *testing.T) { | |||
err := apis.AddToScheme(scheme.Scheme) | |||
require.NoError(t, err) | |||
appstudioTier := tiertest.AppStudioTier(t, tiertest.AppStudioTemplates) | |||
srNamespace := newNamespace("jane") | |||
srNamespace := spacerequesttest.NewNamespace("jane") |
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.
Not your code per se -- can we put "jane" in a const? Seems it's been used in many place, to avoid any typos in the future.
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.
Yeah, thanks for pointing this out, it doesn't look very clean, there are usually some strings that can be extracted to const or variable in our testing code, just didn't saw this aligned with the other unit tests for other controllers.
Or maybe we could put the string into a variable instead of const ? 🤔
|
||
// then | ||
require.EqualError(t, err, "failed to remove finalizer: mock error") | ||
}) |
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 work on testing and mocking this :D +1
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 can't take credit for this , I was just good at copy pasting and following same approach I saw in other unit tests :D .
The repository is mature and full of great examples on how to solve many of these "tricky" problems with testing and not only.
Kudos to the team 🏅
@@ -92,7 +99,360 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl. | |||
} | |||
logger.Info("spacebindingrequest found", "member cluster", memberClusterWithSpaceBindingRequest.Name) | |||
|
|||
// todo implement logic | |||
if util.IsBeingDeleted(spaceBindingRequest) { |
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.
It's not part of this PR but I missed the previous PR which introduced member cluster lookup. So I'm using this opportunity to point out that we can improve the lookup mechanism :)
Let's say there are two member clusters m-1 and m-2. And m-1 cluster is having an outage and temporally down. The reconcile won't work for all space requests even from m-2 if m-1 happens to be first in r.MemberClusters.
What we could do to mitigate it is too save the error in line 82 above but proceed with the lookup. And only if no SBR found in other members then return the saved error. Otherwise just log the error but do not return it. In that case m-2 won't be affected by the m-1 outage.
Feel free to address it in a separate PR though.
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.
Yeah, good point! The logic can be improved as you suggested, also I can check if it can be extracted to a reusable function since I see it's similar to what I'm using in spacerequest_controller and soon also in spacebindingcleanup_controller (for the SBR cleanup task) 🤔
Thanks for your suggestion, I'll address it in a separate PR 👍
/retest infra |
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.
Nice work 👍
spaceBindingLabels := runtimeclient.MatchingLabels{ | ||
toolchainv1alpha1.SpaceBindingRequestLabelKey: spaceBindingRequest.GetName(), | ||
toolchainv1alpha1.SpaceBindingRequestNamespaceLabelKey: spaceBindingRequest.GetNamespace(), | ||
} |
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.
it's just a detail, but you could have a function:
spaceBindingLabels(spaceBindingRequest)
that would return the labels so you could use it when creating and when getting the SpaceBinding - in that way, you could be sure that you use always the same labels
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.
🤔 maybe it doesn't make sense
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 not 100% sure I understand how I would use it, since the spacebinding now is being created by this utility function https://github.com/codeready-toolchain/toolchain-common/blob/c909c72d8f627a9f72917531eded2a5e765faea1/pkg/spacebinding/spacebinding.go#L14
Maybe I can add some utility function like WithSpaceBindingRequestLabels
in that package and use that instead of setting the labels directly as I'm doing now when creating the object https://github.com/codeready-toolchain/host-operator/pull/848/files#diff-e48d49741db80226348ae5ffd08100dd10afa2e83e5d18b2a94776dfdfb616f3R304-R305
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.
never mind - I made the comment when I started reviewing the PR. When I saw the rest of the code, I agree that it doesn't make any sense.
if err := r.setStatusTerminating(memberClusterWithSpaceBindingRequest, spaceBindingRequest); err != nil { | ||
return errs.Wrap(err, "error updating status") | ||
} |
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, the error handling of the failed status updates could be moved into the updateStatus
method, so you would just return whatever you get and you don't have to differentiate between the failed and no-failed ones
return r.setStatusTerminating(memberClusterWithSpaceBindingRequest, spaceBindingRequest)
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've simplified that plz see: 0d24598
I've noticed that in the other cases I was just returning , and that should be probably enough, since the error will already contain the message saying that it was a status update failure, no need to wrap that with the custom message: "error updating status" IMO.
} | ||
// space is being deleted | ||
if util.IsBeingDeleted(space) { | ||
return false, errs.New("space is being deleted") |
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 rather also update the SBR CR to make it visible to end users:
return false, errs.New("space is being deleted") | |
return false, r.setStatusFailedToCreateSpaceBinding(logger, memberCluster, spaceBindingRequest, errs.New("space is being deleted")) |
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.
Makes sense! Thanks! done in 0d24598
} | ||
// mur is being deleted | ||
if util.IsBeingDeleted(mur) { | ||
return false, errs.New("mur is being deleted") |
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 same here
return false, errs.New("mur is being deleted") | |
return false, r.setStatusFailedToCreateSpaceBinding(logger, memberCluster, spaceBindingRequest, errs.New("mur is being deleted")) |
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.
Makes sense! Thanks! done in 0d24598
isRoleValid := false | ||
for actual := range nsTemplTier.Spec.SpaceRoles { | ||
if spaceBindingRequest.Spec.SpaceRole == actual { | ||
isRoleValid = true | ||
} | ||
} | ||
if !isRoleValid { | ||
return fmt.Errorf("invalid role '%s' for space '%s'", spaceBindingRequest.Spec.SpaceRole, space.Name) | ||
} | ||
|
||
return 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.
we could simplify this
isRoleValid := false | |
for actual := range nsTemplTier.Spec.SpaceRoles { | |
if spaceBindingRequest.Spec.SpaceRole == actual { | |
isRoleValid = true | |
} | |
} | |
if !isRoleValid { | |
return fmt.Errorf("invalid role '%s' for space '%s'", spaceBindingRequest.Spec.SpaceRole, space.Name) | |
} | |
return nil | |
for actual := range nsTemplTier.Spec.SpaceRoles { | |
if spaceBindingRequest.Spec.SpaceRole == actual { | |
return nil | |
} | |
} | |
return fmt.Errorf("invalid role '%s' for space '%s'", spaceBindingRequest.Spec.SpaceRole, space.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.
Agree this makes it shorter, thanks! Done in 0d24598
if err := r.Client.Create(context.TODO(), spaceBinding); err != nil { | ||
return errs.Wrap(err, "unable to create SpaceBinding") | ||
} |
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, we should take care of conflicts with the already existing (default) SpaceBindings. Or with those that are created by admins. Such SpaceBindings are immutable for end users, so it would be good to set corresponding status in SBR CR.
It's fine if you do it in a new PR.
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 I'm not mistaken this is already covered by the fact that the name of the SpaceBinding is computed here https://github.com/codeready-toolchain/toolchain-common/blob/c909c72d8f627a9f72917531eded2a5e765faea1/pkg/spacebinding/spacebinding.go#L24
So in case an SBR tries to create a SB resource for mur and space that already has a SpaceBinding, the Create will fail saying that the name already exists and it will be set in the SBR status : https://github.com/codeready-toolchain/host-operator/pull/848/files#diff-e48d49741db80226348ae5ffd08100dd10afa2e83e5d18b2a94776dfdfb616f3R250-R252
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.
sure, my point was to show it properly in the status by saying:
"SpaceBinding for the given name and space already exist but is not managed by SBR controller" or something like that - more explanatory than just returning the conflict.
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.
Ok, then let's do it in separate PR, because if we want to specify if the SB that already exists is or not managed by SBR controller , we need to get it and check the labels. Not sure if we want to provide such information to the user/client?
spacebindingrequesttest.AssertThatSpaceBindingRequest(t, badSBR.GetNamespace(), badSBR.GetName(), member1.Client). | ||
HasSpecSpaceRole("admin"). | ||
HasSpecMasterUserRecord(""). // empty | ||
HasFinalizer() |
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.
verify that it has corresponding reason, status, and message set
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.
Done in 0d24598
Thanks!
spacebindingrequesttest.AssertThatSpaceBindingRequest(t, badSBR.GetNamespace(), badSBR.GetName(), member1.Client). | ||
HasSpecSpaceRole(""). // empty | ||
HasSpecMasterUserRecord(janeMur.Name). | ||
HasFinalizer() |
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 same here
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.
Done in 0d24598
Thanks!
HasLabelWithValue(toolchainv1alpha1.SpaceBindingRequestLabelKey, sbr.GetName()). // check expected labels are there | ||
HasLabelWithValue(toolchainv1alpha1.SpaceBindingRequestNamespaceLabelKey, sbr.GetNamespace()) |
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.
we expect that the labels should match the values in spec - this could be part of the AssertThatSpaceBinding
function.
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 not sure I'm understanding this . The AssertThatSpaceBinding
function is being used by other tests like usersignup
, spacebindingcleanup
. If I move this check inside the function it will not work there, but maybe I'm not understanding this correctly.
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.
my bad.
You are right - I misread the names of the labels 🤦♂️
so never mind ;-)
}) | ||
|
||
t.Run("failure", func(t *testing.T) { | ||
t.Run("SpaceBinding resource is already being deleted", func(t *testing.T) { |
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.
let's make clear why it's a failure case:
t.Run("SpaceBinding resource is already being deleted", func(t *testing.T) { | |
t.Run("SpaceBinding resource is already being deleted for more than 2 minutes", func(t *testing.T) { |
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.
Done in 0d24598
Thanks!
/retest infra again .. |
…eBinding only once, unit tests assertions
/retest infra |
/retest updated e2e PR |
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 great 👍 🚀
Thanks a lot for addressing my comments 🥇
controllers/spacebindingrequest/spacebindingrequest_controller.go
Outdated
Show resolved
Hide resolved
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: MatousJobanek, mfrancisc The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Co-authored-by: Matous Jobanek <[email protected]>
/retest updated e2e PR |
1 similar comment
/retest updated e2e PR |
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## master #848 +/- ##
==========================================
+ Coverage 83.01% 83.38% +0.36%
==========================================
Files 52 52
Lines 5570 5826 +256
==========================================
+ Hits 4624 4858 +234
- Misses 774 790 +16
- Partials 172 178 +6
|
This PR adds main logic for handling SpaceBindingRequests, the logic supports:
Jira: https://issues.redhat.com/browse/ASC-397
Related PRs:
Paired with: codeready-toolchain/toolchain-e2e#779