mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Add tiered configuration.
This commit is contained in:
402
server/config.go
Normal file
402
server/config.go
Normal file
@@ -0,0 +1,402 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
// The default location for the etcd configuration file.
|
||||
const DefaultSystemConfigPath = "/etc/etcd/etcd.toml"
|
||||
|
||||
// Config represents the server configuration.
|
||||
type Config struct {
|
||||
SystemPath string
|
||||
|
||||
AdvertisedUrl string `toml:"advertised_url" env:"ETCD_ADVERTISED_URL"`
|
||||
CAFile string `toml:"ca_file" env:"ETCD_CA_FILE"`
|
||||
CertFile string `toml:"cert_file" env:"ETCD_CERT_FILE"`
|
||||
Cors []string `toml:"cors" env:"ETCD_CORS"`
|
||||
DataDir string `toml:"datadir" env:"ETCD_DATADIR"`
|
||||
KeyFile string `toml:"key_file" env:"ETCD_KEY_FILE"`
|
||||
ListenHost string `toml:"listen_host" env:"ETCD_LISTEN_HOST"`
|
||||
Machines []string `toml:"machines" env:"ETCD_MACHINES"`
|
||||
MachinesFile string `toml:"machines_file" env:"ETCD_MACHINES_FILE"`
|
||||
MaxClusterSize int `toml:"max_cluster_size" env:"ETCD_MAX_CLUSTER_SIZE"`
|
||||
MaxResultBuffer int `toml:"max_result_buffer" env:"ETCD_MAX_RESULT_BUFFER"`
|
||||
MaxRetryAttempts int `toml:"max_retry_attempts" env:"ETCD_MAX_RETRY_ATTEMPTS"`
|
||||
Name string `toml:"name" env:"ETCD_NAME"`
|
||||
Snapshot bool `toml:"snapshot" env:"ETCD_SNAPSHOT"`
|
||||
Verbose bool `toml:"verbose" env:"ETCD_VERBOSE"`
|
||||
VeryVerbose bool `toml:"very_verbose" env:"ETCD_VERY_VERBOSE"`
|
||||
WebURL string `toml:"web_url" env:"ETCD_WEB_URL"`
|
||||
|
||||
Peer struct {
|
||||
AdvertisedUrl string `toml:"advertised_url" env:"ETCD_PEER_ADVERTISED_URL"`
|
||||
CAFile string `toml:"ca_file" env:"ETCD_PEER_CA_FILE"`
|
||||
CertFile string `toml:"cert_file" env:"ETCD_PEER_CERT_FILE"`
|
||||
KeyFile string `toml:"key_file" env:"ETCD_PEER_KEY_FILE"`
|
||||
ListenHost string `toml:"listen_host" env:"ETCD_PEER_LISTEN_HOST"`
|
||||
}
|
||||
}
|
||||
|
||||
// NewConfig returns a Config initialized with default values.
|
||||
func NewConfig() *Config {
|
||||
c := new(Config)
|
||||
c.SystemPath = DefaultSystemConfigPath
|
||||
c.AdvertisedUrl = "127.0.0.1:4001"
|
||||
c.DataDir = "."
|
||||
c.MaxClusterSize = 9
|
||||
c.MaxResultBuffer = 1024
|
||||
c.MaxRetryAttempts = 3
|
||||
c.Peer.AdvertisedUrl = "127.0.0.1:7001"
|
||||
return c
|
||||
}
|
||||
|
||||
// Loads the configuration from the system config, command line config,
|
||||
// environment variables, and finally command line arguments.
|
||||
func (c *Config) Load(arguments []string) error {
|
||||
var path string
|
||||
f := flag.NewFlagSet("etcd", -1)
|
||||
f.SetOutput(ioutil.Discard)
|
||||
f.StringVar(&path, "config", "", "path to config file")
|
||||
f.Parse(arguments)
|
||||
|
||||
// Load from system file.
|
||||
if err := c.LoadSystemFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load from config file specified in arguments.
|
||||
if path != "" {
|
||||
if err := c.LoadFile(path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Load from the environment variables next.
|
||||
if err := c.LoadEnv(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load from command line flags.
|
||||
if err := c.LoadFlags(arguments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Loads machines if a machine file was specified.
|
||||
if err := c.LoadMachineFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sanitize all the input fields.
|
||||
if err := c.Sanitize(); err != nil {
|
||||
return fmt.Errorf("sanitize:", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loads from the system etcd configuration file if it exists.
|
||||
func (c *Config) LoadSystemFile() error {
|
||||
if _, err := os.Stat(c.SystemPath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return c.LoadFile(c.SystemPath)
|
||||
}
|
||||
|
||||
// Loads configuration from a file.
|
||||
func (c *Config) LoadFile(path string) error {
|
||||
_, err := toml.DecodeFile(path, &c)
|
||||
return err
|
||||
}
|
||||
|
||||
// LoadEnv loads the configuration via environment variables.
|
||||
func (c *Config) LoadEnv() error {
|
||||
if err := c.loadEnv(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.loadEnv(&c.Peer); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) loadEnv(target interface{}) error {
|
||||
value := reflect.Indirect(reflect.ValueOf(target))
|
||||
typ := value.Type()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
// Retrieve environment variable.
|
||||
v := strings.TrimSpace(os.Getenv(field.Tag.Get("env")))
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Set the appropriate type.
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Bool:
|
||||
value.Field(i).SetBool(v != "0" && v != "false")
|
||||
case reflect.Int:
|
||||
newValue, err := strconv.ParseInt(v, 10, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Parse error: %s: %s", field.Tag.Get("env"), err)
|
||||
}
|
||||
value.Field(i).SetInt(newValue)
|
||||
case reflect.String:
|
||||
value.Field(i).SetString(v)
|
||||
case reflect.Slice:
|
||||
value.Field(i).Set(reflect.ValueOf(trimsplit(v, ",")))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loads configuration from command line flags.
|
||||
func (c *Config) LoadFlags(arguments []string) error {
|
||||
var machines, cors string
|
||||
var force bool
|
||||
|
||||
f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
|
||||
|
||||
f.BoolVar(&force, "f", false, "force new node configuration if existing is found (WARNING: data loss!)")
|
||||
|
||||
f.BoolVar(&c.Verbose, "v", c.Verbose, "verbose logging")
|
||||
f.BoolVar(&c.VeryVerbose, "vv", c.Verbose, "very verbose logging")
|
||||
|
||||
f.StringVar(&machines, "C", "", "the ip address and port of a existing machines in the cluster, sepearate by comma")
|
||||
f.StringVar(&c.MachinesFile, "CF", c.MachinesFile, "the file contains a list of existing machines in the cluster, seperate by comma")
|
||||
|
||||
f.StringVar(&c.Name, "n", c.Name, "the node name (required)")
|
||||
f.StringVar(&c.AdvertisedUrl, "c", c.AdvertisedUrl, "the advertised public hostname:port for etcd client communication")
|
||||
f.StringVar(&c.Peer.AdvertisedUrl, "s", c.Peer.AdvertisedUrl, "the advertised public hostname:port for raft server communication")
|
||||
f.StringVar(&c.ListenHost, "cl", c.ListenHost, "the listening hostname for etcd client communication (defaults to advertised ip)")
|
||||
f.StringVar(&c.Peer.ListenHost, "sl", c.Peer.ListenHost, "the listening hostname for raft server communication (defaults to advertised ip)")
|
||||
f.StringVar(&c.WebURL, "w", c.WebURL, "the hostname:port of web interface")
|
||||
|
||||
f.StringVar(&c.Peer.CAFile, "serverCAFile", c.Peer.CAFile, "the path of the CAFile")
|
||||
f.StringVar(&c.Peer.CertFile, "serverCert", c.Peer.CertFile, "the cert file of the server")
|
||||
f.StringVar(&c.Peer.KeyFile, "serverKey", c.Peer.KeyFile, "the key file of the server")
|
||||
|
||||
f.StringVar(&c.CAFile, "clientCAFile", c.CAFile, "the path of the client CAFile")
|
||||
f.StringVar(&c.CertFile, "clientCert", c.CertFile, "the cert file of the client")
|
||||
f.StringVar(&c.KeyFile, "clientKey", c.KeyFile, "the key file of the client")
|
||||
|
||||
f.StringVar(&c.DataDir, "d", c.DataDir, "the directory to store log and snapshot")
|
||||
f.BoolVar(&c.Snapshot, "snapshot", c.Snapshot, "open or close snapshot")
|
||||
f.IntVar(&c.MaxResultBuffer, "m", c.MaxResultBuffer, "the max size of result buffer")
|
||||
f.IntVar(&c.MaxRetryAttempts, "r", c.MaxRetryAttempts, "the max retry attempts when trying to join a cluster")
|
||||
f.IntVar(&c.MaxClusterSize, "maxsize", c.MaxClusterSize, "the max size of the cluster")
|
||||
f.StringVar(&cors, "cors", "", "whitelist origins for cross-origin resource sharing (e.g. '*' or 'http://localhost:8001,etc')")
|
||||
|
||||
// These flags are ignored since they were already parsed.
|
||||
var path string
|
||||
f.StringVar(&path, "config", "", "path to config file")
|
||||
|
||||
f.Parse(arguments)
|
||||
|
||||
// Convert some parameters to lists.
|
||||
if machines != "" {
|
||||
c.Machines = trimsplit(machines, ",")
|
||||
}
|
||||
if cors != "" {
|
||||
c.Cors = trimsplit(cors, ",")
|
||||
}
|
||||
|
||||
// Force remove server configuration if specified.
|
||||
if force {
|
||||
c.Reset()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadMachineFile loads the machines listed in the machine file.
|
||||
func (c *Config) LoadMachineFile() error {
|
||||
if c.MachinesFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(c.MachinesFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Machines file error: %s", err)
|
||||
}
|
||||
c.Machines = trimsplit(string(b), ",")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset removes all server configuration files.
|
||||
func (c *Config) Reset() error {
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "info")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "log")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "conf")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(filepath.Join(c.DataDir, "snapshot")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reads the info file from the file system or initializes it based on the config.
|
||||
func (c *Config) Info() (*Info, error) {
|
||||
info := &Info{}
|
||||
path := filepath.Join(c.DataDir, "info")
|
||||
|
||||
// Open info file and read it out.
|
||||
f, err := os.Open(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
} else if f != nil {
|
||||
defer f.Close()
|
||||
if err := json.NewDecoder(f).Decode(&info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// If the file doesn't exist then initialize it.
|
||||
info.Name = strings.TrimSpace(c.Name)
|
||||
info.EtcdURL = c.AdvertisedUrl
|
||||
info.EtcdListenHost = c.ListenHost
|
||||
info.RaftURL = c.Peer.AdvertisedUrl
|
||||
info.RaftListenHost = c.Peer.ListenHost
|
||||
info.WebURL = c.WebURL
|
||||
info.EtcdTLS = c.TLSInfo()
|
||||
info.RaftTLS = c.PeerTLSInfo()
|
||||
|
||||
// Write to file.
|
||||
f, err = os.Create(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := json.NewEncoder(f).Encode(info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// Sanitize cleans the input fields.
|
||||
func (c *Config) Sanitize() error {
|
||||
tlsConfig, err := c.TLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
peerTlsConfig, err := c.PeerTLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sanitize the URLs first.
|
||||
if c.AdvertisedUrl, err = sanitizeURL(c.AdvertisedUrl, tlsConfig.Scheme); err != nil {
|
||||
return fmt.Errorf("Advertised URL: %s", err)
|
||||
}
|
||||
if c.ListenHost, err = sanitizeListenHost(c.ListenHost, c.AdvertisedUrl); err != nil {
|
||||
return fmt.Errorf("Listen Host: %s", err)
|
||||
}
|
||||
if c.WebURL, err = sanitizeURL(c.WebURL, "http"); err != nil {
|
||||
return fmt.Errorf("Web URL: %s", err)
|
||||
}
|
||||
if c.Peer.AdvertisedUrl, err = sanitizeURL(c.Peer.AdvertisedUrl, peerTlsConfig.Scheme); err != nil {
|
||||
return fmt.Errorf("Peer Advertised URL: %s", err)
|
||||
}
|
||||
if c.Peer.ListenHost, err = sanitizeListenHost(c.Peer.ListenHost, c.Peer.AdvertisedUrl); err != nil {
|
||||
return fmt.Errorf("Peer Listen Host: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// TLSInfo retrieves a TLSInfo object for the client server.
|
||||
func (c *Config) TLSInfo() TLSInfo {
|
||||
return TLSInfo{
|
||||
CAFile: c.CAFile,
|
||||
CertFile: c.CertFile,
|
||||
KeyFile: c.KeyFile,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientTLSConfig generates the TLS configuration for the client server.
|
||||
func (c *Config) TLSConfig() (TLSConfig, error) {
|
||||
return c.TLSInfo().Config()
|
||||
}
|
||||
|
||||
// PeerTLSInfo retrieves a TLSInfo object for the peer server.
|
||||
func (c *Config) PeerTLSInfo() TLSInfo {
|
||||
return TLSInfo{
|
||||
CAFile: c.Peer.CAFile,
|
||||
CertFile: c.Peer.CertFile,
|
||||
KeyFile: c.Peer.KeyFile,
|
||||
}
|
||||
}
|
||||
|
||||
// PeerTLSConfig generates the TLS configuration for the peer server.
|
||||
func (c *Config) PeerTLSConfig() (TLSConfig, error) {
|
||||
return c.PeerTLSInfo().Config()
|
||||
}
|
||||
|
||||
// sanitizeURL will cleanup a host string in the format hostname:port and
|
||||
// attach a schema.
|
||||
func sanitizeURL(host string, defaultScheme string) (string, error) {
|
||||
// Blank URLs are fine input, just return it
|
||||
if len(host) == 0 {
|
||||
return host, nil
|
||||
}
|
||||
|
||||
p, err := url.Parse(host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Make sure the host is in Host:Port format
|
||||
_, _, err = net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
p = &url.URL{Host: host, Scheme: defaultScheme}
|
||||
return p.String(), nil
|
||||
}
|
||||
|
||||
// sanitizeListenHost cleans up the ListenHost parameter and appends a port
|
||||
// if necessary based on the advertised port.
|
||||
func sanitizeListenHost(listen string, advertised string) (string, error) {
|
||||
aurl, err := url.Parse(advertised)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ahost, aport, err := net.SplitHostPort(aurl.Host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If the listen host isn't set use the advertised host
|
||||
if listen == "" {
|
||||
listen = ahost
|
||||
}
|
||||
|
||||
return net.JoinHostPort(listen, aport), nil
|
||||
}
|
||||
479
server/config_test.go
Normal file
479
server/config_test.go
Normal file
@@ -0,0 +1,479 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
// Ensures that a configuration can be deserialized from TOML.
|
||||
func TestConfigTOML(t *testing.T) {
|
||||
content := `
|
||||
advertised_url = "127.0.0.1:4002"
|
||||
ca_file = "/tmp/file.ca"
|
||||
cert_file = "/tmp/file.cert"
|
||||
cors = ["*"]
|
||||
cpu_profile_file = "XXX"
|
||||
datadir = "/tmp/data"
|
||||
key_file = "/tmp/file.key"
|
||||
listen_host = "127.0.0.1:4003"
|
||||
machines = ["coreos.com:4001", "coreos.com:4002"]
|
||||
machines_file = "/tmp/machines"
|
||||
max_cluster_size = 10
|
||||
max_result_buffer = 512
|
||||
max_retry_attempts = 5
|
||||
name = "test-name"
|
||||
snapshot = true
|
||||
verbose = true
|
||||
very_verbose = true
|
||||
web_url = "/web"
|
||||
|
||||
[peer]
|
||||
advertised_url = "127.0.0.1:7002"
|
||||
ca_file = "/tmp/peer/file.ca"
|
||||
cert_file = "/tmp/peer/file.cert"
|
||||
key_file = "/tmp/peer/file.key"
|
||||
listen_host = "127.0.0.1:7003"
|
||||
`
|
||||
c := NewConfig()
|
||||
_, err := toml.Decode(content, &c)
|
||||
assert.Nil(t, err, "")
|
||||
assert.Equal(t, c.AdvertisedUrl, "127.0.0.1:4002", "")
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
assert.Equal(t, c.Cors, []string{"*"}, "")
|
||||
assert.Equal(t, c.DataDir, "/tmp/data", "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
assert.Equal(t, c.ListenHost, "127.0.0.1:4003", "")
|
||||
assert.Equal(t, c.Machines, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
assert.Equal(t, c.MachinesFile, "/tmp/machines", "")
|
||||
assert.Equal(t, c.MaxClusterSize, 10, "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 5, "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
assert.Equal(t, c.WebURL, "/web", "")
|
||||
assert.Equal(t, c.Peer.AdvertisedUrl, "127.0.0.1:7002", "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
assert.Equal(t, c.Peer.ListenHost, "127.0.0.1:7003", "")
|
||||
}
|
||||
|
||||
// Ensures that a configuration can be retrieved from environment variables.
|
||||
func TestConfigEnv(t *testing.T) {
|
||||
os.Setenv("ETCD_CA_FILE", "/tmp/file.ca")
|
||||
os.Setenv("ETCD_CERT_FILE", "/tmp/file.cert")
|
||||
os.Setenv("ETCD_CPU_PROFILE_FILE", "XXX")
|
||||
os.Setenv("ETCD_CORS", "localhost:4001,localhost:4002")
|
||||
os.Setenv("ETCD_DATADIR", "/tmp/data")
|
||||
os.Setenv("ETCD_KEY_FILE", "/tmp/file.key")
|
||||
os.Setenv("ETCD_LISTEN_HOST", "127.0.0.1:4003")
|
||||
os.Setenv("ETCD_MACHINES", "coreos.com:4001,coreos.com:4002")
|
||||
os.Setenv("ETCD_MACHINES_FILE", "/tmp/machines")
|
||||
os.Setenv("ETCD_MAX_CLUSTER_SIZE", "10")
|
||||
os.Setenv("ETCD_MAX_RESULT_BUFFER", "512")
|
||||
os.Setenv("ETCD_MAX_RETRY_ATTEMPTS", "5")
|
||||
os.Setenv("ETCD_NAME", "test-name")
|
||||
os.Setenv("ETCD_SNAPSHOT", "true")
|
||||
os.Setenv("ETCD_VERBOSE", "1")
|
||||
os.Setenv("ETCD_VERY_VERBOSE", "yes")
|
||||
os.Setenv("ETCD_WEB_URL", "/web")
|
||||
os.Setenv("ETCD_PEER_ADVERTISED_URL", "127.0.0.1:7002")
|
||||
os.Setenv("ETCD_PEER_CA_FILE", "/tmp/peer/file.ca")
|
||||
os.Setenv("ETCD_PEER_CERT_FILE", "/tmp/peer/file.cert")
|
||||
os.Setenv("ETCD_PEER_KEY_FILE", "/tmp/peer/file.key")
|
||||
os.Setenv("ETCD_PEER_LISTEN_HOST", "127.0.0.1:7003")
|
||||
|
||||
c := NewConfig()
|
||||
c.LoadEnv()
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
assert.Equal(t, c.Cors, []string{"localhost:4001", "localhost:4002"}, "")
|
||||
assert.Equal(t, c.DataDir, "/tmp/data", "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
assert.Equal(t, c.ListenHost, "127.0.0.1:4003", "")
|
||||
assert.Equal(t, c.Machines, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
assert.Equal(t, c.MachinesFile, "/tmp/machines", "")
|
||||
assert.Equal(t, c.MaxClusterSize, 10, "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 5, "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
assert.Equal(t, c.WebURL, "/web", "")
|
||||
assert.Equal(t, c.Peer.AdvertisedUrl, "127.0.0.1:7002", "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
assert.Equal(t, c.Peer.ListenHost, "127.0.0.1:7003", "")
|
||||
}
|
||||
|
||||
// Ensures that a the advertised url can be parsed from the environment.
|
||||
func TestConfigAdvertisedUrlEnv(t *testing.T) {
|
||||
withEnv("ETCD_ADVERTISED_URL", "127.0.0.1:4002", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.AdvertisedUrl, "127.0.0.1:4002", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the advertised flag can be parsed.
|
||||
func TestConfigAdvertisedUrlFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-c", "127.0.0.1:4002"}), "")
|
||||
assert.Equal(t, c.AdvertisedUrl, "127.0.0.1:4002", "")
|
||||
}
|
||||
|
||||
// Ensures that a the CA file can be parsed from the environment.
|
||||
func TestConfigCAFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_CA_FILE", "/tmp/file.ca", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the CA file flag can be parsed.
|
||||
func TestConfigCAFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-clientCAFile", "/tmp/file.ca"}), "")
|
||||
assert.Equal(t, c.CAFile, "/tmp/file.ca", "")
|
||||
}
|
||||
|
||||
// Ensures that a the CA file can be parsed from the environment.
|
||||
func TestConfigCertFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_CERT_FILE", "/tmp/file.cert", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Cert file flag can be parsed.
|
||||
func TestConfigCertFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-clientCert", "/tmp/file.cert"}), "")
|
||||
assert.Equal(t, c.CertFile, "/tmp/file.cert", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Key file can be parsed from the environment.
|
||||
func TestConfigKeyFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_KEY_FILE", "/tmp/file.key", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Key file flag can be parsed.
|
||||
func TestConfigKeyFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-clientKey", "/tmp/file.key"}), "")
|
||||
assert.Equal(t, c.KeyFile, "/tmp/file.key", "")
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host can be parsed from the environment.
|
||||
func TestConfigListenHostEnv(t *testing.T) {
|
||||
withEnv("ETCD_LISTEN_HOST", "127.0.0.1:4003", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.ListenHost, "127.0.0.1:4003", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Listen Host file flag can be parsed.
|
||||
func TestConfigListenHostFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-cl", "127.0.0.1:4003"}), "")
|
||||
assert.Equal(t, c.ListenHost, "127.0.0.1:4003", "")
|
||||
}
|
||||
|
||||
// Ensures that the Machines can be parsed from the environment.
|
||||
func TestConfigMachinesEnv(t *testing.T) {
|
||||
withEnv("ETCD_MACHINES", "coreos.com:4001,coreos.com:4002", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Machines, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Machines flag can be parsed.
|
||||
func TestConfigMachinesFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-C", "coreos.com:4001,coreos.com:4002"}), "")
|
||||
assert.Equal(t, c.Machines, []string{"coreos.com:4001", "coreos.com:4002"}, "")
|
||||
}
|
||||
|
||||
// Ensures that the Machines File can be parsed from the environment.
|
||||
func TestConfigMachinesFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_MACHINES_FILE", "/tmp/machines", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.MachinesFile, "/tmp/machines", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Machines File flag can be parsed.
|
||||
func TestConfigMachinesFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-CF", "/tmp/machines"}), "")
|
||||
assert.Equal(t, c.MachinesFile, "/tmp/machines", "")
|
||||
}
|
||||
|
||||
// Ensures that the Max Cluster Size can be parsed from the environment.
|
||||
func TestConfigMaxClusterSizeEnv(t *testing.T) {
|
||||
withEnv("ETCD_MAX_CLUSTER_SIZE", "5", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.MaxClusterSize, 5, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Max Cluster Size flag can be parsed.
|
||||
func TestConfigMaxClusterSizeFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-maxsize", "5"}), "")
|
||||
assert.Equal(t, c.MaxClusterSize, 5, "")
|
||||
}
|
||||
|
||||
// Ensures that the Max Result Buffer can be parsed from the environment.
|
||||
func TestConfigMaxResultBufferEnv(t *testing.T) {
|
||||
withEnv("ETCD_MAX_RESULT_BUFFER", "512", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Max Result Buffer flag can be parsed.
|
||||
func TestConfigMaxResultBufferFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-m", "512"}), "")
|
||||
assert.Equal(t, c.MaxResultBuffer, 512, "")
|
||||
}
|
||||
|
||||
// Ensures that the Max Retry Attempts can be parsed from the environment.
|
||||
func TestConfigMaxRetryAttemptsEnv(t *testing.T) {
|
||||
withEnv("ETCD_MAX_RETRY_ATTEMPTS", "10", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 10, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Max Retry Attempts flag can be parsed.
|
||||
func TestConfigMaxRetryAttemptsFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-r", "10"}), "")
|
||||
assert.Equal(t, c.MaxRetryAttempts, 10, "")
|
||||
}
|
||||
|
||||
// Ensures that the Name can be parsed from the environment.
|
||||
func TestConfigNameEnv(t *testing.T) {
|
||||
withEnv("ETCD_NAME", "test-name", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Name flag can be parsed.
|
||||
func TestConfigNameFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-n", "test-name"}), "")
|
||||
assert.Equal(t, c.Name, "test-name", "")
|
||||
}
|
||||
|
||||
// Ensures that Snapshot can be parsed from the environment.
|
||||
func TestConfigSnapshotEnv(t *testing.T) {
|
||||
withEnv("ETCD_SNAPSHOT", "1", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Snapshot flag can be parsed.
|
||||
func TestConfigSnapshotFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-snapshot"}), "")
|
||||
assert.Equal(t, c.Snapshot, true, "")
|
||||
}
|
||||
|
||||
// Ensures that Verbose can be parsed from the environment.
|
||||
func TestConfigVerboseEnv(t *testing.T) {
|
||||
withEnv("ETCD_VERBOSE", "true", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Verbose flag can be parsed.
|
||||
func TestConfigVerboseFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-v"}), "")
|
||||
assert.Equal(t, c.Verbose, true, "")
|
||||
}
|
||||
|
||||
// Ensures that Very Verbose can be parsed from the environment.
|
||||
func TestConfigVeryVerboseEnv(t *testing.T) {
|
||||
withEnv("ETCD_VERY_VERBOSE", "true", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Very Verbose flag can be parsed.
|
||||
func TestConfigVeryVerboseFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-vv"}), "")
|
||||
assert.Equal(t, c.VeryVerbose, true, "")
|
||||
}
|
||||
|
||||
// Ensures that Web URL can be parsed from the environment.
|
||||
func TestConfigWebURLEnv(t *testing.T) {
|
||||
withEnv("ETCD_WEB_URL", "/web", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.WebURL, "/web", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Web URL flag can be parsed.
|
||||
func TestConfigWebURLFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-w", "/web"}), "")
|
||||
assert.Equal(t, c.WebURL, "/web", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Advertised URL can be parsed from the environment.
|
||||
func TestConfigPeerAdvertisedUrlEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_ADVERTISED_URL", "localhost:7002", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.AdvertisedUrl, "localhost:7002", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peer Advertised URL flag can be parsed.
|
||||
func TestConfigPeerAdvertisedUrlFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-s", "localhost:7002"}), "")
|
||||
assert.Equal(t, c.Peer.AdvertisedUrl, "localhost:7002", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer CA File can be parsed from the environment.
|
||||
func TestConfigPeerCAFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_CA_FILE", "/tmp/peer/file.ca", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peer CA file flag can be parsed.
|
||||
func TestConfigPeerCAFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-serverCAFile", "/tmp/peer/file.ca"}), "")
|
||||
assert.Equal(t, c.Peer.CAFile, "/tmp/peer/file.ca", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Cert File can be parsed from the environment.
|
||||
func TestConfigPeerCertFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_CERT_FILE", "/tmp/peer/file.cert", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Cert file flag can be parsed.
|
||||
func TestConfigPeerCertFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-serverCert", "/tmp/peer/file.cert"}), "")
|
||||
assert.Equal(t, c.Peer.CertFile, "/tmp/peer/file.cert", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Key File can be parsed from the environment.
|
||||
func TestConfigPeerKeyFileEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_KEY_FILE", "/tmp/peer/file.key", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peer Key file flag can be parsed.
|
||||
func TestConfigPeerKeyFileFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-serverKey", "/tmp/peer/file.key"}), "")
|
||||
assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "")
|
||||
}
|
||||
|
||||
// Ensures that the Peer Listen Host can be parsed from the environment.
|
||||
func TestConfigPeerListenHostEnv(t *testing.T) {
|
||||
withEnv("ETCD_PEER_LISTEN_HOST", "localhost:7004", func(c *Config) {
|
||||
assert.Nil(t, c.LoadEnv(), "")
|
||||
assert.Equal(t, c.Peer.ListenHost, "localhost:7004", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a the Peer Listen Host file flag can be parsed.
|
||||
func TestConfigPeerListenHostFlag(t *testing.T) {
|
||||
c := NewConfig()
|
||||
assert.Nil(t, c.LoadFlags([]string{"-sl", "127.0.0.1:4003"}), "")
|
||||
assert.Equal(t, c.Peer.ListenHost, "127.0.0.1:4003", "")
|
||||
}
|
||||
|
||||
|
||||
// Ensures that a system config field is overridden by a custom config field.
|
||||
func TestConfigCustomConfigOverrideSystemConfig(t *testing.T) {
|
||||
system := `advertised_url = "127.0.0.1:5000"`
|
||||
custom := `advertised_url = "127.0.0.1:6000"`
|
||||
withTempFile(system, func(p1 string) {
|
||||
withTempFile(custom, func(p2 string) {
|
||||
c := NewConfig()
|
||||
c.SystemPath = p1
|
||||
assert.Nil(t, c.Load([]string{"-config", p2}), "")
|
||||
assert.Equal(t, c.AdvertisedUrl, "http://127.0.0.1:6000", "")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that a custom config field is overridden by an environment variable.
|
||||
func TestConfigEnvVarOverrideCustomConfig(t *testing.T) {
|
||||
os.Setenv("ETCD_PEER_ADVERTISED_URL", "127.0.0.1:8000")
|
||||
defer os.Setenv("ETCD_PEER_ADVERTISED_URL", "")
|
||||
|
||||
custom := `[peer]`+"\n"+`advertised_url = "127.0.0.1:9000"`
|
||||
withTempFile(custom, func(path string) {
|
||||
c := NewConfig()
|
||||
c.SystemPath = ""
|
||||
assert.Nil(t, c.Load([]string{"-config", path}), "")
|
||||
assert.Equal(t, c.Peer.AdvertisedUrl, "http://127.0.0.1:8000", "")
|
||||
})
|
||||
}
|
||||
|
||||
// Ensures that an environment variable field is overridden by a command line argument.
|
||||
func TestConfigCLIArgsOverrideEnvVar(t *testing.T) {
|
||||
os.Setenv("ETCD_ADVERTISED_URL", "127.0.0.1:1000")
|
||||
defer os.Setenv("ETCD_ADVERTISED_URL", "")
|
||||
|
||||
c := NewConfig()
|
||||
c.SystemPath = ""
|
||||
assert.Nil(t, c.Load([]string{"-c", "127.0.0.1:2000"}), "")
|
||||
assert.Equal(t, c.AdvertisedUrl, "http://127.0.0.1:2000", "")
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------
|
||||
|
||||
// Sets up the environment with a given environment variable set.
|
||||
func withEnv(key, value string, f func(c *Config)) {
|
||||
os.Setenv(key, value)
|
||||
defer os.Setenv(key, "")
|
||||
c := NewConfig()
|
||||
f(c)
|
||||
}
|
||||
|
||||
// Creates a temp file and calls a function with the context.
|
||||
func withTempFile(content string, fn func(string)) {
|
||||
f, _ := ioutil.TempFile("", "")
|
||||
f.WriteString(content)
|
||||
f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
fn(f.Name())
|
||||
}
|
||||
19
server/info.go
Normal file
19
server/info.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package server
|
||||
|
||||
// Info describes the non-mutable state of the server upon initialization.
|
||||
// These fields cannot be changed without deleting the server fields and
|
||||
// reinitializing.
|
||||
type Info struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
RaftURL string `json:"raftURL"`
|
||||
EtcdURL string `json:"etcdURL"`
|
||||
WebURL string `json:"webURL"`
|
||||
|
||||
RaftListenHost string `json:"raftListenHost"`
|
||||
EtcdListenHost string `json:"etcdListenHost"`
|
||||
|
||||
RaftTLS TLSInfo `json:"raftTLS"`
|
||||
EtcdTLS TLSInfo `json:"etcdTLS"`
|
||||
}
|
||||
|
||||
@@ -283,10 +283,10 @@ func (s *Server) Dispatch(c raft.Command, w http.ResponseWriter, req *http.Reque
|
||||
}
|
||||
|
||||
// Sets a comma-delimited list of origins that are allowed.
|
||||
func (s *Server) AllowOrigins(origins string) error {
|
||||
func (s *Server) AllowOrigins(origins []string) error {
|
||||
// Construct a lookup of all origins.
|
||||
m := make(map[string]bool)
|
||||
for _, v := range strings.Split(origins, ",") {
|
||||
for _, v := range origins {
|
||||
if v != "*" {
|
||||
if _, err := url.Parse(v); err != nil {
|
||||
return fmt.Errorf("Invalid CORS origin: %s", err)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
// TLSConfig holds the TLS configuration.
|
||||
type TLSConfig struct {
|
||||
Scheme string
|
||||
Server tls.Config
|
||||
|
||||
@@ -1,7 +1,76 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// TLSInfo holds the SSL certificates paths.
|
||||
type TLSInfo struct {
|
||||
CertFile string `json:"CertFile"`
|
||||
KeyFile string `json:"KeyFile"`
|
||||
CAFile string `json:"CAFile"`
|
||||
}
|
||||
|
||||
// Generates a TLS configuration from the given files.
|
||||
func (info TLSInfo) Config() (TLSConfig, error) {
|
||||
var t TLSConfig
|
||||
t.Scheme = "http"
|
||||
|
||||
// If the user do not specify key file, cert file and CA file, the type will be HTTP
|
||||
if info.KeyFile == "" && info.CertFile == "" && info.CAFile == "" {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// Both the key and cert must be present.
|
||||
if info.KeyFile == "" || info.CertFile == "" {
|
||||
return t, errors.New("KeyFile and CertFile must both be present")
|
||||
}
|
||||
|
||||
tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
t.Scheme = "https"
|
||||
t.Server.ClientAuth, t.Server.ClientCAs, err = newCertPool(info.CAFile)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
// The client should trust the RootCA that the Server uses since
|
||||
// everyone is a peer in the network.
|
||||
t.Client.Certificates = []tls.Certificate{tlsCert}
|
||||
t.Client.RootCAs = t.Server.ClientCAs
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// newCertPool creates x509 certPool and corresponding Auth Type.
|
||||
// If the given CAfile is valid, add the cert into the pool and verify the clients'
|
||||
// certs against the cert in the pool.
|
||||
// If the given CAfile is empty, do not verify the clients' cert.
|
||||
// If the given CAfile is not valid, fatal.
|
||||
func newCertPool(CAFile string) (tls.ClientAuthType, *x509.CertPool, error) {
|
||||
if CAFile == "" {
|
||||
return tls.NoClientCert, nil, nil
|
||||
}
|
||||
pemByte, err := ioutil.ReadFile(CAFile)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
block, pemByte := pem.Decode(pemByte)
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AddCert(cert)
|
||||
|
||||
return tls.RequireAndVerifyClientCert, certPool, nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package server
|
||||
|
||||
/*
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
@@ -59,3 +60,4 @@ func TestTransporterTimeout(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/etcd/log"
|
||||
)
|
||||
@@ -31,3 +32,16 @@ func redirect(hostname string, w http.ResponseWriter, req *http.Request) {
|
||||
log.Debugf("Redirect to %s", redirectURL.String())
|
||||
http.Redirect(w, req, redirectURL.String(), http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
// trimsplit slices s into all substrings separated by sep and returns a
|
||||
// slice of the substrings between the separator with all leading and trailing
|
||||
// white space removed, as defined by Unicode.
|
||||
func trimsplit(s, sep string) []string {
|
||||
raw := strings.Split(s, ",")
|
||||
trimmed := make([]string, 0)
|
||||
for _, r := range raw {
|
||||
trimmed = append(trimmed, strings.TrimSpace(r))
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user