@@ -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,76 @@ 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
289
}
290
+ isDup := dedupedTimeMillis != timeMillis
291
+ dedupedAttribute := duplicateKey .Bool (isDup )
290
292
entry .Timestamp = dedupedTimeMillis
291
293
292
294
// Always use the returned leaf as the basis for an SCT.
293
295
var loggedLeaf rfc6962.MerkleTreeLeaf
294
296
leafValue := entry .MerkleTreeLeaf (index )
295
297
if rest , err := tls .Unmarshal (leafValue , & loggedLeaf ); err != nil {
296
- return http .StatusInternalServerError , fmt .Errorf ("failed to reconstruct MerkleTreeLeaf: %s" , err )
298
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to reconstruct MerkleTreeLeaf: %s" , err )
297
299
} else if len (rest ) > 0 {
298
- return http .StatusInternalServerError , fmt .Errorf ("extra data (%d bytes) on reconstructing MerkleTreeLeaf" , len (rest ))
300
+ return http .StatusInternalServerError , nil , fmt .Errorf ("extra data (%d bytes) on reconstructing MerkleTreeLeaf" , len (rest ))
299
301
}
300
302
301
303
// As the Log server has definitely got the Merkle tree leaf, we can
302
304
// generate an SCT and respond with it.
303
305
sct , err := log .signSCT (& loggedLeaf )
304
306
if err != nil {
305
- return http .StatusInternalServerError , fmt .Errorf ("failed to generate SCT: %s" , err )
307
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to generate SCT: %s" , err )
306
308
}
307
309
sctBytes , err := tls .Marshal (* sct )
308
310
if err != nil {
309
- return http .StatusInternalServerError , fmt .Errorf ("failed to marshall SCT: %s" , err )
311
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to marshall SCT: %s" , err )
310
312
}
311
313
// We could possibly fail to issue the SCT after this but it's v. unlikely.
312
314
opts .RequestLog .issueSCT (ctx , sctBytes )
313
315
err = marshalAndWriteAddChainResponse (sct , w )
314
316
if err != nil {
315
317
// reason is logged and http status is already set
316
- return http .StatusInternalServerError , fmt .Errorf ("failed to write response: %s" , err )
318
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to write response: %s" , err )
317
319
}
318
320
klog .V (3 ).Infof ("%s: %s <= SCT" , log .origin , method )
319
- if sct . Timestamp == timeMillis {
321
+ if ! isDup {
320
322
lastSCTTimestamp .Record (ctx , otel .Clamp64 (sct .Timestamp ), metric .WithAttributes (originKey .String (log .origin )))
321
323
lastSCTIndex .Record (ctx , otel .Clamp64 (index ), metric .WithAttributes (originKey .String (log .origin )))
322
324
}
323
325
324
- return http .StatusOK , nil
326
+ return http .StatusOK , []attribute. KeyValue { dedupedAttribute }, nil
325
327
}
326
328
327
- func addChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
329
+ func addChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , []attribute. KeyValue , error ) {
328
330
ctx , span := tracer .Start (ctx , "tesseract.addChain" )
329
331
defer span .End ()
330
332
331
333
return addChainInternal (ctx , opts , log , w , r , false )
332
334
}
333
335
334
- func addPreChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
336
+ func addPreChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , []attribute. KeyValue , error ) {
335
337
ctx , span := tracer .Start (ctx , "tesseract.addPreChain" )
336
338
defer span .End ()
337
339
338
340
return addChainInternal (ctx , opts , log , w , r , true )
339
341
}
340
342
341
- func getRoots (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , _ * http.Request ) (int , error ) {
343
+ func getRoots (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , _ * http.Request ) (int , []attribute. KeyValue , error ) {
342
344
_ , span := tracer .Start (ctx , "tesseract.getRoots" )
343
345
defer span .End ()
344
346
@@ -355,10 +357,10 @@ func getRoots(ctx context.Context, opts *HandlerOptions, log *log, w http.Respon
355
357
err := enc .Encode (jsonMap )
356
358
if err != nil {
357
359
klog .Warningf ("%s: get_roots failed: %v" , log .origin , err )
358
- return http .StatusInternalServerError , fmt .Errorf ("get-roots failed with: %s" , err )
360
+ return http .StatusInternalServerError , nil , fmt .Errorf ("get-roots failed with: %s" , err )
359
361
}
360
362
361
- return http .StatusOK , nil
363
+ return http .StatusOK , nil , nil
362
364
}
363
365
364
366
// marshalAndWriteAddChainResponse is used by add-chain and add-pre-chain to create and write
0 commit comments