Skip to content

Commit

Permalink
refactor(decl): improve logging attributes propagation
Browse files Browse the repository at this point in the history
Replace labels with baggages of supported key-value pairs. Augment
the propagated info and use them in logs.

Signed-off-by: Leonardo Di Giovanna <leonardodigiovanna1@gmail.com>
  • Loading branch information
ekoops committed Jan 9, 2025
1 parent cc8a0e2 commit a689b7a
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 216 deletions.
28 changes: 14 additions & 14 deletions cmd/declarative/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ const (
DescriptionFlagName = "description"
// TestIDFlagName is the name of the flag allowing to specify the test identifier.
TestIDFlagName = "test-id"
// LabelsFlagName is the name of the flag allowing to specify labels.
LabelsFlagName = "labels"
// BaggageFlagName is the name of the flag allowing to specify the baggage.
BaggageFlagName = "baggage"
// TimeoutFlagName is the name of the flag allowing to specify the test timeout.
TimeoutFlagName = "timeout"
)
Expand All @@ -51,8 +51,8 @@ type Config struct {
DescriptionEnvKey string
// TestIDEnvKey is the environment variable key corresponding to TestIDFlagName.
TestIDEnvKey string
// LabelsEnvKey is the environment variable key corresponding to LabelsFlagName.
LabelsEnvKey string
// BaggageEnvKey is the environment variable key corresponding to BaggageFlagName.
BaggageEnvKey string
// TimeoutEnvKey is the environment variable key corresponding to TimeoutFlagName.
TimeoutEnvKey string

Expand Down Expand Up @@ -82,9 +82,9 @@ type Config struct {
//
// A process having a test ID in the form <testUID> (i.e.: the leaf process) is the only one that is monitored.
TestID string
// Labels is the string containing the comma-separated list of labels in the form <labelX>=<labelXValue>. It is used
// for logging purposes and to potentially generate the child process/container labels.
Labels string
// Baggage is the string encoding a set of supported key-value pairs. It is used for logging purposes and to
// potentially generate the child process/container baggages.
Baggage string
}

var containerImagePullPolicies = map[builder.ImagePullPolicy][]string{
Expand All @@ -101,7 +101,7 @@ func New(declarativeEnvKey, envKeysPrefix string) *Config {
DescriptionFileEnvKey: envKeyFromFlagName(envKeysPrefix, DescriptionFileFlagName),
DescriptionEnvKey: envKeyFromFlagName(envKeysPrefix, DescriptionFlagName),
TestIDEnvKey: envKeyFromFlagName(envKeysPrefix, TestIDFlagName),
LabelsEnvKey: envKeyFromFlagName(envKeysPrefix, LabelsFlagName),
BaggageEnvKey: envKeyFromFlagName(envKeysPrefix, BaggageFlagName),
TimeoutEnvKey: envKeyFromFlagName(envKeysPrefix, TimeoutFlagName),
}
return commonConf
Expand Down Expand Up @@ -133,13 +133,13 @@ func (c *Config) InitCommandFlags(cmd *cobra.Command) {

// Hidden flags.
flags.StringVar(&c.TestID, TestIDFlagName, "",
"(used during process chain building) The test identifier in the form <ignorePrefix><testUID>. It is "+
"used to propagate the test UID to child processes/container in the process chain")
flags.StringVar(&c.Labels, LabelsFlagName, "",
"(used during process chain building) The list of comma-separated labels in the form <labelX>=<labelXValue>. "+
"It is used for logging purposes and to potentially generate the child process/container labels")
"(used during process chain building) The test identifier in the form <ignorePrefix><testUID>. It is used to "+
"propagate the test UID to child processes/container in the process chain")
flags.StringVar(&c.Baggage, BaggageFlagName, "",
"(used during process chain building) The string encoding a set of supported key-value pais. It is used for "+
"logging purposes and to potentially generate the child process/container baggage")
_ = flags.MarkHidden(TestIDFlagName)
_ = flags.MarkHidden(LabelsFlagName)
_ = flags.MarkHidden(BaggageFlagName)
}

// envKeyFromFlagName converts the provided flag name into the corresponding environment variable key.
Expand Down
96 changes: 58 additions & 38 deletions cmd/declarative/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ import (

"github.com/falcosecurity/event-generator/cmd/declarative/config"
"github.com/falcosecurity/event-generator/pkg/alert/retriever/grpcretriever"
"github.com/falcosecurity/event-generator/pkg/baggage"
containerbuilder "github.com/falcosecurity/event-generator/pkg/container/builder"
"github.com/falcosecurity/event-generator/pkg/label"
processbuilder "github.com/falcosecurity/event-generator/pkg/process/builder"
"github.com/falcosecurity/event-generator/pkg/test"
testbuilder "github.com/falcosecurity/event-generator/pkg/test/builder"
Expand Down Expand Up @@ -186,13 +186,13 @@ func (cw *CommandWrapper) run(cmd *cobra.Command, _ []string) {
os.Exit(1)
}

labels, err := label.ParseSet(cw.Labels)
baseBag, err := baggage.Parse(cw.Baggage)
if err != nil {
logger.Error(err, "Error parsing labels")
logger.Error(err, "Error parsing baggage")
cancelAndExit()
}

logger = enrichLoggerWithLabels(logger, labels)
logger = enrichLoggerWithBaggage(logger, baseBag)

testSuites, err := loadTestSuites(logger, cw.TestsDescriptionFile, cw.TestsDescription)
if err != nil {
Expand All @@ -206,10 +206,8 @@ func (cw *CommandWrapper) run(cmd *cobra.Command, _ []string) {
cancelAndExit()
}

// Retrieve the already populated test ID.
testID := cw.TestID
// The test ID absence is used to uniquely identify the root process in the process chain.
isRootProcess := testID == ""
isRootProcess := cw.TestID == ""

// globalWaitGroup accounts for all spawned goroutines, while testerWaitGroup accounts for all goroutines producing
// reports.
Expand Down Expand Up @@ -256,45 +254,45 @@ func (cw *CommandWrapper) run(cmd *cobra.Command, _ []string) {

// Prepare parameters shared by all runners.
runnerEnviron := cw.buildRunnerEnviron(cmd)
var runnerLabels *label.Set
if labels != nil {
runnerLabels = labels.Clone()
}

// Run test suites.
baseLogger := logger
for _, testSuite := range testSuites {
logger := logger.WithValues("testSuiteName", testSuite.RuleName)
logger.Info("Starting test suite execution...")

success := cw.runTestSuite(ctx, logger, testSuite, runnerBuilder, runnerEnviron, labels, runnerLabels, testr,
&globalWaitGroup, &testerWaitGroup, isRootProcess, testID)
logger := baseLogger.WithValues("testSuiteName", testSuite.RuleName)
logInfoIf(logger, isRootProcess, "Starting test suite execution...")
success := cw.runTestSuite(ctx, baseLogger, testSuite, runnerBuilder, runnerEnviron, baseBag, testr,
&globalWaitGroup, &testerWaitGroup, isRootProcess)
if !success {
logger.Info("Test suite execution failed")
logInfoIf(logger, isRootProcess, "Test suite execution failed")
waitAndExit()
}

logger.Info("Test suite execution completed")
logInfoIf(logger, isRootProcess, "Test suite execution completed")
}

testerWaitGroup.Wait()
cancel()
globalWaitGroup.Wait()
}

// enrichLoggerWithLabels creates a new logger, starting from the provided one, with the information extracted from the
// provided labels.
func enrichLoggerWithLabels(logger logr.Logger, labels *label.Set) logr.Logger {
if labels == nil {
// enrichLoggerWithBaggage creates a new logger, starting from the provided one, with the information extracted from the
// provided baggage.
func enrichLoggerWithBaggage(logger logr.Logger, bag *baggage.Baggage) logr.Logger {
if bag == nil {
return logger
}

logger = logger.WithValues("testIndex", labels.TestIndex, "procIndex", labels.ProcIndex, "inContainer",
labels.IsContainer)
if labels.IsContainer {
if imageName := labels.ImageName; imageName != "" {
logger = logger.WithValues("testSuiteName", bag.TestSuiteName, "testName", bag.TestName, "testSourceName",
bag.TestSourceName, "testSourceIndex", bag.TestSourceIndex)
if bag.ProcIndex != -1 {
logger = logger.WithValues("procIndex", bag.ProcIndex)
}
if bag.IsContainer {
logger = logger.WithValues("inContainer", bag.IsContainer)
if imageName := bag.ContainerImageName; imageName != "" {
logger = logger.WithValues("containerImageName", imageName)
}
if containerName := labels.ContainerName; containerName != "" {
if containerName := bag.ContainerName; containerName != "" {
logger = logger.WithValues("containerName", containerName)
}
}
Expand Down Expand Up @@ -411,20 +409,42 @@ func (cw *CommandWrapper) appendFlags(environ []string, flagSets ...*pflag.FlagS
return environ
}

// logInfoIf outputs to the provided logger the provided informational message only if the provided condition is true.
func logInfoIf(logger logr.Logger, cond bool, msg string) {
if cond {
logger.Info(msg)
}
}

// runTestSuite runs the provided test suite.
// TODO: simplify the following function signature once the termination logic is relaxed.
func (cw *CommandWrapper) runTestSuite(ctx context.Context, logger logr.Logger, testSuite *suite.Suite,
runnerBuilder runner.Builder, runnerEnviron []string, labels, runnerLabels *label.Set, testr tester.Tester,
globalWaitGroup, testerWaitGroup *sync.WaitGroup, isRootProcess bool, testID string) bool {
func (cw *CommandWrapper) runTestSuite(ctx context.Context, baseLogger logr.Logger, testSuite *suite.Suite,
runnerBuilder runner.Builder, runnerEnviron []string, baseBag *baggage.Baggage, testr tester.Tester,
globalWaitGroup, testerWaitGroup *sync.WaitGroup, isRootProcess bool) bool {
// Build and run the tests.
for _, testInfo := range testSuite.TestsInfo {
testIndex, testDesc := testInfo.Index, testInfo.Test
// Init baggage and logger for the current test.
var bag *baggage.Baggage
if baseBag != nil {
bag = baseBag.Clone()
}
logger := baseLogger

logger := logger.WithValues("testName", testDesc.Name)
testSourceName, testSourceIndex, testDesc := testInfo.SourceName, testInfo.Index, testInfo.Test

testID := cw.TestID
var testUID uuid.UUID
if isRootProcess {
logger = logger.WithValues("testIndex", testIndex)
// The root process must initialize the baggage.
bag = &baggage.Baggage{
TestSuiteName: testSuite.RuleName,
TestName: testDesc.Name,
TestSourceName: testSourceName,
TestSourceIndex: testSourceIndex,
// ProcIndex is set to -1 on the root process.
ProcIndex: -1,
}
logger = enrichLoggerWithBaggage(logger, bag)

// Generate a new UID for the test.
testUID = uuid.New()
Expand All @@ -449,22 +469,22 @@ func (cw *CommandWrapper) runTestSuite(ctx context.Context, logger logr.Logger,
TestDescriptionFileEnvKey: cw.DescriptionFileEnvKey,
TestIDEnvKey: cw.TestIDEnvKey,
TestIDIgnorePrefix: testIDIgnorePrefix,
LabelsEnvKey: cw.LabelsEnvKey,
Labels: runnerLabels,
BaggageEnvKey: cw.BaggageEnvKey,
Baggage: bag,
}
runnerInstance, err := runnerBuilder.Build(testDesc.Runner, runnerLogger, runnerDescription)
if err != nil {
logger.Error(err, "Error creating runner")
return false
}

logger.Info("Starting test execution...")
if err := runnerInstance.Run(ctx, testID, testIndex, testDesc); err != nil {
logInfoIf(logger, isRootProcess, "Starting test execution...")
if err := runnerInstance.Run(ctx, testID, testDesc); err != nil {
logRunnerError(logger, err)
return false
}

logger.Info("Test execution completed")
logInfoIf(logger, isRootProcess, "Test execution completed")

if testr != nil {
produceReport(globalWaitGroup, testerWaitGroup, testr, &testUID, testDesc, cw.reportFormat)
Expand Down
71 changes: 71 additions & 0 deletions pkg/baggage/baggage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2025 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package baggage

import (
"io"
"strings"

"github.com/goccy/go-yaml"
)

// Baggage stores values for the supported key-value pairs.
type Baggage struct {
TestSuiteName string `yaml:"testSuiteName"`
TestName string `yaml:"testName"`
TestSourceName string `yaml:"testSourceName"`
TestSourceIndex int `yaml:"testSourceIndex"`
// ProcIndex is set to -1 for the root process.
ProcIndex int `yaml:"procIndex"`
IsContainer bool `yaml:"isContainer,omitempty"`
ContainerImageName string `yaml:"containerImageName,omitempty"`
ContainerName string `yaml:"containerName,omitempty"`
}

// Write writes the key-value pairs to the provided writer.
func (b *Baggage) Write(w io.Writer) error {
return yaml.NewEncoder(w).Encode(b)
}

// Clone creates and returns a clone of the baggage.
func (b *Baggage) Clone() *Baggage {
baggage := &Baggage{
TestSuiteName: b.TestSuiteName,
TestName: b.TestName,
TestSourceName: b.TestSourceName,
TestSourceIndex: b.TestSourceIndex,
ProcIndex: b.ProcIndex,
IsContainer: b.IsContainer,
ContainerImageName: b.ContainerImageName,
ContainerName: b.ContainerName,
}
return baggage
}

// Parse parses the key-value pairs encoded in the provided string and returns the parsed baggage. If an empty string is
// provided, the function returns nil.
func Parse(baggage string) (*Baggage, error) {
if baggage == "" {
return nil, nil
}

b := &Baggage{ProcIndex: -1}
if err := yaml.NewDecoder(strings.NewReader(baggage)).Decode(b); err != nil {
return nil, err
}

return b, nil
}
11 changes: 5 additions & 6 deletions pkg/label/doc.go → pkg/baggage/doc.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2024 The Falco Authors
// Copyright (C) 2025 The Falco Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -13,8 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package label provides support for parsing a set of supported labels from a comma-separated list of values in the
// form <labelX>=<labelXValue>. The Set container is used to store the supported labels. A Set can be serialized (using
// the pre-defined aforementioned comma-separated format) and written to a generic destination through the Set.Write
// method.
package label
// Package baggage provides support for parsing and serializing a set of supported key-pair pairs. The Baggage type is
// used to store the pairs. A Baggage can be obtained by calling Parse on a stringified representation of the pairs. The
// stringified (YAML) representation can be obtained by calling Baggage.Write on a generic destination.
package baggage
Loading

0 comments on commit a689b7a

Please # to comment.