···11package agent
2233import (
44- "fmt"
54 "net"
65 "os"
76)
···3029 NodeName string
3130 StartJoin []string
3231}
3333-3434-// BindAddrParts returns the parts of the BindAddr that should be
3535-// used to configure Serf.
3636-func (c *Config) AddrParts(address string) (string, int, error) {
3737- checkAddr := address
3838-3939-START:
4040- _, _, err := net.SplitHostPort(checkAddr)
4141- if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
4242- checkAddr = fmt.Sprintf("%s:%d", checkAddr, DefaultSerfPort)
4343- goto START
4444- }
4545- if err != nil {
4646- return "", 0, err
4747- }
4848-4949- // Get the address
5050- addr, err := net.ResolveTCPAddr("tcp", checkAddr)
5151- if err != nil {
5252- return "", 0, err
5353- }
5454-5555- return addr.IP.String(), addr.Port, nil
5656-}
-34
ipaddr/ipaddr.go
···11-package ipaddr
22-33-import (
44- "log"
55- "net"
66- "strconv"
77- "strings"
88-)
99-1010-// resolveHost will take a single host string and convert it to a list of TCPAddrs
1111-// This will process any port in the input as well as looking up the hostname using
1212-// normal DNS resolution.
1313-func ParseIPPort(IPPort string) *net.TCPAddr {
1414- var addr net.TCPAddr
1515- ip, portStr, err := net.SplitHostPort(IPPort)
1616- if err != nil {
1717- if strings.Contains(err.Error(), "missing port in address") {
1818- ip = IPPort
1919- } else {
2020- log.Printf("error splitting ip+port '%s' into IP and port: %s\n", IPPort, err)
2121- return &addr
2222- }
2323- } else {
2424- addr.Port, err = strconv.Atoi(portStr)
2525- if err != nil {
2626- log.Printf("Parsed port '%s' is not an integer: %s\n", portStr, err)
2727- return &addr
2828- }
2929- }
3030-3131- addr.IP = net.ParseIP(ip)
3232-3333- return &addr
3434-}
+5-123
main.go
···2233import (
44 "fmt"
55- "log"
65 "os"
77- "os/signal"
88- "strings"
99- "syscall"
1010- "time"
1161212- "git.j3s.sh/cascade/agent"
1313- "git.j3s.sh/cascade/ipaddr"
77+ "git.j3s.sh/cascade/command/agent"
148 "git.j3s.sh/cascade/list"
159)
1610···2014cascade rtt estimate latency between nodes
2115`
22162323-// gracefulTimeout controls how long we wait before forcefully terminating
2424-// note that this value interacts with serf's LeavePropagateDelay config
2525-const gracefulTimeout = 10 * time.Second
2626-2717// TODO: rename agent to something cooler
2818func main() {
2919 if len(os.Args) == 1 {
···3121 os.Exit(1)
3222 }
33233434- run(os.Args[1], os.Args[2:])
3535-}
3636-3737-func run(command string, args []string) {
3838- switch command {
2424+ command := os.Args[1]
2525+ args := os.Args[2:]
2626+ switch os.Args[1] {
3927 case "agent":
4040- handleAgent()
2828+ agent.Run(args)
4129 case "list", "ls":
4230 list.Run(args)
4331 default:
···4533 os.Exit(1)
4634 }
4735}
4848-4949-func handleAgent() {
5050- config := getAgentConfig()
5151- agent := agent.New(config)
5252- if err := agent.Start(); err != nil {
5353- fmt.Println(err)
5454- os.Exit(1)
5555- }
5656- defer agent.Shutdown()
5757- // join any specified startup nodes
5858- if err := startupJoin(agent); err != nil {
5959- fmt.Println(err)
6060- os.Exit(1)
6161- }
6262- if err := handleSignals(agent); err != nil {
6363- fmt.Println(err)
6464- os.Exit(1)
6565- }
6666-}
6767-6868-// handleSignals blocks until we get an exit-causing signal
6969-func handleSignals(agent *agent.Agent) error {
7070- signalCh := make(chan os.Signal, 4)
7171- signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
7272-7373- // Wait for a signal
7474- var sig os.Signal
7575- select {
7676- case s := <-signalCh:
7777- sig = s
7878- case <-agent.ShutdownCh():
7979- // Agent is already shutdown!
8080- return nil
8181- }
8282- fmt.Fprintf(os.Stderr, "caught signal %s", sig)
8383-8484- // Check if we should do a graceful leave
8585- graceful := false
8686- if sig == os.Interrupt || sig == syscall.SIGTERM {
8787- graceful = true
8888- }
8989-9090- // Bail fast if not doing a graceful leave
9191- if !graceful {
9292- fmt.Fprintf(os.Stderr, "leave cluster with zero grace")
9393- return nil
9494- }
9595-9696- // Attempt a graceful leave
9797- gracefulCh := make(chan struct{})
9898- fmt.Printf("shutting down gracefully")
9999- go func() {
100100- if err := agent.Leave(); err != nil {
101101- fmt.Println("Error: ", err)
102102- return
103103- }
104104- close(gracefulCh)
105105- }()
106106-107107- // Wait for leave or another signal
108108- select {
109109- case <-signalCh:
110110- return fmt.Errorf("idfk")
111111- case <-time.After(gracefulTimeout):
112112- return fmt.Errorf("leave timed out")
113113- case <-gracefulCh:
114114- return nil
115115- }
116116-}
117117-118118-func startupJoin(a *agent.Agent) error {
119119- if len(a.Config.StartJoin) == 0 {
120120- return nil
121121- }
122122-123123- n, err := a.Join(a.Config.StartJoin)
124124- if err != nil {
125125- return err
126126- }
127127- if n > 0 {
128128- log.Printf("issue join request nodes=%d\n", n)
129129- }
130130-131131- return nil
132132-}
133133-134134-func getAgentConfig() *agent.Config {
135135- config := agent.DefaultConfig()
136136- // CASCADE_BIND=192.168.0.15:12345
137137- if os.Getenv("CASCADE_BIND") != "" {
138138- addr := ipaddr.ParseIPPort(os.Getenv("CASCADE_BIND"))
139139- config.BindAddr.IP = addr.IP
140140- if addr.Port != 0 {
141141- config.BindAddr.Port = addr.Port
142142- }
143143- }
144144- // CASCADE_JOIN=127.0.0.1,127.0.0.5
145145- if os.Getenv("CASCADE_JOIN") != "" {
146146- config.StartJoin = strings.Split(os.Getenv("CASCADE_JOIN"), ",")
147147- }
148148- // CASCADE_NAME=nostromo.j3s.sh
149149- if os.Getenv("CASCADE_NAME") != "" {
150150- config.NodeName = os.Getenv("CASCADE_NAME")
151151- }
152152- return config
153153-}