View
2,234 etcd.go

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1,18 @@
+---
+graph: mygraph
+resources:
+ file:
+ - name: file1d
+ path: "/tmp/mgmtD/f1d"
+ content: |
+ i am f1
+ state: exists
+ - name: "@@file2d"
+ path: "/tmp/mgmtD/f2d"
+ content: |
+ i am f2, exported from host D
+ state: exists
+collect:
+- kind: file
+ pattern: "/tmp/mgmtD/"
+edges: []
View
157 main.go
@@ -19,6 +19,7 @@ package main
import (
"github.com/codegangsta/cli"
+ etcdtypes "github.com/coreos/etcd/pkg/types"
"github.com/coreos/pkg/capnslog"
"log"
"os"
@@ -35,7 +36,9 @@ var (
)
const (
- DEBUG = false
+ DEBUG = false // add additional log messages
+ TRACE = false // add execution flow log messages
+ VERBOSE = false // add extra log message output
)
// signal handler
@@ -59,17 +62,52 @@ func waitForSignal(exit chan bool) {
func run(c *cli.Context) error {
var start = time.Now().UnixNano()
- var wg sync.WaitGroup
- exit := make(chan bool) // exit signal
log.Printf("This is: %v, version: %v", program, version)
log.Printf("Main: Start: %v", start)
- var G, fullGraph *Graph
+
+ hostname := c.String("hostname")
+ if hostname == "" {
+ hostname, _ = os.Hostname()
+ }
+ noop := c.Bool("noop")
+
+ seeds, err := etcdtypes.NewURLs(
+ FlattenListWithSplit(c.StringSlice("seeds"), []string{",", ";", " "}),
+ )
+ if err != nil && len(c.StringSlice("seeds")) > 0 {
+ log.Printf("Main: Error: seeds didn't parse correctly!")
+ return cli.NewExitError("", 1)
+ }
+ clientURLs, err := etcdtypes.NewURLs(
+ FlattenListWithSplit(c.StringSlice("client-urls"), []string{",", ";", " "}),
+ )
+ if err != nil && len(c.StringSlice("client-urls")) > 0 {
+ log.Printf("Main: Error: clientURLs didn't parse correctly!")
+ return cli.NewExitError("", 1)
+ }
+ serverURLs, err := etcdtypes.NewURLs(
+ FlattenListWithSplit(c.StringSlice("server-urls"), []string{",", ";", " "}),
+ )
+ if err != nil && len(c.StringSlice("server-urls")) > 0 {
+ log.Printf("Main: Error: serverURLs didn't parse correctly!")
+ return cli.NewExitError("", 1)
+ }
+
+ idealClusterSize := uint16(c.Int("ideal-cluster-size"))
+ if idealClusterSize < 1 {
+ log.Printf("Main: Error: idealClusterSize should be at least one!")
+ return cli.NewExitError("", 1)
+ }
if c.IsSet("file") && c.IsSet("puppet") {
- log.Println("the --file and --puppet parameters cannot be used together")
+ log.Println("Main: Error: the --file and --puppet parameters cannot be used together!")
return cli.NewExitError("", 1)
}
+ var wg sync.WaitGroup
+ exit := make(chan bool) // exit signal
+ var G, fullGraph *Graph
+
// exit after `max-runtime` seconds for no reason at all...
if i := c.Int("max-runtime"); i > 0 {
go func() {
@@ -88,27 +126,25 @@ func run(c *cli.Context) error {
)
go converger.Loop(true) // main loop for converger, true to start paused
- // initial etcd peer endpoint
- seed := c.String("seed")
- if seed == "" {
- // XXX: start up etcd server, others will join me!
- seed = "http://127.0.0.1:2379" // thus we use the local server!
+ // embedded etcd
+ if len(seeds) == 0 {
+ log.Printf("Main: Seeds: No seeds specified!")
+ } else {
+ log.Printf("Main: Seeds(%v): %v", len(seeds), seeds)
}
- // then, connect to `seed` as a client
-
- // FIXME: validate seed, or wait for it to fail in etcd init?
-
- // etcd
- etcdO := &EtcdWObject{
- seed: seed,
- converger: converger,
- }
-
- hostname := c.String("hostname")
- if hostname == "" {
- hostname, _ = os.Hostname() // etcd watch key // XXX: this is not the correct key name this is the set key name... WOOPS
+ EmbdEtcd := NewEmbdEtcd(
+ hostname,
+ seeds,
+ clientURLs,
+ serverURLs,
+ c.Bool("no-server"),
+ idealClusterSize,
+ converger,
+ )
+ if err := EmbdEtcd.Startup(); err != nil { // startup (returns when etcd main loop is running)
+ log.Printf("Main: Etcd: Startup failed: %v", err)
+ exit <- true
}
- noop := c.Bool("noop")
exitchan := make(chan Event) // exit event
go func() {
@@ -124,26 +160,23 @@ func run(c *cli.Context) error {
puppetchan = time.Tick(time.Duration(interval) * time.Second)
}
log.Println("Etcd: Starting...")
- etcdchan := etcdO.EtcdWatch()
+ etcdchan := EtcdWatch(EmbdEtcd)
first := true // first loop or not
for {
log.Println("Main: Waiting...")
select {
- case _ = <-startchan: // kick the loop once at start
+ case <-startchan: // kick the loop once at start
// pass
- case msg := <-etcdchan:
- switch msg {
- // some types of messages we ignore...
- case etcdFoo, etcdBar:
+
+ case b := <-etcdchan:
+ if !b { // ignore the message
continue
- // while others passthrough and cause a compile!
- case etcdStart, etcdEvent:
- // pass
- default:
- log.Fatal("Etcd: Unhandled message: ", msg)
}
- case _ = <-puppetchan:
+ // everything else passes through to cause a compile!
+
+ case <-puppetchan:
// nothing, just go on
+
case msg := <-configchan:
if c.Bool("no-watch") || !msg {
continue // not ready to read config
@@ -174,7 +207,7 @@ func run(c *cli.Context) error {
// build graph from yaml file on events (eg: from etcd)
// we need the vertices to be paused to work on them
- if newFullgraph, err := fullGraph.NewGraphFromConfig(config, etcdO, hostname, noop); err == nil { // keep references to all original elements
+ if newFullgraph, err := fullGraph.NewGraphFromConfig(config, EmbdEtcd, hostname, noop); err == nil { // keep references to all original elements
fullGraph = newFullgraph
} else {
log.Printf("Config: Error making new graph from config: %v", err)
@@ -215,11 +248,19 @@ func run(c *cli.Context) error {
waitForSignal(exit) // pass in exit channel to watch
+ log.Println("Destroy...")
+
G.Exit() // tell all the children to exit
// tell inner main loop to exit
resp := NewResp()
- exitchan <- Event{eventExit, resp, "", false}
+ go func() { exitchan <- Event{eventExit, resp, "", false} }()
+
+ // cleanup etcd main loop last so it can process everything first
+ if err := EmbdEtcd.Destroy(); err != nil { // shutdown and cleanup etcd
+ log.Printf("Etcd exited poorly with: %v", err)
+ }
+
resp.ACKWait() // let inner main loop finish cleanly just in case
if DEBUG {
@@ -243,7 +284,11 @@ func main() {
// un-hijack from capnslog...
log.SetOutput(os.Stderr)
- capnslog.SetFormatter(capnslog.NewLogFormatter(os.Stderr, "(etcd) ", flags))
+ if VERBOSE {
+ capnslog.SetFormatter(capnslog.NewLogFormatter(os.Stderr, "(etcd) ", flags))
+ } else {
+ capnslog.SetFormatter(capnslog.NewNilFormatter())
+ }
// test for sanity
if program == "" || version == "" {
@@ -294,11 +339,35 @@ func main() {
Usage: "hostname to use",
},
// if empty, it will startup a new server
- cli.StringFlag{
- Name: "seed, s",
- Value: "",
- Usage: "default etc peer endpoint",
- EnvVar: "MGMT_SEED_ENDPOINT",
+ cli.StringSliceFlag{
+ Name: "seeds, s",
+ Value: &cli.StringSlice{}, // empty slice
+ Usage: "default etc client endpoint",
+ EnvVar: "MGMT_SEEDS",
+ },
+ // port 2379 and 4001 are common
+ cli.StringSliceFlag{
+ Name: "client-urls",
+ Value: &cli.StringSlice{},
+ Usage: "list of URLs to listen on for client traffic",
+ EnvVar: "MGMT_CLIENT_URLS",
+ },
+ // port 2380 and 7001 are common
+ cli.StringSliceFlag{
+ Name: "server-urls, peer-urls",
+ Value: &cli.StringSlice{},
+ Usage: "list of URLs to listen on for server (peer) traffic",
+ EnvVar: "MGMT_SERVER_URLS",
+ },
+ cli.BoolFlag{
+ Name: "no-server",
+ Usage: "do not let other servers peer with me",
+ },
+ cli.IntFlag{
+ Name: "ideal-cluster-size",
+ Value: defaultIdealClusterSize,
+ Usage: "ideal number of server peers in cluster, only read by initial server",
+ EnvVar: "MGMT_IDEAL_CLUSTER_SIZE",
},
cli.IntFlag{
Name: "converged-timeout, t",
View
30 misc.go
@@ -40,6 +40,15 @@ func StrInList(needle string, haystack []string) bool {
return false
}
+func Uint64KeyFromStrInMap(needle string, haystack map[uint64]string) (uint64, bool) {
+ for k, v := range haystack {
+ if v == needle {
+ return k, true
+ }
+ }
+ return 0, false
+}
+
// remove any duplicate values in the list
// possibly sub-optimal, O(n^2)? implementation
func StrRemoveDuplicatesInList(list []string) []string {
@@ -87,7 +96,7 @@ func ReverseStringList(in []string) []string {
// return the sorted list of string keys in a map with string keys
// NOTE: i thought it would be nice for this to use: map[string]interface{} but
-// it turns out that's not allowed. I know we don't have generics, but common!
+// it turns out that's not allowed. I know we don't have generics, but come on!
func StrMapKeys(m map[string]string) []string {
result := []string{}
for k, _ := range m {
@@ -97,6 +106,15 @@ func StrMapKeys(m map[string]string) []string {
return result
}
+func StrMapKeysUint64(m map[string]uint64) []string {
+ result := []string{}
+ for k, _ := range m {
+ result = append(result, k)
+ }
+ sort.Strings(result) // deterministic order
+ return result
+}
+
// return the sorted list of bool values in a map with string values
func BoolMapValues(m map[string]bool) []bool {
result := []bool{}
@@ -117,6 +135,16 @@ func StrMapValues(m map[string]string) []string {
return result
}
+// return the sorted list of string values in a map with string values
+func StrMapValuesUint64(m map[uint64]string) []string {
+ result := []string{}
+ for _, v := range m {
+ result = append(result, v)
+ }
+ sort.Strings(result) // deterministic order
+ return result
+}
+
// return true if everyone is true
func BoolMapTrue(l []bool) bool {
for _, b := range l {
View
@@ -68,6 +68,7 @@ type MetaParams struct {
type Base interface {
GetName() string // can't be named "Name()" because of struct field
SetName(string)
+ setKind(string)
Kind() string
Meta() *MetaParams
AssociateData(Converger)
@@ -162,7 +163,12 @@ func (obj *BaseRes) SetName(name string) {
obj.Name = name
}
-// return the kind of resource this is
+// setKind sets the kind. This is used internally for exported resources.
+func (obj *BaseRes) setKind(kind string) {
+ obj.kind = kind
+}
+
+// Kind returns the kind of resource this is
func (obj *BaseRes) Kind() string {
return obj.kind
}