go.dedis.ch/onet/v3@v3.2.11-0.20210930124529-e36530bca7ef/simul/platform/localhost.go (about) 1 package platform 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "runtime" 8 "strconv" 9 "sync" 10 11 "strings" 12 13 "time" 14 15 "go.dedis.ch/onet/v3" 16 "go.dedis.ch/onet/v3/app" 17 "go.dedis.ch/onet/v3/log" 18 "go.dedis.ch/onet/v3/simul/monitor" 19 "golang.org/x/xerrors" 20 ) 21 22 // Localhost is responsible for launching the app with the specified number of nodes 23 // directly on your machine, for local testing. 24 25 // Localhost is the platform for launching thee apps locally 26 type Localhost struct { 27 // Need mutex because build.go has a global variable that 28 // is used for multiple experiments 29 sync.Mutex 30 31 // Address of the logger (can be local or not) 32 logger string 33 34 // The simulation to run 35 Simulation string 36 37 // Where is the Localhost package located 38 localDir string 39 // Where to build the executables + 40 // where to read the config file 41 // it will be assembled like LocalDir/RunDir 42 runDir string 43 44 // Debug level 1 - 5 45 debug int 46 47 // The number of servers 48 servers int 49 // All addresses - we use 'localhost1'..'localhostn' to 50 // identify the different cothorities, but when opening the 51 // ports they will be converted to normal 'localhost' 52 addresses []string 53 54 // Whether we started a simulation 55 running bool 56 // WaitGroup for running processes 57 wgRun sync.WaitGroup 58 59 // errors go here: 60 errChan chan error 61 62 // Listening monitor port 63 monitorPort int 64 65 // Suite used for the simulation 66 Suite string 67 68 // SimulationConfig holds all things necessary for the run 69 sc *onet.SimulationConfig 70 71 // PreScript is run before the simulation is started 72 PreScript string 73 74 // RunWait for long simulations 75 RunWait string 76 } 77 78 // Configure various internal variables 79 func (d *Localhost) Configure(pc *Config) { 80 d.Lock() 81 defer d.Unlock() 82 pwd, _ := os.Getwd() 83 d.runDir = pwd + "/build" 84 os.RemoveAll(d.runDir) 85 log.ErrFatal(os.Mkdir(d.runDir, 0770)) 86 d.Suite = pc.Suite 87 d.localDir = pwd 88 d.debug = pc.Debug 89 d.running = false 90 d.monitorPort = pc.MonitorPort 91 if d.Simulation == "" { 92 log.Fatal("No simulation defined in simulation") 93 } 94 log.Lvl3(fmt.Sprintf("Localhost dirs: RunDir %s", d.runDir)) 95 log.Lvl3("Localhost configured ...") 96 } 97 98 // Build does nothing, as we're using our own binary, no need to build 99 func (d *Localhost) Build(build string, arg ...string) error { 100 return nil 101 } 102 103 // Cleanup kills all running cothority-binaryes 104 func (d *Localhost) Cleanup() error { 105 log.Lvl1("Nothing to clean up") 106 return nil 107 } 108 109 // Deploy copies all files to the run-directory 110 func (d *Localhost) Deploy(rc *RunConfig) error { 111 d.Lock() 112 defer d.Unlock() 113 if runtime.GOOS == "darwin" { 114 files, err := exec.Command("ulimit", "-n").Output() 115 if err != nil { 116 return xerrors.Errorf("ulimit: %v", err) 117 } 118 filesNbr, err := strconv.Atoi(strings.TrimSpace(string(files))) 119 if err != nil { 120 return xerrors.Errorf("atoi: %v", err) 121 } 122 hosts, _ := strconv.Atoi(rc.Get("hosts")) 123 if filesNbr < hosts*2 { 124 maxfiles := 10000 + hosts*2 125 return xerrors.Errorf("Maximum open files is too small. Please run the following command:\n"+ 126 "sudo sysctl -w kern.maxfiles=%d\n"+ 127 "sudo sysctl -w kern.maxfilesperproc=%d\n"+ 128 "ulimit -n %d\n"+ 129 "sudo sysctl -w kern.ipc.somaxconn=2048\n", 130 maxfiles, maxfiles, maxfiles) 131 } 132 } 133 134 // Check for PreScript and copy it to the deploy-dir 135 d.PreScript = rc.Get("PreScript") 136 if d.PreScript != "" { 137 _, err := os.Stat(d.PreScript) 138 if !os.IsNotExist(err) { 139 if err := app.Copy(d.runDir, d.PreScript); err != nil { 140 return xerrors.Errorf("copying: %v", err) 141 } 142 } 143 } 144 145 d.servers, _ = strconv.Atoi(rc.Get("servers")) 146 log.Lvl2("Localhost: Deploying and writing config-files for", d.servers, "servers") 147 sim, err := onet.NewSimulation(d.Simulation, string(rc.Toml())) 148 if err != nil { 149 return xerrors.Errorf("simulation error: %v", err) 150 } 151 d.addresses = make([]string, d.servers) 152 for i := range d.addresses { 153 d.addresses[i] = "127.0.0." + strconv.Itoa(i+1) 154 } 155 d.sc, err = sim.Setup(d.runDir, d.addresses) 156 if err != nil { 157 return xerrors.Errorf("simulation setup: %v", err) 158 } 159 d.sc.Config = string(rc.Toml()) 160 if err := d.sc.Save(d.runDir); err != nil { 161 return xerrors.Errorf("saving folder: %v", err) 162 } 163 log.Lvl2("Localhost: Done deploying") 164 d.wgRun.Add(d.servers) 165 // add one to the channel length to indicate it's done 166 d.errChan = make(chan error, d.servers+1) 167 return nil 168 } 169 170 // Start will execute one cothority-binary for each server 171 // configured 172 func (d *Localhost) Start(args ...string) error { 173 d.Lock() 174 defer d.Unlock() 175 if err := os.Chdir(d.runDir); err != nil { 176 return err 177 } 178 log.Lvl4("Localhost: chdir into", d.runDir) 179 ex := d.runDir + "/" + d.Simulation 180 d.running = true 181 log.Lvl1("Starting", d.servers, "applications of", ex) 182 time.Sleep(100 * time.Millisecond) 183 184 // If PreScript is defined, run the appropriate script _before_ the simulation. 185 if d.PreScript != "" { 186 out, err := exec.Command("sh", "-c", "./"+d.PreScript+" localhost").CombinedOutput() 187 outStr := strings.TrimRight(string(out), "\n") 188 if err != nil { 189 return xerrors.Errorf("error deploying PreScript: " + err.Error() + " " + outStr) 190 } 191 log.Lvl1(outStr) 192 } 193 194 err := monitor.ConnectSink("localhost:" + strconv.Itoa(d.monitorPort)) 195 if err != nil { 196 return xerrors.Errorf("monitor: %v", err) 197 } 198 199 for index := 0; index < d.servers; index++ { 200 log.Lvl3("Starting", index) 201 host := "127.0.0." + strconv.Itoa(index+1) 202 go func(i int, h string) { 203 log.Lvl3("Localhost: will start host", i, h) 204 err := Simulate(d.Suite, host, d.Simulation, "") 205 if err != nil { 206 log.Error("Error running localhost", h, ":", err) 207 d.errChan <- err 208 } 209 d.wgRun.Done() 210 log.Lvl3("host (index", i, ")", h, "done") 211 }(index, host) 212 } 213 return nil 214 } 215 216 // Wait for all processes to finish 217 func (d *Localhost) Wait() error { 218 d.Lock() 219 defer d.Unlock() 220 log.Lvl3("Waiting for processes to finish") 221 222 wait, err := time.ParseDuration(d.RunWait) 223 if err != nil || wait == 0 { 224 wait = 600 * time.Second 225 err = nil 226 } 227 228 go func() { 229 d.wgRun.Wait() 230 log.Lvl3("WaitGroup is 0") 231 // write to error channel when done: 232 d.errChan <- nil 233 }() 234 235 // if one of the hosts fails, stop waiting and return the error: 236 select { 237 case e := <-d.errChan: 238 log.Lvl3("Finished waiting for hosts:", e) 239 if e != nil { 240 if err := d.Cleanup(); err != nil { 241 log.Errorf("Couldn't cleanup running instances: %+v", err) 242 } 243 err = xerrors.Errorf("localhost error: %v", err) 244 } 245 case <-time.After(wait): 246 log.Lvl1("Quitting after waiting", wait) 247 } 248 249 errCleanup := os.Chdir(d.localDir) 250 if errCleanup != nil { 251 log.Error("Fail to restore the cwd: " + errCleanup.Error()) 252 } 253 254 monitor.EndAndCleanup() 255 log.Lvl2("Processes finished") 256 return err 257 }