-
Notifications
You must be signed in to change notification settings - Fork 25
feat: call cluster service to create identity cluster relation while creating user #745
Conversation
Codecov Report
@@ Coverage Diff @@
## master #745 +/- ##
==========================================
- Coverage 79.06% 79.03% -0.03%
==========================================
Files 94 94
Lines 8563 8623 +60
==========================================
+ Hits 6770 6815 +45
- Misses 1320 1329 +9
- Partials 473 479 +6
Continue to review full report at Codecov.
|
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, just a few suggestions/comments/questions
cluster/service/cluster.go
Outdated
signer := cluster.NewJWTSASigner(ctx, s.config, options...) | ||
remoteClusterService, err := signer.CreateSignedClient() | ||
if err != nil { | ||
return err |
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 wrap this error?
cluster/service/cluster.go
Outdated
} | ||
res, err := remoteClusterService.LinkIdentityToClusterClusters(goasupport.ForwardContextRequestID(ctx), clusterclient.LinkIdentityToClusterClustersPath(), identityToClusterData) | ||
if err != nil { | ||
return err |
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 here, wrap the error?
cluster/service/cluster.go
Outdated
} | ||
res, err := remoteClusterService.RemoveIdentityToClusterLinkClusters(goasupport.ForwardContextRequestID(ctx), clusterclient.RemoveIdentityToClusterLinkClustersPath(), identityToClusterData) | ||
if err != nil { | ||
return err |
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 here
@@ -93,10 +155,10 @@ func Start(ctx context.Context, factory service.ClusterCacheFactory, options ... | |||
} else { | |||
clusterCache = nil | |||
} | |||
return (clusterCache != nil && started == uint32(1)), err | |||
return clusterCache != nil && started == uint32(1), err |
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 this code tries to start the cache only once, then maybe https://golang.org/pkg/sync/#Once.Do would be more appropriate ?
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 code can start the cache multiple times. It should initialize the cache successfully only once. But if it failed then it should keep trying. This is why we can't use once.Do()
here.
And initialization can fail during auth service startup if cluster service is not ready yet (since it may be waiting for Auth service to start).
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
@@ -109,9 +171,9 @@ func Clusters(clusters map[string]cluster.Cluster) []cluster.Cluster { | |||
} | |||
|
|||
func ClusterByURL(clusters map[string]cluster.Cluster, url string) *cluster.Cluster { | |||
for apiURL, cluster := range clusters { | |||
for apiURL, c := range clusters { |
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.
good work in avoiding var names with package names 👍
"identity_id": identityID, | ||
"cluster_url": clusterURL, | ||
}, "failed to link identity to cluster in cluster service") | ||
// Not a blocker. Log the error and proceed. |
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 what happens next if this error occurs? is there a way to retry the Identity/cluster link creation ?
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 error is critical enough to fail here instead of proceed I think.
We should roll back user creation here (hard delete user&identity from DB) and return an error, so, the reg app can repeat provisioning later.
But this is not that easy to implement and will complicate that PR. So, let's add a TODO here and create a new issue to address this in a separate 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.
Created #747
application/service/services.go
Outdated
@@ -62,6 +62,8 @@ type ClusterService interface { | |||
Clusters(ctx context.Context, options ...rest.HTTPClientOption) ([]cluster.Cluster, error) | |||
ClusterByURL(ctx context.Context, url string, options ...rest.HTTPClientOption) (*cluster.Cluster, error) | |||
Status(ctx context.Context, options ...rest.HTTPClientOption) (bool, error) | |||
RemoveIdentityToClusterLink(ctx context.Context, identityID uuid.UUID, clusterURL string, options ...rest.HTTPClientOption) error | |||
AddIdentityToClusterLink(ctx context.Context, identityID uuid.UUID, clusterURL string, options ...rest.HTTPClientOption) 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.
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.
@alexeykazakov yes, I like your naming suggestions better
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.
Addressed in ebaf3d2
cluster/cache.go
Outdated
@@ -100,7 +98,8 @@ func (c *cache) refreshCache(ctx context.Context) error { | |||
|
|||
// fetchClusters fetches a new list of clusters from Cluster Management Service | |||
func (c *cache) fetchClusters(ctx context.Context) (map[string]Cluster, error) { | |||
cln, err := c.createClientWithServiceAccountSigner(ctx) | |||
signer := NewJWTSASigner(ctx, c.config, c.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.
Instead of exporting that NewJWTSASigner() function let's create a new method in the Cluster Service - AuthClientClusters()
. Basically let's move all the business logic from fetchClusters()
method of cache to AuthClientClusters()
method of the service.
JWT signers and clients are an implementation details of the service.
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.
But I see that it would create cycle dependency between cache (cluster) and service (cluster/service)...
We could solve this problem by moving cache.go
to cluster/service
.
We should get rid of this cache anyway btw. We should just load cluster by URL from cluster service every time and do not rely on the full list of clusters at all. But it's worth creating a separate issue and PR for that.
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.
but how do we get a list of all cluster URL to fetch it once? Or are you saying when there is a request for any cluster explicitly from any client, then we should get it from cluster service each time?
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.
External clients (such as tenant service, oso-proxy, jenkins-idler, etc) are proxied to cluster service. The cache is not used.
Internal clients (different auth services, like account linking) should not ask for the list of all clusters at all. They should ask for the particular cluster with particular URL. And they should call our cluster service every time.
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.
But as I said let's just create an issue for that and do it in a separate 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.
Created #750
cluster/cluster.go
Outdated
return cln, nil | ||
} | ||
|
||
func (c JWTSASigner) createClient(ctx context.Context) (*cluster.Client, 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.
As I mentioned above all these changes in cluster/cluster.go should be implementation details of the service package.
cluster/service/cluster.go
Outdated
@@ -77,6 +83,62 @@ func (s *clusterService) Stop() { | |||
} | |||
} | |||
|
|||
func (s *clusterService) AddIdentityToClusterLink(ctx context.Context, identityID uuid.UUID, clusterURL string, options ...rest.HTTPClientOption) 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.
Add docs please explaining what that method does.
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.
Addressed in ebaf3d2
cluster/service/cluster.go
Outdated
return nil | ||
} | ||
|
||
func (s *clusterService) RemoveIdentityToClusterLink(ctx context.Context, identityID uuid.UUID, clusterURL string, options ...rest.HTTPClientOption) 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.
Add docs.
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.
Addressed in ebaf3d2
@@ -93,10 +155,10 @@ func Start(ctx context.Context, factory service.ClusterCacheFactory, options ... | |||
} else { | |||
clusterCache = nil | |||
} | |||
return (clusterCache != nil && started == uint32(1)), err | |||
return clusterCache != nil && started == uint32(1), err |
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 code can start the cache multiple times. It should initialize the cache successfully only once. But if it failed then it should keep trying. This is why we can't use once.Do()
here.
And initialization can fail during auth service startup if cluster service is not ready yet (since it may be waiting for Auth service to start).
test/recorder/cluster.go
Outdated
} | ||
} | ||
|
||
// WithLinkIdentityToClusterRequestPayloadMatcher an option to specify the RequestPayload matcher for the recorder |
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.
Fix the description. It's WithUnlink...
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.
Addressed in ebaf3d2
test/recorder/recorder.go
Outdated
if NotifyRequestPayloadMatcher(messageID)(httpRequest, cassetteRequest) { | ||
authorization := httpRequest.Header.Get("Authorization") | ||
reqID := httpRequest.Header.Get("X-Request-Id") | ||
// WithLinkIdentityToClusterRequestPayloadMatcher an option to specify the RequestPayload matcher for the recorder |
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.
Fix the description.
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.
Addressed in ebaf3d2
) | ||
|
||
type RequestParamMatcher interface { | ||
MatchClusterURL(r cassette.Request, url string) bool |
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 also should match identityID. Or I missed something?
But actually I like https://github.com/h2non/gock more than go-vcr. It's IMO less verbose and code looks cleaner and easier to understand.
@MatousJobanek has been using it in tenant service for awhile.
@dipak-pawar would you mind trying to switch your test to gock instead? If you have concerns then we can discuss it. We could do 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.
Ideally yes we should match identityID
, but I am using same identityID and different clusterURL, to match request from yaml file
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.
Created #748
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 but with IdentityID marcher you would also make sure that the identityID is set properly in the out-coming request. Right now if you don't pass the identity ID from the service to cluster the test won't fail.
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 will improve it when introducing ginko
"identity_id": identityID, | ||
"cluster_url": clusterURL, | ||
}, "failed to link identity to cluster in cluster service") | ||
// Not a blocker. Log the error and proceed. |
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 error is critical enough to fail here instead of proceed I think.
We should roll back user creation here (hard delete user&identity from DB) and return an error, so, the reg app can repeat provisioning later.
But this is not that easy to implement and will complicate that PR. So, let's add a TODO here and create a new issue to address this in a separate 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.
PR looks good to me. Good job, @dipak-pawar !
@@ -93,10 +155,10 @@ func Start(ctx context.Context, factory service.ClusterCacheFactory, options ... | |||
} else { | |||
clusterCache = nil | |||
} | |||
return (clusterCache != nil && started == uint32(1)), err | |||
return clusterCache != nil && started == uint32(1), err |
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
require.EqualError(t, err, "failed to unlink identity to cluster in cluster management service. Response status: 401 Unauthorized. Response body: unauthorized") | ||
}) | ||
|
||
s.T().Run("404", 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.
in its current state, the POST /api/clusters/identities
and DELETE /api/clusters/identities
endpoints cannot return 404 Not Found
responses (https://github.com/fabric8-services/fabric8-cluster/blob/master/design/clusters.go#L125-L149), so this test may be necessary (but I guess there's nothing wrong in supporting this case at the client level anyways...)
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 returns for DELETE look at https://github.com/fabric8-services/fabric8-cluster/blob/master/design/clusters.go#L146
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.
oh thanks, I missed that :/ But it's not used in the controller (https://github.com/fabric8-services/fabric8-cluster/blob/master/controller/clusters.go#L175-L198) and IMO, we shouldn't return a 404 Not Found
, but a 400 Bad Request
if the parameters in the payload are invalid, IMO
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.
Hmm, but it might be possible parameters in the payload are valid and there is no cluster available in DB. So, in that case, we should return 404 right?
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.
you mean, returning a 400 Bad Request
if the identity ID is not a valid UUID or if the cluster URL is not a parseable URL, but returning a 404 Not Found
response if there's no record matching the identityID/clusterURL ? Yeah, I guess we could do that, indeed.
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 exactly, but we can do that only for clusterURL, because we try to find cluster using url. We don't try to find identity using identityID as we don't have any identity info in cluster. And I think it would be too much complext to check existence of identity by talking to auth.
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.
but for unlinking, the 404
response would mean that the identity/cluster link record was not found, right? No need to call auth
to check that there's actually an identify matching the ID?
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 right
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 are already covering this. Look at https://github.com/fabric8-services/fabric8-cluster/blob/master/controller/clusters_blackbox_test.go#L354
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, cool then ;)
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. But IdentityID marcher would still improve the tests :)
Fixes: #744