Example #1
0
// Init runs some startup code for this resource.
func (obj *PkgRes) Init() error {
	obj.BaseRes.kind = "Pkg"
	if err := obj.BaseRes.Init(); err != nil { // call base init, b/c we're overriding
		return err
	}

	bus := packagekit.NewBus()
	if bus == nil {
		return fmt.Errorf("Can't connect to PackageKit bus.")
	}
	defer bus.Close()

	result, err := obj.pkgMappingHelper(bus)
	if err != nil {
		return errwrap.Wrapf(err, "The pkgMappingHelper failed")
	}

	data, ok := result[obj.Name] // lookup single package (init does just one)
	// package doesn't exist, this is an error!
	if !ok || !data.Found {
		return fmt.Errorf("Can't find package named '%s'.", obj.Name)
	}

	packageIDs := []string{data.PackageID} // just one for now
	filesMap, err := bus.GetFilesByPackageID(packageIDs)
	if err != nil {
		return errwrap.Wrapf(err, "Can't run GetFilesByPackageID")
	}
	if files, ok := filesMap[data.PackageID]; ok {
		obj.fileList = util.DirifyFileList(files, false)
	}
	return nil
}
Example #2
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.
func (obj *PkgRes) CheckApply(apply bool) (checkOK bool, err error) {
	log.Printf("%s: Check", obj.fmtNames(obj.getNames()))

	bus := packagekit.NewBus()
	if bus == nil {
		return false, fmt.Errorf("Can't connect to PackageKit bus.")
	}
	defer bus.Close()

	result, err := obj.pkgMappingHelper(bus)
	if err != nil {
		return false, errwrap.Wrapf(err, "The pkgMappingHelper failed")
	}

	packageMap := obj.groupMappingHelper() // map[string]string
	packageList := []string{obj.Name}
	packageList = append(packageList, util.StrMapKeys(packageMap)...)
	//stateList := []string{obj.State}
	//stateList = append(stateList, util.StrMapValues(packageMap)...)

	// TODO: at the moment, all the states are the same, but
	// eventually we might be able to drop this constraint!
	states, err := packagekit.FilterState(result, packageList, obj.State)
	if err != nil {
		return false, errwrap.Wrapf(err, "The FilterState method failed")
	}
	data, _ := result[obj.Name] // if above didn't error, we won't either!
	validState := util.BoolMapTrue(util.BoolMapValues(states))

	// obj.State == "installed" || "uninstalled" || "newest" || "4.2-1.fc23"
	switch obj.State {
	case "installed":
		fallthrough
	case "uninstalled":
		fallthrough
	case "newest":
		if validState {
			return true, nil // state is correct, exit!
		}
	default: // version string
		if obj.State == data.Version && data.Version != "" {
			return true, nil
		}
	}

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

	// apply portion
	log.Printf("%s: Apply", obj.fmtNames(obj.getNames()))
	readyPackages, err := packagekit.FilterPackageState(result, packageList, obj.State)
	if err != nil {
		return false, err // fail
	}
	// these are the packages that actually need their states applied!
	applyPackages := util.StrFilterElementsInList(readyPackages, packageList)
	packageIDs, _ := packagekit.FilterPackageIDs(result, applyPackages) // would be same err as above

	var transactionFlags uint64 // initializes at the "zero" value of 0
	if !obj.AllowUntrusted {    // allow
		transactionFlags += packagekit.PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED
	}
	// apply correct state!
	log.Printf("%s: Set: %v...", obj.fmtNames(util.StrListIntersection(applyPackages, obj.getNames())), obj.State)
	switch obj.State {
	case "uninstalled": // run remove
		// NOTE: packageID is different than when installed, because now
		// it has the "installed" flag added to the data portion if it!!
		err = bus.RemovePackages(packageIDs, transactionFlags)

	case "newest": // TODO: isn't this the same operation as install, below?
		err = bus.UpdatePackages(packageIDs, transactionFlags)

	case "installed":
		fallthrough // same method as for "set specific version", below
	default: // version string
		err = bus.InstallPackages(packageIDs, transactionFlags)
	}
	if err != nil {
		return false, err // fail
	}
	log.Printf("%s: Set: %v success!", obj.fmtNames(util.StrListIntersection(applyPackages, obj.getNames())), obj.State)
	return false, nil // success
}
Example #3
0
// Watch is the primary listener for this resource and it outputs events.
// It uses the PackageKit UpdatesChanged signal to watch for changes.
// TODO: https://github.com/hughsie/PackageKit/issues/109
// TODO: https://github.com/hughsie/PackageKit/issues/110
func (obj *PkgRes) Watch(processChan chan event.Event) error {
	if obj.IsWatching() {
		return nil
	}
	obj.SetWatching(true)
	defer obj.SetWatching(false)
	cuid := obj.converger.Register()
	defer cuid.Unregister()

	var startup bool
	Startup := func(block bool) <-chan time.Time {
		if block {
			return nil // blocks forever
			//return make(chan time.Time) // blocks forever
		}
		return time.After(time.Duration(500) * time.Millisecond) // 1/2 the resolution of converged timeout
	}

	bus := packagekit.NewBus()
	if bus == nil {
		return fmt.Errorf("Can't connect to PackageKit bus.")
	}
	defer bus.Close()

	ch, err := bus.WatchChanges()
	if err != nil {
		return errwrap.Wrapf(err, "Error adding signal match")
	}

	var send = false // send event?
	var exit = false

	for {
		if obj.debug {
			log.Printf("%s: Watching...", obj.fmtNames(obj.getNames()))
		}

		obj.SetState(ResStateWatching) // reset
		select {
		case event := <-ch:
			cuid.SetConverged(false)

			// FIXME: ask packagekit for info on what packages changed
			if obj.debug {
				log.Printf("%s: Event: %v", obj.fmtNames(obj.getNames()), event.Name)
			}

			// since the chan is buffered, remove any supplemental
			// events since they would just be duplicates anyways!
			for len(ch) > 0 { // we can detect pending count here!
				<-ch // discard
			}

			send = true
			obj.StateOK(false) // dirty

		case event := <-obj.Events():
			cuid.SetConverged(false)
			if exit, send = obj.ReadEvent(&event); exit {
				return nil // exit
			}
			//obj.StateOK(false) // these events don't invalidate state

		case <-cuid.ConvergedTimer():
			cuid.SetConverged(true) // converged!
			continue

		case <-Startup(startup):
			cuid.SetConverged(false)
			send = true
			obj.StateOK(false) // dirty
		}

		// do all our event sending all together to avoid duplicate msgs
		if send {
			startup = true // startup finished
			send = false
			if exit, err := obj.DoSend(processChan, ""); exit || err != nil {
				return err // we exit or bubble up a NACK...
			}
		}
	}
}