@@ -26,11 +26,12 @@ import (
26
26
"sync"
27
27
"time"
28
28
29
+ "github.com/transparency-dev/tessera"
29
30
"github.com/transparency-dev/tesseract/internal/otel"
30
31
"github.com/transparency-dev/tesseract/internal/types/rfc6962"
31
32
"github.com/transparency-dev/tesseract/internal/types/tls"
32
33
"github.com/transparency-dev/tesseract/internal/x509util"
33
- "github.com/transparency-dev/tessera "
34
+ "go.opentelemetry.io/otel/attribute "
34
35
"go.opentelemetry.io/otel/metric"
35
36
"k8s.io/klog/v2"
36
37
)
69
70
// setupMetrics initializes all the exported metrics.
70
71
func setupMetrics () {
71
72
// TODO(phboneff): add metrics for chain storage.
72
- // TODO(phboneff): add metrics for deduplication.
73
- // TODO(phboneff): break down metrics by whether or not the response has been deduped.
74
73
knownLogs = mustCreate (meter .Int64Gauge ("tesseract.known_logs" ,
75
74
metric .WithDescription ("Set to 1 for known logs" )))
76
75
@@ -108,25 +107,25 @@ type pathHandlers map[string]appHandler
108
107
type appHandler struct {
109
108
log * log
110
109
opts * HandlerOptions
111
- handler func (context.Context , * HandlerOptions , * log , http.ResponseWriter , * http.Request ) (int , error )
110
+ handler func (context.Context , * HandlerOptions , * log , http.ResponseWriter , * http.Request ) (int , []attribute. KeyValue , error )
112
111
name entrypointName
113
112
method string // http.MethodGet or http.MethodPost
114
113
}
115
114
116
115
// ServeHTTP for an AppHandler invokes the underlying handler function but
117
116
// does additional common error and stats processing.
118
117
func (a appHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
119
- var statusCode int
120
-
121
118
originAttr := originKey .String (a .log .origin )
122
119
operationAttr := operationKey .String (a .name )
123
- reqCounter .Add (r .Context (), 1 , metric .WithAttributes (originAttr , operationAttr ))
120
+ attrs := []attribute.KeyValue {originAttr , operationAttr }
121
+
122
+ reqCounter .Add (r .Context (), 1 , metric .WithAttributes (attrs ... ))
124
123
startTime := time .Now ()
125
124
logCtx := a .opts .RequestLog .start (r .Context ())
126
125
a .opts .RequestLog .origin (logCtx , a .log .origin )
127
126
defer func () {
128
127
latency := time .Since (startTime ).Seconds ()
129
- reqDuration .Record (r .Context (), latency , metric .WithAttributes (originAttr , operationAttr , codeKey . Int ( statusCode ) ))
128
+ reqDuration .Record (r .Context (), latency , metric .WithAttributes (attrs ... ))
130
129
}()
131
130
132
131
klog .V (2 ).Infof ("%s: request %v %q => %s" , a .log .origin , r .Method , r .URL , a .name )
@@ -153,11 +152,12 @@ func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
153
152
ctx , cancel := context .WithTimeout (logCtx , a .opts .Deadline )
154
153
defer cancel ()
155
154
156
- var err error
157
- statusCode , err = a .handler (ctx , a .opts , a .log , w , r )
155
+ statusCode , hattrs , err := a .handler (ctx , a .opts , a .log , w , r )
156
+ attrs = append (attrs , hattrs ... )
157
+ attrs = append (attrs , codeKey .Int (statusCode ))
158
158
a .opts .RequestLog .status (ctx , statusCode )
159
159
klog .V (2 ).Infof ("%s: %s <= st=%d" , a .log .origin , a .name , statusCode )
160
- rspCounter .Add (r .Context (), 1 , metric .WithAttributes (originAttr , operationAttr , codeKey . Int ( statusCode ) ))
160
+ rspCounter .Add (r .Context (), 1 , metric .WithAttributes (attrs ... ))
161
161
if err != nil {
162
162
klog .Warningf ("%s: %s handler error: %v" , a .log .origin , a .name , err )
163
163
a .opts .sendHTTPError (w , statusCode , err )
@@ -240,7 +240,7 @@ func parseBodyAsJSONChain(r *http.Request) (rfc6962.AddChainRequest, error) {
240
240
241
241
// addChainInternal is called by add-chain and add-pre-chain as the logic involved in
242
242
// processing these requests is almost identical
243
- func addChainInternal (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request , isPrecert bool ) (int , error ) {
243
+ func addChainInternal (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request , isPrecert bool ) (int , []attribute. KeyValue , error ) {
244
244
var method entrypointName
245
245
if isPrecert {
246
246
method = addPreChainName
@@ -251,15 +251,15 @@ func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w htt
251
251
// Check the contents of the request and convert to slice of certificates.
252
252
addChainReq , err := parseBodyAsJSONChain (r )
253
253
if err != nil {
254
- return http .StatusBadRequest , fmt .Errorf ("%s: failed to parse add-chain body: %s" , log .origin , err )
254
+ return http .StatusBadRequest , nil , fmt .Errorf ("%s: failed to parse add-chain body: %s" , log .origin , err )
255
255
}
256
256
// Log the DERs now because they might not parse as valid X.509.
257
257
for _ , der := range addChainReq .Chain {
258
258
opts .RequestLog .addDERToChain (ctx , der )
259
259
}
260
260
chain , err := log .chainValidator .Validate (addChainReq , isPrecert )
261
261
if err != nil {
262
- return http .StatusBadRequest , fmt .Errorf ("failed to verify add-chain contents: %s" , err )
262
+ return http .StatusBadRequest , nil , fmt .Errorf ("failed to verify add-chain contents: %s" , err )
263
263
}
264
264
for _ , cert := range chain {
265
265
opts .RequestLog .addCertToChain (ctx , cert )
@@ -271,74 +271,79 @@ func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w htt
271
271
272
272
entry , err := x509util .EntryFromChain (chain , isPrecert , timeMillis )
273
273
if err != nil {
274
- return http .StatusBadRequest , fmt .Errorf ("failed to build MerkleTreeLeaf: %s" , err )
274
+ return http .StatusBadRequest , nil , fmt .Errorf ("failed to build MerkleTreeLeaf: %s" , err )
275
275
}
276
276
277
277
if err := log .storage .AddIssuerChain (ctx , chain [1 :]); err != nil {
278
- return http .StatusInternalServerError , fmt .Errorf ("failed to store issuer chain: %s" , err )
278
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to store issuer chain: %s" , err )
279
279
}
280
280
281
281
klog .V (2 ).Infof ("%s: %s => storage.Add" , log .origin , method )
282
282
index , dedupedTimeMillis , err := log .storage .Add (ctx , entry )
283
283
if err != nil {
284
284
if errors .Is (err , tessera .ErrPushback ) {
285
285
w .Header ().Add ("Retry-After" , "1" )
286
- return http .StatusServiceUnavailable , fmt .Errorf ("received pushback from Tessera sequencer: %v" , err )
286
+ return http .StatusServiceUnavailable , nil , fmt .Errorf ("received pushback from Tessera sequencer: %v" , err )
287
287
}
288
- return http .StatusInternalServerError , fmt .Errorf ("couldn't store the leaf: %v" , err )
288
+ return http .StatusInternalServerError , nil , fmt .Errorf ("couldn't store the leaf: %v" , err )
289
+ }
290
+ dedupedAttribute := duplicateKey .Bool (false )
291
+ isDup := dedupedTimeMillis != timeMillis
292
+ if isDup {
293
+ dedupedAttribute = duplicateKey .Bool (true )
289
294
}
290
295
entry .Timestamp = dedupedTimeMillis
291
296
292
297
// Always use the returned leaf as the basis for an SCT.
293
298
var loggedLeaf rfc6962.MerkleTreeLeaf
294
299
leafValue := entry .MerkleTreeLeaf (index )
295
300
if rest , err := tls .Unmarshal (leafValue , & loggedLeaf ); err != nil {
296
- return http .StatusInternalServerError , fmt .Errorf ("failed to reconstruct MerkleTreeLeaf: %s" , err )
301
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to reconstruct MerkleTreeLeaf: %s" , err )
297
302
} else if len (rest ) > 0 {
298
- return http .StatusInternalServerError , fmt .Errorf ("extra data (%d bytes) on reconstructing MerkleTreeLeaf" , len (rest ))
303
+ return http .StatusInternalServerError , nil , fmt .Errorf ("extra data (%d bytes) on reconstructing MerkleTreeLeaf" , len (rest ))
299
304
}
300
305
301
306
// As the Log server has definitely got the Merkle tree leaf, we can
302
307
// generate an SCT and respond with it.
303
308
sct , err := log .signSCT (& loggedLeaf )
304
309
if err != nil {
305
- return http .StatusInternalServerError , fmt .Errorf ("failed to generate SCT: %s" , err )
310
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to generate SCT: %s" , err )
306
311
}
307
312
sctBytes , err := tls .Marshal (* sct )
308
313
if err != nil {
309
- return http .StatusInternalServerError , fmt .Errorf ("failed to marshall SCT: %s" , err )
314
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to marshall SCT: %s" , err )
310
315
}
311
316
// We could possibly fail to issue the SCT after this but it's v. unlikely.
312
317
opts .RequestLog .issueSCT (ctx , sctBytes )
313
318
err = marshalAndWriteAddChainResponse (sct , w )
314
319
if err != nil {
315
320
// reason is logged and http status is already set
316
- return http .StatusInternalServerError , fmt .Errorf ("failed to write response: %s" , err )
321
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to write response: %s" , err )
317
322
}
318
323
klog .V (3 ).Infof ("%s: %s <= SCT" , log .origin , method )
319
- if sct . Timestamp == timeMillis {
324
+ if ! isDup {
320
325
lastSCTTimestamp .Record (ctx , otel .Clamp64 (sct .Timestamp ), metric .WithAttributes (originKey .String (log .origin )))
321
326
lastSCTIndex .Record (ctx , otel .Clamp64 (index ), metric .WithAttributes (originKey .String (log .origin )))
322
327
}
323
328
324
- return http .StatusOK , nil
329
+ return http .StatusOK , []attribute. KeyValue { dedupedAttribute }, nil
325
330
}
326
331
327
- func addChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
332
+ func addChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , []attribute. KeyValue , error ) {
328
333
ctx , span := tracer .Start (ctx , "tesseract.addChain" )
329
334
defer span .End ()
330
335
331
336
return addChainInternal (ctx , opts , log , w , r , false )
332
337
}
333
338
334
- func addPreChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
339
+ func addPreChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , []attribute. KeyValue , error ) {
335
340
ctx , span := tracer .Start (ctx , "tesseract.addPreChain" )
336
341
defer span .End ()
337
342
338
343
return addChainInternal (ctx , opts , log , w , r , true )
339
344
}
340
345
341
- func getRoots (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , _ * http.Request ) (int , error ) {
346
+ func getRoots (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , _ * http.Request ) (int , []attribute. KeyValue , error ) {
342
347
_ , span := tracer .Start (ctx , "tesseract.getRoots" )
343
348
defer span .End ()
344
349
@@ -355,10 +360,10 @@ func getRoots(ctx context.Context, opts *HandlerOptions, log *log, w http.Respon
355
360
err := enc .Encode (jsonMap )
356
361
if err != nil {
357
362
klog .Warningf ("%s: get_roots failed: %v" , log .origin , err )
358
- return http .StatusInternalServerError , fmt .Errorf ("get-roots failed with: %s" , err )
363
+ return http .StatusInternalServerError , nil , fmt .Errorf ("get-roots failed with: %s" , err )
359
364
}
360
365
361
- return http .StatusOK , nil
366
+ return http .StatusOK , nil , nil
362
367
}
363
368
364
369
// marshalAndWriteAddChainResponse is used by add-chain and add-pre-chain to create and write
0 commit comments