gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/trace/config/config.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package config providides helper functions to configure trace sessions.
    16  package config
    17  
    18  import (
    19  	"bufio"
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"os/exec"
    25  	"strings"
    26  
    27  	"gvisor.dev/gvisor/pkg/sentry/seccheck"
    28  	"gvisor.dev/gvisor/runsc/boot"
    29  )
    30  
    31  // Builder helps with building of trace session configuration.
    32  type Builder struct {
    33  	points []seccheck.PointConfig
    34  	sinks  []seccheck.SinkConfig
    35  }
    36  
    37  // WriteInitConfig writes the current configuration in a format compatible with
    38  // the flag --pod-init-config.
    39  func (b *Builder) WriteInitConfig(w io.Writer) error {
    40  	init := &boot.InitConfig{
    41  		TraceSession: seccheck.SessionConfig{
    42  			Name:   seccheck.DefaultSessionName,
    43  			Points: b.points,
    44  			Sinks:  b.sinks,
    45  		},
    46  	}
    47  
    48  	encoder := json.NewEncoder(w)
    49  	return encoder.Encode(&init)
    50  }
    51  
    52  // LoadAllPoints enables all points together with all optional and context
    53  // fields.
    54  func (b *Builder) LoadAllPoints(runscPath string) error {
    55  	cmd := exec.Command(runscPath, "trace", "metadata")
    56  	out, err := cmd.CombinedOutput()
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	// The command above produces an output like the following:
    62  	//   POINTS (907)
    63  	//   Name: container/start, optional fields: [], context fields: [time|thread_id]
    64  	//
    65  	//   SINKS (2)
    66  	//   Name: remote
    67  	scanner := bufio.NewScanner(bytes.NewReader(out))
    68  	if !scanner.Scan() {
    69  		return fmt.Errorf("%q returned empty", cmd)
    70  	}
    71  	if line := scanner.Text(); !strings.HasPrefix(line, "POINTS (") {
    72  		return fmt.Errorf("%q missing POINTS header: %q", cmd, line)
    73  	}
    74  	for scanner.Scan() {
    75  		line := scanner.Text()
    76  		if len(line) == 0 {
    77  			continue // Skip empty lines.
    78  		}
    79  		if strings.HasPrefix(line, "SINKS (") {
    80  			break // Starting SINKS section, POINTS section is over.
    81  		}
    82  		elems := strings.Split(line, ",")
    83  		if len(elems) != 3 {
    84  			return fmt.Errorf("invalid line: %q", line)
    85  		}
    86  		name := strings.TrimPrefix(elems[0], "Name: ")
    87  		optFields, err := parseFields(elems[1], "optional fields: ")
    88  		if err != nil {
    89  			return err
    90  		}
    91  		ctxFields, err := parseFields(elems[2], "context fields: ")
    92  		if err != nil {
    93  			return err
    94  		}
    95  		b.points = append(b.points, seccheck.PointConfig{
    96  			Name:           name,
    97  			OptionalFields: optFields,
    98  			ContextFields:  ctxFields,
    99  		})
   100  	}
   101  	if len(b.points) == 0 {
   102  		return fmt.Errorf("%q returned no points", cmd)
   103  	}
   104  	return scanner.Err()
   105  }
   106  
   107  func parseFields(elem, prefix string) ([]string, error) {
   108  	stripped := strings.TrimPrefix(strings.TrimSpace(elem), prefix)
   109  	switch {
   110  	case len(stripped) < 2:
   111  		return nil, fmt.Errorf("invalid %s format: %q", prefix, elem)
   112  	case len(stripped) == 2:
   113  		return nil, nil
   114  	}
   115  	// Remove [] from `stripped`.
   116  	clean := stripped[1 : len(stripped)-1]
   117  	return strings.Split(clean, "|"), nil
   118  }
   119  
   120  // AddSink adds the sink to the configuration.
   121  func (b *Builder) AddSink(sink seccheck.SinkConfig) {
   122  	b.sinks = append(b.sinks, sink)
   123  }