Add some of the pkg and svc autoedge logic

This adds another chunk of it, and makes some other small fixes.
This commit is contained in:
James Shubin
2016-03-09 03:10:03 -05:00
parent f39551952f
commit e60dda5027
6 changed files with 380 additions and 72 deletions

29
examples/autoedges3.yaml Normal file
View File

@@ -0,0 +1,29 @@
---
graph: mygraph
resources:
pkg:
- name: drbd-utils
meta:
autoedge: true
state: installed
file:
- name: file1
meta:
autoedge: true
path: "/etc/drbd.conf"
content: |
# this is an mgmt test
state: exists
- name: file2
meta:
autoedge: true
path: "/etc/drbd.d/"
content: |
i am a directory
state: exists
svc:
- name: drbd
meta:
autoedge: true
state: stopped
edges: []

View File

@@ -656,4 +656,138 @@ func TestMiscT11(t *testing.T) {
if !reflect.DeepEqual(ex9, out9) {
t.Errorf("RemoveCommonFilePrefixes expected: %v; got: %v.", ex9, out9)
}
in10 := []string{
"/etc/drbd.conf",
"/etc/drbd.d/", // watch me, i'm a dir
"/etc/drbd.d/global_common.conf", // and watch me i'm a file!
"/lib/drbd/drbd",
"/lib/drbd/drbdadm-83",
"/lib/drbd/drbdadm-84",
"/lib/drbd/drbdsetup-83",
"/lib/drbd/drbdsetup-84",
"/usr/lib/drbd/crm-fence-peer.sh",
"/usr/lib/drbd/crm-unfence-peer.sh",
"/usr/lib/drbd/notify-emergency-reboot.sh",
"/usr/lib/drbd/notify-emergency-shutdown.sh",
"/usr/lib/drbd/notify-io-error.sh",
"/usr/lib/drbd/notify-out-of-sync.sh",
"/usr/lib/drbd/notify-pri-lost-after-sb.sh",
"/usr/lib/drbd/notify-pri-lost.sh",
"/usr/lib/drbd/notify-pri-on-incon-degr.sh",
"/usr/lib/drbd/notify-split-brain.sh",
"/usr/lib/drbd/notify.sh",
"/usr/lib/drbd/outdate-peer.sh",
"/usr/lib/drbd/rhcs_fence",
"/usr/lib/drbd/snapshot-resync-target-lvm.sh",
"/usr/lib/drbd/stonith_admin-fence-peer.sh",
"/usr/lib/drbd/unsnapshot-resync-target-lvm.sh",
"/usr/lib/systemd/system/drbd.service",
"/usr/lib/tmpfiles.d/drbd.conf",
"/usr/sbin/drbd-overview",
"/usr/sbin/drbdadm",
"/usr/sbin/drbdmeta",
"/usr/sbin/drbdsetup",
"/usr/share/doc/drbd-utils/", // watch me, i'm a dir too
"/usr/share/doc/drbd-utils/COPYING",
"/usr/share/doc/drbd-utils/ChangeLog",
"/usr/share/doc/drbd-utils/README",
"/usr/share/doc/drbd-utils/drbd.conf.example",
"/usr/share/man/man5/drbd.conf-8.3.5.gz",
"/usr/share/man/man5/drbd.conf-8.4.5.gz",
"/usr/share/man/man5/drbd.conf-9.0.5.gz",
"/usr/share/man/man5/drbd.conf.5.gz",
"/usr/share/man/man8/drbd-8.3.8.gz",
"/usr/share/man/man8/drbd-8.4.8.gz",
"/usr/share/man/man8/drbd-9.0.8.gz",
"/usr/share/man/man8/drbd-overview-9.0.8.gz",
"/usr/share/man/man8/drbd-overview.8.gz",
"/usr/share/man/man8/drbd.8.gz",
"/usr/share/man/man8/drbdadm-8.3.8.gz",
"/usr/share/man/man8/drbdadm-8.4.8.gz",
"/usr/share/man/man8/drbdadm-9.0.8.gz",
"/usr/share/man/man8/drbdadm.8.gz",
"/usr/share/man/man8/drbddisk-8.3.8.gz",
"/usr/share/man/man8/drbddisk-8.4.8.gz",
"/usr/share/man/man8/drbdmeta-8.3.8.gz",
"/usr/share/man/man8/drbdmeta-8.4.8.gz",
"/usr/share/man/man8/drbdmeta-9.0.8.gz",
"/usr/share/man/man8/drbdmeta.8.gz",
"/usr/share/man/man8/drbdsetup-8.3.8.gz",
"/usr/share/man/man8/drbdsetup-8.4.8.gz",
"/usr/share/man/man8/drbdsetup-9.0.8.gz",
"/usr/share/man/man8/drbdsetup.8.gz",
"/var/lib/drbd",
}
ex10 := []string{
"/etc/drbd.conf",
"/etc/drbd.d/global_common.conf",
"/lib/drbd/drbd",
"/lib/drbd/drbdadm-83",
"/lib/drbd/drbdadm-84",
"/lib/drbd/drbdsetup-83",
"/lib/drbd/drbdsetup-84",
"/usr/lib/drbd/crm-fence-peer.sh",
"/usr/lib/drbd/crm-unfence-peer.sh",
"/usr/lib/drbd/notify-emergency-reboot.sh",
"/usr/lib/drbd/notify-emergency-shutdown.sh",
"/usr/lib/drbd/notify-io-error.sh",
"/usr/lib/drbd/notify-out-of-sync.sh",
"/usr/lib/drbd/notify-pri-lost-after-sb.sh",
"/usr/lib/drbd/notify-pri-lost.sh",
"/usr/lib/drbd/notify-pri-on-incon-degr.sh",
"/usr/lib/drbd/notify-split-brain.sh",
"/usr/lib/drbd/notify.sh",
"/usr/lib/drbd/outdate-peer.sh",
"/usr/lib/drbd/rhcs_fence",
"/usr/lib/drbd/snapshot-resync-target-lvm.sh",
"/usr/lib/drbd/stonith_admin-fence-peer.sh",
"/usr/lib/drbd/unsnapshot-resync-target-lvm.sh",
"/usr/lib/systemd/system/drbd.service",
"/usr/lib/tmpfiles.d/drbd.conf",
"/usr/sbin/drbd-overview",
"/usr/sbin/drbdadm",
"/usr/sbin/drbdmeta",
"/usr/sbin/drbdsetup",
"/usr/share/doc/drbd-utils/COPYING",
"/usr/share/doc/drbd-utils/ChangeLog",
"/usr/share/doc/drbd-utils/README",
"/usr/share/doc/drbd-utils/drbd.conf.example",
"/usr/share/man/man5/drbd.conf-8.3.5.gz",
"/usr/share/man/man5/drbd.conf-8.4.5.gz",
"/usr/share/man/man5/drbd.conf-9.0.5.gz",
"/usr/share/man/man5/drbd.conf.5.gz",
"/usr/share/man/man8/drbd-8.3.8.gz",
"/usr/share/man/man8/drbd-8.4.8.gz",
"/usr/share/man/man8/drbd-9.0.8.gz",
"/usr/share/man/man8/drbd-overview-9.0.8.gz",
"/usr/share/man/man8/drbd-overview.8.gz",
"/usr/share/man/man8/drbd.8.gz",
"/usr/share/man/man8/drbdadm-8.3.8.gz",
"/usr/share/man/man8/drbdadm-8.4.8.gz",
"/usr/share/man/man8/drbdadm-9.0.8.gz",
"/usr/share/man/man8/drbdadm.8.gz",
"/usr/share/man/man8/drbddisk-8.3.8.gz",
"/usr/share/man/man8/drbddisk-8.4.8.gz",
"/usr/share/man/man8/drbdmeta-8.3.8.gz",
"/usr/share/man/man8/drbdmeta-8.4.8.gz",
"/usr/share/man/man8/drbdmeta-9.0.8.gz",
"/usr/share/man/man8/drbdmeta.8.gz",
"/usr/share/man/man8/drbdsetup-8.3.8.gz",
"/usr/share/man/man8/drbdsetup-8.4.8.gz",
"/usr/share/man/man8/drbdsetup-9.0.8.gz",
"/usr/share/man/man8/drbdsetup.8.gz",
"/var/lib/drbd",
}
sort.Strings(ex10)
out10 := RemoveCommonFilePrefixes(in10)
sort.Strings(out10)
if !reflect.DeepEqual(ex10, out10) {
t.Errorf("RemoveCommonFilePrefixes expected: %v; got: %v.", ex10, out10)
for i := 0; i < len(ex10); i++ {
if ex10[i] != out10[i] {
t.Errorf("# %d: %v <> %v", i, ex10[i], out10[i])
}
}
}
}

View File

@@ -196,16 +196,10 @@ func (bus *Conn) matchSignal(ch chan *dbus.Signal, path dbus.ObjectPath, iface s
if call.Err != nil {
return call.Err
}
if PK_DEBUG {
log.Println("PackageKit: matchSignal(): Added!")
}
// The caller has to make sure that ch is sufficiently buffered; if a
// message arrives when a write to c is not possible, it is discarded!
// This can be disastrous if we're waiting for a "Finished" signal!
bus.GetBus().Signal(ch)
if PK_DEBUG {
log.Println("PackageKit: matchSignal(): Success!")
}
return nil
}
@@ -411,11 +405,9 @@ loop:
// a package was installed...
// only start the timer once we're here...
timeout = PkSignalPackageTimeout
continue loop
} else if signal.Name == FmtTransactionMethod("Finished") {
finished = true
timeout = PkSignalDestroyTimeout // wait a bit
continue loop
} else if signal.Name == FmtTransactionMethod("Destroy") {
return nil // success
} else {
@@ -508,8 +500,6 @@ loop:
if signal.Name == FmtTransactionMethod("ErrorCode") {
return errors.New(fmt.Sprintf("PackageKit: Error: %v", signal.Body))
} else if signal.Name == FmtTransactionMethod("Package") {
// a package was installed...
continue loop
} else if signal.Name == FmtTransactionMethod("Finished") {
// TODO: should we wait for the Destroy signal?
break loop
@@ -574,8 +564,6 @@ loop:
continue loop // failed conversion
}
files[key] = fileList // build up map
continue loop
} else if signal.Name == FmtTransactionMethod("Finished") {
// TODO: should we wait for the Destroy signal?
break loop
@@ -593,6 +581,9 @@ loop:
// get list of packages that are installed and which can be updated, mod filter
func (bus *Conn) GetUpdates(filter uint64) ([]string, error) {
if PK_DEBUG {
log.Println("PackageKit: GetUpdates()")
}
packageIds := []string{}
ch := make(chan *dbus.Signal, PkBufferSize) // we need to buffer :(
interfacePath, err := bus.CreateTransaction()
@@ -635,7 +626,6 @@ loop:
}
}
packageIds = append(packageIds, packageId)
} else if signal.Name == FmtTransactionMethod("Finished") {
// TODO: should we wait for the Destroy signal?
break loop
@@ -708,8 +698,9 @@ func (bus *Conn) PackagesToPackageIds(packageMap map[string]string, filter uint6
return nil, errors.New(fmt.Sprintf("Empty package state for %v", pkg))
}
found[index] = true
stateIsVersion := (state != "installed" && state != "uninstalled" && state != "newest") // must be a ver. string
if state != "installed" && state != "uninstalled" && state != "newest" { // must be a ver. string
if stateIsVersion {
if state == ver && ver != "" { // we match what we want...
usePackageId[index] = packageId
}
@@ -718,11 +709,13 @@ func (bus *Conn) PackagesToPackageIds(packageMap map[string]string, filter uint6
if FlagInData("installed", data) {
installed[index] = true
version[index] = ver
if state == "uninstalled" {
// state of "uninstalled" matched during CheckApply, and
// states of "installed" and "newest" for fileList
if !stateIsVersion {
usePackageId[index] = packageId // save for later
}
} else { // not installed...
if state == "installed" || state == "newest" {
if !stateIsVersion {
// if there is more than one result, eg: there
// is the old and newest version of a package,
// then this section can run more than once...

174
pkg.go
View File

@@ -22,6 +22,8 @@ import (
"errors"
"fmt"
"log"
"path"
"strings"
)
type PkgRes struct {
@@ -30,6 +32,7 @@ type PkgRes struct {
AllowUntrusted bool `yaml:"allowuntrusted"` // allow untrusted packages to be installed?
AllowNonFree bool `yaml:"allownonfree"` // allow nonfree packages to be found?
AllowUnsupported bool `yaml:"allowunsupported"` // allow unsupported packages to be found?
//bus *Conn // pk bus connection
fileList []string // FIXME: update if pkg changes
}
@@ -53,10 +56,35 @@ func NewPkgRes(name, state string, allowuntrusted, allownonfree, allowunsupporte
func (obj *PkgRes) Init() {
obj.BaseRes.kind = "Pkg"
obj.BaseRes.Init() // call base init, b/c we're overriding
bus := NewBus()
if bus == nil {
log.Fatal("Can't connect to PackageKit bus.")
}
defer bus.Close()
data, err := obj.PkgMappingHelper(bus)
if err != nil {
// FIXME: return error?
log.Fatalf("The PkgMappingHelper failed with: %v.", err)
return
}
func (obj *PkgRes) Kind() string {
return "Pkg"
packageIds := []string{data.PackageId} // just one for now
filesMap, err := bus.GetFilesByPackageId(packageIds)
if err != nil {
// FIXME: return error?
log.Fatalf("Can't run GetFilesByPackageId: %v", err)
return
}
if files, ok := filesMap[data.PackageId]; ok {
obj.fileList = DirifyFileList(files, false)
}
}
// XXX: run this when resource exits
func (obj *PkgRes) Close() {
//obj.bus.Close()
}
func (obj *PkgRes) Validate() bool {
@@ -143,22 +171,7 @@ func (obj *PkgRes) Watch() {
}
}
func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
log.Printf("%v[%v]: CheckApply(%t)", obj.Kind(), obj.GetName(), apply)
if obj.State == "" { // TODO: Validate() should replace this check!
log.Fatalf("%v[%v]: Package state is undefined!", obj.Kind(), obj.GetName())
}
if obj.isStateOK { // cache the state
return true, nil
}
bus := NewBus()
if bus == nil {
return false, errors.New("Can't connect to PackageKit bus.")
}
defer bus.Close()
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
@@ -180,13 +193,38 @@ func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
}
result, e := bus.PackagesToPackageIds(packageMap, filter)
if e != nil {
return false, errors.New(fmt.Sprintf("PackagesToPackageIds error: %v", e))
return nil, errors.New(fmt.Sprintf("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 false, errors.New(fmt.Sprintf("Can't find package named '%s'.", obj.Name))
return nil, errors.New(fmt.Sprintf("Can't find package named '%s'.", obj.Name))
}
return data, nil
}
func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
log.Printf("%v[%v]: CheckApply(%t)", obj.Kind(), obj.GetName(), apply)
if obj.State == "" { // TODO: Validate() should replace this check!
log.Fatalf("%v[%v]: Package state is undefined!", obj.Kind(), obj.GetName())
}
if obj.isStateOK { // cache the state
return true, nil
}
bus := NewBus()
if bus == nil {
return false, errors.New("Can't connect to PackageKit bus.")
}
defer bus.Close()
data, err := obj.PkgMappingHelper(bus)
if err != nil {
return false, errors.New(fmt.Sprintf("The PkgMappingHelper failed with: %v.", err))
}
// obj.State == "installed" || "uninstalled" || "newest" || "4.2-1.fc23"
@@ -248,8 +286,26 @@ func (obj *PkgRes) CheckApply(apply bool) (stateok bool, err error) {
return false, nil // success
}
type PkgUUID struct {
BaseUUID
name string // pkg name
state string // pkg state or "version"
}
// if and only if they are equivalent, return true
// if they are not equivalent, return false
func (obj *PkgUUID) IFF(uuid ResUUID) bool {
res, ok := uuid.(*PkgUUID)
if !ok {
return false
}
// FIXME: match on obj.state vs. res.state ?
return obj.name == res.name
}
type PkgResAutoEdges struct {
fileList []string
svcUUIDs []ResUUID
testIsNext bool // safety
name string // saved data from PkgRes obj
kind string
@@ -260,8 +316,13 @@ func (obj *PkgResAutoEdges) Next() []ResUUID {
log.Fatal("Expecting a call to Test()")
}
obj.testIsNext = true // set after all the errors paths are past
var result []ResUUID
// first return any matching svcUUIDs
if x := obj.svcUUIDs; len(x) > 0 {
return x
}
var result []ResUUID
// return UUID's for whatever is in obj.fileList
for _, x := range obj.fileList {
var reversed bool = false // cheat by passing a pointer
@@ -281,6 +342,17 @@ func (obj *PkgResAutoEdges) Test(input []bool) bool {
if !obj.testIsNext {
log.Fatal("Expecting a call to Next()")
}
// ack the svcUUID's...
if x := obj.svcUUIDs; len(x) > 0 {
if y := len(x); y != len(input) {
log.Fatalf("Expecting %d value(s)!", y)
}
obj.svcUUIDs = []ResUUID{} // empty
obj.testIsNext = false
return true
}
count := len(obj.fileList)
if count != len(input) {
log.Fatalf("Expecting %d value(s)!", count)
@@ -316,27 +388,29 @@ func (obj *PkgResAutoEdges) Test(input []bool) bool {
return true // continue, there are more files!
}
type PkgUUID struct {
BaseUUID
}
// if and only if they are equivalent, return true
// if they are not equivalent, return false
func (obj *PkgUUID) IFF(uuid ResUUID) bool {
res, ok := uuid.(*PkgUUID)
if !ok {
return false
}
return obj.name == res.name
}
// produce an object which generates a minimal pkg file optimization sequence
func (obj *PkgRes) AutoEdges() AutoEdge {
// in contrast with the FileRes AutoEdges() function which contains
// more of the mechanics, most of the AutoEdge mechanics for the PkgRes
// is contained in the Test() method! This design is completely okay!
// add matches for any svc resources found in pkg definition!
var svcUUIDs []ResUUID
for _, x := range ReturnSvcInFileList(obj.fileList) {
var reversed bool = false
svcUUIDs = append(svcUUIDs, &SvcUUID{
BaseUUID: BaseUUID{
name: obj.GetName(),
kind: obj.Kind(),
reversed: &reversed,
},
name: x, // the svc name itself in the SvcUUID object!
}) // build list
}
return &PkgResAutoEdges{
fileList: obj.fileList,
fileList: RemoveCommonFilePrefixes(obj.fileList), // clean start!
svcUUIDs: svcUUIDs,
testIsNext: false, // start with Next() call
name: obj.GetName(), // save data for PkgResAutoEdges obj
kind: obj.Kind(),
@@ -347,15 +421,10 @@ func (obj *PkgRes) AutoEdges() AutoEdge {
func (obj *PkgRes) GetUUIDs() []ResUUID {
x := &PkgUUID{
BaseUUID: BaseUUID{name: obj.GetName(), kind: obj.Kind()},
name: obj.Name,
state: obj.State,
}
result := []ResUUID{x}
for _, path := range obj.fileList {
x := &FileUUID{
BaseUUID: BaseUUID{name: obj.GetName(), kind: obj.Kind()},
path: path,
}
result = append(result, x)
}
return result
}
@@ -383,3 +452,22 @@ func (obj *PkgRes) Compare(res Res) bool {
}
return true
}
// return a list of svc names for matches like /usr/lib/systemd/system/*.service
func ReturnSvcInFileList(fileList []string) []string {
result := []string{}
for _, x := range fileList {
dirname, basename := path.Split(path.Clean(x))
// TODO: do we also want to look for /etc/systemd/system/ ?
if dirname != "/usr/lib/systemd/system/" {
continue
}
if !strings.HasSuffix(basename, ".service") {
continue
}
if s := strings.TrimSuffix(basename, ".service"); !StrInList(s, result) {
result = append(result, s)
}
}
return result
}

View File

@@ -52,7 +52,7 @@ type ResUUID interface {
}
type BaseUUID struct {
name string
name string // name and kind are the values of where this is coming from
kind string
reversed *bool // piggyback edge information here
@@ -467,6 +467,7 @@ func (obj *NoopRes) CheckApply(apply bool) (stateok bool, err error) {
type NoopUUID struct {
BaseUUID
name string
}
func (obj *NoopRes) AutoEdges() AutoEdge {
@@ -474,10 +475,11 @@ func (obj *NoopRes) AutoEdges() AutoEdge {
}
// include all params to make a unique identification of this object
// most resources only return one
// most resources only return one, although some resources return multiple
func (obj *NoopRes) GetUUIDs() []ResUUID {
x := &NoopUUID{
BaseUUID: BaseUUID{name: obj.GetName(), kind: obj.Kind()},
name: obj.Name,
}
return []ResUUID{x}
}

72
svc.go
View File

@@ -310,7 +310,12 @@ func (obj *SvcRes) CheckApply(apply bool) (stateok bool, err error) {
}
type SvcUUID struct {
// NOTE: there is also a name variable in the BaseUUID struct, this is
// information about where this UUID came from, and is unrelated to the
// information about the resource we're matching. That data which is
// used in the IFF function, is what you see in the struct fields here.
BaseUUID
name string // the svc name
}
// if and only if they are equivalent, return true
@@ -323,17 +328,74 @@ func (obj *SvcUUID) IFF(uuid ResUUID) bool {
return obj.name == res.name
}
func (obj *SvcRes) AutoEdges() AutoEdge {
// TODO: add auto edges to the files that provide the service files,
// which might come from a pkg resource perhaps!
type SvcResAutoEdges struct {
data []ResUUID
pointer int
found bool
}
func (obj *SvcResAutoEdges) Next() []ResUUID {
if obj.found {
log.Fatal("Shouldn't be called anymore!")
}
if len(obj.data) == 0 { // check length for rare scenarios
return nil
}
value := obj.data[obj.pointer]
obj.pointer += 1
return []ResUUID{value} // we return one, even though api supports N
}
// get results of the earlier Next() call, return if we should continue!
func (obj *SvcResAutoEdges) Test(input []bool) bool {
// if there aren't any more remaining
if len(obj.data) <= obj.pointer {
return false
}
if obj.found { // already found, done!
return false
}
if len(input) != 1 { // in case we get given bad data
log.Fatal("Expecting a single value!")
}
if input[0] { // if a match is found, we're done!
obj.found = true // no more to find!
return false
}
return true // keep going
}
func (obj *SvcRes) AutoEdges() AutoEdge {
var data []ResUUID
svcFiles := []string{
fmt.Sprintf("/etc/systemd/system/%s.service", obj.Name), // takes precedence
fmt.Sprintf("/usr/lib/systemd/system/%s.service", obj.Name), // pkg default
}
for _, x := range svcFiles {
var reversed bool = true
data = append(data, &FileUUID{
BaseUUID: BaseUUID{
name: obj.GetName(),
kind: obj.Kind(),
reversed: &reversed,
},
path: x, // what matters
})
}
return &FileResAutoEdges{
data: data,
pointer: 0,
found: false,
}
}
// include all params to make a unique identification of this object
func (obj *SvcRes) GetUUIDs() []ResUUID {
x := &SvcUUID{BaseUUID: BaseUUID{name: obj.GetName(), kind: obj.Kind()}}
x := &SvcUUID{
BaseUUID: BaseUUID{name: obj.GetName(), kind: obj.Kind()},
name: obj.Name, // svc name
}
return []ResUUID{x}
}
func (obj *SvcRes) Compare(res Res) bool {