Ejemplo n.º 1
0
// watchCommand is an helper method to send back the command outputs of
// commands like Halt,Destroy or Up to the callback function passed in the
// request.
func (h *Handlers) watchCommand(r *kite.Request, filePath string, fn commandFunc) (interface{}, error) {
	var params struct {
		Success   dnode.Function
		Failure   dnode.Function
		Output    dnode.Function
		Heartbeat dnode.Function
	}

	if r.Args == nil {
		return nil, errors.New("arguments are not passed")
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, err
	}

	path := filepath.Join(h.opts.Home, "logs", filepath.Base(filePath), strings.ToLower(r.Method)+"-"+r.ID+".log")

	output, err := h.output(path)
	if err != nil {
		return nil, err
	}

	fail := func(err error) error {
		go retry(func() error {
			return params.Failure.Call(err.Error())
		})

		if e := output.Close(); e != nil {
			h.log().Warning("failure closing %q: %s", path, e)
		}

		return err
	}

	if !params.Failure.IsValid() {
		return nil, errors.New("invalid request: missing failure callback")
	}

	if !params.Success.IsValid() {
		return nil, fail(errors.New("invalid request: missing success callback"))
	}

	var verr error
	var fns OutputFuncs

	fns = append(fns, func(line string) {
		fmt.Fprintln(output, line)

		i := strings.Index(strings.ToLower(line), "error:")
		if i == -1 {
			return
		}

		msg := strings.TrimSpace(line[i+len("error:"):])

		if msg != "" {
			msg = unquoter.Replace(msg)
			verr = multierror.Append(verr, errors.New(msg))
		}
	})

	if params.Output.IsValid() {
		h.log().Debug("sending output to %q for %q", r.Username, r.Method)

		fns = append(fns, func(line string) {
			h.log().Debug("%s: %s", r.Method, line)
			params.Output.Call(line)
		})
	}

	w := &vagrantutil.Waiter{
		OutputFunc: fns.Output,
	}

	out, err := fn()
	if err != nil {
		return nil, fail(err)
	}

	go func() {
		h.log().Debug("vagrant: waiting for output from %q...", r.Method)

		defer func() {
			if err := output.Close(); err != nil && !logrotate.IsNop(err) {
				h.log().Warning("failure closing %q: %s", path, err)
			}
		}()

		if params.Heartbeat.IsValid() {
			stop := make(chan struct{})
			defer close(stop)

			go func() {
				t := time.NewTicker(5 * time.Second)
				defer t.Stop()

				for {
					select {
					case <-stop:
						h.log().Debug("stopping heartbeat for %q", filePath)
						return
					case <-t.C:
						h.log().Debug("sending heartbeat for %q", filePath)

						if err := params.Heartbeat.Call(); err != nil {
							h.log().Debug("heartbeat failure for %q: %s", filePath, err)
						}
					}
				}
			}()
		}

		err := w.Wait(out, nil)

		if err != nil {
			verr = multierror.Append(verr, err)

			h.log().Error("Klient %q error for %q: %s", r.Method, filePath, verr)
			fail(verr)
			return
		}

		h.log().Info("Klient %q success for %q", r.Method, filePath)

		retry(func() error {
			return params.Success.Call()
		})
	}()

	return true, nil
}
Ejemplo n.º 2
0
// process is a worker goroutine that serializes access
// to up.rotate; the MetaStore used in default implementation
// is not goroutine safe - trying to upload the same file
// in two concurrent goroutines could corrupt BoltDB database.
//
// TODO(rjeczalik): To increase throughput process could spawn
// multiple goroutines ensuring there's at most one concurrent
// upload per unique key.
func (up *Uploader) process() {
	watched := make(map[string]struct{})
	prefix := up.cfg.Kite.Config.Id

	for {
		select {
		case <-up.close:
			return

		case req := <-up.req:
			// Upload file at the given interval, if the file
			// is requested to be watched.
			if req.Interval > 0 && req.File != "" {
				if req.Interval < 15*time.Minute {
					req.Interval = 15 * time.Minute
				}

				if _, ok := watched[req.File]; !ok {
					go func(file string) {
						t := time.NewTicker(req.Interval)
						defer t.Stop()

						for {
							select {
							case <-up.close:
								return
							case <-t.C:
								up.req <- &request{
									UploadRequest: &UploadRequest{
										File: file,
									},
								}
							}
						}
					}(req.File)
				}
			}

			var r response

			if req.File != "" {
				r.url, r.err = up.rotate.UploadFile(prefix, req.File)
			} else {
				r.url, r.err = up.rotate.Upload(path.Clean(prefix+"/"+req.Key), bytes.NewReader(req.Content))
			}

			if req.respC != nil {
				select {
				case req.respC <- &r:
				default:
				}
			}

			switch {
			case r.err == nil:
				up.log().Debug("%s: uploaded successfully", req.File)
			case logrotate.IsNop(r.err):
				up.log().Debug("%s: nothing to upload", req.File)
			case os.IsNotExist(r.err):
				up.log().Debug("%s: file does not exist", req.File)
			default:
				up.log().Debug("%s: failed to upload: %s", req.File, r.err)
			}
		}
	}
}