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()
func newTestConfig(dir string) *Config {
c := Config{
Dirs: []string{filepath.Join(dir, "config")},
AccountKeyPath: filepath.Join(dir, "account.key"),
Email: "test@example.com",
}
c.Output.Path = filepath.Join(dir, "certs")
return &c
}
// 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"),
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())
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.configDirs[0], "../certs/example.com/cert.pem")
if _, err := os.Stat(p); err != nil {
t.Fatalf("file not created: %v", err)
}
p = filepath.Join(m.configDirs[0], "../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())
}
}