-
Notifications
You must be signed in to change notification settings - Fork 46
/
status.go
163 lines (140 loc) · 4.92 KB
/
status.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
package otelcli
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/equinix-labs/otel-cli/otlpclient"
"github.com/spf13/cobra"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)
// StatusOutput captures all the data we want to print out for this subcommand
// and is also used in ../main_test.go for automated testing.
type StatusOutput struct {
Config Config `json:"config"`
Spans []map[string]string `json:"spans"`
SpanData map[string]string `json:"span_data"`
Env map[string]string `json:"env"`
Diagnostics Diagnostics `json:"diagnostics"`
Errors otlpclient.ErrorList `json:"errors"`
}
func statusCmd(config *Config) *cobra.Command {
cmd := cobra.Command{
Use: "status",
Short: "send at least one canary and dump status",
Long: `This subcommand is still experimental and the output format is not yet frozen.
By default just one canary is sent. When --canary-count is set, that number of canaries
are sent. If --canary-interval is set, status will sleep the specified duration
between canaries, up to --timeout (default 1s).
Example:
otel-cli status
otel-cli status --canary-count 10 --canary-interval 10 --timeout 10s
`,
Run: doStatus,
}
defaults := DefaultConfig()
cmd.Flags().IntVar(&config.StatusCanaryCount, "canary-count", defaults.StatusCanaryCount, "number of canaries to send")
cmd.Flags().StringVar(&config.StatusCanaryInterval, "canary-interval", defaults.StatusCanaryInterval, "number of milliseconds to wait between canaries")
addCommonParams(&cmd, config)
addClientParams(&cmd, config)
addSpanParams(&cmd, config)
return &cmd
}
func doStatus(cmd *cobra.Command, args []string) {
var err error
var exitCode int
allSpans := []map[string]string{}
ctx := cmd.Context()
config := getConfig(ctx)
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(config.GetTimeout()))
defer cancel()
ctx, client := StartClient(ctx, config)
env := make(map[string]string)
for _, e := range os.Environ() {
parts := strings.SplitN(e, "=", 2)
if len(parts) == 2 {
// TODO: this is just enough so I can sleep tonight.
// should be a list at top of file and needs a flag to turn it off
// TODO: for sure need to mask OTEL_EXPORTER_OTLP_HEADERS
if strings.Contains(strings.ToLower(parts[0]), "token") || parts[0] == "OTEL_EXPORTER_OTLP_HEADERS" {
env[parts[0]] = "--- redacted ---"
} else {
env[parts[0]] = parts[1]
}
} else {
config.SoftFail("BUG in otel-cli: this shouldn't happen")
}
}
var canaryCount int
var lastSpan *tracepb.Span
deadline := time.Now().Add(config.GetTimeout())
interval := config.ParseStatusCanaryInterval()
for {
// should be rare but a caller could request 0 canaries, in which case the
// client will be started and stopped, but no canaries sent
if config.StatusCanaryCount == 0 {
// TODO: remove this after SpanData is eliminated
lastSpan = otlpclient.NewProtobufSpan()
lastSpan.Name = "unsent canary"
break
}
span := config.NewProtobufSpan()
span.Name = "otel-cli status"
if canaryCount > 0 {
span.Name = fmt.Sprintf("otel-cli status canary %d", canaryCount)
}
span.Kind = tracepb.Span_SPAN_KIND_INTERNAL
// when doing multiple canaries, child each new span to the previous one
if lastSpan != nil {
span.TraceId = lastSpan.TraceId
span.ParentSpanId = lastSpan.SpanId
}
lastSpan = span
allSpans = append(allSpans, otlpclient.SpanToStringMap(span, nil))
// send it to the server. ignore errors here, they'll happen for sure
// and the base errors will be tunneled up through otlpclient.GetErrorList()
ctx, _ = otlpclient.SendSpan(ctx, client, config, span)
canaryCount++
if canaryCount == config.StatusCanaryCount {
break
} else if time.Now().After(deadline) {
break
} else {
time.Sleep(interval)
}
}
ctx, err = client.Stop(ctx)
if err != nil {
config.SoftFail("client.Stop() failed: %s", err)
}
// otlpclient saves all errors to a key in context so they can be used
// to validate assumptions here & in tests
errorList := otlpclient.GetErrorList(ctx)
// TODO: does it make sense to turn SpanData into a list of spans?
outData := StatusOutput{
Config: config,
Env: env,
Spans: allSpans,
// use only the last span's data here, leftover from when status only
// ever sent one canary
// legacy, will be removed once test suite is updated
SpanData: map[string]string{
"trace_id": hex.EncodeToString(lastSpan.TraceId),
"span_id": hex.EncodeToString(lastSpan.SpanId),
"is_sampled": strconv.FormatBool(config.GetIsRecording()),
},
// Diagnostics is deprecated, being replaced by Errors below and eventually
// another stringmap of stuff that was tunneled through context.Context
Diagnostics: Diag,
Errors: errorList,
}
js, err := json.MarshalIndent(outData, "", " ")
config.SoftFailIfErr(err)
os.Stdout.Write(js)
os.Stdout.WriteString("\n")
os.Exit(exitCode)
}