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

VAULT-29738 CE changes for client count warning #28068

Merged
merged 2 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog/28068.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:improvement
cli: `vault operator usage` will now include a warning if the specified usage period contains estimated client counts.
```

```release-note:improvement
activity: `/sys/internal/counters/activity` will now include a warning if the specified usage period contains estimated client counts.
```
6 changes: 6 additions & 0 deletions command/operator_usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ func (c *OperatorUsageCommand) Run(args []string) int {
colConfig.Empty = " " // Do not show n/a on intentional blank lines
colConfig.Glue = " "
c.UI.Output(tableOutput(out, colConfig))

// Also, output the warnings returned, if any:
for _, warning := range resp.Warnings {
c.UI.Warn(warning)
}

return 0
}

Expand Down
42 changes: 39 additions & 3 deletions vault/logical_system_activity.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/timeutil"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
)
Expand All @@ -25,6 +26,9 @@ var defaultToRetentionMonthsMaxWarning = fmt.Sprintf("retention_months cannot be
const (
// WarningCurrentBillingPeriodDeprecated is a warning string that is used to indicate that the current_billing_period field, as the default start time will automatically be the billing period start date
WarningCurrentBillingPeriodDeprecated = "current_billing_period is deprecated; unless otherwise specified, all requests will default to the current billing period"

// WarningCurrentMonthIsAnEstimate is a warning string that is used to let the customer know that for this query, the current month's data is estimated.
WarningCurrentMonthIsAnEstimate = "Since this usage period includes both the current month and at least one historical month, counts returned in this usage period are an estimate. Client counts for this period will no longer be estimated at the start of the next month."
)

// activityQueryPath is available in every namespace
Expand Down Expand Up @@ -224,7 +228,35 @@ func (b *SystemBackend) rootActivityPaths() []*framework.Path {
return paths
}

func parseStartEndTimes(a *ActivityLog, d *framework.FieldData, billingStartTime time.Time) (time.Time, time.Time, error) {
// queryContainsEstimates calculates if the query for client counts will contain estimates.
// A query between months N-2 and N-1 would not be an estimate, as with a query for month N.
// But a query between N-2 and N or N-1 and N would be an estimate.
func queryContainsEstimates(startTime time.Time, endTime time.Time) bool {
startTime = timeutil.EndOfMonth(startTime)
endTime = timeutil.EndOfMonth(endTime)

if startTime == endTime {
// If we're only estimating the current month, then we have no estimation
return false
}

if timeutil.IsCurrentMonth(endTime, time.Now().UTC()) {
// Our query includes the current month and previous months, so we have estimation
return true
}

// If the endTime is in the future, the behaviour is equivalent to when endTime is set to the current month
// (it includes the current month and previous months, so we have estimation)
endOfCurrentMonth := timeutil.EndOfMonth(time.Now().UTC())
if endTime.After(endOfCurrentMonth) {
return true
}

// Our query doesn't include the current month
return false
}

func parseStartEndTimes(d *framework.FieldData, billingStartTime time.Time) (time.Time, time.Time, error) {
startTime := d.Get("start_time").(time.Time)
endTime := d.Get("end_time").(time.Time)

Expand Down Expand Up @@ -262,7 +294,7 @@ func (b *SystemBackend) handleClientExport(ctx context.Context, req *logical.Req
return logical.ErrorResponse("no activity log present"), nil
}

startTime, endTime, err := parseStartEndTimes(a, d, b.Core.BillingStart())
startTime, endTime, err := parseStartEndTimes(d, b.Core.BillingStart())
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}
Expand Down Expand Up @@ -315,7 +347,7 @@ func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logica
}

var err error
startTime, endTime, err = parseStartEndTimes(a, d, b.Core.BillingStart())
startTime, endTime, err = parseStartEndTimes(d, b.Core.BillingStart())
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}
Expand All @@ -336,6 +368,10 @@ func (b *SystemBackend) handleClientMetricQuery(ctx context.Context, req *logica
return resp204, err
}

if queryContainsEstimates(startTime, endTime) {
warnings = append(warnings, WarningCurrentMonthIsAnEstimate)
}

return &logical.Response{
Warnings: warnings,
Data: results,
Expand Down
Loading