@@ -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.
1722type 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.
8196func (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.
119132func (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.
189193func 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 {
0 commit comments