Skip to content

Commit 189c0c7

Browse files
Move trace result writing from tracer package to triggertracer package (#151)
* Separate trace collection from result writing * Write trace results from triggertrace package * Disable broken docker build step
1 parent 80859e7 commit 189c0c7

File tree

7 files changed

+81
-50
lines changed

7 files changed

+81
-50
lines changed

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@ RUN mkdir -p /var/empty && \
4040
COPY --from=build_caller /go/bin/traceroute-caller /
4141
# Copy the dynamically-linked scamper binary and its associated libraries.
4242
COPY --from=build_tracers /scamper /usr/local
43+
4344
# Install fast-mda-traceroute from PyPI.
4445
# We build pycaracal from source to avoid pulling precompiled binaries.
45-
RUN pip3 install --no-binary pycaracal --no-cache-dir --verbose fast-mda-traceroute==0.1.10
46+
# TODO(#152): Build failures as of 2022-12-16. Re-enable once needed.
47+
# RUN pip3 install --no-binary pycaracal --no-cache-dir --verbose fast-mda-traceroute==0.1.10
48+
4649
# Run ldconfig to locate all new libraries and verify the tools we need
4750
# are available.
4851
RUN ldconfig && \

internal/ipcache/ipcache.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
// Tracer is the generic interface for all things that can perform a traceroute.
1414
type Tracer interface {
1515
Trace(remoteIP, uuid string, t time.Time) ([]byte, error)
16-
CachedTrace(uuid string, t time.Time, cachedTrace []byte) error
16+
CachedTrace(uuid string, t time.Time, cachedTrace []byte) ([]byte, error)
1717
DontTrace()
1818
}
1919

@@ -98,8 +98,8 @@ func (ic *IPCache) FetchTrace(remoteIP, uuid string) ([]byte, error) {
9898
ic.tracetool.DontTrace()
9999
return nil, cachedTrace.err
100100
}
101-
_ = ic.tracetool.CachedTrace(uuid, time.Now(), cachedTrace.data)
102-
return cachedTrace.data, nil
101+
data, err := ic.tracetool.CachedTrace(uuid, time.Now(), cachedTrace.data)
102+
return data, err
103103
}
104104
cachedTrace.data, cachedTrace.err = ic.tracetool.Trace(remoteIP, uuid, cachedTrace.timeStamp)
105105
close(cachedTrace.dataReady)

internal/ipcache/ipcache_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ func (ft *fakeTracer) Trace(remoteIP, uuid string, t time.Time) ([]byte, error)
2828
return []byte("fake traceroute data to " + remoteIP), nil
2929
}
3030

31-
func (ft *fakeTracer) CachedTrace(uuid string, t time.Time, cachedTest []byte) error {
31+
func (ft *fakeTracer) CachedTrace(uuid string, t time.Time, cachedTest []byte) ([]byte, error) {
3232
ft.nCachedTrace++
33-
return nil
33+
return cachedTest, nil
3434
}
3535

3636
func (ft *fakeTracer) DontTrace() {
@@ -132,10 +132,10 @@ func (pt *pausingTracer) Trace(remoteIP, uuid string, t time.Time) ([]byte, erro
132132
return []byte("fake traceroute data to " + remoteIP), nil
133133
}
134134

135-
func (pt *pausingTracer) CachedTrace(uuid string, t time.Time, cachedTest []byte) error {
135+
func (pt *pausingTracer) CachedTrace(uuid string, t time.Time, cachedTest []byte) ([]byte, error) {
136136
randomDelay()
137137
atomic.AddInt64(&pt.successes, 1)
138-
return nil
138+
return cachedTest, nil
139139
}
140140

141141
func (pt *pausingTracer) DontTrace() {

internal/triggertrace/triggertrace.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ type AnnotateAndArchiver interface {
5050
WriteAnnotations(map[string]*annotator.ClientAnnotations, time.Time) []error
5151
}
5252

53+
type TracerWriter interface {
54+
ipcache.Tracer
55+
WriteFile(uuid string, t time.Time, b []byte) error
56+
}
57+
5358
// Handler implements the tcp-info/eventsocket.Handler's interface.
5459
type Handler struct {
5560
Destinations map[string]Destination // key is UUID
@@ -58,11 +63,12 @@ type Handler struct {
5863
IPCache FetchTracer
5964
Parser ParseTracer
6065
HopAnnotator AnnotateAndArchiver
66+
Tracer TracerWriter
6167
done chan struct{} // For testing.
6268
}
6369

6470
// NewHandler returns a new instance of Handler.
65-
func NewHandler(ctx context.Context, tracetool ipcache.Tracer, ipcCfg ipcache.Config, newParser parser.TracerouteParser, haCfg hopannotation.Config) (*Handler, error) {
71+
func NewHandler(ctx context.Context, tracetool TracerWriter, ipcCfg ipcache.Config, newParser parser.TracerouteParser, haCfg hopannotation.Config) (*Handler, error) {
6672
ipCache, err := ipcache.New(ctx, tracetool, ipcCfg)
6773
if err != nil {
6874
return nil, err
@@ -81,6 +87,7 @@ func NewHandler(ctx context.Context, tracetool ipcache.Tracer, ipcCfg ipcache.Co
8187
IPCache: ipCache,
8288
Parser: newParser,
8389
HopAnnotator: hopCache,
90+
Tracer: tracetool,
8491
}, nil
8592
}
8693

@@ -145,13 +152,17 @@ func (h *Handler) traceAnnotateAndArchive(ctx context.Context, uuid string, dest
145152
log.Printf("context %p: failed to parse traceroute output (error: %v)\n", ctx, err)
146153
return
147154
}
155+
traceStartTime := parsedData.StartTime()
156+
err = h.Tracer.WriteFile(uuid, traceStartTime, rawData)
157+
if err != nil {
158+
log.Printf("context %p: failed to write trace file for uuid: %s: (error: %v)\n", ctx, uuid, err)
159+
}
160+
148161
hops := parsedData.ExtractHops()
149162
if len(hops) == 0 {
150163
log.Printf("context %p: failed to extract hops from traceroute %+v\n", ctx, string(rawData))
151164
return
152165
}
153-
154-
traceStartTime := parsedData.StartTime()
155166
annotations, allErrs := h.HopAnnotator.Annotate(ctx, hops, traceStartTime)
156167
if allErrs != nil {
157168
log.Printf("context %p: failed to annotate some or all hops (errors: %+v)\n", ctx, allErrs)

internal/triggertrace/triggertrace_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ func (ft *fakeTracer) Trace(remoteIP, uuid string, t time.Time) ([]byte, error)
5656
return content, nil
5757
}
5858

59-
func (ft *fakeTracer) CachedTrace(uuid string, t time.Time, cachedTest []byte) error {
59+
func (ft *fakeTracer) WriteFile(uuid string, t time.Time, data []byte) error {
60+
return nil
61+
}
62+
63+
func (ft *fakeTracer) CachedTrace(uuid string, t time.Time, cachedTest []byte) ([]byte, error) {
6064
defer func() { atomic.AddInt32(&ft.nCachedTraces, 1) }()
6165
fmt.Printf("\nCachedTrace()\n")
62-
return nil
66+
return nil, nil
6367
}
6468

6569
func (ft *fakeTracer) DontTrace() {

tracer/scamper.go

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import (
1313
"time"
1414
)
1515

16+
var (
17+
// ErrEmptyUUID is returned when a required UUID is empty.
18+
ErrEmptyUUID = errors.New("uuid is empty")
19+
)
20+
1621
// ScamperConfig contains configuration parameters of scamper.
1722
type ScamperConfig struct {
1823
Binary string
@@ -76,35 +81,43 @@ func NewScamper(cfg ScamperConfig) (*Scamper, error) {
7681
}, nil
7782
}
7883

84+
// WriteFile writes the given data to a file in the configured Scamper output
85+
// path using the given UUID and time.
86+
func (s *Scamper) WriteFile(uuid string, t time.Time, data []byte) error {
87+
filename, err := generateFilename(s.outputPath, uuid, t)
88+
if err != nil {
89+
return err
90+
}
91+
return os.WriteFile(filename, data, 0644)
92+
}
93+
7994
// Trace starts a new scamper process to run a traceroute based on the
8095
// traceroute type and saves it in a file.
8196
func (s *Scamper) Trace(remoteIP, uuid string, t time.Time) ([]byte, error) {
8297
tracesInProgress.WithLabelValues("scamper").Inc()
8398
defer tracesInProgress.WithLabelValues("scamper").Dec()
99+
if uuid == "" {
100+
return nil, ErrEmptyUUID
101+
}
84102
return s.trace(remoteIP, uuid, t)
85103
}
86104

87-
// CachedTrace creates a traceroute from the traceroute cache and saves it in a file.
88-
func (s *Scamper) CachedTrace(uuid string, t time.Time, cachedTrace []byte) error {
89-
filename, err := generateFilename(s.outputPath, uuid, t)
90-
if err != nil {
91-
log.Printf("failed to generate filename (error: %v)\n", err)
92-
tracerCacheErrors.WithLabelValues("scamper", err.Error()).Inc()
93-
return err
105+
// CachedTrace creates an updated traceroute using the given uuid and time based on the traceroute cache.
106+
func (s *Scamper) CachedTrace(uuid string, t time.Time, cachedTrace []byte) ([]byte, error) {
107+
if uuid == "" {
108+
return nil, ErrEmptyUUID
94109
}
95-
96110
// Remove the first line of the cached traceroute.
97111
split := bytes.Index(cachedTrace, []byte{'\n'})
98112
if split <= 0 || split == len(cachedTrace) {
99113
log.Printf("failed to split cached traceroute (split: %v)\n", split)
100114
tracerCacheErrors.WithLabelValues("scamper", "badcache").Inc()
101-
return errors.New("invalid cached traceroute")
115+
return nil, errors.New("invalid cached traceroute")
102116
}
103117

104118
// Create and add the first line to the cached traceroute.
105119
newTrace := append(createMetaline(uuid, true, extractUUID(cachedTrace[:split])), cachedTrace[split+1:]...)
106-
// Make the file readable so it won't be overwritten.
107-
return os.WriteFile(filename, []byte(newTrace), 0444)
120+
return []byte(newTrace), nil
108121
}
109122

110123
// DontTrace is called when a previous traceroute that we were waiting for
@@ -117,23 +130,15 @@ func (*Scamper) DontTrace() {
117130
// command line to invoke scamper varies depending on the traceroute type
118131
// and its options.
119132
func (s *Scamper) trace(remoteIP, uuid string, t time.Time) ([]byte, error) {
120-
// Make sure a directory path based on the current date exists,
121-
// generate a filename to save in that directory, and create
122-
// a buffer to hold traceroute data.
123-
filename, err := generateFilename(s.outputPath, uuid, t)
124-
if err != nil {
125-
return nil, err
126-
}
127-
128-
// Create a context, run a traceroute, and write the output to file.
133+
// Create a context, run a traceroute, and return the output.
129134
ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
130135
defer cancel()
131136
cmd := []string{s.binary, "-o-", "-O", "json", "-I", fmt.Sprintf("%s %s", s.cmd, remoteIP)}
132-
return traceAndWrite(ctx, "scamper", filename, cmd, uuid)
137+
return traceWithMeta(ctx, "scamper", cmd, uuid)
133138
}
134139

135-
// traceAndWrite runs a traceroute and writes the result.
136-
func traceAndWrite(ctx context.Context, label string, filename string, cmd []string, uuid string) ([]byte, error) {
140+
// traceWithMeta runs a traceroute and adds a metadata line to the result.
141+
func traceWithMeta(ctx context.Context, label string, cmd []string, uuid string) ([]byte, error) {
137142
data, err := runCmd(ctx, label, cmd)
138143
if err != nil {
139144
return nil, err
@@ -147,8 +152,7 @@ func traceAndWrite(ctx context.Context, label string, filename string, cmd []str
147152
// the buffer becomes too large, Write() will panic with ErrTooLarge.
148153
_, _ = buff.Write(createMetaline(uuid, false, ""))
149154
_, _ = buff.Write(data)
150-
// Make the file readable so it won't be overwritten.
151-
return buff.Bytes(), os.WriteFile(filename, buff.Bytes(), 0444)
155+
return buff.Bytes(), nil
152156
}
153157

154158
// runCmd runs the given command and returns its output.
@@ -188,7 +192,7 @@ func runCmd(ctx context.Context, label string, cmd []string) ([]byte, error) {
188192
// generateFilename creates the string filename for storing the data.
189193
func generateFilename(path, uuid string, t time.Time) (string, error) {
190194
if uuid == "" {
191-
return "", errors.New("uuid is empty")
195+
return "", ErrEmptyUUID
192196
}
193197
dir, err := createDatePath(path, t)
194198
if err != nil {

tracer/scamper_test.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func TestEmptyUUID(t *testing.T) {
8282
if err == nil || !strings.Contains(err.Error(), wantErr) {
8383
t.Errorf("Trace() = %v, want %q", err, wantErr)
8484
}
85-
err = s.CachedTrace("", time.Now(), []byte("does not matter"))
85+
_, err = s.CachedTrace("", time.Now(), []byte("does not matter"))
8686
if err == nil || !strings.Contains(err.Error(), wantErr) {
8787
t.Errorf("Trace() = %v, want %q", err, wantErr)
8888
}
@@ -139,6 +139,11 @@ func TestTrace(t *testing.T) {
139139
if strings.TrimSpace(got) != strings.TrimSpace(test.want) {
140140
t.Errorf("Trace() = %q, want %q", strings.TrimSpace(got), strings.TrimSpace(test.want))
141141
}
142+
err = s.WriteFile(uuid, now, out)
143+
if err != nil {
144+
t.Errorf("WriteFile() = %v, want nil", err)
145+
continue
146+
}
142147
// Make sure that the output was correctly written to file.
143148
out, err = os.ReadFile(filename)
144149
if err != nil {
@@ -175,16 +180,20 @@ func TestTraceWritesMeta(t *testing.T) {
175180
faketime := time.Date(2019, time.April, 1, 3, 45, 51, 0, time.UTC)
176181
prometheusx.GitShortCommit = "Fake Version"
177182
wantUUID := "0123456789"
178-
_, err = s.Trace("1.2.3.4", wantUUID, faketime)
183+
out, err := s.Trace("1.2.3.4", wantUUID, faketime)
179184
if err != nil {
180185
t.Errorf("Trace() = %v, want nil", err)
181186
}
187+
err = s.WriteFile(wantUUID, faketime, out)
188+
if err != nil {
189+
t.Errorf("WriteFile() = %v, want nil", err)
190+
}
182191

183192
// Unmarshal the first line of the output file.
184-
b, err := os.ReadFile(tempdir + "/2019/04/01/20190401T034551Z_" + wantUUID + ".jsonl")
193+
out, err = os.ReadFile(tempdir + "/2019/04/01/20190401T034551Z_" + wantUUID + ".jsonl")
185194
rtx.Must(err, "failed to read file")
186195
m := Metadata{}
187-
lines := strings.Split(string(b), "\n")
196+
lines := strings.Split(string(out), "\n")
188197
if len(lines) < 2 {
189198
t.Errorf("len(lines) = %d, want 2", len(lines))
190199
}
@@ -228,16 +237,16 @@ func TestCachedTrace(t *testing.T) {
228237
{"type":"tracelb", "version":"0.1", "userid":0, "method":"icmp-echo", "src":"::ffff:180.87.97.101", "dst":"::ffff:1.47.236.62", "start":{"sec":1566691298, "usec":476221, "ftime":"2019-08-25 00:01:38"}, "probe_size":60, "firsthop":1, "attempts":3, "confidence":95, "tos":0, "gaplimit":3, "wait_timeout":5, "wait_probe":250, "probec":0, "probec_max":3000, "nodec":0, "linkc":0}
229238
{"type":"cycle-stop", "list_name":"/tmp/scamperctrl:51811", "id":1, "hostname":"ndt-plh7v", "stop_time":1566691298}`)
230239

231-
_ = s.CachedTrace(uuid, faketime, []byte("Broken cached traceroute"))
232-
_, errInvalidTest := os.ReadFile(tempdir + "/2019/04/01/20190401T034551Z_" + uuid + ".jsonl")
233-
if errInvalidTest == nil {
234-
t.Error("CachedTrace() = nil, want error")
240+
_, err = s.CachedTrace(uuid, faketime, []byte("Broken cached traceroute"))
241+
if err == nil {
242+
t.Error("CacheTrace() returned nil error, want error")
235243
}
236244

237-
_ = s.CachedTrace(uuid, faketime, cachedTrace)
245+
b, err := s.CachedTrace(uuid, faketime, cachedTrace)
246+
if err != nil {
247+
t.Errorf("CacheTrace() = %v, want nil", err)
248+
}
238249
// Unmarshal the first line of the output file.
239-
b, err := os.ReadFile(tempdir + "/2019/04/01/20190401T034551Z_" + uuid + ".jsonl")
240-
rtx.Must(err, "failed to read file")
241250
m := Metadata{}
242251
lines := strings.Split(string(b), "\n")
243252
if len(lines) < 2 {

0 commit comments

Comments
 (0)