internal/pkg/testing/actions.go (147 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
//go:build integration
package testing
import (
"context"
"encoding/json"
"testing"
"time"
"github.com/elastic/fleet-server/v7/internal/pkg/bulk"
"github.com/elastic/fleet-server/v7/internal/pkg/model"
"github.com/elastic/fleet-server/v7/internal/pkg/testing/rnd"
"github.com/gofrs/uuid"
"github.com/rs/xid"
)
type CreateActionsConfig struct {
minAgentsCount int
maxAgentsCount int
minActionsCount int
maxActionsCount int
timestampOffset time.Duration // offset for the action timestamp from the current time
expirationOffset time.Duration // expiration offset since the action creation
}
type CreateActionsOpt func(c *CreateActionsConfig)
func CreateActionsWithMinAgentsCount(count int) CreateActionsOpt {
return func(c *CreateActionsConfig) {
c.minAgentsCount = count
}
}
func CreateActionsWithMaxAgentsCount(count int) CreateActionsOpt {
return func(c *CreateActionsConfig) {
c.maxAgentsCount = count
}
}
func CreateActionsWithMinActionsCount(count int) CreateActionsOpt {
return func(c *CreateActionsConfig) {
c.minActionsCount = count
}
}
func CreateActionsWithMaxActionsCount(count int) CreateActionsOpt {
return func(c *CreateActionsConfig) {
c.maxActionsCount = count
}
}
func CreateActionsWithTimestampOffset(timestampOffset time.Duration) CreateActionsOpt {
return func(c *CreateActionsConfig) {
c.timestampOffset = timestampOffset
}
}
func CreateActionsWithExpirationOffset(expirationOffset time.Duration) CreateActionsOpt {
return func(c *CreateActionsConfig) {
c.expirationOffset = expirationOffset
}
}
func CreateRandomActions(opts ...CreateActionsOpt) ([]model.Action, error) {
c := CreateActionsConfig{
minAgentsCount: 1,
maxAgentsCount: 1,
minActionsCount: 4, // previously hardcoded, using as default
maxActionsCount: 9, // previously hardcoded, using as default
expirationOffset: 5 * time.Minute, // default expiration of the action since the action timestamp
}
for _, opt := range opts {
opt(&c)
}
r := rnd.New()
sz := r.Int(c.minAgentsCount, c.maxAgentsCount)
agentIds := make([]string, sz)
for i := 0; i < sz; i++ {
agentIds[i] = uuid.Must(uuid.NewV4()).String()
}
sz = r.Int(c.minActionsCount, c.maxActionsCount)
now := time.Now().UTC()
actions := make([]model.Action, sz)
for i := 0; i < sz; i++ {
start := r.Int(0, len(agentIds))
end := start + r.Int(0, len(agentIds)-start)
payload := map[string]interface{}{
uuid.Must(uuid.NewV4()).String(): uuid.Must(uuid.NewV4()).String(),
}
data, err := json.Marshal(payload)
if err != nil {
return nil, err
}
aid := agentIds[start:end]
if len(aid) == 0 {
aid = nil
}
timestamp := r.Time(now, 1, 3, time.Second, rnd.TimeBefore)
if c.timestampOffset != 0 {
timestamp = timestamp.Add(c.timestampOffset)
}
expiration := timestamp.Add(c.expirationOffset)
action := model.Action{
ESDocument: model.ESDocument{
Id: xid.New().String(),
},
ActionID: uuid.Must(uuid.NewV4()).String(),
Timestamp: timestamp.Format(time.RFC3339),
Expiration: expiration.Format(time.RFC3339),
Type: "APP_ACTION",
InputType: "osquery",
Agents: aid,
Data: data,
}
actions[i] = action
}
return actions, nil
}
func StoreRandomAction(ctx context.Context, bulker bulk.Bulk, index string) ([]model.Action, error) {
actions, err := CreateRandomActions(
CreateActionsWithMinActionsCount(1),
CreateActionsWithMaxActionsCount(1),
)
if err != nil {
return nil, err
}
return actions, StoreActions(ctx, bulker, index, actions)
}
func StoreRandomActions(ctx context.Context, bulker bulk.Bulk, index string, min, max int) ([]model.Action, error) {
actions, err := CreateRandomActions(
CreateActionsWithMinAgentsCount(min),
CreateActionsWithMaxAgentsCount(max),
)
if err != nil {
return nil, err
}
return actions, StoreActions(ctx, bulker, index, actions)
}
func StoreActions(ctx context.Context, bulker bulk.Bulk, index string, actions []model.Action) error {
for _, action := range actions {
body, err := json.Marshal(action)
if err != nil {
return err
}
_, err = bulker.Create(ctx, index, action.Id, body, bulk.WithRefresh())
if err != nil {
return err
}
}
return nil
}
func SetupActions(ctx context.Context, t *testing.T, min, max int) (string, bulk.Bulk, []model.Action) {
// Could not use dl.FleetActions here because that would introduce
// cirucular dependency between "testing" and "dl" packages
// TODO(AM): Address later as cleanup
index, bulker := SetupCleanIndex(ctx, t, ".fleet-actions")
actions, err := StoreRandomActions(ctx, bulker, index, min, max)
if err != nil {
t.Fatal(err)
}
return index, bulker, actions
}