// 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 }
// 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 }
// 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... } } } }