Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
admin listener as opt-in for initial config (#2834)
* Always cleanup admin endpoint first * Error out if no config has been set (#2833) * Ignore explicitly missing admin config (#2833) * Separate config loading from admin initialization (#2833) * Add admin option to specify admin listener address (#2833) * Use zap for reporting admin endpoint status
Andreas Schneider committed
Oct 30, 2019 at 22:12 UTC
432b94239d6096e75b413de5a79c6707ab808bc9
4 files changed
+55
-20
admin.go
+22
-18
@@ -18,6 +18,7 @@ import (
18
"bytes"
19
"context"
20
"encoding/json"
21
+ "errors"
22
"fmt"
23
"io"
24
"io/ioutil"
@@ -42,6 +43,8 @@ var (
43
cfgEndptSrvMu sync.Mutex
44
)
45
46
+var ErrAdminInterfaceNotConfigured = errors.New("no admin configuration has been set")
47
+
48
// AdminConfig configures the admin endpoint.
49
type AdminConfig struct {
50
Listen string `json:"listen,omitempty"`
@@ -60,10 +63,24 @@ var DefaultAdminConfig = &AdminConfig{
63
// in the format of JSON bytes. It opens a listener
64
// resource. When no longer needed, StopAdmin should
65
// be called.
66
+// If no configuration is given, a default listener is
67
+// started. If a configuration is given that does NOT
68
+// specifically configure the admin interface,
69
+// `ErrAdminInterfaceNotConfigured` is returned and no
70
+// listener is initialized.
71
func StartAdmin(initialConfigJSON []byte) error {
72
cfgEndptSrvMu.Lock()
73
defer cfgEndptSrvMu.Unlock()
74
75
+ if cfgEndptSrv != nil {
76
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
77
+ defer cancel()
78
+ err := cfgEndptSrv.Shutdown(ctx)
79
+ if err != nil {
80
+ return fmt.Errorf("shutting down old admin endpoint: %v", err)
81
+ }
82
+ }
83
+
84
adminConfig := DefaultAdminConfig
85
if len(initialConfigJSON) > 0 {
86
var config *Config
@@ -71,16 +88,11 @@ func StartAdmin(initialConfigJSON []byte) error {
88
if err != nil {
89
return fmt.Errorf("unmarshaling bootstrap config: %v", err)
90
}
74
- if config != nil && config.Admin != nil {
75
- adminConfig = config.Admin
76
- }
77
- if cfgEndptSrv != nil {
78
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
79
- defer cancel()
80
- err := cfgEndptSrv.Shutdown(ctx)
81
- if err != nil {
82
- return fmt.Errorf("shutting down old admin endpoint: %v", err)
91
+ if config != nil {
92
+ if config.Admin == nil {
93
+ return ErrAdminInterfaceNotConfigured
94
}
95
+ adminConfig = config.Admin
96
}
97
}
98
@@ -136,15 +148,7 @@ func StartAdmin(initialConfigJSON []byte) error {
148
149
go cfgEndptSrv.Serve(ln)
150
139
- fmt.Println("Caddy 2 admin endpoint listening on", adminConfig.Listen)
140
-
141
- if len(initialConfigJSON) > 0 {
142
- err := Load(bytes.NewReader(initialConfigJSON))
143
- if err != nil {
144
- return fmt.Errorf("loading initial config: %v", err)
145
- }
146
- fmt.Println("Caddy 2 serving initial configuration")
147
- }
151
+ Log().Named("admin").Info("Caddy 2 admin endpoint started.", zap.String("listenAddress", adminConfig.Listen))
152
153
return nil
154
}
caddyconfig/httpcaddyfile/httptype.go
+5
@@ -74,6 +74,8 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
74
val, err = parseOptACMECA(disp)
75
case "email":
76
val, err = parseOptEmail(disp)
77
+ case "admin":
78
+ val, err = parseOptAdmin(disp)
79
default:
80
return nil, warnings, fmt.Errorf("unrecognized parameter name: %s", dir)
81
}
@@ -254,6 +256,9 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
256
storageCvtr.(caddy.Module).CaddyModule().ID(),
257
&warnings)
258
}
259
+ if adminConfig, ok := options["admin"].(string); ok && adminConfig != "" {
260
+ cfg.Admin = &caddy.AdminConfig{Listen: adminConfig}
261
+ }
262
263
return cfg, warnings, nil
264
}
caddyconfig/httpcaddyfile/options.go
+14
@@ -132,3 +132,17 @@ func parseOptEmail(d *caddyfile.Dispenser) (string, error) {
132
}
133
return val, nil
134
}
135
+
136
+func parseOptAdmin(d *caddyfile.Dispenser) (string, error) {
137
+ if d.Next() {
138
+ var listenAddress string
139
+ d.AllArgs(&listenAddress)
140
+
141
+ if listenAddress == "" {
142
+ listenAddress = caddy.DefaultAdminListen
143
+ }
144
+
145
+ return listenAddress, nil
146
+ }
147
+ return "", nil
148
+}
cmd/commandfuncs.go
+14
-2
@@ -161,12 +161,24 @@ func cmdRun(fl Flags) (int, error) {
161
certmagic.UserAgent = "Caddy/" + cleanModVersion
162
163
// start the admin endpoint along with any initial config
164
+ // a configuration without admin config is considered fine
165
+ // but does not enable the admin endpoint at all
166
err = caddy.StartAdmin(config)
165
- if err != nil {
167
+ if err == nil {
168
+ defer caddy.StopAdmin()
169
+ } else if err != caddy.ErrAdminInterfaceNotConfigured {
170
return caddy.ExitCodeFailedStartup,
171
fmt.Errorf("starting caddy administration endpoint: %v", err)
172
}
169
- defer caddy.StopAdmin()
173
+
174
+ // if a config has been supplied, load it as initial config
175
+ if len(config) > 0 {
176
+ err := caddy.Load(bytes.NewReader(config))
177
+ if err != nil {
178
+ return caddy.ExitCodeFailedStartup, fmt.Errorf("loading initial config: %v", err)
179
+ }
180
+ caddy.Log().Named("admin").Info("Caddy 2 serving initial configuration")
181
+ }
182
183
// if we are to report to another process the successful start
184
// of the server, do so now by echoing back contents of stdin