small pixel drawing of a pufferfish cascade

main.go

// design touchstones
//   configured entirely via environment variables
//   minimal configurable options
//   a single global cluster
//   easy cluster formation
// todo
//   dns resolver for services

package main

import (
	"log"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/memberlist"
	"github.com/hashicorp/serf/serf"
)

func main() {
	config := readConfig()
	agent := setupAgent(config)
	if err := agent.Start(); err != nil {
		log.Panic(err)
	}
	defer agent.Shutdown()
	// join any specified startup nodes
	if err := startupJoin(config, agent); err != nil {
		log.Panic(err)
	}
//	handleSignals(config, agent)
}

func readConfig() *Config {
	config := DefaultConfig()
	if os.Getenv("CASCADE_BIND") != "" {
		config.BindAddr = os.Getenv("CASCADE_BIND")
	}
	// CASCADE_JOIN=127.0.0.1,127.0.0.5
	if os.Getenv("CASCADE_JOIN") != "" {
		config.StartJoin = strings.Split(os.Getenv("CASCADE_JOIN"), ",")
	}
	return config
}

func startupJoin(config *Config, agent *Agent) error {
	if len(config.StartJoin) == 0 {
		return nil
	}

	log.Printf("Joining cluster...")
	n, err := agent.Join(config.StartJoin)
	if err != nil {
		return err
	}

	log.Printf("Join completed. Synced with %d initial agents", n)
	return nil
}

func setupAgent(config *Config) *Agent {
	bindIP, bindPort, err := config.AddrParts(config.BindAddr)
	if err != nil {
		log.Panic(err)
	}
	serfConfig := serf.DefaultConfig()
	serfConfig.MemberlistConfig.BindAddr = bindIP
	serfConfig.MemberlistConfig.BindPort = bindPort
	serfConfig.MemberlistConfig.AdvertiseAddr = ""
	serfConfig.MemberlistConfig.AdvertisePort = 0
	serfConfig.ProtocolVersion = uint8(serf.ProtocolVersionMax)
	serfConfig.CoalescePeriod = 3 * time.Second
	serfConfig.QuiescentPeriod = time.Second
	serfConfig.QueryResponseSizeLimit = 1024
	serfConfig.QuerySizeLimit = 1024
	serfConfig.UserEventSizeLimit = 512
	serfConfig.UserCoalescePeriod = 3 * time.Second
	serfConfig.UserQuiescentPeriod = time.Second
	// TODO: look at reconnect/tombstone settings w more scrutiny
	serfConfig.ReconnectInterval = 0
	serfConfig.ReconnectTimeout = 0
	serfConfig.TombstoneTimeout = 0
	serfConfig.BroadcastTimeout = 0
	// TODO: what are the implications of true here o_O
	serfConfig.EnableNameConflictResolution = true

	// hardcode DefaultWANConfig because cascade is designed to be
	// used as a single global system.
	serfConfig.MemberlistConfig = memberlist.DefaultWANConfig()
	serfConfig.MemberlistConfig.BindAddr = bindIP
	serfConfig.MemberlistConfig.BindPort = bindPort

	agent := Create(serfConfig)
	return agent
}