mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
Refactor the core of the lib (#700)
- Packages - Isolate code used by the CLI into the package `cmd` - (experimental) Add e2e tests for HTTP01, TLS-ALPN-01 and DNS-01, use [Pebble](https://github.com/letsencrypt/pebble) and [challtestsrv](https://github.com/letsencrypt/boulder/tree/master/test/challtestsrv) - Support non-ascii domain name (punnycode) - Check all challenges in a predictable order - No more global exported variables - Archive revoked certificates - Fixes revocation for subdomains and non-ascii domains - Disable pending authorizations - use pointer for RemoteError/ProblemDetails - Poll authz URL instead of challenge URL - The ability for a DNS provider to solve the challenge sequentially - Check all nameservers in a predictable order - Option to disable the complete propagation Requirement - CLI, support for renew with CSR - CLI, add SAN on renew - Add command to list certificates. - Logs every iteration of waiting for the propagation - update DNSimple client - update github.com/miekg/dns
This commit is contained in:
parent
4e842a5eb6
commit
42941ccea6
308 changed files with 16428 additions and 10265 deletions
304
e2e/loader/loader.go
Normal file
304
e2e/loader/loader.go
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
package loader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/platform/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
cmdNamePebble = "pebble"
|
||||
cmdNameChallSrv = "challtestsrv"
|
||||
)
|
||||
|
||||
type CmdOption struct {
|
||||
HealthCheckURL string
|
||||
Args []string
|
||||
Env []string
|
||||
Dir string
|
||||
}
|
||||
|
||||
type EnvLoader struct {
|
||||
PebbleOptions *CmdOption
|
||||
LegoOptions []string
|
||||
ChallSrv *CmdOption
|
||||
lego string
|
||||
}
|
||||
|
||||
func (l *EnvLoader) MainTest(m *testing.M) int {
|
||||
_, force := os.LookupEnv("LEGO_E2E_TESTS")
|
||||
if _, ci := os.LookupEnv("CI"); !ci && !force {
|
||||
fmt.Fprintln(os.Stderr, "skipping test: e2e tests are disabled. (no 'CI' or 'LEGO_E2E_TESTS' env var)")
|
||||
fmt.Println("PASS")
|
||||
return 0
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "skipping because git command not found")
|
||||
fmt.Println("PASS")
|
||||
return 0
|
||||
}
|
||||
|
||||
if l.PebbleOptions != nil {
|
||||
if _, err := exec.LookPath(cmdNamePebble); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "skipping because pebble binary not found")
|
||||
fmt.Println("PASS")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
if l.ChallSrv != nil {
|
||||
if _, err := exec.LookPath(cmdNameChallSrv); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "skipping because challtestsrv binary not found")
|
||||
fmt.Println("PASS")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
pebbleTearDown := l.launchPebble()
|
||||
defer pebbleTearDown()
|
||||
|
||||
challSrvTearDown := l.launchChallSrv()
|
||||
defer challSrvTearDown()
|
||||
|
||||
legoBinary, tearDown, err := buildLego()
|
||||
defer tearDown()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
|
||||
l.lego = legoBinary
|
||||
|
||||
if l.PebbleOptions != nil && l.PebbleOptions.HealthCheckURL != "" {
|
||||
pebbleHealthCheck(l.PebbleOptions)
|
||||
}
|
||||
|
||||
return m.Run()
|
||||
}
|
||||
|
||||
func (l *EnvLoader) RunLego(arg ...string) ([]byte, error) {
|
||||
cmd := exec.Command(l.lego, arg...)
|
||||
cmd.Env = l.LegoOptions
|
||||
|
||||
fmt.Printf("$ %s\n", strings.Join(cmd.Args, " "))
|
||||
|
||||
return cmd.CombinedOutput()
|
||||
}
|
||||
|
||||
func (l *EnvLoader) launchPebble() func() {
|
||||
if l.PebbleOptions == nil {
|
||||
return func() {}
|
||||
}
|
||||
|
||||
pebble, outPebble := l.cmdPebble()
|
||||
go func() {
|
||||
err := pebble.Run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return func() {
|
||||
err := pebble.Process.Kill()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(outPebble.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (l *EnvLoader) cmdPebble() (*exec.Cmd, *bytes.Buffer) {
|
||||
cmd := exec.Command(cmdNamePebble, l.PebbleOptions.Args...)
|
||||
cmd.Env = l.PebbleOptions.Env
|
||||
|
||||
dir, err := filepath.Abs(l.PebbleOptions.Dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cmd.Dir = dir
|
||||
|
||||
fmt.Printf("$ %s\n", strings.Join(cmd.Args, " "))
|
||||
|
||||
var b bytes.Buffer
|
||||
cmd.Stdout = &b
|
||||
cmd.Stderr = &b
|
||||
|
||||
return cmd, &b
|
||||
}
|
||||
|
||||
func pebbleHealthCheck(options *CmdOption) {
|
||||
client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
|
||||
err := wait.For(10*time.Second, 500*time.Millisecond, func() (bool, error) {
|
||||
resp, err := client.Get(options.HealthCheckURL)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *EnvLoader) launchChallSrv() func() {
|
||||
if l.ChallSrv == nil {
|
||||
return func() {}
|
||||
}
|
||||
|
||||
challtestsrv, outChalSrv := l.cmdChallSrv()
|
||||
go func() {
|
||||
err := challtestsrv.Run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return func() {
|
||||
err := challtestsrv.Process.Kill()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(outChalSrv.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (l *EnvLoader) cmdChallSrv() (*exec.Cmd, *bytes.Buffer) {
|
||||
cmd := exec.Command(cmdNameChallSrv, l.ChallSrv.Args...)
|
||||
|
||||
fmt.Printf("$ %s\n", strings.Join(cmd.Args, " "))
|
||||
|
||||
var b bytes.Buffer
|
||||
cmd.Stdout = &b
|
||||
cmd.Stderr = &b
|
||||
|
||||
return cmd, &b
|
||||
}
|
||||
|
||||
func buildLego() (string, func(), error) {
|
||||
here, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", func() {}, err
|
||||
}
|
||||
defer func() { _ = os.Chdir(here) }()
|
||||
|
||||
buildPath, err := ioutil.TempDir("", "lego_test")
|
||||
if err != nil {
|
||||
return "", func() {}, err
|
||||
}
|
||||
|
||||
projectRoot, err := getProjectRoot()
|
||||
if err != nil {
|
||||
return "", func() {}, err
|
||||
}
|
||||
|
||||
mainFolder := filepath.Join(projectRoot, "cmd", "lego")
|
||||
|
||||
err = os.Chdir(mainFolder)
|
||||
if err != nil {
|
||||
return "", func() {}, err
|
||||
}
|
||||
|
||||
binary := filepath.Join(buildPath, "lego")
|
||||
|
||||
err = build(binary)
|
||||
if err != nil {
|
||||
return "", func() {}, err
|
||||
}
|
||||
|
||||
err = os.Chdir(here)
|
||||
if err != nil {
|
||||
return "", func() {}, err
|
||||
}
|
||||
|
||||
return binary, func() {
|
||||
_ = os.RemoveAll(buildPath)
|
||||
CleanLegoFiles()
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getProjectRoot() (string, error) {
|
||||
git := exec.Command("git", "rev-parse", "--show-toplevel")
|
||||
|
||||
output, err := git.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", output)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
func build(binary string) error {
|
||||
toolPath, err := goToolPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command(toolPath, "build", "-o", binary)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", output)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func goToolPath() (string, error) {
|
||||
// inspired by go1.11.1/src/internal/testenv/testenv.go
|
||||
if os.Getenv("GO_GCFLAGS") != "" {
|
||||
return "", errors.New("'go build' not compatible with setting $GO_GCFLAGS")
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && strings.HasPrefix(runtime.GOARCH, "arm") {
|
||||
return "", fmt.Errorf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
return goTool()
|
||||
}
|
||||
|
||||
func goTool() (string, error) {
|
||||
var exeSuffix string
|
||||
if runtime.GOOS == "windows" {
|
||||
exeSuffix = ".exe"
|
||||
}
|
||||
|
||||
path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
goBin, err := exec.LookPath("go" + exeSuffix)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot find go tool: %v", err)
|
||||
}
|
||||
|
||||
return goBin, nil
|
||||
}
|
||||
|
||||
func CleanLegoFiles() {
|
||||
cmd := exec.Command("rm", "-rf", ".lego")
|
||||
fmt.Printf("$ %s\n", strings.Join(cmd.Args, " "))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue