99 "net"
1010 "os"
1111 "slices"
12+ "strconv"
13+ "strings"
1214 "time"
1315
1416 "github.com/containerd/console"
@@ -34,51 +36,55 @@ type traceOptions struct {
3436 addr string
3537}
3638
37- func runTrace (ctx context.Context , dockerCli command.Cli , opts traceOptions ) error {
38- b , err := builder .New (dockerCli , builder .WithName (opts .builder ))
39- if err != nil {
40- return err
41- }
42-
43- nodes , err := b .LoadNodes (ctx )
44- if err != nil {
45- return err
46- }
47- for _ , node := range nodes {
48- if node .Err != nil {
49- return node .Err
39+ func loadTrace (ctx context.Context , ref string , nodes []builder.Node ) (string , []byte , error ) {
40+ var offset * int
41+ if strings .HasPrefix (ref , "^" ) {
42+ off , err := strconv .Atoi (ref [1 :])
43+ if err != nil {
44+ return "" , nil , errors .Wrapf (err , "invalid offset %q" , ref )
5045 }
46+ offset = & off
47+ ref = ""
5148 }
5249
53- recs , err := queryRecords (ctx , opts . ref , nodes )
50+ recs , err := queryRecords (ctx , ref , nodes )
5451 if err != nil {
55- return err
52+ return "" , nil , err
5653 }
5754
5855 var rec * historyRecord
5956
60- if opts . ref == "" {
57+ if ref == "" {
6158 slices .SortFunc (recs , func (a , b historyRecord ) int {
6259 return b .CreatedAt .AsTime ().Compare (a .CreatedAt .AsTime ())
6360 })
6461 for _ , r := range recs {
6562 if r .CompletedAt != nil {
63+ if offset != nil {
64+ if * offset > 0 {
65+ * offset --
66+ continue
67+ }
68+ }
6669 rec = & r
6770 break
6871 }
6972 }
73+ if offset != nil && * offset > 0 {
74+ return "" , nil , errors .Errorf ("no completed build found with offset %d" , * offset )
75+ }
7076 } else {
7177 rec = & recs [0 ]
7278 }
7379 if rec == nil {
74- if opts . ref == "" {
75- return errors .New ("no records found" )
80+ if ref == "" {
81+ return "" , nil , errors .New ("no records found" )
7682 }
77- return errors .Errorf ("no record found for ref %q" , opts . ref )
83+ return "" , nil , errors .Errorf ("no record found for ref %q" , ref )
7884 }
7985
8086 if rec .CompletedAt == nil {
81- return errors .Errorf ("build %q is not completed, only completed builds can be traced" , rec .Ref )
87+ return "" , nil , errors .Errorf ("build %q is not completed, only completed builds can be traced" , rec .Ref )
8288 }
8389
8490 if rec .Trace == nil {
@@ -87,34 +93,34 @@ func runTrace(ctx context.Context, dockerCli command.Cli, opts traceOptions) err
8793
8894 c , err := rec .node .Driver .Client (ctx )
8995 if err != nil {
90- return err
96+ return "" , nil , err
9197 }
9298 _ , err = c .ControlClient ().UpdateBuildHistory (ctx , & controlapi.UpdateBuildHistoryRequest {
9399 Ref : rec .Ref ,
94100 Finalize : true ,
95101 })
96102 if err != nil {
97- return err
103+ return "" , nil , err
98104 }
99105
100106 recs , err := queryRecords (ctx , rec .Ref , []builder.Node {* rec .node })
101107 if err != nil {
102- return err
108+ return "" , nil , err
103109 }
104110
105111 if len (recs ) == 0 {
106- return errors .Errorf ("build record %q was deleted" , rec .Ref )
112+ return "" , nil , errors .Errorf ("build record %q was deleted" , rec .Ref )
107113 }
108114
109115 rec = & recs [0 ]
110116 if rec .Trace == nil {
111- return errors .Errorf ("build record %q is missing a trace" , rec .Ref )
117+ return "" , nil , errors .Errorf ("build record %q is missing a trace" , rec .Ref )
112118 }
113119 }
114120
115121 c , err := rec .node .Driver .Client (ctx )
116122 if err != nil {
117- return err
123+ return "" , nil , err
118124 }
119125
120126 store := proxy .NewContentStore (c .ContentClient ())
@@ -125,12 +131,12 @@ func runTrace(ctx context.Context, dockerCli command.Cli, opts traceOptions) err
125131 Size : rec .Trace .Size ,
126132 })
127133 if err != nil {
128- return err
134+ return "" , nil , err
129135 }
130136
131137 spans , err := otelutil .ParseSpanStubs (io .NewSectionReader (ra , 0 , ra .Size ()))
132138 if err != nil {
133- return err
139+ return "" , nil , err
134140 }
135141
136142 wrapper := struct {
@@ -139,31 +145,54 @@ func runTrace(ctx context.Context, dockerCli command.Cli, opts traceOptions) err
139145 Data : spans .JaegerData ().Data ,
140146 }
141147
142- var term bool
143- if _ , err := console .ConsoleFromFile (os .Stdout ); err == nil {
144- term = true
145- }
146-
147148 if len (wrapper .Data ) == 0 {
148- return errors .New ("no trace data" )
149- }
150-
151- if ! term {
152- enc := json .NewEncoder (dockerCli .Out ())
153- enc .SetIndent ("" , " " )
154- return enc .Encode (wrapper )
149+ return "" , nil , errors .New ("no trace data" )
155150 }
156151
157- srv := jaegerui .NewServer (jaegerui.Config {})
158-
159152 buf := & bytes.Buffer {}
160153 enc := json .NewEncoder (buf )
161154 enc .SetIndent ("" , " " )
162155 if err := enc .Encode (wrapper ); err != nil {
156+ return "" , nil , err
157+ }
158+
159+ return string (wrapper .Data [0 ].TraceID ), buf .Bytes (), nil
160+ }
161+
162+ func runTrace (ctx context.Context , dockerCli command.Cli , opts traceOptions ) error {
163+ b , err := builder .New (dockerCli , builder .WithName (opts .builder ))
164+ if err != nil {
165+ return err
166+ }
167+
168+ nodes , err := b .LoadNodes (ctx )
169+ if err != nil {
170+ return err
171+ }
172+ for _ , node := range nodes {
173+ if node .Err != nil {
174+ return node .Err
175+ }
176+ }
177+
178+ traceid , data , err := loadTrace (ctx , opts .ref , nodes )
179+ if err != nil {
163180 return err
164181 }
165182
166- if err := srv .AddTrace (string (wrapper .Data [0 ].TraceID ), bytes .NewReader (buf .Bytes ())); err != nil {
183+ var term bool
184+ if _ , err := console .ConsoleFromFile (os .Stdout ); err == nil {
185+ term = true
186+ }
187+
188+ if ! term {
189+ fmt .Fprintln (dockerCli .Out (), string (data ))
190+ return nil
191+ }
192+
193+ srv := jaegerui .NewServer (jaegerui.Config {})
194+
195+ if err := srv .AddTrace (traceid , bytes .NewReader (data )); err != nil {
167196 return err
168197 }
169198
@@ -172,7 +201,7 @@ func runTrace(ctx context.Context, dockerCli command.Cli, opts traceOptions) err
172201 return err
173202 }
174203
175- url := "http://" + ln .Addr ().String () + "/trace/" + string ( wrapper . Data [ 0 ]. TraceID )
204+ url := "http://" + ln .Addr ().String () + "/trace/" + traceid
176205
177206 go func () {
178207 time .Sleep (100 * time .Millisecond )
0 commit comments