From 1fb006633b074cab13cf7bf05da4f44eabfb874e Mon Sep 17 00:00:00 2001 From: akshya96 <87045294+akshya96@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:20:10 -0700 Subject: [PATCH] Update clientcountutil to accommodate entity record usage time changes CE changes (#31301) * moving clientcountutil changes from ent * adding random usage time for repeated clients * replace math.rand with crypto.rand --- helper/timeutil/timeutil.go | 46 +++++++++++++++++++ helper/timeutil/timeutil_test.go | 35 ++++++++++++++ .../logical_system_activity_write_testonly.go | 14 +++++- ...cal_system_activity_write_testonly_test.go | 12 ++--- 4 files changed, 99 insertions(+), 8 deletions(-) diff --git a/helper/timeutil/timeutil.go b/helper/timeutil/timeutil.go index 30b894d9cc..899dc3bef6 100644 --- a/helper/timeutil/timeutil.go +++ b/helper/timeutil/timeutil.go @@ -4,8 +4,10 @@ package timeutil import ( + "crypto/rand" "errors" "fmt" + "math/big" "strconv" "strings" "testing" @@ -189,3 +191,47 @@ func NormalizeToYear(date, normal time.Time) time.Time { } return date } + +// GetRandomTimeInMonth gets a random time in same month as input time +func GetRandomTimeInMonth(inputTime time.Time) (time.Time, error) { + firstOfMonth := StartOfMonth(inputTime) + year, month, _ := firstOfMonth.Date() + + // Get the last day of the target month + _, _, lastDayOfMonth := EndOfMonth(inputTime).Date() + + // Generate random day, hour, minute, and second + maxDay := big.NewInt(int64(lastDayOfMonth)) + randomDayPtr, err := rand.Int(rand.Reader, maxDay) + if err != nil { + return time.Time{}, err + } + + maxHour := big.NewInt(int64(24)) + randomHourPtr, err := rand.Int(rand.Reader, maxHour) + if err != nil { + return time.Time{}, err + } + + maxMinute := big.NewInt(int64(60)) + randomMinutePtr, err := rand.Int(rand.Reader, maxMinute) + if err != nil { + return time.Time{}, err + } + + maxSecond := big.NewInt(int64(60)) + randomSecondPtr, err := rand.Int(rand.Reader, maxSecond) + if err != nil { + return time.Time{}, err + } + + randomDay := int(randomDayPtr.Int64() + 1) // +1 because rand.Int returns [0, n) + randomHour := int(randomHourPtr.Int64()) + randomMinute := int(randomMinutePtr.Int64()) + randomSecond := int(randomSecondPtr.Int64()) + + // Create the random time + randomTime := time.Date(year, month, randomDay, randomHour, randomMinute, randomSecond, 0, time.UTC) + + return randomTime, nil +} diff --git a/helper/timeutil/timeutil_test.go b/helper/timeutil/timeutil_test.go index 7692fd280d..0cb0ffca66 100644 --- a/helper/timeutil/timeutil_test.go +++ b/helper/timeutil/timeutil_test.go @@ -460,3 +460,38 @@ func TestTimeUtil_NormalizeToYear(t *testing.T) { require.Equal(t, tc.expectedNormalizedDate, normalizedDate) } } + +// Test_GetRandomTimeInMonth verifies that the random time generated is in the same month of input time. +func Test_GetRandomTimeInMonth(t *testing.T) { + currYear, _, _ := time.Now().Date() + middleOfMonth := time.Date(currYear, 8, 15, 1, 2, 3, 0, time.UTC) + + cases := []struct { + name string + inputTime time.Time + }{ + { + name: "input time is in the start of a month", + inputTime: StartOfMonth(time.Now()), + }, + { + name: "input time is in the middle of a month", + inputTime: middleOfMonth, + }, + { + name: "input time is in the last of a month ", + inputTime: EndOfMonth(time.Now()), + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + randomTime, err := GetRandomTimeInMonth(tc.inputTime) + require.NoError(t, err) + startOfMonth := StartOfMonth(tc.inputTime) + endOfMonth := EndOfMonth(tc.inputTime) + if !((randomTime.After(startOfMonth) || randomTime.Equal(startOfMonth)) && (randomTime.Before(endOfMonth) || randomTime.Equal(endOfMonth))) { + t.Fatalf("random time %v is not in the same month as input time %v", randomTime.UTC(), tc.inputTime.UTC()) + } + }) + } +} diff --git a/vault/logical_system_activity_write_testonly.go b/vault/logical_system_activity_write_testonly.go index 2402a6641b..0636361ab4 100644 --- a/vault/logical_system_activity_write_testonly.go +++ b/vault/logical_system_activity_write_testonly.go @@ -225,6 +225,7 @@ func (s *singleMonthActivityClients) addNewClients(c *generation.Client, mountAc NonEntity: isNonEntity, ClientType: c.ClientType, Timestamp: ts.Unix(), + UsageTime: ts.Unix(), } if record.ClientID == "" { var err error @@ -330,12 +331,12 @@ func (m *multipleMonthsActivityClients) processMonth(ctx context.Context, core * func (m *multipleMonthsActivityClients) addClientToMonth(monthsAgo int32, c *generation.Client, mountAccessor string, segmentIndex *int, now time.Time) error { if c.Repeated || c.RepeatedFromMonth > 0 { - return m.addRepeatedClients(monthsAgo, c, mountAccessor, segmentIndex) + return m.addRepeatedClients(monthsAgo, c, mountAccessor, segmentIndex, now) } return m.months[monthsAgo].addNewClients(c, mountAccessor, segmentIndex, monthsAgo, now) } -func (m *multipleMonthsActivityClients) addRepeatedClients(monthsAgo int32, c *generation.Client, mountAccessor string, segmentIndex *int) error { +func (m *multipleMonthsActivityClients) addRepeatedClients(monthsAgo int32, c *generation.Client, mountAccessor string, segmentIndex *int, now time.Time) error { addingTo := m.months[monthsAgo] repeatedFromMonth := monthsAgo + 1 if c.RepeatedFromMonth > 0 { @@ -346,8 +347,17 @@ func (m *multipleMonthsActivityClients) addRepeatedClients(monthsAgo int32, c *g if c.Count > 0 { numClients = int(c.Count) } + + // usage time of the client in the month that the client is being added to + // this is a random time in the usage month + usageTime, err := timeutil.GetRandomTimeInMonth(timeutil.MonthsPreviousTo(int(monthsAgo), now)) + if err != nil { + return err + } + for _, client := range repeatedFrom.clients { if c.ClientType == client.ClientType && mountAccessor == client.MountAccessor && c.Namespace == client.NamespaceID { + client.UsageTime = usageTime.Unix() addingTo.addEntityRecord(client, segmentIndex) numClients-- if numClients == 0 { diff --git a/vault/logical_system_activity_write_testonly_test.go b/vault/logical_system_activity_write_testonly_test.go index d4ec8595a9..6ea429339b 100644 --- a/vault/logical_system_activity_write_testonly_test.go +++ b/vault/logical_system_activity_write_testonly_test.go @@ -354,26 +354,26 @@ func Test_multipleMonthsActivityClients_addRepeatedClients(t *testing.T) { thisMonth := m.months[0] // this will match the first client in month 1 - require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true}, defaultMount, nil)) + require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true}, defaultMount, nil, time.Now().UTC())) require.Contains(t, month1Clients, thisMonth.clients[0]) // this will match the 3rd client in month 1 - require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true, ClientType: "non-entity"}, defaultMount, nil)) + require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, Repeated: true, ClientType: "non-entity"}, defaultMount, nil, time.Now().UTC())) require.Equal(t, month1Clients[2], thisMonth.clients[1]) // this will match the first two clients in month 1 - require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 2, Repeated: true}, defaultMount, nil)) + require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 2, Repeated: true}, defaultMount, nil, time.Now().UTC())) require.Equal(t, month1Clients[0:2], thisMonth.clients[2:4]) // this will match the first client in month 2 - require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2}, "identity", nil)) + require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2}, "identity", nil, time.Now().UTC())) require.Equal(t, month2Clients[0], thisMonth.clients[4]) // this will match the 3rd client in month 2 - require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, defaultMount, nil)) + require.NoError(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, defaultMount, nil, time.Now().UTC())) require.Equal(t, month2Clients[2], thisMonth.clients[5]) - require.Error(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, "other_mount", nil)) + require.Error(t, m.addRepeatedClients(0, &generation.Client{Count: 1, RepeatedFromMonth: 2, Namespace: "other_ns"}, "other_mount", nil, time.Now().UTC())) } // Test_singleMonthActivityClients_populateSegments calls populateSegments for a