Пример #1
0
// ConvergedTimer adds a timeout to a select call and blocks until then
// TODO: this means we could eventually have per resource converged timeouts
func (obj *converger) ConvergedTimer(uuid ConvergerUUID) <-chan time.Time {
	// be clever: if i'm already converged, this timeout should block which
	// avoids unnecessary new signals being sent! this avoids fast loops if
	// we have a low timeout, or in particular a timeout == 0
	if uuid.IsConverged() {
		// blocks the case statement in select forever!
		return util.TimeAfterOrBlock(-1)
	}
	return util.TimeAfterOrBlock(obj.timeout)
}
Пример #2
0
// passwordCallback is a function which returns the appropriate type of callback.
func (obj *Remotes) passwordCallback(user, host string) func() (string, error) {
	timeout := nonInteractivePasswordTimeout // default
	if obj.interactive {                     // return after a timeout if not interactive
		timeout = -1 // unlimited when we asked for interactive mode!
	}
	cb := func() (string, error) {
		passchan := make(chan string)
		failchan := make(chan error)

		go func() {
			log.Printf("Remote: Prompting for %s@%s password...", user, host)
			fmt.Printf("Password: "******"", e
		case <-util.TimeAfterOrBlock(timeout):
			return "", fmt.Errorf("Interactive timeout reached!")
		}
	}
	return cb
}
Пример #3
0
// InstallPackages installs a list of packages by packageID.
func (bus *Conn) InstallPackages(packageIDs []string, transactionFlags uint64) error {

	ch := make(chan *dbus.Signal, PkBufferSize)   // we need to buffer :(
	interfacePath, err := bus.CreateTransaction() // emits Destroy on close
	if err != nil {
		return err
	}

	var signals = []string{"Package", "ErrorCode", "Finished", "Destroy"} // "ItemProgress", "Status" ?
	bus.matchSignal(ch, interfacePath, PkIfaceTransaction, signals)

	obj := bus.GetBus().Object(PkIface, interfacePath) // pass in found transaction path
	call := obj.Call(FmtTransactionMethod("InstallPackages"), 0, transactionFlags, packageIDs)
	if call.Err != nil {
		return call.Err
	}
	timeout := -1 // disabled initially
	finished := false
loop:
	for {
		select {
		case signal := <-ch:
			if signal.Path != interfacePath {
				log.Printf("PackageKit: Woops: Signal.Path: %+v", signal.Path)
				continue loop
			}

			if signal.Name == FmtTransactionMethod("ErrorCode") {
				return fmt.Errorf("PackageKit: Error: %v", signal.Body)
			} else if signal.Name == FmtTransactionMethod("Package") {
				// a package was installed...
				// only start the timer once we're here...
				timeout = PkSignalPackageTimeout
			} else if signal.Name == FmtTransactionMethod("Finished") {
				finished = true
				timeout = PkSignalDestroyTimeout // wait a bit
			} else if signal.Name == FmtTransactionMethod("Destroy") {
				return nil // success
			} else {
				return fmt.Errorf("PackageKit: Error: %v", signal.Body)
			}
		case <-util.TimeAfterOrBlock(timeout):
			if finished {
				log.Println("PackageKit: Timeout: InstallPackages: Waiting for 'Destroy'")
				return nil // got tired of waiting for Destroy
			}
			return fmt.Errorf("PackageKit: Timeout: InstallPackages: %v", strings.Join(packageIDs, ", "))
		}
	}
}
Пример #4
0
// CheckApply checks the resource state and applies the resource if the bool
// input is true. It returns error info and if the state check passed or not.
// TODO: expand the IfCmd to be a list of commands
func (obj *ExecRes) CheckApply(apply bool) (checkok bool, err error) {
	log.Printf("%v[%v]: CheckApply(%t)", obj.Kind(), obj.GetName(), apply)

	// if there is a watch command, but no if command, run based on state
	if obj.WatchCmd != "" && obj.IfCmd == "" {
		if obj.isStateOK {
			return true, nil
		}

		// if there is no watcher, but there is an onlyif check, run it to see
	} else if obj.IfCmd != "" { // && obj.WatchCmd == ""
		// there is a watcher, but there is also an if command
		//} else if obj.IfCmd != "" && obj.WatchCmd != "" {

		if obj.PollInt > 0 { // && obj.WatchCmd == ""
			// XXX have the Watch() command output onlyif poll events...
			// XXX we can optimize by saving those results for returning here
			// return XXX
		}

		var cmdName string
		var cmdArgs []string
		if obj.IfShell == "" {
			// call without a shell
			// FIXME: are there still whitespace splitting issues?
			split := strings.Fields(obj.IfCmd)
			cmdName = split[0]
			//d, _ := os.Getwd() // TODO: how does this ever error ?
			//cmdName = path.Join(d, cmdName)
			cmdArgs = split[1:]
		} else {
			cmdName = obj.IfShell // usually bash, or sh
			cmdArgs = []string{"-c", obj.IfCmd}
		}
		err = exec.Command(cmdName, cmdArgs...).Run()
		if err != nil {
			// TODO: check exit value
			return true, nil // don't run
		}

		// if there is no watcher and no onlyif check, assume we should run
	} else { // if obj.WatchCmd == "" && obj.IfCmd == "" {
		// just run if state is dirty
		if obj.isStateOK {
			return true, nil
		}
	}

	// state is not okay, no work done, exit, but without error
	if !apply {
		return false, nil
	}

	// apply portion
	log.Printf("%v[%v]: Apply", obj.Kind(), obj.GetName())
	var cmdName string
	var cmdArgs []string
	if obj.Shell == "" {
		// call without a shell
		// FIXME: are there still whitespace splitting issues?
		// TODO: we could make the split character user selectable...!
		split := strings.Fields(obj.Cmd)
		cmdName = split[0]
		//d, _ := os.Getwd() // TODO: how does this ever error ?
		//cmdName = path.Join(d, cmdName)
		cmdArgs = split[1:]
	} else {
		cmdName = obj.Shell // usually bash, or sh
		cmdArgs = []string{"-c", obj.Cmd}
	}
	cmd := exec.Command(cmdName, cmdArgs...)
	//cmd.Dir = "" // look for program in pwd ?
	var out bytes.Buffer
	cmd.Stdout = &out

	if err = cmd.Start(); err != nil {
		log.Printf("%v[%v]: Error starting Cmd: %v", obj.Kind(), obj.GetName(), err)
		return false, err
	}

	timeout := obj.Timeout
	if timeout == 0 { // zero timeout means no timer, so disable it
		timeout = -1
	}
	done := make(chan error)
	go func() { done <- cmd.Wait() }()

	select {
	case err = <-done:
		if err != nil {
			log.Printf("%v[%v]: Error waiting for Cmd: %v", obj.Kind(), obj.GetName(), err)
			return false, err
		}

	case <-util.TimeAfterOrBlock(timeout):
		log.Printf("%v[%v]: Timeout waiting for Cmd", obj.Kind(), obj.GetName())
		//cmd.Process.Kill() // TODO: is this necessary?
		return false, errors.New("Timeout waiting for Cmd!")
	}

	// TODO: if we printed the stdout while the command is running, this
	// would be nice, but it would require terminal log output that doesn't
	// interleave all the parallel parts which would mix it all up...
	if s := out.String(); s == "" {
		log.Printf("Exec[%v]: Command output is empty!", obj.Name)
	} else {
		log.Printf("Exec[%v]: Command output is:", obj.Name)
		log.Printf(out.String())
	}
	// XXX: return based on exit value!!

	// the state tracking is for exec resources that can't "detect" their
	// state, and assume it's invalid when the Watch() function triggers.
	// if we apply state successfully, we should reset it here so that we
	// know that we have applied since the state was set not ok by event!
	obj.isStateOK = true // reset
	return false, nil    // success
}