forked from git-ecosystem/trace2receiver
-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
173 lines (150 loc) · 5.42 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package trace2receiver
import (
"fmt"
"path/filepath"
"runtime"
"strings"
)
// `Config` represents the complete configuration settings for
// an individual receiver declaration from the `config.yaml`.
//
// These fields must be public (start with capital letter) so
// that the generic code in the collector can find them.
//
// We have different types of OS-specific paths where we listen
// for Trace2 telemetry. We allow both types in a single config
// file, so that we can share it between clients; only the
// correct one for the platform will actually be used.
type Config struct {
// On Windows, this is a named pipe. The canonical form is
// (the backslash spelling of) `//./pipe/<pipename>`.
//
// `CreateNamedPipeW()` documents that named pipes can only be
// created on the local NPFS and must use `.` rather than a
// general UNC hostname. (Subsequent clients can connect to
// a remote pipe, but a server can only CREATE a local one.
//
// Therefore, we allow the pipename to be abbreviated in the
// `config.yaml` as just `<pipename>` and assume the prefix.
//
// This config file field is ignored on non-Windows platforms.
NamedPipePath string `mapstructure:"pipe"`
// On Unix, this is a Unix domain socket. This is an absolute
// or relative pathname on the local file system. To avoid
// confusion with the existing Git Trace2 setup, we allow this
// to be of the form `af_unix:[<mode>:]<pathname>` and strip
// off the prefix.
//
// This config file field is ignored on Windows platforms.
UnixSocketPath string `mapstructure:"socket"`
// Allow command and control verbs to be embedded in the Trace2
// data stream.
AllowCommandControlVerbs bool `mapstructure:"enable_commands"`
// Pathname to YML file containing PII settings.
PiiSettingsPath string `mapstructure:"pii"`
piiSettings *PiiSettings
// Pathname to YML file containing our filter settings.
FilterSettingsPath string `mapstructure:"filter"`
filterSettings *FilterSettings
}
// `Validate()` checks if the receiver configuration is valid.
//
// This function is called once for each `trace2receiver[/<qualifier>]:`
// declaration (in the top-level `receivers:` section).
//
// The file format and the customer collector framework
// allows more than one instance of a `trace2receiver` to be
// defined (presumably with different source types, pathnames,
// or verbosity) and run concurrently within this process.
// See: https://opentelemetry.io/docs/collector/configuration/
//
// A receiver declaration does not imply that it will actually
// be instantiated (realized) in the factory. The receiver
// declaration causes a `cfg *Config` to be instantiated and
// that's it. (The instantiation in the factory is controlled
// by the `service.pipelines.traces.receivers:` array.)
func (cfg *Config) Validate() error {
var path string
var err error
if runtime.GOOS == "windows" {
if len(cfg.NamedPipePath) == 0 {
return fmt.Errorf("receivers.trace2receiver.pipe not defined")
}
path, err = normalize_named_pipe_path(cfg.NamedPipePath)
if err != nil {
return fmt.Errorf("receivers.trace2receiver.pipe invalid: '%s'",
err.Error())
}
cfg.NamedPipePath = path
} else {
if len(cfg.UnixSocketPath) == 0 {
return fmt.Errorf("receivers.trace2receiver.socket not defined")
}
path, err = normalize_uds_path(cfg.UnixSocketPath)
if err != nil {
return fmt.Errorf("receivers.trace2receiver.socket invalid: '%s'",
err.Error())
}
cfg.UnixSocketPath = path
}
if len(cfg.PiiSettingsPath) > 0 {
cfg.piiSettings, err = parsePiiFile(cfg.PiiSettingsPath)
if err != nil {
return err
}
}
if len(cfg.FilterSettingsPath) > 0 {
cfg.filterSettings, err = parseFilterSettings(cfg.FilterSettingsPath)
if err != nil {
return err
}
}
return nil
}
// Require (the backslash spelling of) `//./pipe/<pipename>` but allow
// `<pipename>` as an alias for the full spelling. Complain if given a
// regular UNC or drive letter pathname.
func normalize_named_pipe_path(in string) (string, error) {
in_lower := strings.ToLower(in) // normalize to lowercase
in_slash := filepath.Clean(in_lower) // normalize to backslashes
if strings.HasPrefix(in_slash, `\\.\pipe\`) {
// We were given a NPFS path. Use the original as is.
return in, nil
}
if strings.HasPrefix(in_slash, `\\`) {
// We were given a general UNC path. Reject it.
return "", fmt.Errorf(`expect '[\\.\pipe\]<pipename>'`)
}
if len(in) > 2 && in[1] == ':' {
// We have a drive letter. Reject it.
return "", fmt.Errorf(`expect '[\\.\pipe\]<pipename>'`)
}
// We cannot use `filepath.VolumeName()` or `filepath.Abs()`
// because they will be interpreted relative to the CWD
// which is not on the NPFS.
//
// So assume that this relative path is a shortcut and join it
// with our required prefix.
out := filepath.Join(`\\.\pipe`, in)
return out, nil
}
// Pathnames for Unix domain sockets are just normal Unix
// pathnames. However, we do allow an optional `af_unix:`
// or `af_unix:stream:` prefix. (This helps if they set it
// to the value of the GIT_TRACE2_EVENT string, which does
// require the prefix.)
func normalize_uds_path(in string) (string, error) {
p, found := strings.CutPrefix(in, "af_unix:stream:")
if found {
return p, nil
}
_, found = strings.CutPrefix(in, "af_unix:dgram:")
if found {
return "", fmt.Errorf("SOCK_DGRAM sockets are not supported")
}
p, found = strings.CutPrefix(in, "af_unix:")
if found {
return p, nil
}
return in, nil
}