From c29404e272de2a68131593c69a4c1f8922397ca2 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 12 May 2015 17:11:39 -0700 Subject: [PATCH 1/3] add --cluster-driver and --cluster-opt Signed-off-by: Victor Vieux --- cli/cli.go | 5 +++-- cli/flags.go | 20 ++++++++++---------- cli/help.go | 2 +- cli/manage.go | 13 ++++++++----- cluster/options.go | 8 ++++---- cluster/swarm/cluster.go | 41 ++++++++++++++++++++++++++++------------ 6 files changed, 55 insertions(+), 34 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index b1296a8d6c..57783fed75 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -104,9 +104,10 @@ func Run() { Flags: []cli.Flag{ flStore, flStrategy, flFilter, - flHosts, flHeartBeat, flOverCommit, + flHosts, flHeartBeat, flTLS, flTLSCaCert, flTLSCert, flTLSKey, flTLSVerify, - flEnableCors}, + flEnableCors, + flCluster, flClusterOpt}, Action: manage, }, { diff --git a/cli/flags.go b/cli/flags.go index ece1c6ddd8..9b2485d944 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -77,11 +77,6 @@ var ( Name: "tlsverify", Usage: "use TLS and verify the remote", } - flOverCommit = cli.Float64Flag{ - Name: "overcommit, oc", - Usage: "overcommit to apply on resources", - Value: 0.05, - } flStrategy = cli.StringFlag{ Name: "strategy", Usage: "placement strategy to use [" + strings.Join(strategy.List(), ", ") + "]", @@ -99,9 +94,14 @@ var ( Value: &flFilterValue, } -// flCluster = cli.StringFlag{ -// Name: "cluster, c", -// Usage: "cluster to use [swarm, mesos]", -// Value: "swarm", -// } + flCluster = cli.StringFlag{ + Name: "cluster-driver, c", + Usage: "cluster driver to use [swarm]", + Value: "swarm", + } + flClusterOpt = cli.StringSliceFlag{ + Name: "cluster-opt", + Usage: "cluster driver options", + Value: &cli.StringSlice{}, + } ) diff --git a/cli/help.go b/cli/help.go index fb33db5ce8..5f649df02e 100644 --- a/cli/help.go +++ b/cli/help.go @@ -25,7 +25,7 @@ ARGUMENTS: {{printf "\t"}} * ,{{end}}{{if .Flags}} OPTIONS: {{range .Flags}}{{.}} - {{end}}{{ end }} + {{end}}{{if (eq .Name "manage")}}{{printf "\t * swarm.overcommit=0.05\tovercommit to apply on resources\n"}}{{end}}{{ end }} ` } diff --git a/cli/manage.go b/cli/manage.go index 62c27c2d98..8447d060b7 100644 --- a/cli/manage.go +++ b/cli/manage.go @@ -125,13 +125,16 @@ func manage(c *cli.Context) { log.Fatal("--heartbeat should be an unsigned integer and greater than 0") } options := &cluster.Options{ - TLSConfig: tlsConfig, - OvercommitRatio: c.Float64("overcommit"), - Discovery: dflag, - Heartbeat: hb, + TLSConfig: tlsConfig, + Opts: c.StringSlice("cluster-opt"), + Discovery: dflag, + Heartbeat: hb, } - cluster := swarm.NewCluster(sched, store, options) + cluster, err := swarm.NewCluster(sched, store, options) + if err != nil { + log.Fatal(err) + } // see https://github.com/codegangsta/cli/issues/160 hosts := c.StringSlice("host") diff --git a/cluster/options.go b/cluster/options.go index 5c3b37ffee..d0b91b85f8 100644 --- a/cluster/options.go +++ b/cluster/options.go @@ -4,8 +4,8 @@ import "crypto/tls" // Options is exported type Options struct { - TLSConfig *tls.Config - OvercommitRatio float64 - Discovery string - Heartbeat uint64 + TLSConfig *tls.Config + Opts []string + Discovery string + Heartbeat uint64 } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 40db12b139..681abf0260 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "sort" + "strconv" "strings" "sync" @@ -23,22 +24,38 @@ import ( type Cluster struct { sync.RWMutex - eventHandler cluster.EventHandler - engines map[string]*cluster.Engine - scheduler *scheduler.Scheduler - options *cluster.Options - store *state.Store + eventHandler cluster.EventHandler + engines map[string]*cluster.Engine + scheduler *scheduler.Scheduler + options *cluster.Options + store *state.Store + overcommitRatio float64 } // NewCluster is exported -func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, options *cluster.Options) cluster.Cluster { +func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, options *cluster.Options) (cluster.Cluster, error) { log.WithFields(log.Fields{"name": "swarm"}).Debug("Initializing cluster") cluster := &Cluster{ - engines: make(map[string]*cluster.Engine), - scheduler: scheduler, - options: options, - store: store, + engines: make(map[string]*cluster.Engine), + scheduler: scheduler, + options: options, + store: store, + overcommitRatio: 0.05, + } + + for _, opt := range options.Opts { + kv := strings.SplitN(opt, "=", 2) + switch kv[0] { + case "swarm.overcommit": + overcommit, err := strconv.ParseFloat(kv[1], 64) + if err != nil { + return nil, err + } + cluster.overcommitRatio = overcommit + default: + return nil, fmt.Errorf("unsuported opt %q", kv[0]) + } } // get the list of entries from the discovery service @@ -58,7 +75,7 @@ func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, options *clu go d.Watch(cluster.newEntries) }() - return cluster + return cluster, nil } // Handle callbacks for the events @@ -151,7 +168,7 @@ func (c *Cluster) newEntries(entries []*discovery.Entry) { for _, entry := range entries { go func(m *discovery.Entry) { if !c.hasEngine(m.String()) { - engine := cluster.NewEngine(m.String(), c.options.OvercommitRatio) + engine := cluster.NewEngine(m.String(), c.overcommitRatio) if err := engine.Connect(c.options.TLSConfig); err != nil { log.Error(err) return From 5db3a9cd0e4423338e3113bdb712af878e5da4e7 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 13 May 2015 00:00:24 -0700 Subject: [PATCH 2/3] remove options struct, yay! Signed-off-by: Victor Vieux --- cli/cli.go | 2 +- cli/help.go | 3 +- cli/manage.go | 14 +---- cluster/options.go | 50 +++++++++++++++--- cluster/options_test.go | 97 +++++++++++++++++++++++++++++++++++ cluster/swarm/cluster.go | 42 ++++++++------- test/integration/helpers.bash | 2 +- 7 files changed, 165 insertions(+), 45 deletions(-) create mode 100644 cluster/options_test.go diff --git a/cli/cli.go b/cli/cli.go index 57783fed75..b1b31a673e 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -104,7 +104,7 @@ func Run() { Flags: []cli.Flag{ flStore, flStrategy, flFilter, - flHosts, flHeartBeat, + flHosts, flTLS, flTLSCaCert, flTLSCert, flTLSKey, flTLSVerify, flEnableCors, flCluster, flClusterOpt}, diff --git a/cli/help.go b/cli/help.go index 5f649df02e..593630493a 100644 --- a/cli/help.go +++ b/cli/help.go @@ -25,7 +25,8 @@ ARGUMENTS: {{printf "\t"}} * ,{{end}}{{if .Flags}} OPTIONS: {{range .Flags}}{{.}} - {{end}}{{if (eq .Name "manage")}}{{printf "\t * swarm.overcommit=0.05\tovercommit to apply on resources\n"}}{{end}}{{ end }} + {{end}}{{if (eq .Name "manage")}}{{printf "\t * swarm.overcommit=0.05\tovercommit to apply on resources"}} + {{printf "\t * swarm.heartbeat=25\ttime in second between each heartbeat"}}{{end}}{{ end }} ` } diff --git a/cli/manage.go b/cli/manage.go index 8447d060b7..080f558efb 100644 --- a/cli/manage.go +++ b/cli/manage.go @@ -6,7 +6,6 @@ import ( "fmt" "io/ioutil" "path" - "strconv" log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" @@ -120,18 +119,7 @@ func manage(c *cli.Context) { sched := scheduler.New(s, fs) - hb, err := strconv.ParseUint(c.String("heartbeat"), 0, 32) - if hb < 1 || err != nil { - log.Fatal("--heartbeat should be an unsigned integer and greater than 0") - } - options := &cluster.Options{ - TLSConfig: tlsConfig, - Opts: c.StringSlice("cluster-opt"), - Discovery: dflag, - Heartbeat: hb, - } - - cluster, err := swarm.NewCluster(sched, store, options) + cluster, err := swarm.NewCluster(sched, store, tlsConfig, dflag, c.StringSlice("cluster-opt")) if err != nil { log.Fatal(err) } diff --git a/cluster/options.go b/cluster/options.go index d0b91b85f8..c32fab39ec 100644 --- a/cluster/options.go +++ b/cluster/options.go @@ -1,11 +1,47 @@ package cluster -import "crypto/tls" +import ( + "strconv" + "strings" +) -// Options is exported -type Options struct { - TLSConfig *tls.Config - Opts []string - Discovery string - Heartbeat uint64 +// DriverOpts are key=values options +type DriverOpts []string + +// String returns a string from the driver options +func (do DriverOpts) String(key string) (string, bool) { + for _, opt := range do { + kv := strings.SplitN(opt, "=", 2) + if kv[0] == key { + return kv[1], true + } + } + return "", false +} + +// Int returns an int64 from the driver options +func (do DriverOpts) Int(key string) (int64, bool) { + if value, ok := do.String(key); ok { + v, _ := strconv.ParseInt(value, 0, 64) + return v, true + } + return 0, false +} + +// Uint returns an int64 from the driver options +func (do DriverOpts) Uint(key string) (uint64, bool) { + if value, ok := do.String(key); ok { + v, _ := strconv.ParseUint(value, 0, 64) + return v, true + } + return 0, false +} + +// Float returns a float64 from the driver options +func (do DriverOpts) Float(key string) (float64, bool) { + if value, ok := do.String(key); ok { + v, _ := strconv.ParseFloat(value, 64) + return v, true + } + return 0.0, false } diff --git a/cluster/options_test.go b/cluster/options_test.go new file mode 100644 index 0000000000..42146b1e26 --- /dev/null +++ b/cluster/options_test.go @@ -0,0 +1,97 @@ +package cluster + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var opts = DriverOpts{"foo1=bar", "foo2=-5", "foo3=7", "foo4=0.6"} + +func TestString(t *testing.T) { + val, ok := opts.String("foo1") + assert.True(t, ok) + assert.Equal(t, val, "bar") + + val, ok = opts.String("foo2") + assert.True(t, ok) + assert.Equal(t, val, "-5") + + val, ok = opts.String("foo3") + assert.True(t, ok) + assert.Equal(t, val, "7") + + val, ok = opts.String("foo4") + assert.True(t, ok) + assert.Equal(t, val, "0.6") + + val, ok = opts.String("invalid") + assert.False(t, ok) + assert.Equal(t, val, "") +} + +func TestInt(t *testing.T) { + val, ok := opts.Int("foo1") + assert.True(t, ok) + assert.Equal(t, val, 0) + + val, ok = opts.Int("foo2") + assert.True(t, ok) + assert.Equal(t, val, -5) + + val, ok = opts.Int("foo3") + assert.True(t, ok) + assert.Equal(t, val, 7) + + val, ok = opts.Int("foo4") + assert.True(t, ok) + assert.Equal(t, val, 0) + + val, ok = opts.Int("invalid") + assert.False(t, ok) + assert.Equal(t, val, 0) +} + +func TestUint(t *testing.T) { + val, ok := opts.Uint("foo1") + assert.True(t, ok) + assert.Equal(t, val, uint(0)) + + val, ok = opts.Uint("foo2") + assert.True(t, ok) + assert.Equal(t, val, uint(0)) + + val, ok = opts.Uint("foo3") + assert.True(t, ok) + assert.Equal(t, val, uint(7)) + + val, ok = opts.Uint("foo4") + assert.True(t, ok) + assert.Equal(t, val, uint(0)) + + val, ok = opts.Uint("invalid") + assert.False(t, ok) + assert.Equal(t, val, uint(0)) +} + +func TestFloat(t *testing.T) { + val, ok := opts.Float("foo1") + assert.True(t, ok) + assert.Equal(t, val, 0.0) + + val, ok = opts.Float("foo2") + assert.True(t, ok) + assert.Equal(t, val, -5.0) + + val, ok = opts.Float("foo3") + assert.True(t, ok) + assert.Equal(t, val, 7.0) + + val, ok = opts.Float("foo4") + assert.True(t, ok) + assert.Equal(t, val, 0.6) + + val, ok = opts.Float("invalid") + assert.False(t, ok) + assert.Equal(t, val, 0.0) +} diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 681abf0260..160589a0fd 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -1,11 +1,11 @@ package swarm import ( + "crypto/tls" "errors" "fmt" "io" "sort" - "strconv" "strings" "sync" @@ -24,43 +24,41 @@ import ( type Cluster struct { sync.RWMutex - eventHandler cluster.EventHandler - engines map[string]*cluster.Engine - scheduler *scheduler.Scheduler - options *cluster.Options - store *state.Store + eventHandler cluster.EventHandler + engines map[string]*cluster.Engine + scheduler *scheduler.Scheduler + store *state.Store + overcommitRatio float64 + discovery string + heartbeat uint64 + TLSConfig *tls.Config } // NewCluster is exported -func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, options *cluster.Options) (cluster.Cluster, error) { +func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, TLSConfig *tls.Config, dflag string, options cluster.DriverOpts) (cluster.Cluster, error) { log.WithFields(log.Fields{"name": "swarm"}).Debug("Initializing cluster") cluster := &Cluster{ engines: make(map[string]*cluster.Engine), scheduler: scheduler, - options: options, store: store, overcommitRatio: 0.05, + discovery: dflag, + TLSConfig: TLSConfig, } - for _, opt := range options.Opts { - kv := strings.SplitN(opt, "=", 2) - switch kv[0] { - case "swarm.overcommit": - overcommit, err := strconv.ParseFloat(kv[1], 64) - if err != nil { - return nil, err - } - cluster.overcommitRatio = overcommit - default: - return nil, fmt.Errorf("unsuported opt %q", kv[0]) - } + if val, ok := options.Float("swarm.overcommit"); ok { + cluster.overcommitRatio = val + } + + if cluster.heartbeat, _ = options.Uint("swarm.heartbeat"); cluster.heartbeat < 1 { + return nil, errors.New("heartbeat should be an unsigned integer and greater than 0") } // get the list of entries from the discovery service go func() { - d, err := discovery.New(options.Discovery, options.Heartbeat) + d, err := discovery.New(cluster.discovery, cluster.heartbeat) if err != nil { log.Fatal(err) } @@ -169,7 +167,7 @@ func (c *Cluster) newEntries(entries []*discovery.Entry) { go func(m *discovery.Entry) { if !c.hasEngine(m.String()) { engine := cluster.NewEngine(m.String(), c.overcommitRatio) - if err := engine.Connect(c.options.TLSConfig); err != nil { + if err := engine.Connect(c.TLSConfig); err != nil { log.Error(err) return } diff --git a/test/integration/helpers.bash b/test/integration/helpers.bash index 9865006b66..235daf1942 100644 --- a/test/integration/helpers.bash +++ b/test/integration/helpers.bash @@ -99,7 +99,7 @@ function swarm_manage() { discovery="$@" fi - "$SWARM_BINARY" manage -H "$SWARM_HOST" --heartbeat=1 "$discovery" & + "$SWARM_BINARY" manage -H "$SWARM_HOST" --cluster-opt "swarm.heartbeat=1" "$discovery" & SWARM_PID=$! wait_until_reachable "$SWARM_HOST" retry 10 1 check_swarm_nodes From 59cd11b94607b97e3b19778ed483c88583a2c704 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 13 May 2015 15:48:07 -0700 Subject: [PATCH 3/3] change swarm.heartbeat to swarm.discovery.heartbeat Signed-off-by: Victor Vieux --- cli/help.go | 2 +- cluster/swarm/cluster.go | 2 +- test/integration/helpers.bash | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/help.go b/cli/help.go index 593630493a..6c13e4fff2 100644 --- a/cli/help.go +++ b/cli/help.go @@ -26,7 +26,7 @@ ARGUMENTS: OPTIONS: {{range .Flags}}{{.}} {{end}}{{if (eq .Name "manage")}}{{printf "\t * swarm.overcommit=0.05\tovercommit to apply on resources"}} - {{printf "\t * swarm.heartbeat=25\ttime in second between each heartbeat"}}{{end}}{{ end }} + {{printf "\t * swarm.discovery.heartbeat=25\ttime in second between each heartbeat"}}{{end}}{{ end }} ` } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 160589a0fd..259cf66b86 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -52,7 +52,7 @@ func NewCluster(scheduler *scheduler.Scheduler, store *state.Store, TLSConfig *t cluster.overcommitRatio = val } - if cluster.heartbeat, _ = options.Uint("swarm.heartbeat"); cluster.heartbeat < 1 { + if cluster.heartbeat, _ = options.Uint("swarm.discovery.heartbeat"); cluster.heartbeat < 1 { return nil, errors.New("heartbeat should be an unsigned integer and greater than 0") } diff --git a/test/integration/helpers.bash b/test/integration/helpers.bash index 235daf1942..d183072570 100644 --- a/test/integration/helpers.bash +++ b/test/integration/helpers.bash @@ -99,7 +99,7 @@ function swarm_manage() { discovery="$@" fi - "$SWARM_BINARY" manage -H "$SWARM_HOST" --cluster-opt "swarm.heartbeat=1" "$discovery" & + "$SWARM_BINARY" manage -H "$SWARM_HOST" --cluster-opt "swarm.discovery.heartbeat=1" "$discovery" & SWARM_PID=$! wait_until_reachable "$SWARM_HOST" retry 10 1 check_swarm_nodes