func (t *tenet) OpenService() (TenetService, error) { ds, err := t.Driver.Service() if err != nil { log.Print(err.Error()) // TODO(waigani) this logs are quick hacks. Work out the error paths and log them all at the root. return nil, errors.Trace(err) } cfg := &api.Config{} for k, v := range t.options { cfg.Options = append(cfg.Options, &api.Option{ Name: k, Value: fmt.Sprintf("%v", v), }) } s := &tenetService{ Service: ds, cfg: cfg, editFilename: t.Driver.EditFilename, editIssue: t.Driver.EditIssue, mutex: &sync.Mutex{}, } if err := s.start(); err != nil { log.Println("got err opening service") // TODO(waigani) add retry logic here. 1. Keep retrying until service // is up. 2. Keep retrying until service is connected. log.Printf("err: %#v", errors.ErrorStack(err)) return nil, errors.Trace(err) } log.Print("opened service, no issue") return s, nil }
func OSErrf(format string, a ...interface{}) { format = fmt.Sprintf("error: %s\n", format) errStr := fmt.Sprintf(format, a...) log.Print(errStr) Stderr.Write([]byte(errStr)) Exiter(1) }
// Init the service. func (d *Docker) Service() (Service, error) { log.Print("Docker Service called") // Ensure we have an image. c, err := d.getDockerClient() if err != nil { return nil, errors.Trace(err) } if !docker.HaveImage(c, d.Name) { // TODO(waigani) I think we should ask for user confirmation. fmt.Printf("\nno local image found for %s. Pulling new image from %s", d.Name, d.Registry) if err := d.Pull(false); err != nil { return nil, err } } return docker.NewService(d.Name) }
// returns a chan of tenet reviews and a cancel chan that blocks until the user cancels. func reviewQueue(ctx *cli.Context, mappings <-chan cfgMap, changed *map[string][]int, errc chan error) (<-chan *tenetReview, chan struct{}) { reviews := make(map[string]*tenetReview) reviewChannel := make(chan *tenetReview) cleanupWG := &sync.WaitGroup{} // setup a cancel exit path. cancelc := make(chan os.Signal, 1) signal.Notify(cancelc, os.Interrupt) signal.Notify(cancelc, syscall.SIGTERM) cancelledc := make(chan struct{}) cancelled := func() bool { select { case _, ok := <-cancelledc: if ok { close(cancelc) } return true default: return false } } // Kill all open tenets on cancel. go func() { var i int for { <-cancelc if i > 0 { // on the second exit, just do it. fmt.Print("failed.\nSome docker containers may still be running.") os.Exit(1) } i++ go func() { // TODO(waigani) add progress bar here fmt.Print("\ncleaning up tenets ... ") // Anything waiting on the cancelled chan will now fire. close(cancelledc) // Wait for all tenets to be cleaned up. cleanupWG.Wait() // say bye. fmt.Println("done.") os.Exit(1) }() } }() // TODO(waigani) reenable buffering to: // 1. Allow found tenets to keep running. // 2. Stop building new tenets until there is room in the buffer. // TODO(waigani) make cfg vars // buffLimit := 3 // if ctx.Bool("keep-all") { // buffLimit = 100 // } // buff := util.NewBuffer(buffLimit, cancelledc) go func() { for m := range mappings { // Glob all the files in the associated directories for this config, and assign to each tenet by hash for _, tc := range m.cfg.AllTenets() { if cancelled() { // empty files and dirs to stop feeding tenet reviews in progress. m.files = []string{} m.dirs = []string{} return } // Open the tenet service if we haven't seen this config before. configHash := tc.hash() r, found := reviews[configHash] if !found { // Don't build a new tenet until there is room in the buffer. // Found tenets will keep running until they are not fed files for 5 seconds. // WaitRoom will not block if we get a cancel signal. // buff.WaitRoom() tn, err := newTenet(ctx, tc) if err != nil { errc <- err continue } // Note: service should not be called outside this if block. service, err := tn.OpenService() if err != nil { errc <- err continue } info, err := service.Info() if err != nil { errc <- err continue } r = &tenetReview{ configHash: configHash, filesc: make(chan *api.File), issuesc: make(chan *api.Issue), info: info, issuesWG: &sync.WaitGroup{}, filesTM: &tomb.Tomb{}, } reviews[configHash] = r // Setup the takedown of this review. r.issuesWG.Add(1) cleanupWG.Add(1) // buff.Add(1) go func(r *tenetReview) { // The following fires when: select { // 1. all files have been sent or timed out // 2. the tenet buffer is full case <-r.filesTM.Dying(): // 3. lingo has been stopped case <-cancelledc: } // make room for another tenet to start and ensure // that any configHash's matching this one will have // to start a new tenet instance. delete(reviews, configHash) // buff.Add(-1) // signal to the tenet that no more files are coming. close(r.filesc) // wait for the tenet to signal to us that it's finished it's review. r.issuesWG.Wait() // we can now safely close the backing service. if err := service.Close(); err != nil { log.Println("ERROR closing sevice:", err) } log.Println("cleanup done") cleanupWG.Done() }(r) // Make sure we're ready to handle results before we start // the review. reviewChannel <- r // Start this tenet's review. service.Review(r.filesc, r.issuesc, r.filesTM) } regexPattern, globPattern := fileExtFilterForLang(r.info.Language) for _, d := range m.dirs { files, err := filepath.Glob(path.Join(d, globPattern)) if err != nil { // Non-fatal log.Printf("Error reading files in %s: %v\n", d, err) } l: for i, f := range files { select { case <-cancelledc: log.Println("user cancelled, dropping files.") case <-r.filesTM.Dying(): dropped := len(files) - i log.Print("WARNING a tenet review timed out waiting for files to be sent. %d files dropped", dropped) break l case r.filesc <- &api.File{Name: f}: } } } z: for i, f := range m.files { if matches, err := regexp.MatchString(regexPattern, f); !matches { if err != nil { log.Println("error in regex: ", regexPattern) } continue } fileinfo := &api.File{Name: f} if changed != nil { if diffLines, ok := (*changed)[f]; ok { // This can be false if --diff and fileargs are specified for _, l := range diffLines { fileinfo.Lines = append(fileinfo.Lines, int64(l)) } } } // TODO: Refactor so as not to have copy/pasted code with above dir handler select { case <-cancelledc: log.Println("user cancelled, dropping files.") case <-r.filesTM.Dying(): dropped := len(m.files) - i log.Print("WARNING a tenet review timed out waiting for files to be sent. %d files dropped", dropped) break z case r.filesc <- fileinfo: } } } } for _, r := range reviews { // this says all files have been sent. For this review. r.filesTM.Done() } // wait for all tenets to be cleaned up. cleanupWG.Wait() // Closing this chan will start the wind down to end the lingo // process. close(reviewChannel) }() return reviewChannel, cancelledc }