Newer
Older
package acmeserver
import (
"context"
"crypto"
"crypto/x509"
// A CertGenerator that is just very slow (and will return an error
// in any case).
type slowACME struct{}
func (s *slowACME) GetCertificate(ctx context.Context, _ crypto.Signer, _ *certConfig) ([][]byte, *x509.Certificate, error) {
t := time.After(60 * time.Second)
select {
case <-t:
return nil, nil, errors.New("timed out")
case <-ctx.Done():
return nil, nil, ctx.Err()
// Create a new test function.
// The first function returned is a cleanup callback.
// The second function returned is the cancel callback.
func newTestManager(t testing.TB, g CertGenerator) (func(), context.CancelFunc, *Manager) {
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
os.Mkdir(filepath.Join(dir, "config"), 0700)
ioutil.WriteFile(
filepath.Join(dir, "config", "test.yml"),
0644,
)
m, err := NewManager(&Config{
Dir: dir,
Email: "test@example.com",
ctx, cancel := context.WithCancel(context.Background())
if err := m.Start(ctx); err != nil {
t.Fatal("Start:", err)
}
// Wait just a little bit to give a chance to m.loop() to run.
time.Sleep(50 * time.Millisecond)
}
func TestManager_Reload(t *testing.T) {
cleanup, _, m := newTestManager(t, NewSelfSignedCertGenerator())
// Data race: we read data owned by another goroutine!
if len(m.certs) < 1 {
t.Fatal("configuration not loaded?")
}
if m.certs[0].cn() != "example.com" {
t.Fatalf("certs[0].cn() is %s, expected example.com", m.certs[0].cn())
}
// Try a reload, catch obvious errors.
m.Reload()
time.Sleep(50 * time.Millisecond)
if len(m.certs) != 1 {
t.Fatalf("certs count is %d, expected 1", len(m.certs))
}
if m.certs[0].cn() != "example.com" {
t.Fatalf("certs[0].cn() is %s, expected example.com", m.certs[0].cn())
cleanup, _, m := newTestManager(t, NewSelfSignedCertGenerator())
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
defer cleanup()
now := time.Now()
ci := m.certs[0]
if ci.retryDeadline.After(now) {
t.Fatalf("retry deadline is in the future: %v", ci.retryDeadline)
}
testUpdateCh <- true
time.Sleep(100 * time.Millisecond)
// Verify that the retry/renewal timestamp is in the future.
if ci.retryDeadline.Before(now) {
t.Fatalf("retry deadline is in the past after renewal: %v", ci.retryDeadline)
}
// Do we think we have a valid certificate?
if !ci.valid {
t.Fatal("we don't have a valid certificate")
}
// Verify that the credentials have successfully been written
// to storage.
p := filepath.Join(m.configDir, "../certs/example.com/cert.pem")
if _, err := os.Stat(p); err != nil {
t.Fatalf("file not created: %v", err)
}
p = filepath.Join(m.configDir, "../certs/example.com/private_key.pem")
if _, err := os.Stat(p); err != nil {
t.Fatalf("file not created: %v", err)
}
// By triggering a reload now, we should cause the Manager to
// reload the certificate from storage.
m.Reload()
time.Sleep(50 * time.Millisecond)
ci = m.certs[0]
if !ci.valid {
t.Fatal("certificate is invalid after a reload")
}
}
func TestManager_CancelUpdate(t *testing.T) {
start := time.Now()
cleanup, cancel, m := newTestManager(t, &slowACME{})
defer cleanup()
time.Sleep(100 * time.Millisecond)
cancel()
m.Wait()
elapsed := time.Since(start)
if elapsed > 1*time.Second {
t.Fatalf("too much time elapsed (%fs), canceling didn't work?", elapsed.Seconds())
}
}