From fe2b8c9fee465d755bffea34c4c7b6d85cd7df72 Mon Sep 17 00:00:00 2001 From: Joe Groocock Date: Tue, 4 May 2021 13:09:32 +0100 Subject: [PATCH] engine: resources: exec: AutoEdge to User/Group/File Fixes https://github.com/purpleidea/mgmt/issues/221 Signed-off-by: Joe Groocock --- engine/resources/exec.go | 54 ++++++++++++++++++++++--- engine/resources/exec_test.go | 76 +++++++++++++++++++++++++++++++++++ engine/resources/user.go | 21 ++++++++++ 3 files changed, 145 insertions(+), 6 deletions(-) diff --git a/engine/resources/exec.go b/engine/resources/exec.go index 9b83ebf0..c96b7a08 100644 --- a/engine/resources/exec.go +++ b/engine/resources/exec.go @@ -570,26 +570,38 @@ type ExecUID struct { // ExecResAutoEdges holds the state of the auto edge generator. type ExecResAutoEdges struct { - edges []engine.ResUID + edges []engine.ResUID + pointer int } // Next returns the next automatic edge. func (obj *ExecResAutoEdges) Next() []engine.ResUID { - return obj.edges + if len(obj.edges) == 0 { + return nil + } + value := obj.edges[obj.pointer] + obj.pointer++ + return []engine.ResUID{value} } // Test gets results of the earlier Next() call, & returns if we should // continue! func (obj *ExecResAutoEdges) Test(input []bool) bool { - return false // never keep going - // TODO: we could return false if we find as many edges as the number of different path's in cmdFiles() + if len(obj.edges) <= obj.pointer { + return false + } + if len(input) != 1 { // in case we get given bad data + panic(fmt.Sprintf("Expecting a single value!")) + } + return true // keep going } // AutoEdges returns the AutoEdge interface. In this case the systemd units. func (obj *ExecRes) AutoEdges() (engine.AutoEdge, error) { var data []engine.ResUID + var reversed = true + for _, x := range obj.cmdFiles() { - var reversed = true data = append(data, &PkgFileUID{ BaseUID: engine.BaseUID{ Name: obj.Name(), @@ -598,9 +610,39 @@ func (obj *ExecRes) AutoEdges() (engine.AutoEdge, error) { }, path: x, // what matters }) + data = append(data, &FileUID{ + BaseUID: engine.BaseUID{ + Name: obj.Name(), + Kind: obj.Kind(), + Reversed: &reversed, + }, + path: x, + }) } + if obj.User != "" { + data = append(data, &UserUID{ + BaseUID: engine.BaseUID{ + Name: obj.Name(), + Kind: obj.Kind(), + Reversed: &reversed, + }, + name: obj.User, + }) + } + if obj.Group != "" { + data = append(data, &GroupUID{ + BaseUID: engine.BaseUID{ + Name: obj.Name(), + Kind: obj.Kind(), + Reversed: &reversed, + }, + name: obj.Group, + }) + } + return &ExecResAutoEdges{ - edges: data, + edges: data, + pointer: 0, }, nil } diff --git a/engine/resources/exec_test.go b/engine/resources/exec_test.go index e92459fa..5e8c433e 100644 --- a/engine/resources/exec_test.go +++ b/engine/resources/exec_test.go @@ -28,6 +28,8 @@ import ( "time" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/engine/graph/autoedge" + "github.com/purpleidea/mgmt/pgraph" ) func fakeExecInit(t *testing.T) (*engine.Init, *ExecSends) { @@ -257,3 +259,77 @@ func TestExecTimeoutBehaviour(t *testing.T) { // no error } + +func TestExecAutoEdge1(t *testing.T) { + g, err := pgraph.NewGraph("TestGraph") + if err != nil { + t.Errorf("error creating graph: %v", err) + return + } + + resUser, err := engine.NewNamedResource("user", "someuser") + if err != nil { + t.Errorf("error creating user resource: %v", err) + return + } + + resGroup, err := engine.NewNamedResource("group", "somegroup") + if err != nil { + t.Errorf("error creating group resource: %v", err) + return + } + + resFile, err := engine.NewNamedResource("file", "/somefile") + if err != nil { + t.Errorf("error creating group resource: %v", err) + return + } + + resExec, err := engine.NewNamedResource("exec", "somefile") + if err != nil { + t.Errorf("error creating exec resource: %v", err) + return + } + exc := resExec.(*ExecRes) + exc.Cmd = resFile.Name() + exc.User = resUser.Name() + exc.Group = resGroup.Name() + + g.AddVertex(resUser, resGroup, resFile, resExec) + + if i := g.NumEdges(); i != 0 { + t.Errorf("should have 0 edges instead of: %d", i) + return + } + + debug := testing.Verbose() // set via the -test.v flag to `go test` + logf := func(format string, v ...interface{}) { + t.Logf("test: "+format, v...) + } + if err := autoedge.AutoEdge(g, debug, logf); err != nil { + t.Errorf("error running autoedges: %v", err) + return + } + + expected, err := pgraph.NewGraph("Expected") + if err != nil { + t.Errorf("error creating graph: %v", err) + return + } + + expectEdge := func(from, to pgraph.Vertex) { + edge := &engine.Edge{Name: fmt.Sprintf("%s -> %s (expected)", from, to)} + expected.AddEdge(from, to, edge) + } + expectEdge(resFile, resExec) + expectEdge(resUser, resExec) + expectEdge(resGroup, resExec) + + vertexCmp := func(v1, v2 pgraph.Vertex) (bool, error) { return v1 == v2, nil } // pointer compare is sufficient + edgeCmp := func(e1, e2 pgraph.Edge) (bool, error) { return true, nil } // we don't care about edges here + + if err := expected.GraphCmp(g, vertexCmp, edgeCmp); err != nil { + t.Errorf("graph doesn't match expected: %s", err) + return + } +} diff --git a/engine/resources/user.go b/engine/resources/user.go index 7a434772..7dc53be6 100644 --- a/engine/resources/user.go +++ b/engine/resources/user.go @@ -333,6 +333,26 @@ func (obj *UserRes) Cmp(r engine.Res) error { type UserUID struct { engine.BaseUID name string + uid *uint32 +} + +// IFF aka if and only if they are equivalent, return true. If not, false. +func (obj *UserUID) IFF(uid engine.ResUID) bool { + res, ok := uid.(*UserUID) + if !ok { + return false + } + if obj.uid != nil && res.uid != nil { + if *obj.uid != *res.uid { + return false + } + } + if obj.name != "" && res.name != "" { + if obj.name != res.name { + return false + } + } + return true } // UserResAutoEdges holds the state of the auto edge generator. @@ -411,6 +431,7 @@ func (obj *UserRes) UIDs() []engine.ResUID { x := &UserUID{ BaseUID: engine.BaseUID{Name: obj.Name(), Kind: obj.Kind()}, name: obj.Name(), + uid: obj.UID, } return []engine.ResUID{x} }