~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/golang.org/x/crypto/acme/autocert/renewal.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 The Go Authors. All rights reserved.
 
2
// Use of this source code is governed by a BSD-style
 
3
// license that can be found in the LICENSE file.
 
4
 
 
5
package autocert
 
6
 
 
7
import (
 
8
        "crypto"
 
9
        "sync"
 
10
        "time"
 
11
 
 
12
        "golang.org/x/net/context"
 
13
)
 
14
 
 
15
// maxRandRenew is a maximum deviation from Manager.RenewBefore.
 
16
const maxRandRenew = time.Hour
 
17
 
 
18
// domainRenewal tracks the state used by the periodic timers
 
19
// renewing a single domain's cert.
 
20
type domainRenewal struct {
 
21
        m      *Manager
 
22
        domain string
 
23
        key    crypto.Signer
 
24
 
 
25
        timerMu sync.Mutex
 
26
        timer   *time.Timer
 
27
}
 
28
 
 
29
// start starts a cert renewal timer at the time
 
30
// defined by the certificate expiration time exp.
 
31
//
 
32
// If the timer is already started, calling start is a noop.
 
33
func (dr *domainRenewal) start(exp time.Time) {
 
34
        dr.timerMu.Lock()
 
35
        defer dr.timerMu.Unlock()
 
36
        if dr.timer != nil {
 
37
                return
 
38
        }
 
39
        dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
 
40
}
 
41
 
 
42
// stop stops the cert renewal timer.
 
43
// If the timer is already stopped, calling stop is a noop.
 
44
func (dr *domainRenewal) stop() {
 
45
        dr.timerMu.Lock()
 
46
        defer dr.timerMu.Unlock()
 
47
        if dr.timer == nil {
 
48
                return
 
49
        }
 
50
        dr.timer.Stop()
 
51
        dr.timer = nil
 
52
}
 
53
 
 
54
// renew is called periodically by a timer.
 
55
// The first renew call is kicked off by dr.start.
 
56
func (dr *domainRenewal) renew() {
 
57
        dr.timerMu.Lock()
 
58
        defer dr.timerMu.Unlock()
 
59
        if dr.timer == nil {
 
60
                return
 
61
        }
 
62
 
 
63
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
 
64
        defer cancel()
 
65
        // TODO: rotate dr.key at some point?
 
66
        next, err := dr.do(ctx)
 
67
        if err != nil {
 
68
                next = maxRandRenew / 2
 
69
                next += time.Duration(pseudoRand.int63n(int64(next)))
 
70
        }
 
71
        dr.timer = time.AfterFunc(next, dr.renew)
 
72
        testDidRenewLoop(next, err)
 
73
}
 
74
 
 
75
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
 
76
// Instead, it requests a new certificate independently and, upon success,
 
77
// replaces dr.m.state item with a new one and updates cache for the given domain.
 
78
//
 
79
// It may return immediately if the expiration date of the currently cached cert
 
80
// is far enough in the future.
 
81
//
 
82
// The returned value is a time interval after which the renewal should occur again.
 
83
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
 
84
        // a race is likely unavoidable in a distributed environment
 
85
        // but we try nonetheless
 
86
        if tlscert, err := dr.m.cacheGet(dr.domain); err == nil {
 
87
                next := dr.next(tlscert.Leaf.NotAfter)
 
88
                if next > dr.m.renewBefore()+maxRandRenew {
 
89
                        return next, nil
 
90
                }
 
91
        }
 
92
 
 
93
        der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain)
 
94
        if err != nil {
 
95
                return 0, err
 
96
        }
 
97
        state := &certState{
 
98
                key:  dr.key,
 
99
                cert: der,
 
100
                leaf: leaf,
 
101
        }
 
102
        tlscert, err := state.tlscert()
 
103
        if err != nil {
 
104
                return 0, err
 
105
        }
 
106
        dr.m.cachePut(dr.domain, tlscert)
 
107
        dr.m.stateMu.Lock()
 
108
        defer dr.m.stateMu.Unlock()
 
109
        // m.state is guaranteed to be non-nil at this point
 
110
        dr.m.state[dr.domain] = state
 
111
        return dr.next(leaf.NotAfter), nil
 
112
}
 
113
 
 
114
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
 
115
        d := expiry.Sub(timeNow()) - dr.m.renewBefore()
 
116
        // add a bit of randomness to renew deadline
 
117
        n := pseudoRand.int63n(int64(maxRandRenew))
 
118
        d -= time.Duration(n)
 
119
        if d < 0 {
 
120
                return 0
 
121
        }
 
122
        return d
 
123
}
 
124
 
 
125
var testDidRenewLoop = func(next time.Duration, err error) {}