func runBootstrap(args *docopt.Args) error { log.SetFlags(log.Lmicroseconds) logf := textLogger if args.Bool["--json"] { logf = jsonLogger } var cfg bootstrap.Config manifestFile := args.String["<manifest>"] if manifestFile == "" { manifestFile = "/etc/flynn/bootstrap-manifest.json" } var steps []string if s := args.String["--steps"]; s != "" { steps = strings.Split(s, ",") } var err error manifest, err = readBootstrapManifest(manifestFile) if err != nil { return fmt.Errorf("Error reading manifest: %s", err) } if n := args.String["--min-hosts"]; n != "" { if cfg.MinHosts, err = strconv.Atoi(n); err != nil || cfg.MinHosts < 1 { return fmt.Errorf("invalid --min-hosts value") } } cfg.Timeout, err = strconv.Atoi(args.String["--timeout"]) if err != nil { return fmt.Errorf("invalid --timeout value") } if ipList := args.String["--peer-ips"]; ipList != "" { cfg.IPs = strings.Split(ipList, ",") if cfg.MinHosts == 0 { cfg.MinHosts = len(cfg.IPs) } } if cfg.MinHosts == 0 { cfg.MinHosts = 1 } cfg.Singleton = cfg.MinHosts == 1 if s := os.Getenv("SINGLETON"); s != "" { cfg.Singleton = s == "true" } ch := make(chan *bootstrap.StepInfo) done := make(chan struct{}) var last error go func() { for si := range ch { logf(si) last = si.Err } close(done) }() cfg.ClusterURL = args.String["--discovery"] if bf := args.String["--from-backup"]; bf != "" { err = runBootstrapBackup(manifest, bf, ch, cfg) } else { err = bootstrap.Run(manifest, ch, cfg, steps) } <-done if err != nil && last != nil && err.Error() == last.Error() { return ErrAlreadyLogged{err} } return err }