-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathconfig.go
167 lines (154 loc) · 5.28 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package etcd
import (
"fmt"
"io/ioutil"
"log"
"net/url"
"os"
"path"
"strings"
"time"
"github.com/pkg/errors"
)
// ClusterConfig maintains configuration information for cluster
// resources such as etcd server instances
type ClusterConfig struct {
KeyPrefix string
ServerIP []string
LockTimeout time.Duration
CaddyFile []byte
CaddyFilePath string
DisableCaddyLoad bool
// TODO: Add roles, auth, and mutual TLS
}
// ConfigOption represents a functional option for ClusterConfig
type ConfigOption func(c *ClusterConfig) error
// NewClusterConfig returns a new configuration with options passed as functional
// options
func NewClusterConfig(opts ...ConfigOption) (*ClusterConfig, error) {
c := &ClusterConfig{
KeyPrefix: "/caddy",
LockTimeout: 5 * time.Minute,
}
for _, opt := range opts {
if err := opt(c); err != nil {
return nil, err
}
}
if len(c.ServerIP) == 0 {
c.ServerIP = []string{"http://127.0.0.1:2379"}
}
if len(c.CaddyFile) == 0 {
}
return c, nil
}
// ConfigOptsFromEnvironment reads environment variables and returns options that can be applied via
// NewClusterConfig
func ConfigOptsFromEnvironment() (opts []ConfigOption) {
var env = map[string]func(s string) ConfigOption{
"CADDY_CLUSTERING_ETCD_SERVERS": WithServers,
"CADDY_CLUSTERING_ETCD_PREFIX": WithPrefix,
"CADDY_CLUSTERING_ETCD_TIMEOUT": WithTimeout,
"CADDY_CLUSTERING_ETCD_CADDYFILE": WithCaddyFile,
"CADDY_CLUSTERING_ETCD_CADDYFILE_LOADER": WithDisableCaddyfileLoad,
}
for e, f := range env {
val := os.Getenv(e)
if len(val) != 0 {
opts = append(opts, f(val))
}
}
return opts
}
// WithServers sets the etcd server endpoints. Multiple endpoints are assumed to
// be separated by a comma, and consist of a full URL, including scheme and port
// (i.e., http://127.0.0.1:2379) The default config uses port 2379 on localhost.
func WithServers(s string) ConfigOption {
return func(c *ClusterConfig) error {
var srvs []string
switch {
case strings.Index(s, ";") >= 0:
srvs = strings.Split(s, ";")
default:
srvs = strings.Split(s, ",")
}
for _, srv := range srvs {
csrv := strings.TrimSpace(srv)
u, err := url.Parse(csrv)
if err != nil {
return errors.Wrap(err, "CADDY_CLUSTERING_ETCD_SERVERS is an invalid format: servers should be separated by comma and be a full URL including scheme")
}
if u.Scheme != "http" && u.Scheme != "https" {
return errors.New("CADDY_CLUSTERING_ETCD_SERVERS is an invalid format: servers must specify a scheme, either http or https")
}
c.ServerIP = append(c.ServerIP, csrv)
}
return nil
}
}
// WithPrefix sets the etcd namespace for caddy data. Default is `/caddy`.
// Prefixes are normalized to use `/` as a path separator.
func WithPrefix(s string) ConfigOption {
return func(c *ClusterConfig) error {
c.KeyPrefix = path.Clean("/" + strings.Trim(strings.Replace(s, "\\", "/", -1), "/"))
return nil
}
}
// WithTimeout sets the time locks should be considered abandoned. Locks that
// exist longer than this setting will be overwritten by the next client that
// acquires the lock. The default is 5 minutes. This option takes standard
// Go duration formats such as 5m, 1h, etc.
func WithTimeout(s string) ConfigOption {
return func(c *ClusterConfig) error {
d, err := time.ParseDuration(s)
if err != nil {
return errors.Wrap(err, "CADDY_CLUSTERING_ETCD_TIMEOUT is an invalid format: must be a go standard time duration")
}
c.LockTimeout = d
return nil
}
}
// WithCaddyFile sets the path to the bootstrap Caddyfile to load on initial start if configuration
// information is not already present in etcd. The first cluster instance will load this
// file and store it in etcd. Subsequent members of the cluster will prioritize configuration
// from etcd even if this file is present. This function will not error even if the Caddyfile is
// not present. If a caddyfile cannot be read from etcd, from this file, or from the default loader,
// caddy will start with an empty default configuration.
func WithCaddyFile(s string) ConfigOption {
return func(c *ClusterConfig) error {
p := path.Clean(s)
if !strings.HasPrefix(p, "/") {
// assume a relative directory
cwd, err := os.Getwd()
if err != nil {
log.Print("[WARN] etcd: could not ready configured caddyfile source, this may not indicate a problem if another cluster member has stored this data in etcd")
return nil
}
p = path.Join(cwd, p)
}
r, err := ioutil.ReadFile(p)
if err != nil {
log.Print("[WARN] etcd: could not ready configured caddyfile source, this may not indicate a problem if another cluster member has stored this data in etcd")
return nil
}
c.CaddyFilePath = p
c.CaddyFile = r
return nil
}
}
// WithDisableCaddyfileLoad will skip all attempts at loading the caddyfile from etcd and force caddy to fall back
// to other enabled caddyfile loader plugins or the default loader
func WithDisableCaddyfileLoad(s string) ConfigOption {
return func(c *ClusterConfig) error {
val := strings.ToLower(strings.TrimSpace(s))
switch val {
case "disable":
c.DisableCaddyLoad = true
return nil
case "enable", "":
return nil
default:
return errors.New(fmt.Sprintf("CADDY_CLUSTERING_ETCD_CADDYFILE_LOADER is an invalid format: %s is an unknown option", val))
}
}
}