mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 06:03:48 +03:00
noot
This commit is contained in:
parent
8e0d3e1ec5
commit
93a1853022
6 changed files with 99 additions and 68 deletions
6
caddy.go
6
caddy.go
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
@ -778,7 +779,10 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
|
||||||
} else {
|
} else {
|
||||||
logger.Error("unclean shutdown")
|
logger.Error("unclean shutdown")
|
||||||
}
|
}
|
||||||
os.Exit(exitCode)
|
// check if we are in test environment, and dont call exit if we are
|
||||||
|
if flag.Lookup("test.v") == nil || strings.Contains(os.Args[0], ".test") {
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if remoteAdminServer != nil {
|
if remoteAdminServer != nil {
|
||||||
|
|
|
@ -81,6 +81,10 @@ func NewTester(t testing.TB) *Tester {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tester) T() testing.TB {
|
||||||
|
return t.t
|
||||||
|
}
|
||||||
|
|
||||||
type configLoadError struct {
|
type configLoadError struct {
|
||||||
Response string
|
Response string
|
||||||
}
|
}
|
||||||
|
@ -92,33 +96,37 @@ func timeElapsed(start time.Time, name string) {
|
||||||
log.Printf("%s took %s", name, elapsed)
|
log.Printf("%s took %s", name, elapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitServer this will configure the server with a configurion of a specific
|
// launch caddy will start the server
|
||||||
// type. The configType must be either "json" or the adapter type.
|
func (tc *Tester) LaunchCaddy() {
|
||||||
func (tc *Tester) InitServer(rawConfig string, configType string) {
|
if err := tc.startServer(); err != nil {
|
||||||
if err := tc.initServer(rawConfig, configType); err != nil {
|
tc.t.Logf("failed to start server: %s", err)
|
||||||
tc.t.Logf("failed to load config: %s", err)
|
|
||||||
tc.t.Fail()
|
tc.t.Fail()
|
||||||
}
|
}
|
||||||
if err := tc.ensureConfigRunning(rawConfig, configType); err != nil {
|
}
|
||||||
|
|
||||||
|
// DEPRECATED: InitServer
|
||||||
|
// Initserver is shorthand for LaunchCaddy() and LoadConfig(rawConfig, configType string)
|
||||||
|
func (tc *Tester) InitServer(rawConfig string, configType string) {
|
||||||
|
if err := tc.startServer(); err != nil {
|
||||||
|
tc.t.Logf("failed to start server: %s", err)
|
||||||
|
tc.t.Fail()
|
||||||
|
}
|
||||||
|
if err := tc.LoadConfig(rawConfig, configType); err != nil {
|
||||||
tc.t.Logf("failed ensuring config is running: %s", err)
|
tc.t.Logf("failed ensuring config is running: %s", err)
|
||||||
tc.t.Fail()
|
tc.t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitServer this will configure the server with a configurion of a specific
|
func (tc *Tester) startServer() error {
|
||||||
// type. The configType must be either "json" or the adapter type.
|
|
||||||
func (tc *Tester) initServer(rawConfig string, configType string) error {
|
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
tc.t.SkipNow()
|
tc.t.SkipNow()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
err := validateAndStartServer(tc.t)
|
||||||
err := validateTestPrerequisites(tc.t)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
|
tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tc.t.Cleanup(func() {
|
tc.t.Cleanup(func() {
|
||||||
if tc.t.Failed() && tc.configLoaded {
|
if tc.t.Failed() && tc.configLoaded {
|
||||||
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
|
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
|
||||||
|
@ -133,8 +141,35 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
|
||||||
_ = json.Indent(&out, body, "", " ")
|
_ = json.Indent(&out, body, "", " ")
|
||||||
tc.t.Logf("----------- failed with config -----------\n%s", out.String())
|
tc.t.Logf("----------- failed with config -----------\n%s", out.String())
|
||||||
}
|
}
|
||||||
})
|
// now shutdown the server, since the test is done.
|
||||||
|
|
||||||
|
_, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", Default.AdminPort), "", nil)
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Log("couldn't stop admin server")
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
// try ensure the admin api is stopped three times.
|
||||||
|
for retries := 0; retries < 3; retries++ {
|
||||||
|
if isCaddyAdminRunning() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
tc.t.Log("timed out waiting for admin server to stop")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *Tester) MustLoadConfig(rawConfig string, configType string) {
|
||||||
|
if err := tc.LoadConfig(rawConfig, configType); err != nil {
|
||||||
|
tc.t.Logf("failed ensuring config is running: %s", err)
|
||||||
|
tc.t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig loads the config to the tester server and also ensures that the config was loaded
|
||||||
|
func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
|
||||||
|
originalRawConfig := rawConfig
|
||||||
rawConfig = prependCaddyFilePath(rawConfig)
|
rawConfig = prependCaddyFilePath(rawConfig)
|
||||||
// normalize JSON config
|
// normalize JSON config
|
||||||
if configType == "json" {
|
if configType == "json" {
|
||||||
|
@ -185,7 +220,7 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
tc.configLoaded = true
|
tc.configLoaded = true
|
||||||
return nil
|
return tc.ensureConfigRunning(originalRawConfig, configType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error {
|
func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error {
|
||||||
|
@ -226,7 +261,9 @@ func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error
|
||||||
return actual
|
return actual
|
||||||
}
|
}
|
||||||
|
|
||||||
for retries := 10; retries > 0; retries-- {
|
// TODO: does this really need to be tried more than once?
|
||||||
|
// Caddy should block until the new config is loaded, which means needing to wait is a caddy bug
|
||||||
|
for retries := 3; retries > 0; retries-- {
|
||||||
if reflect.DeepEqual(expected, fetchConfig(client)) {
|
if reflect.DeepEqual(expected, fetchConfig(client)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -241,9 +278,9 @@ const initConfig = `{
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// validateTestPrerequisites ensures the certificates are available in the
|
// validateAndStartServer ensures the certificates are available in the
|
||||||
// designated path and Caddy sub-process is running.
|
// designated path, launches caddy, and then ensures the Caddy sub-process is running.
|
||||||
func validateTestPrerequisites(t testing.TB) error {
|
func validateAndStartServer(t testing.TB) error {
|
||||||
// check certificates are found
|
// check certificates are found
|
||||||
for _, certName := range Default.Certificates {
|
for _, certName := range Default.Certificates {
|
||||||
if _, err := os.Stat(getIntegrationDir() + certName); errors.Is(err, fs.ErrNotExist) {
|
if _, err := os.Stat(getIntegrationDir() + certName); errors.Is(err, fs.ErrNotExist) {
|
||||||
|
@ -269,9 +306,8 @@ func validateTestPrerequisites(t testing.TB) error {
|
||||||
go func() {
|
go func() {
|
||||||
caddycmd.Main()
|
caddycmd.Main()
|
||||||
}()
|
}()
|
||||||
|
// wait for caddy admin api to start. it should happen quickly.
|
||||||
// wait for caddy to start serving the initial config
|
for retries := 3; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
|
||||||
for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,8 +378,8 @@ func CreateTestingTransport() *http.Transport {
|
||||||
// AssertLoadError will load a config and expect an error
|
// AssertLoadError will load a config and expect an error
|
||||||
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
|
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
|
||||||
tc := NewTester(t)
|
tc := NewTester(t)
|
||||||
|
tc.LaunchCaddy()
|
||||||
err := tc.initServer(rawConfig, configType)
|
err := tc.LoadConfig(rawConfig, configType)
|
||||||
if !strings.Contains(err.Error(), expectedError) {
|
if !strings.Contains(err.Error(), expectedError) {
|
||||||
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
|
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,19 +19,27 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func curry2[A, B any](fn func(A, B)) func(a A) func(b B) {
|
||||||
|
return func(a A) func(B) {
|
||||||
|
return func(b B) {
|
||||||
|
fn(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const acmeChallengePort = 9081
|
const acmeChallengePort = 9081
|
||||||
|
|
||||||
// Test the basic functionality of Caddy's ACME server
|
func TestAcmeServer(t *testing.T) {
|
||||||
func TestACMEServerWithDefaults(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
logger, err := zap.NewDevelopment()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tester := caddytest.NewTester(t)
|
tester := caddytest.NewTester(t)
|
||||||
tester.InitServer(`
|
tester.LaunchCaddy()
|
||||||
|
t.Run("WithDefaults", curry2(testACMEServerWithDefaults)(tester))
|
||||||
|
t.Run("WithMismatchedChallenges", curry2(testACMEServerWithDefaults)(tester))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the basic functionality of Caddy's ACME server
|
||||||
|
func testACMEServerWithDefaults(tester *caddytest.Tester, t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
tester.MustLoadConfig(`
|
||||||
{
|
{
|
||||||
skip_install_trust
|
skip_install_trust
|
||||||
admin localhost:2999
|
admin localhost:2999
|
||||||
|
@ -44,6 +52,7 @@ func TestACMEServerWithDefaults(t *testing.T) {
|
||||||
}
|
}
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
|
logger := caddy.Log().Named("acmeserver")
|
||||||
client := acmez.Client{
|
client := acmez.Client{
|
||||||
Client: &acme.Client{
|
Client: &acme.Client{
|
||||||
Directory: "https://acme.localhost:9443/acme/local/directory",
|
Directory: "https://acme.localhost:9443/acme/local/directory",
|
||||||
|
@ -93,20 +102,10 @@ func TestACMEServerWithDefaults(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestACMEServerWithMismatchedChallenges(t *testing.T) {
|
func testACMEServerWithMismatchedChallenges(tester *caddytest.Tester, t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
logger := caddy.Log().Named("acmez")
|
logger := caddy.Log().Named("acmez")
|
||||||
|
tester.MustLoadConfig(`
|
||||||
tester := caddytest.NewTester(t)
|
|
||||||
tester.InitServer(`
|
|
||||||
{
|
|
||||||
skip_install_trust
|
|
||||||
admin localhost:2999
|
|
||||||
http_port 9080
|
|
||||||
https_port 9443
|
|
||||||
local_certs
|
|
||||||
}
|
|
||||||
acme.localhost {
|
|
||||||
acme_server {
|
acme_server {
|
||||||
challenges tls-alpn-01
|
challenges tls-alpn-01
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddytest"
|
"github.com/caddyserver/caddy/v2/caddytest"
|
||||||
"github.com/mholt/acmez/v2"
|
"github.com/mholt/acmez/v2"
|
||||||
"github.com/mholt/acmez/v2/acme"
|
"github.com/mholt/acmez/v2/acme"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestACMEServerDirectory(t *testing.T) {
|
func TestACMEServerDirectory(t *testing.T) {
|
||||||
|
@ -66,11 +66,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
logger, err := zap.NewDevelopment()
|
logger := caddy.Log().Named("acmez")
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := acmez.Client{
|
client := acmez.Client{
|
||||||
Client: &acme.Client{
|
Client: &acme.Client{
|
||||||
|
@ -155,11 +151,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
|
||||||
`, "caddyfile")
|
`, "caddyfile")
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
logger, err := zap.NewDevelopment()
|
logger := caddy.Log().Named("acmez")
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := acmez.Client{
|
client := acmez.Client{
|
||||||
Client: &acme.Client{
|
Client: &acme.Client{
|
||||||
|
@ -197,7 +189,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
|
||||||
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
|
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("obtaining certificate for 'deny.localhost' domain")
|
t.Errorf("obtaining certificate for 'deny.localhost' domain")
|
||||||
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
|
} else if !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
|
||||||
t.Logf("unexpected error: %v", err)
|
t.Logf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,13 +210,6 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
|
||||||
"admin": {
|
"admin": {
|
||||||
"listen": "localhost:2999"
|
"listen": "localhost:2999"
|
||||||
},
|
},
|
||||||
"logging": {
|
|
||||||
"logs": {
|
|
||||||
"default": {
|
|
||||||
"level": "DEBUG"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apps": {
|
"apps": {
|
||||||
"http": {
|
"http": {
|
||||||
"http_port": 9080,
|
"http_port": 9080,
|
||||||
|
|
|
@ -16,6 +16,7 @@ package caddy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
@ -699,7 +700,13 @@ type defaultCustomLog struct {
|
||||||
// and enables INFO-level logs and higher.
|
// and enables INFO-level logs and higher.
|
||||||
func newDefaultProductionLog() (*defaultCustomLog, error) {
|
func newDefaultProductionLog() (*defaultCustomLog, error) {
|
||||||
cl := new(CustomLog)
|
cl := new(CustomLog)
|
||||||
cl.writerOpener = StderrWriter{}
|
f := flag.Lookup("test.v")
|
||||||
|
if (f != nil && f.Value.String() != "true") || strings.Contains(os.Args[0], ".test") {
|
||||||
|
cl.writerOpener = &DiscardWriter{}
|
||||||
|
} else {
|
||||||
|
cl.writerOpener = StderrWriter{}
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
cl.writer, err = cl.writerOpener.OpenWriter()
|
cl.writer, err = cl.writerOpener.OpenWriter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue