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
Vault 2823 cc namespace #12393
Vault 2823 cc namespace #12393
Conversation
the failed |
Also - big fan of the description with the 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.
I think it's looking pretty good so far. I left a few comments/suggestions - let me know your thoughts
vault/activity_log_testing_util.go
Outdated
@@ -36,6 +36,29 @@ func (c *Core) InjectActivityLogDataThisMonth(t *testing.T) (map[string]struct{} | |||
return activeEntities, tokens | |||
} | |||
|
|||
// InjectActivityLogDataThisMonth populates the in-memory client store | |||
// with some entities and tokens for root namespace , overriding what was already there |
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.
// with some entities and tokens for root namespace , overriding what was already there | |
// with some entities and tokens for root namespace, overriding what was already there |
vault/activity_log.go
Outdated
|
||
} | ||
|
||
func (a *ActivityLog) AddEntity(e *activity.EntityRecord) { |
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 this should be private - i didn't see any outside-package usage (or any reason to make it public)
vault/activity_log.go
Outdated
tokenCount += int(countByNS) | ||
queryNS, err := namespace.FromContext(ctx) | ||
if err != nil { | ||
return responseData, 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.
what are your thoughts on returning this empty responseData
vs nil
when there is an err
? It seems to be a pretty common pattern to return nil with an 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.
same in other err != nil
cases
vault/activity_log.go
Outdated
responseData["non_entity_tokens"] = tokenCount | ||
responseData["clients"] = clientCount | ||
for nsID, clients := range clientCountTable { | ||
|
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.
vault/activity_log.go
Outdated
clientCountTable[nsID].distinctEntities += count | ||
|
||
} | ||
//add non-entity token count |
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 non-entity token count |
vault/activity_log.go
Outdated
|
||
//createClientCountTable maps the entitycount and token count to the namespace id | ||
func createClientCountTable(entityMap map[string]uint64, tokenMap map[string]uint64) map[string]*clients { | ||
//add distinct entity count |
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 distinct entity count |
vault/activity_log.go
Outdated
|
||
} | ||
return clientCountTable | ||
|
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.
@@ -142,7 +142,12 @@ func (b *SystemBackend) handleMonthlyActivityCount(ctx context.Context, req *log | |||
return logical.ErrorResponse("no activity log present"), nil | |||
} | |||
|
|||
results := a.partialMonthClientCount(ctx) | |||
results, err := a.partialMonthClientCount(ctx) |
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 made this comment earlier, but it doesn't look like results
has any info in it when err != nil
- it might be more clear to show that results has nothing in it and put an empty Data
object in the response in this error case
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.
did you have any thoughts on 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.
The Go convention for funcs that return (x,error) is to populate only one of the two return values - I'd prefer if it were exactly one, but sometimes we do return (nil,nil), which tends to cause bugs because people are so used to the more common practice of always returning exactly one non-nil value.
This carries over to our handlers as well. If they return a non-nil error, they should be returning a nil response. The complication is that we can return errors in two ways. We can either return (nil, err) in which case the user will not an error message, they'll just get a 500 internal server error. Or we can return (logical.ErrorResponse("message"), nil) in which case we'll put "message" into the response's error
field and return a 400 bad request. Or we can return (logical.ErrorReponse("message"), err), in which case they'll see "message" in the response's error
field and the status code of the response will depend on err, see logical.RespondErrorCommon for specifics.
There's no general guidance about when to use these different approaches, though we should probably write some. One tip I can offer is that if the error is an "expected" error, e.g. the user provided bad input in their request, or asked to do something we don't want to allow, it's good to provide an explanation of the problem in the response's error message. If it's an internal failure that shouldn't have happened, that isn't predictable based on the inputs, then we usually tend more towards the (nil, err) return where they just a 500. This is because we don't know what appear in the error, and we might wind up leaking information that the user shouldn't see. Often in that case we'll log the error.
Anyway, in this case, when err != nil, I would simply return (nil, err).
vault/activity_log_test.go
Outdated
} | ||
|
||
var tokenRecords map[string]uint64 | ||
if includeTokens { |
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.
everything up to this point looks identical with setupActivityRecordsInStorage
- it might be a good idea to break some of that out into a base/helper function, and then just add the namespace flavor on top for the namespace version of the function. this would result in more clear and maintainable code
vault/activity_log_test.go
Outdated
results := a.partialMonthClientCount(ctx) | ||
results, err := a.partialMonthClientCount(ctx) | ||
if err != nil { | ||
t.Fatal(err.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.
no need to call .Error()
here if you don't want to
@@ -142,7 +142,12 @@ func (b *SystemBackend) handleMonthlyActivityCount(ctx context.Context, req *log | |||
return logical.ErrorResponse("no activity log present"), nil | |||
} | |||
|
|||
results := a.partialMonthClientCount(ctx) | |||
results, err := a.partialMonthClientCount(ctx) |
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.
did you have any thoughts on this?
Timestamp: time.Now().Unix(), | ||
}, | ||
{ | ||
EntityID: "33333333-2222-2222-2222-222222222222", | ||
NamespaceID: "root", | ||
NamespaceID: namespace.RootNamespaceID, |
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!
Timestamp: time.Now().Unix(), | ||
}, | ||
} | ||
if constants.IsEnterprise { |
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 probably better to put this in vault/activity_log_ent_test.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.
Why so?
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 an enterprise specific test involving enterprise features (namespaces). that said, it does mean less code duplication by having this as an optional add-on, rather than two separate tests
what do we generally do in this situation?
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 went back and forth on some variations on this theme. Typically we have two variations on the same test, one oss, one ent, or we have the test only in ent. I think this approach is fine too, and has some advantages. We should probably expand the wiki "Writing Tests: Models to Follow" to talk about this topic.
vault/activity_log.go
Outdated
for nsID, clients := range clientCountTable { | ||
ns, err := NamespaceByID(ctx, nsID, a.core) | ||
if err != nil { | ||
return responseData, 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.
I think this should be return nil, err
.
* vault-2823 adding changes * VAULT-2823 adding alias * Vault-2823 addressing comments * Vault-2823 removing comments * Vault-2823 removing comments * vault-2823 removing q debug * adding changelog * Vault-2823 updating external test * adding approved changes * fixing returns * fixing returns
https://hashicorp.atlassian.net/browse/VAULT-2823
(PR addresses comments on https://github.com/hashicorp/vault-enterprise/pull/2103)
will be creating a PR for enterprise updating the test for partialmonthlycount with entitites and tokens created using different namespaces
Issue: Currently, the partial client count API /sys/internal/counters - HTTP API | Vault by HashiCorp does not return the information subdivided by namespaces. The reason for that, is that we do not store entity counts broken down by namespace in memory at the moment. Ideally, we want customers to be able to observe the client counts broken down by namespace for the current month as well as prior months.
Change: Using a namespace->entity counter map (a.entityCountByNamespaceID) and incrementing it only if the entity is not in the map
Using non-entity token count (a.currentSegment.tokenCount.CountByNamespaceID) and distinct entity count (a.entityCountByNamespaceID) to divide the response by namespaces
Testing: (Open Source Vault)
{"request_id":"21dfb313-80b5-63df-25cf-9ced0cbe389d","lease_id":"","renewable":false,"lease_duration":0,"data":{"by_namespace":[{"namespace_id":"root","namespace_path":"","counts":{"distinct_entities":3,"non_entity_tokens":0,"clients":3}}],"clients":3,"distinct_entities":3,"non_entity_tokens":0},"wrap_info":null,"warnings":null,"auth":null}
(Enterprise : with namespaces)
{"request_id":"992b21de-0d7d-650d-5d6f-930b70bb2ecf","lease_id":"","renewable":false,"lease_duration":0,"data":{"by_namespace":[{"namespace_id":"root","namespace_path":"","counts":{"distinct_entities":3,"non_entity_tokens":0,"clients":3}},{"namespace_id":"N3MZO","namespace_path":"education/","counts":{"distinct_entities":1,"non_entity_tokens":0,"clients":1}},{"namespace_id":"kOiIk","namespace_path":"education/training/","counts":{"distinct_entities":1,"non_entity_tokens":0,"clients":1}}],"clients":5,"distinct_entities":5,"non_entity_tokens":0},"wrap_info":null,"warnings":null,"auth":null}