2015-01-25 06:19:16 +03:00
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-12-20 01:47:07 +03:00
package etcdmain
import (
"flag"
"fmt"
"log"
"net/url"
"os"
"strings"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/pkg/cors"
"github.com/coreos/etcd/pkg/flags"
"github.com/coreos/etcd/pkg/transport"
"github.com/coreos/etcd/version"
)
const (
proxyFlagOff = "off"
proxyFlagReadonly = "readonly"
proxyFlagOn = "on"
fallbackFlagExit = "exit"
fallbackFlagProxy = "proxy"
clusterStateFlagNew = "new"
clusterStateFlagExisting = "existing"
2015-03-19 02:59:41 +03:00
defaultName = "default"
2014-12-20 01:47:07 +03:00
)
var (
ignored = [ ] string {
"cluster-active-size" ,
"cluster-remove-delay" ,
"cluster-sync-interval" ,
"config" ,
"force" ,
"max-result-buffer" ,
"max-retry-attempts" ,
"peer-heartbeat-interval" ,
"peer-election-timeout" ,
"retry-interval" ,
"snapshot" ,
"v" ,
"vv" ,
}
2014-12-20 21:31:53 +03:00
ErrConflictBootstrapFlags = fmt . Errorf ( "multiple discovery or bootstrap flags are set" +
"Choose one of \"initial-cluster\", \"discovery\" or \"discovery-srv\"" )
2014-12-20 01:47:07 +03:00
)
type config struct {
* flag . FlagSet
// member
corsInfo * cors . CORSInfo
dir string
lpurls , lcurls [ ] url . URL
maxSnapFiles uint
maxWalFiles uint
name string
snapCount uint64
2015-01-15 22:11:33 +03:00
// TODO: decouple tickMs and heartbeat tick (current heartbeat tick = 1).
// make ticks a cluster wide configuration.
TickMs uint
ElectionMs uint
2014-12-20 01:47:07 +03:00
// clustering
apurls , acurls [ ] url . URL
clusterState * flags . StringsFlag
dnsCluster string
dproxy string
durl string
fallback * flags . StringsFlag
initialCluster string
initialClusterToken string
// proxy
proxy * flags . StringsFlag
// security
clientTLSInfo , peerTLSInfo transport . TLSInfo
// unsafe
forceNewCluster bool
printVersion bool
ignored [ ] string
}
func NewConfig ( ) * config {
cfg := & config {
corsInfo : & cors . CORSInfo { } ,
clusterState : flags . NewStringsFlag (
clusterStateFlagNew ,
clusterStateFlagExisting ,
) ,
fallback : flags . NewStringsFlag (
fallbackFlagExit ,
fallbackFlagProxy ,
) ,
ignored : ignored ,
proxy : flags . NewStringsFlag (
proxyFlagOff ,
proxyFlagReadonly ,
proxyFlagOn ,
) ,
}
cfg . FlagSet = flag . NewFlagSet ( "etcd" , flag . ContinueOnError )
fs := cfg . FlagSet
fs . Usage = func ( ) {
fmt . Println ( usageline )
fmt . Println ( flagsline )
}
// member
fs . Var ( cfg . corsInfo , "cors" , "Comma-separated white list of origins for CORS (cross-origin resource sharing)." )
fs . StringVar ( & cfg . dir , "data-dir" , "" , "Path to the data directory" )
fs . Var ( flags . NewURLsValue ( "http://localhost:2380,http://localhost:7001" ) , "listen-peer-urls" , "List of URLs to listen on for peer traffic" )
fs . Var ( flags . NewURLsValue ( "http://localhost:2379,http://localhost:4001" ) , "listen-client-urls" , "List of URLs to listen on for client traffic" )
fs . UintVar ( & cfg . maxSnapFiles , "max-snapshots" , defaultMaxSnapshots , "Maximum number of snapshot files to retain (0 is unlimited)" )
fs . UintVar ( & cfg . maxWalFiles , "max-wals" , defaultMaxWALs , "Maximum number of wal files to retain (0 is unlimited)" )
2015-03-19 02:59:41 +03:00
fs . StringVar ( & cfg . name , "name" , defaultName , "Unique human-readable name for this node" )
2014-12-20 01:47:07 +03:00
fs . Uint64Var ( & cfg . snapCount , "snapshot-count" , etcdserver . DefaultSnapCount , "Number of committed transactions to trigger a snapshot" )
2015-01-15 22:11:33 +03:00
fs . UintVar ( & cfg . TickMs , "heartbeat-interval" , 100 , "Time (in milliseconds) of a heartbeat interval." )
fs . UintVar ( & cfg . ElectionMs , "election-timeout" , 1000 , "Time (in milliseconds) for an election to timeout." )
2014-12-20 01:47:07 +03:00
// clustering
fs . Var ( flags . NewURLsValue ( "http://localhost:2380,http://localhost:7001" ) , "initial-advertise-peer-urls" , "List of this member's peer URLs to advertise to the rest of the cluster" )
fs . Var ( flags . NewURLsValue ( "http://localhost:2379,http://localhost:4001" ) , "advertise-client-urls" , "List of this member's client URLs to advertise to the rest of the cluster" )
2014-12-22 23:10:04 +03:00
fs . StringVar ( & cfg . durl , "discovery" , "" , "Discovery service used to bootstrap the initial cluster" )
2014-12-20 01:47:07 +03:00
fs . Var ( cfg . fallback , "discovery-fallback" , fmt . Sprintf ( "Valid values include %s" , strings . Join ( cfg . fallback . Values , ", " ) ) )
if err := cfg . fallback . Set ( fallbackFlagProxy ) ; err != nil {
// Should never happen.
log . Panicf ( "unexpected error setting up discovery-fallback flag: %v" , err )
}
fs . StringVar ( & cfg . dproxy , "discovery-proxy" , "" , "HTTP proxy to use for traffic to discovery service" )
2014-12-22 23:10:04 +03:00
fs . StringVar ( & cfg . dnsCluster , "discovery-srv" , "" , "DNS domain used to bootstrap initial cluster" )
2015-03-19 02:59:41 +03:00
fs . StringVar ( & cfg . initialCluster , "initial-cluster" , initialClusterFromName ( defaultName ) , "Initial cluster configuration for bootstrapping" )
2014-12-20 01:47:07 +03:00
fs . StringVar ( & cfg . initialClusterToken , "initial-cluster-token" , "etcd-cluster" , "Initial cluster token for the etcd cluster during bootstrap" )
fs . Var ( cfg . clusterState , "initial-cluster-state" , "Initial cluster configuration for bootstrapping" )
if err := cfg . clusterState . Set ( clusterStateFlagNew ) ; err != nil {
// Should never happen.
log . Panicf ( "unexpected error setting up clusterStateFlag: %v" , err )
}
// proxy
fs . Var ( cfg . proxy , "proxy" , fmt . Sprintf ( "Valid values include %s" , strings . Join ( cfg . proxy . Values , ", " ) ) )
if err := cfg . proxy . Set ( proxyFlagOff ) ; err != nil {
// Should never happen.
log . Panicf ( "unexpected error setting up proxyFlag: %v" , err )
}
// security
2015-04-07 01:26:41 +03:00
fs . StringVar ( & cfg . clientTLSInfo . CAFile , "ca-file" , "" , "DEPRECATED: Path to the client server TLS CA file." )
2014-12-20 01:47:07 +03:00
fs . StringVar ( & cfg . clientTLSInfo . CertFile , "cert-file" , "" , "Path to the client server TLS cert file." )
fs . StringVar ( & cfg . clientTLSInfo . KeyFile , "key-file" , "" , "Path to the client server TLS key file." )
2015-03-13 08:26:46 +03:00
fs . BoolVar ( & cfg . clientTLSInfo . ClientCertAuth , "client-cert-auth" , false , "Enable client cert authentication." )
fs . StringVar ( & cfg . clientTLSInfo . TrustedCAFile , "trusted-ca-file" , "" , "Path to the client server TLS trusted CA key file." )
2015-04-07 01:26:41 +03:00
fs . StringVar ( & cfg . peerTLSInfo . CAFile , "peer-ca-file" , "" , "DEPRECATED: Path to the peer server TLS CA file." )
2014-12-20 01:47:07 +03:00
fs . StringVar ( & cfg . peerTLSInfo . CertFile , "peer-cert-file" , "" , "Path to the peer server TLS cert file." )
fs . StringVar ( & cfg . peerTLSInfo . KeyFile , "peer-key-file" , "" , "Path to the peer server TLS key file." )
2015-03-13 08:26:46 +03:00
fs . BoolVar ( & cfg . peerTLSInfo . ClientCertAuth , "peer-client-cert-auth" , false , "Enable peer client cert authentication." )
fs . StringVar ( & cfg . peerTLSInfo . TrustedCAFile , "peer-trusted-ca-file" , "" , "Path to the peer server TLS trusted CA file." )
2014-12-20 01:47:07 +03:00
// unsafe
fs . BoolVar ( & cfg . forceNewCluster , "force-new-cluster" , false , "Force to create a new one member cluster" )
// version
fs . BoolVar ( & cfg . printVersion , "version" , false , "Print the version and exit" )
// backwards-compatibility with v0.4.6
fs . Var ( & flags . IPAddressPort { } , "addr" , "DEPRECATED: Use -advertise-client-urls instead." )
fs . Var ( & flags . IPAddressPort { } , "bind-addr" , "DEPRECATED: Use -listen-client-urls instead." )
fs . Var ( & flags . IPAddressPort { } , "peer-addr" , "DEPRECATED: Use -initial-advertise-peer-urls instead." )
fs . Var ( & flags . IPAddressPort { } , "peer-bind-addr" , "DEPRECATED: Use -listen-peer-urls instead." )
fs . Var ( & flags . DeprecatedFlag { Name : "peers" } , "peers" , "DEPRECATED: Use -initial-cluster instead" )
fs . Var ( & flags . DeprecatedFlag { Name : "peers-file" } , "peers-file" , "DEPRECATED: Use -initial-cluster instead" )
// ignored
for _ , f := range cfg . ignored {
fs . Var ( & flags . IgnoredFlag { Name : f } , f , "" )
}
return cfg
}
func ( cfg * config ) Parse ( arguments [ ] string ) error {
2014-12-20 21:31:53 +03:00
perr := cfg . FlagSet . Parse ( arguments )
2014-12-20 01:47:07 +03:00
switch perr {
case nil :
case flag . ErrHelp :
os . Exit ( 0 )
default :
os . Exit ( 2 )
}
2015-03-21 03:01:53 +03:00
if len ( cfg . FlagSet . Args ( ) ) != 0 {
return fmt . Errorf ( "'%s' is not a valid flag" , cfg . FlagSet . Arg ( 0 ) )
}
2014-12-20 01:47:07 +03:00
if cfg . printVersion {
fmt . Println ( "etcd version" , version . Version )
os . Exit ( 0 )
}
err := flags . SetFlagsFromEnv ( cfg . FlagSet )
if err != nil {
log . Fatalf ( "etcd: %v" , err )
}
set := make ( map [ string ] bool )
cfg . FlagSet . Visit ( func ( f * flag . Flag ) {
set [ f . Name ] = true
} )
nSet := 0
2014-12-20 21:31:53 +03:00
for _ , v := range [ ] bool { set [ "discovery" ] , set [ "initial-cluster" ] , set [ "discovery-srv" ] } {
2014-12-20 01:47:07 +03:00
if v {
nSet += 1
}
}
if nSet > 1 {
2014-12-20 21:31:53 +03:00
return ErrConflictBootstrapFlags
2014-12-20 01:47:07 +03:00
}
2015-02-10 09:27:43 +03:00
flags . SetBindAddrFromAddr ( cfg . FlagSet , "peer-bind-addr" , "peer-addr" )
flags . SetBindAddrFromAddr ( cfg . FlagSet , "bind-addr" , "addr" )
cfg . lpurls , err = flags . URLsFromFlags ( cfg . FlagSet , "listen-peer-urls" , "peer-bind-addr" , cfg . peerTLSInfo )
2014-12-20 01:47:07 +03:00
if err != nil {
return err
}
cfg . apurls , err = flags . URLsFromFlags ( cfg . FlagSet , "initial-advertise-peer-urls" , "peer-addr" , cfg . peerTLSInfo )
if err != nil {
return err
}
2015-02-10 09:27:43 +03:00
cfg . lcurls , err = flags . URLsFromFlags ( cfg . FlagSet , "listen-client-urls" , "bind-addr" , cfg . clientTLSInfo )
2014-12-20 01:47:07 +03:00
if err != nil {
return err
}
cfg . acurls , err = flags . URLsFromFlags ( cfg . FlagSet , "advertise-client-urls" , "addr" , cfg . clientTLSInfo )
if err != nil {
return err
}
2015-03-13 03:45:49 +03:00
if 5 * cfg . TickMs > cfg . ElectionMs {
return fmt . Errorf ( "-election-timeout[%vms] should be at least as 5 times as -heartbeat-interval[%vms]" , cfg . ElectionMs , cfg . TickMs )
}
2014-12-20 01:47:07 +03:00
return nil
}
2015-03-19 02:59:41 +03:00
func initialClusterFromName ( name string ) string {
n := name
if name == "" {
n = defaultName
}
return fmt . Sprintf ( "%s=http://localhost:2380,%s=http://localhost:7001" , n , n )
}
2014-12-20 01:47:07 +03:00
func ( cfg config ) isNewCluster ( ) bool { return cfg . clusterState . String ( ) == clusterStateFlagNew }
func ( cfg config ) isProxy ( ) bool { return cfg . proxy . String ( ) != proxyFlagOff }
func ( cfg config ) isReadonlyProxy ( ) bool { return cfg . proxy . String ( ) == proxyFlagReadonly }
func ( cfg config ) shouldFallbackToProxy ( ) bool { return cfg . fallback . String ( ) == fallbackFlagProxy }
2015-01-15 22:11:33 +03:00
func ( cfg config ) electionTicks ( ) int { return int ( cfg . ElectionMs / cfg . TickMs ) }