Skip to content

Add inital CTPolicy impl#3414

Merged
rolandshoemaker merged 8 commits into
masterfrom
ct-policy
Feb 6, 2018
Merged

Add inital CTPolicy impl#3414
rolandshoemaker merged 8 commits into
masterfrom
ct-policy

Conversation

@rolandshoemaker

@rolandshoemaker rolandshoemaker commented Jan 30, 2018

Copy link
Copy Markdown
Contributor

Adds a package which implements group based SCT retrieval.

Fixes #3412.

@rolandshoemaker rolandshoemaker requested review from cpu and jsha January 30, 2018 23:29
@rolandshoemaker

Copy link
Copy Markdown
Contributor Author

(Oh also: this doesn't implement publisher.SubmitToSingleCTWithResult yet, it may be possible to fit this into SubmitToSingleCT with some hackery but I'm not entirely sure, need to look at the protos more closely.)

@jsha jsha left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent turnaround on this, thanks! I think the code would gain some clarity by having types for SCTs and certs, i.e.:

type SCT []byte
type CertDER []byte

These could probably go in core/types.go and we could start using them in more places. Otherwise, the basic approach looks good.

Comment thread ctpolicy/ctpolicy.go Outdated
}
select {
case scts <- sct:
case <-subCtx.Done():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little weird to check for .Done() here, because there's no action to skip. AFAICT the idea is that all enqueues to scts after the first will block, so this provides a way out of the blocking, right? I think we actually don't need to check subCtx.Done() here, because the goal is to tell the deeper layers (like gRPC and HTTP) that we want to cancel.

Perhaps a more straightforward way to implement this function would be: Define a small struct containing {sct, err}, and make the goroutine very small: it calls SubmitToSingleCTWithResult and enqueues one of these structs onto a chan of size == len(group). Then run the for loop for exactly len(group) iterations, and return the first non-error result. If you fall off the end of the for loop, return the last error.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed OOB: while gRPC can cancel the RPC, without checking doing a parallel check of .Done() the goroutine we spawn here will live on forever until the write on scts is unblocked, which will never happen.

Comment thread ctpolicy/ctpolicy.go
func (ctp *CTPolicy) race(ctx context.Context, cert []byte, group []cmd.LogDescription) ([]byte, error) {
scts := make(chan []byte, 1)
errs := make(chan error, len(group))
subCtx, cancel := context.WithCancel(ctx)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally you should use defer cancel() here; otherwise you can leak resources.

Comment thread ctpolicy/ctpolicy.go Outdated
errs := make(chan error, len(ctp.groups))
subCtx, cancel := context.WithCancel(ctx)
for _, v := range ctp.groups {
wg.Add(1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function can also be simplified by the pattern I suggested above; you'd only need one chan.

@cpu cpu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seconding @jsha's ask for some concrete types. It's a WIP and this is probably on your radar already but I think the code would benefit from some more commenting throughout the bodies of the functions added.

Overall I think this is a solid approach! I think the intent is clear and the approach you took to managing the concurrency was very readable. Thanks!

Comment thread ctpolicy/ctpolicy.go Outdated
}

numErr := 0
for {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well make this a more traditional for loop with a loop variable, since that's what numErr acts as. Then instead of if numErr == len(group) we can just fall out the bottom of the loop and return the "all submissions for group failed" error there.

Comment thread ctpolicy/ctpolicy.go Outdated
}

go func() {
// The for loop below is blocked on the results channel being open,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make the for loop below go from 0 to len(ctp.groups)? Then you don't need to worry about blocking on the channel close.

Comment thread ctpolicy/ctpolicy.go Outdated
close(results)
}()

ret := []core.SCTDER{}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been leaning towards what I think is slightly more idiomatic style:

var ret []core.SCTDER{}

(which starts as nil).

Comment thread publisher/publisher.go Outdated
sct, err := ctLog.client.AddChain(ctx, chain)
took := time.Since(start).Seconds()
status := "success"
defer func() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why defer here? If we only care about the success or failure of the submission function, it would be more straightforward to do one stat increment inside the if err != nil clause (error) and one outside of it (success). We could put in a little helper function if we wanted to DRY it up a bit, but I don't think that is strictly necessary.

Deferring the stat increment has a somewhat minor unexpected effect where the success stat doesn't get incremented until after the SA write is successful, which could be a second or two if SA is slow.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly just to avoid calling the method twice, I'm fine with just doing that though if people don't really care :/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this is a case where the defer seems more "special" and forces you to think a little about what's going on, when we really want something straightforward.

For instance, when I first read this defer, I though "Oh, this is intended to let any of the error cases set the status to error, while the main case sets it to success. But in that case, the sense is wrong: The status field should start as error, and be set to success only if none of the error cases apply. Oh, but the second two error cases don't really measure errors during submission, so we wouldn't actually want that." So I'm hoping to short-circuit that thought process for future readers. :-)

@cpu cpu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @rolandshoemaker !

@cpu

cpu commented Feb 6, 2018

Copy link
Copy Markdown
Contributor

@rolandshoemaker Do you still think this needs additional test coverage? If not can you update the PR description to give a commit message for this that can be used with a squash merge.

@rolandshoemaker rolandshoemaker merged commit 62f3978 into master Feb 6, 2018
@cpu cpu deleted the ct-policy branch February 6, 2018 19:26
jsha pushed a commit that referenced this pull request Feb 8, 2018
And collect the metrics on success/failure rates. Built on top of #3414.

Fixes #3413.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants