Add initial plumbing for autogroups

This adds some of the API changes and improvements to the pkg resource
so that it can make use of this feature.
This commit is contained in:
James Shubin
2016-03-14 00:57:01 -04:00
parent 50c458b6cc
commit 05b4066ba6
10 changed files with 340 additions and 49 deletions

158
pkg.go
View File

@@ -63,10 +63,18 @@ func (obj *PkgRes) Init() {
}
defer bus.Close()
data, err := obj.PkgMappingHelper(bus)
result, err := obj.pkgMappingHelper(bus)
if err != nil {
// FIXME: return error?
log.Fatalf("The PkgMappingHelper failed with: %v.", err)
log.Fatalf("The pkgMappingHelper failed with: %v.", err)
return
}
data, ok := result[obj.Name] // lookup single package (init does just one)
// package doesn't exist, this is an error!
if !ok || !data.Found {
// FIXME: return error?
log.Fatalf("Can't find package named '%s'.", obj.Name)
return
}
@@ -82,11 +90,6 @@ func (obj *PkgRes) Init() {
}
}
// XXX: run this when resource exits
func (obj *PkgRes) Close() {
//obj.bus.Close()
}
func (obj *PkgRes) Validate() bool {
if obj.State == "" {
@@ -123,7 +126,7 @@ func (obj *PkgRes) Watch() {
for {
if DEBUG {
log.Printf("Pkg[%v]: Watching...", obj.GetName())
log.Printf("%v: Watching...", obj.fmtNames(obj.getNames()))
}
obj.SetState(resStateWatching) // reset
@@ -131,7 +134,7 @@ func (obj *PkgRes) Watch() {
case event := <-ch:
// FIXME: ask packagekit for info on what packages changed
if DEBUG {
log.Printf("Pkg[%v]: Event: %v", obj.GetName(), event.Name)
log.Printf("%v: Event: %v", obj.fmtNames(obj.getNames()), event.Name)
}
// since the chan is buffered, remove any supplemental
@@ -170,13 +173,48 @@ func (obj *PkgRes) Watch() {
}
}
func (obj *PkgRes) PkgMappingHelper(bus *Conn) (*PkPackageIDActionData, error) {
var packageMap = map[string]string{
obj.Name: obj.State, // key is pkg name, value is pkg state
// get list of names when grouped or not
func (obj *PkgRes) getNames() []string {
if g := obj.GetGroup(); len(g) > 0 { // grouped elements
names := []string{obj.GetName()}
for _, x := range g {
pkg, ok := x.(*PkgRes) // convert from Res
if ok {
names = append(names, pkg.Name)
}
}
return names
}
var filter uint64 // initializes at the "zero" value of 0
filter += PK_FILTER_ENUM_ARCH // always search in our arch (optional!)
return []string{obj.GetName()}
}
// pretty print for header values
func (obj *PkgRes) fmtNames(names []string) string {
if len(obj.GetGroup()) > 0 { // grouped elements
return fmt.Sprintf("%v[autogroup:(%v)]", obj.Kind(), strings.Join(names, ","))
}
return fmt.Sprintf("%v[%v]", obj.Kind(), obj.GetName())
}
func (obj *PkgRes) groupMappingHelper() map[string]string {
var result = make(map[string]string)
if g := obj.GetGroup(); len(g) > 0 { // add any grouped elements
for _, x := range g {
pkg, ok := x.(*PkgRes) // convert from Res
if !ok {
log.Fatalf("Grouped member %v is not a %v", x, obj.Kind())
}
result[pkg.Name] = pkg.State
}
}
return result
}
func (obj *PkgRes) pkgMappingHelper(bus *Conn) (map[string]*PkPackageIDActionData, error) {
packageMap := obj.groupMappingHelper() // get the grouped values
packageMap[obj.Name] = obj.State // key is pkg name, value is pkg state
var filter uint64 // initializes at the "zero" value of 0
filter += PK_FILTER_ENUM_ARCH // always search in our arch (optional!)
// we're requesting latest version, or to narrow down install choices!
if obj.State == "newest" || obj.State == "installed" {
// if we add this, we'll still see older packages if installed
@@ -194,21 +232,14 @@ func (obj *PkgRes) PkgMappingHelper(bus *Conn) (*PkPackageIDActionData, error) {
if e != nil {
return nil, fmt.Errorf("Can't run PackagesToPackageIDs: %v", e)
}
data, ok := result[obj.Name] // lookup single package
// package doesn't exist, this is an error!
if !ok || !data.Found {
return nil, fmt.Errorf("Can't find package named '%s'.", obj.Name)
}
return data, nil
return result, nil
}
func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
log.Printf("%v[%v]: CheckApply(%t)", obj.Kind(), obj.GetName(), apply)
log.Printf("%v: CheckApply(%t)", obj.fmtNames(obj.getNames()), apply)
if obj.State == "" { // TODO: Validate() should replace this check!
log.Fatalf("%v[%v]: Package state is undefined!", obj.Kind(), obj.GetName())
log.Fatalf("%v: Package state is undefined!", obj.fmtNames(obj.getNames()))
}
if obj.isStateOK { // cache the state
@@ -221,24 +252,35 @@ func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
}
defer bus.Close()
data, err := obj.PkgMappingHelper(bus)
result, err := obj.pkgMappingHelper(bus)
if err != nil {
return false, fmt.Errorf("The PkgMappingHelper failed with: %v.", err)
return false, fmt.Errorf("The pkgMappingHelper failed with: %v.", err)
}
packageMap := obj.groupMappingHelper() // map[string]string
packageList := []string{obj.Name}
packageList = append(packageList, StrMapKeys(packageMap)...)
//stateList := []string{obj.State}
//stateList = append(stateList, StrMapValues(packageMap)...)
// TODO: at the moment, all the states are the same, but
// eventually we might be able to drop this constraint!
states, err := FilterState(result, packageList, obj.State)
if err != nil {
return false, fmt.Errorf("The FilterState method failed with: %v.", err)
}
data, _ := result[obj.Name] // if above didn't error, we won't either!
validState := BoolMapTrue(BoolMapValues(states))
// obj.State == "installed" || "uninstalled" || "newest" || "4.2-1.fc23"
switch obj.State {
case "installed":
if data.Installed {
return true, nil // state is correct, exit!
}
fallthrough
case "uninstalled":
if !data.Installed {
return true, nil
}
fallthrough
case "newest":
if data.Newest {
return true, nil
if validState {
return true, nil // state is correct, exit!
}
default: // version string
if obj.State == data.Version && data.Version != "" {
@@ -246,42 +288,45 @@ func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
}
}
if data.PackageID == "" {
return false, errors.New("Can't find package id to use.")
}
// 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())
packageList := []string{data.PackageID}
log.Printf("%v: Apply", obj.fmtNames(obj.getNames()))
readyPackages, err := FilterPackageState(result, packageList, obj.State)
if err != nil {
return false, err // fail
}
// these are the packages that actually need their states applied!
applyPackages := StrFilterElementsInList(readyPackages, packageList)
packageIDs, _ := FilterPackageIDs(result, applyPackages) // would be same err as above
var transactionFlags uint64 // initializes at the "zero" value of 0
if !obj.AllowUntrusted { // allow
transactionFlags += PK_TRANSACTION_FLAG_ENUM_ONLY_TRUSTED
}
// apply correct state!
log.Printf("%v[%v]: Set: %v...", obj.Kind(), obj.GetName(), obj.State)
log.Printf("%v: Set: %v...", obj.fmtNames(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(packageList, transactionFlags)
err = bus.RemovePackages(packageIDs, transactionFlags)
case "newest": // TODO: isn't this the same operation as install, below?
err = bus.UpdatePackages(packageList, transactionFlags)
err = bus.UpdatePackages(packageIDs, transactionFlags)
case "installed":
fallthrough // same method as for "set specific version", below
default: // version string
err = bus.InstallPackages(packageList, transactionFlags)
err = bus.InstallPackages(packageIDs, transactionFlags)
}
if err != nil {
return false, err // fail
}
log.Printf("%v[%v]: Set: %v success!", obj.Kind(), obj.GetName(), obj.State)
log.Printf("%v: Set: %v success!", obj.fmtNames(StrListIntersection(applyPackages, obj.getNames())), obj.State)
return false, nil // success
}
@@ -427,6 +472,27 @@ func (obj *PkgRes) GetUUIDs() []ResUUID {
return result
}
// can these two resources be merged ?
// (aka does this resource support doing so?)
// will resource allow itself to be grouped _into_ this obj?
func (obj *PkgRes) GroupCmp(r Res) bool {
res, ok := r.(*PkgRes)
if !ok {
return false
}
objStateIsVersion := (obj.State != "installed" && obj.State != "uninstalled" && obj.State != "newest") // must be a ver. string
resStateIsVersion := (res.State != "installed" && res.State != "uninstalled" && res.State != "newest") // must be a ver. string
if objStateIsVersion || resStateIsVersion {
// can't merge specific version checks atm
return false
}
// FIXME: keep it simple for now, only merge same states
if obj.State != res.State {
return false
}
return true
}
func (obj *PkgRes) Compare(res Res) bool {
switch res.(type) {
case *PkgRes: