Skip to content

Commit d2a2826

Browse files
jshacpu
authored andcommitted
Log fewer spurious CT submission errors. (letsencrypt#3447)
With the CT policy changes, we cancel any outstanding requests in a group once we've gotten a successful response from any log in that group. That means various function calls will return early with an error code indicating cancellation. We want to avoid logging such error codes, because they were not really errors, they were intentional. This change introduces a small utility package called "canceled", which checks for both context.Canceled and the gRPC return code indicating cancelled.
1 parent 007a75f commit d2a2826

File tree

5 files changed

+68
-20
lines changed

5 files changed

+68
-20
lines changed

canceled/canceled.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package canceled
2+
3+
import (
4+
"context"
5+
6+
"google.golang.org/grpc"
7+
"google.golang.org/grpc/codes"
8+
)
9+
10+
// Is returns true if err is non-nil and is either context.Canceled, or has a
11+
// grpc code of Canceled. This is useful because cancelations propagate through
12+
// gRPC boundaries, and if we choose to treat in-process cancellations a certain
13+
// way, we usually want to treat cross-process cancellations the same way.
14+
func Is(err error) bool {
15+
return err == context.Canceled || grpc.Code(err) == codes.Canceled
16+
}

canceled/canceled_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package canceled
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"google.golang.org/grpc"
9+
"google.golang.org/grpc/codes"
10+
)
11+
12+
func TestCanceled(t *testing.T) {
13+
if !Is(context.Canceled) {
14+
t.Errorf("Expected context.Canceled to be canceled, but wasn't.")
15+
}
16+
if !Is(grpc.Errorf(codes.Canceled, "hi")) {
17+
t.Errorf("Expected gRPC cancellation to be cancelled, but wasn't.")
18+
}
19+
if Is(errors.New("hi")) {
20+
t.Errorf("Expected random error to not be cancelled, but was.")
21+
}
22+
}

ctpolicy/ctpolicy.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77

8+
"github.com/letsencrypt/boulder/canceled"
89
"github.com/letsencrypt/boulder/cmd"
910
"github.com/letsencrypt/boulder/core"
1011
blog "github.com/letsencrypt/boulder/log"
@@ -50,7 +51,7 @@ func (ctp *CTPolicy) race(ctx context.Context, cert core.CertDER, group []cmd.Lo
5051
})
5152
if err != nil {
5253
// Only log the error if it is not a result of canceling subCtx
53-
if err != context.Canceled {
54+
if !canceled.Is(err) {
5455
ctp.log.Warning(fmt.Sprintf("ct submission to %q failed: %s", l.URI, err))
5556
}
5657
results <- result{err: err}
@@ -76,7 +77,7 @@ func (ctp *CTPolicy) race(ctx context.Context, cert core.CertDER, group []cmd.Lo
7677
// back from another log.
7778
}
7879
}
79-
return nil, errors.New("all submissions for group failed")
80+
return nil, errors.New("all submissions failed")
8081
}
8182

8283
// GetSCTs attempts to retrieve a SCT from each configured grouping of logs and returns
@@ -85,12 +86,15 @@ func (ctp *CTPolicy) GetSCTs(ctx context.Context, cert core.CertDER) ([]core.SCT
8586
results := make(chan result, len(ctp.groups))
8687
subCtx, cancel := context.WithCancel(ctx)
8788
defer cancel()
88-
for _, g := range ctp.groups {
89-
go func(g []cmd.LogDescription) {
89+
for i, g := range ctp.groups {
90+
go func(i int, g []cmd.LogDescription) {
9091
sct, err := ctp.race(subCtx, cert, g)
9192
// Only one of these will be non-nil
92-
results <- result{sct: sct, err: err}
93-
}(g)
93+
if err != nil {
94+
results <- result{err: fmt.Errorf("CT log group %d: %s", i, err)}
95+
}
96+
results <- result{sct: sct}
97+
}(i, g)
9498
}
9599

96100
var ret []core.SCTDER

ctpolicy/ctpolicy_test.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ctpolicy
33
import (
44
"context"
55
"errors"
6+
"regexp"
67
"testing"
78
"time"
89

@@ -38,12 +39,12 @@ func TestGetSCTs(t *testing.T) {
3839
expired, cancel := context.WithDeadline(context.Background(), time.Now())
3940
defer cancel()
4041
testCases := []struct {
41-
name string
42-
mock core.Publisher
43-
groups [][]cmd.LogDescription
44-
ctx context.Context
45-
result []core.SCTDER
46-
err error
42+
name string
43+
mock core.Publisher
44+
groups [][]cmd.LogDescription
45+
ctx context.Context
46+
result []core.SCTDER
47+
errRegexp *regexp.Regexp
4748
}{
4849
{
4950
name: "basic success case",
@@ -74,8 +75,8 @@ func TestGetSCTs(t *testing.T) {
7475
{URI: "ghi", Key: "jkl"},
7576
},
7677
},
77-
ctx: context.Background(),
78-
err: errors.New("all submissions for group failed"),
78+
ctx: context.Background(),
79+
errRegexp: regexp.MustCompile("CT log group .: all submissions failed"),
7980
},
8081
{
8182
name: "parent context timeout failure case",
@@ -90,8 +91,8 @@ func TestGetSCTs(t *testing.T) {
9091
{URI: "ghi", Key: "jkl"},
9192
},
9293
},
93-
ctx: expired,
94-
err: context.DeadlineExceeded,
94+
ctx: expired,
95+
errRegexp: regexp.MustCompile("CT log group .: context deadline exceeded"),
9596
},
9697
}
9798

@@ -101,8 +102,10 @@ func TestGetSCTs(t *testing.T) {
101102
ret, err := ctp.GetSCTs(tc.ctx, []byte{0})
102103
if tc.result != nil {
103104
test.AssertDeepEquals(t, ret, tc.result)
104-
} else if tc.err != nil {
105-
test.AssertDeepEquals(t, err, tc.err)
105+
} else if tc.errRegexp != nil {
106+
if !tc.errRegexp.MatchString(err.Error()) {
107+
t.Errorf("Error %q did not match expected regexp %q", err, tc.errRegexp)
108+
}
106109
}
107110
})
108111
}

publisher/publisher.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/prometheus/client_golang/prometheus"
1818
"golang.org/x/net/context"
1919

20+
"github.com/letsencrypt/boulder/canceled"
2021
"github.com/letsencrypt/boulder/core"
2122
blog "github.com/letsencrypt/boulder/log"
2223
"github.com/letsencrypt/boulder/metrics"
@@ -221,8 +222,10 @@ func (pub *Impl) SubmitToSingleCTWithResult(ctx context.Context, req *pubpb.Requ
221222
core.SerialToString(cert.SerialNumber),
222223
ctLog)
223224
if err != nil {
224-
pub.log.AuditErr(
225-
fmt.Sprintf("Failed to submit certificate to CT log at %s: %s", ctLog.uri, err))
225+
if !canceled.Is(err) {
226+
pub.log.AuditErr(
227+
fmt.Sprintf("Failed to submit certificate to CT log at %s: %s", ctLog.uri, err))
228+
}
226229
return nil, err
227230
}
228231

0 commit comments

Comments
 (0)