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 }