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