func (a *Agent) Start() { go func() { state.WaitGroup().Wait() if a.cmd != nil && a.cmd.Process != nil { a.cmd.Process.Signal(syscall.SIGTERM) a.cmd = nil } state.WaitGroup().Done() }() a.Lock() defer a.Unlock() if a.cmd == nil { a.start() } }
func (link *Link) listen(l *label, address string) net.Listener { sock, err := net.Listen("unix", address) if err != nil { log.Fatalf("listen: %s", err) } go func() { state.WaitGroup().Wait() defer state.WaitGroup().Done() sock.Close() }() if err = os.Chmod(address, 0777); err != nil { log.Fatalf("chmod: %s", address) } return sock }
func (a *Agent) start() { if a.cmd != nil { log.Fatal("Cannot call start(): program already running!") } if a.killcount > 0 { return } cmd := exec.Command(a.args[0], a.args[1:]...) stdout, err := cmd.StdoutPipe() if err != nil { log.Fatal("Unable to open pipe to stdout: %v", err) } stderr, err := cmd.StderrPipe() if err != nil { log.Fatal("Unable to open pipe to stderr: %v", err) } if state.Local() { cmd.Dir = a.dir } go copyLines(a.Name, stdout) go copyLines(a.Name, stderr) log.Printf("Starting %v", a) err = cmd.Start() if err != nil { log.Fatalf("Could't spawn %v: %s", a, err) } go func() { err := cmd.Wait() a.Lock() defer a.Unlock() // Not the active command if a.cmd != cmd { return } var d string if err != nil { d = fmt.Sprintf("Command exited unexpectedly: %s (%s)", cmd, err) } else { d = fmt.Sprintf("Command exited unexpectedly (but cleanly!): %s", cmd) } state.RecordDisqualifier(d) state.WaitGroup().Exit() }() a.cmd = cmd }
func (h *Harness) boot() bool { rng := state.NewRand("boot") bootQuery := h.generateInitialQuery() i := rng.Intn(len(h.agents)) node := h.agents[i] for !h.issueQuery(0, node, bootQuery) { time.Sleep(100 * time.Millisecond) select { case <-state.WaitGroup().Quit: return false default: } } return true }
func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, ` Usage: %s [options] [...args] Octopus will run your sqlcluster and issue queries to it. It simulates a lossy network, and to make things even more fun, will turn loose the following chaotic monkeys: NET MONKEYS - latency: adjusts base latency between nodes - jitter: adjusts latency variance between nodes - lagsplit: spikes latency along a network partition - link: destroys individual links between nodes - netsplit: destroys links along a network partition NODE MONKEYS - freeze: stops nodes (by sending SIGTSTP) - murder: ruthlessly murders nodes (by sending SIGTERM) Any positional arguments given to Octopus will be passed through to SQLCluster. This should be mostly useful for passing a -v flag, like so: ./octopus -- -v OPTIONS: `, os.Args[0]) flag.PrintDefaults() } state.AddFlags() flag.Parse() state.AfterParse() // Handle SIGINT sigchan := make(chan os.Signal) signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) d := director.NewDirector() if state.Dryrun() { d.Dryrun() return } // Create the working directory if err := os.MkdirAll(state.Root(), os.ModeDir|0755); err != nil { log.Fatal(err) } d.Start() go func() { select { case <-sigchan: log.Println("Terminating due to signal") case <-state.WaitGroup().Quit: // Someone else requested an exit case <-time.After(state.Duration()): // Time's up! log.Printf("The allotted %s have elapsed. Exiting!", state.Duration()) } state.WaitGroup().Exit() }() h := harness.New(d.Agents()) h.Start() d.StartMonkeys() <-state.WaitGroup().Quit if state.Write() != "" { results := state.JSONResults() if err := ioutil.WriteFile(state.Write(), results, 0755); err != nil { log.Fatalf("Could not write resuts: %s", err) } } else { results := state.PrettyPrintResults() log.Println(results) } state.WaitGroup().Exit() }
func (h *Harness) losef(msg string, v ...interface{}) { disqualifier := fmt.Sprintf(msg, v...) state.RecordDisqualifier(disqualifier) state.WaitGroup().Exit() }