View
492 config.go

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -138,7 +138,7 @@ func ConfigWatch(file string) chan bool {
}
case err := <-watcher.Errors:
- log.Println("error:", err)
+ log.Printf("error: %v", err)
log.Fatal(err)
}
View
10 etcd.go
@@ -207,20 +207,14 @@ func (etcdO *EtcdWObject) EtcdWatch() chan etcdMsg {
}
// helper function to store our data in etcd
-func (etcdO *EtcdWObject) EtcdPut(hostname, key, res string, obj interface{}) bool {
+func (etcdO *EtcdWObject) EtcdPut(hostname, key, res string, data string) bool {
kapi := etcdO.GetKAPI()
- output, ok := ObjToB64(obj)
- if !ok {
- log.Printf("Etcd: Could not encode %v key.", key)
- return false
- }
-
path := fmt.Sprintf("/exported/%s/resources/%s/res", hostname, key)
_, err := kapi.Set(etcd_context.Background(), path, res, nil)
// XXX validate...
path = fmt.Sprintf("/exported/%s/resources/%s/value", hostname, key)
- resp, err := kapi.Set(etcd_context.Background(), path, output, nil)
+ resp, err := kapi.Set(etcd_context.Background(), path, data, nil)
if err != nil {
if cerr, ok := err.(*etcd.ClusterError); ok {
// not running or disconnected
View
@@ -13,6 +13,6 @@ resources:
i am f2, exported from host A
state: exists
collect:
-- res: file
+- kind: file
pattern: "/tmp/mgmtA/"
edges: []
View
@@ -13,6 +13,6 @@ resources:
i am f2, exported from host B
state: exists
collect:
-- res: file
+- kind: file
pattern: "/tmp/mgmtB/"
edges: []
View
@@ -13,6 +13,6 @@ resources:
i am f2, exported from host C
state: exists
collect:
-- res: file
+- kind: file
pattern: "/tmp/mgmtC/"
edges: []
View
@@ -45,15 +45,15 @@ resources:
edges:
- name: e1
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec2
- name: e2
from:
- res: exec
+ kind: exec
name: exec2
to:
- res: exec
+ kind: exec
name: exec3
View
@@ -25,8 +25,8 @@ resources:
edges:
- name: e1
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec2
View
@@ -25,8 +25,8 @@ resources:
edges:
- name: e1
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec2
View
@@ -25,8 +25,8 @@ resources:
edges:
- name: e1
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec2
View
@@ -55,29 +55,29 @@ resources:
edges:
- name: e1
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec2
- name: e2
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec3
- name: e3
from:
- res: exec
+ kind: exec
name: exec2
to:
- res: exec
+ kind: exec
name: exec4
- name: e4
from:
- res: exec
+ kind: exec
name: exec3
to:
- res: exec
+ kind: exec
name: exec4
View
@@ -27,15 +27,15 @@ resources:
edges:
- name: e1
from:
- res: file
+ kind: file
name: file1
to:
- res: file
+ kind: file
name: file2
- name: e2
from:
- res: file
+ kind: file
name: file2
to:
- res: file
+ kind: file
name: file3
View
@@ -13,8 +13,8 @@ resources:
edges:
- name: e1
from:
- res: noop
+ kind: noop
name: noop1
to:
- res: file
+ kind: file
name: file1
View
@@ -86,43 +86,43 @@ resources:
edges:
- name: e1
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec4
- name: e2
from:
- res: exec
+ kind: exec
name: exec2
to:
- res: exec
+ kind: exec
name: exec4
- name: e3
from:
- res: exec
+ kind: exec
name: exec3
to:
- res: exec
+ kind: exec
name: exec4
- name: e4
from:
- res: exec
+ kind: exec
name: exec4
to:
- res: exec
+ kind: exec
name: exec5
- name: e5
from:
- res: exec
+ kind: exec
name: exec4
to:
- res: exec
+ kind: exec
name: exec6
- name: e6
from:
- res: exec
+ kind: exec
name: exec4
to:
- res: exec
+ kind: exec
name: exec7
View
@@ -15,8 +15,8 @@ resources:
edges:
- name: e1
from:
- res: file
+ kind: file
name: file1
to:
- res: file
+ kind: file
name: file2
View
@@ -15,8 +15,8 @@ resources:
edges:
- name: e2
from:
- res: file
+ kind: file
name: file2
to:
- res: file
+ kind: file
name: file3
View
@@ -23,6 +23,6 @@ resources:
i am f4, exported from host A
state: exists
collect:
-- res: file
+- kind: file
pattern: "/tmp/mgmtA/"
edges: []
View
@@ -23,6 +23,6 @@ resources:
i am f4, exported from host B
state: exists
collect:
-- res: file
+- kind: file
pattern: "/tmp/mgmtB/"
edges: []
View
@@ -23,6 +23,6 @@ resources:
i am f4, exported from host C
state: exists
collect:
-- res: file
+- kind: file
pattern: "/tmp/mgmtC/"
edges: []
View
@@ -13,6 +13,6 @@ resources:
i am f3, exported from host A
state: exists
collect:
-- res: file
+- kind: file
pattern: ''
edges:
View
@@ -8,6 +8,6 @@ resources:
i am f1
state: exists
collect:
-- res: file
+- kind: file
pattern: ''
edges:
View
@@ -56,22 +56,22 @@ resources:
edges:
- name: e1
from:
- res: exec
+ kind: exec
name: exec1
to:
- res: exec
+ kind: exec
name: exec5
- name: e2
from:
- res: exec
+ kind: exec
name: exec2
to:
- res: exec
+ kind: exec
name: exec5
- name: e3
from:
- res: exec
+ kind: exec
name: exec3
to:
- res: exec
+ kind: exec
name: exec5
View
@@ -16,15 +16,15 @@ resources:
edges:
- name: e1
from:
- res: noop
+ kind: noop
name: noop1
to:
- res: file
+ kind: file
name: file1
- name: e2
from:
- res: file
+ kind: file
name: file1
to:
- res: svc
+ kind: svc
name: purpleidea
View
11 exec.go
@@ -20,12 +20,17 @@ package main
import (
"bufio"
"bytes"
+ "encoding/gob"
"errors"
"log"
"os/exec"
"strings"
)
+func init() {
+ gob.Register(&ExecRes{})
+}
+
type ExecRes struct {
BaseRes `yaml:",inline"`
State string `yaml:"state"` // state: exists/present?, absent, (undefined?)
@@ -97,7 +102,7 @@ func (obj *ExecRes) BufioChanScanner(scanner *bufio.Scanner) (chan string, chan
}
// Exec watcher
-func (obj *ExecRes) Watch() {
+func (obj *ExecRes) Watch(processChan chan struct{}) {
if obj.IsWatching() {
return
}
@@ -187,8 +192,8 @@ func (obj *ExecRes) Watch() {
if send {
send = false
// it is okay to invalidate the clean state on poke too
- obj.isStateOK = false // something made state dirty
- Process(obj) // XXX: rename this function
+ obj.isStateOK = false // something made state dirty
+ processChan <- struct{}{} // trigger process
}
}
}
View
16 file.go
@@ -22,6 +22,7 @@ import (
"encoding/hex"
"gopkg.in/fsnotify.v1"
//"github.com/go-fsnotify/fsnotify" // git master of "gopkg.in/fsnotify.v1"
+ "encoding/gob"
"io"
"log"
"math"
@@ -31,6 +32,10 @@ import (
"syscall"
)
+func init() {
+ gob.Register(&FileRes{})
+}
+
type FileRes struct {
BaseRes `yaml:",inline"`
Path string `yaml:"path"` // path variable (should default to name)
@@ -97,7 +102,7 @@ func (obj *FileRes) Validate() bool {
// File watcher for files and directories
// Modify with caution, probably important to write some test cases first!
// obj.GetPath(): file or directory
-func (obj *FileRes) Watch() {
+func (obj *FileRes) Watch(processChan chan struct{}) {
if obj.IsWatching() {
return
}
@@ -230,7 +235,7 @@ func (obj *FileRes) Watch() {
case err := <-watcher.Errors:
obj.SetConvergedState(resConvergedNil) // XXX ?
- log.Println("error:", err)
+ log.Printf("error: %v", err)
log.Fatal(err)
//obj.events <- fmt.Sprintf("file: %v", "error") // XXX: how should we handle errors?
@@ -255,7 +260,7 @@ func (obj *FileRes) Watch() {
dirty = false
obj.isStateOK = false // something made state dirty
}
- Process(obj) // XXX: rename this function
+ processChan <- struct{}{} // trigger process
}
}
}
@@ -488,3 +493,8 @@ func (obj *FileRes) Compare(res Res) bool {
}
return true
}
+
+func (obj *FileRes) CollectPattern(pattern string) {
+ // XXX: currently the pattern for files can only override the Dirname variable :P
+ obj.Dirname = pattern // XXX: simplistic for now
+}
View
33 main.go
@@ -63,7 +63,7 @@ func run(c *cli.Context) {
converged := make(chan bool) // converged signal
log.Printf("This is: %v, version: %v", program, version)
log.Printf("Main: Start: %v", start)
- G := NewGraph("Graph") // give graph a default name
+ var G, fullGraph *Graph
// exit after `max-runtime` seconds for no reason at all...
if i := c.Int("max-runtime"); i > 0 {
@@ -102,10 +102,11 @@ func run(c *cli.Context) {
if !c.Bool("no-watch") {
configchan = ConfigWatch(file)
}
- log.Printf("Etcd: Starting...")
+ log.Println("Etcd: Starting...")
etcdchan := etcdO.EtcdWatch()
first := true // first loop or not
for {
+ log.Println("Main: Waiting...")
select {
case _ = <-startchan: // kick the loop once at start
// pass
@@ -134,17 +135,29 @@ func run(c *cli.Context) {
}
// run graph vertex LOCK...
- if !first { // XXX: we can flatten this check out I think
- log.Printf("State: %v -> %v", G.SetState(graphPausing), G.GetState())
+ if !first { // TODO: we can flatten this check out I think
G.Pause() // sync
- log.Printf("State: %v -> %v", G.SetState(graphPaused), G.GetState())
}
- // build the graph from a config file
- // build the graph on events (eg: from etcd)
- if !UpdateGraphFromConfig(config, hostname, G, etcdO) {
- log.Fatal("Config: We borked the graph.") // XXX
+ // build graph from yaml file on events (eg: from etcd)
+ // we need the vertices to be paused to work on them
+ if newFullgraph, err := fullGraph.NewGraphFromConfig(config, etcdO, hostname); err == nil { // keep references to all original elements
+ fullGraph = newFullgraph
+ } else {
+ log.Printf("Config: Error making new graph from config: %v", err)
+ // unpause!
+ if !first {
+ G.Start(&wg, first) // sync
+ }
+ continue
}
+
+ G = fullGraph.Copy() // copy to active graph
+ // XXX: do etcd transaction out here...
+ G.AutoEdges() // add autoedges; modifies the graph
+ //G.AutoGroup() // run autogroup; modifies the graph // TODO
+ // TODO: do we want to do a transitive reduction?
+
log.Printf("Graph: %v", G) // show graph
err := G.ExecGraphviz(c.String("graphviz-filter"), c.String("graphviz"))
if err != nil {
@@ -159,9 +172,7 @@ func run(c *cli.Context) {
// some are not ready yet and the EtcdWatch
// loops, we'll cause G.Pause(...) before we
// even got going, thus causing nil pointer errors
- log.Printf("State: %v -> %v", G.SetState(graphStarting), G.GetState())
G.Start(&wg, first) // sync
- log.Printf("State: %v -> %v", G.SetState(graphStarted), G.GetState())
first = false
}
}()
View
41 misc.go
@@ -18,16 +18,18 @@
package main
import (
- "bytes"
- "encoding/base64"
- "encoding/gob"
"github.com/godbus/dbus"
"path"
"sort"
"strings"
"time"
)
+// returns the string with the first character capitalized
+func FirstToUpper(str string) string {
+ return strings.ToUpper(str[0:1]) + str[1:]
+}
+
// return true if a string exists inside a list, otherwise false
func StrInList(needle string, haystack []string) bool {
for _, x := range haystack {
@@ -136,6 +138,9 @@ func Dirname(p string) string {
func Basename(p string) string {
_, b := path.Split(path.Clean(p))
+ if p == "" {
+ return ""
+ }
if p[len(p)-1:] == "/" { // don't loose the tail slash
b += "/"
}
@@ -265,36 +270,6 @@ func DirifyFileList(fileList []string, removeDirs bool) []string {
return result
}
-// encode an object as base 64, serialize and then base64 encode
-func ObjToB64(obj interface{}) (string, bool) {
- b := bytes.Buffer{}
- e := gob.NewEncoder(&b)
- err := e.Encode(obj)
- if err != nil {
- //log.Println("Gob failed to Encode: ", err)
- return "", false
- }
- return base64.StdEncoding.EncodeToString(b.Bytes()), true
-}
-
-// TODO: is it possible to somehow generically just return the obj?
-// decode an object into the waiting obj which you pass a reference to
-func B64ToObj(str string, obj interface{}) bool {
- bb, err := base64.StdEncoding.DecodeString(str)
- if err != nil {
- //log.Println("Base64 failed to Decode: ", err)
- return false
- }
- b := bytes.NewBuffer(bb)
- d := gob.NewDecoder(b)
- err = d.Decode(obj)
- if err != nil {
- //log.Println("Gob failed to Decode: ", err)
- return false
- }
- return true
-}
-
// special version of time.After that blocks when given a negative integer
// when used in a case statement, the timer restarts on each select call to it
func TimeAfterOrBlock(t int) <-chan time.Time {
View
@@ -18,7 +18,6 @@
package main
import (
- "fmt"
"reflect"
"sort"
"testing"
@@ -58,6 +57,9 @@ func TestMiscT1(t *testing.T) {
t.Errorf("Result is incorrect.")
}
+ if Basename("") != "" { // TODO: should this equal something different?
+ t.Errorf("Result is incorrect.")
+ }
}
func TestMiscT2(t *testing.T) {
@@ -169,57 +171,6 @@ func TestMiscT5(t *testing.T) {
}
}
-func TestMiscT6(t *testing.T) {
-
- type foo struct {
- Name string `yaml:"name"`
- Res string `yaml:"res"`
- Value int `yaml:"value"`
- }
-
- obj := foo{"dude", "sweet", 42}
- output, ok := ObjToB64(obj)
- if ok != true {
- t.Errorf("First result should be true.")
- }
- var data foo
- if B64ToObj(output, &data) != true {
- t.Errorf("Second result should be true.")
- }
- // TODO: there is probably a better way to compare these two...
- if fmt.Sprintf("%+v\n", obj) != fmt.Sprintf("%+v\n", data) {
- t.Errorf("Strings should match.")
- }
-}
-
-func TestMiscT7(t *testing.T) {
-
- type Foo struct {
- Name string `yaml:"name"`
- Res string `yaml:"res"`
- Value int `yaml:"value"`
- }
-
- type bar struct {
- Foo `yaml:",inline"` // anonymous struct must be public!
- Comment string `yaml:"comment"`
- }
-
- obj := bar{Foo{"dude", "sweet", 42}, "hello world"}
- output, ok := ObjToB64(obj)
- if ok != true {
- t.Errorf("First result should be true.")
- }
- var data bar
- if B64ToObj(output, &data) != true {
- t.Errorf("Second result should be true.")
- }
- // TODO: there is probably a better way to compare these two...
- if fmt.Sprintf("%+v\n", obj) != fmt.Sprintf("%+v\n", data) {
- t.Errorf("Strings should match.")
- }
-}
-
func TestMiscT8(t *testing.T) {
r0 := []string{"/"}
View
@@ -18,9 +18,14 @@
package main
import (
+ "encoding/gob"
"log"
)
+func init() {
+ gob.Register(&NoopRes{})
+}
+
type NoopRes struct {
BaseRes `yaml:",inline"`
Comment string `yaml:"comment"` // extra field for example purposes
@@ -48,7 +53,7 @@ func (obj *NoopRes) Validate() bool {
return true
}
-func (obj *NoopRes) Watch() {
+func (obj *NoopRes) Watch(processChan chan struct{}) {
if obj.IsWatching() {
return
}
@@ -79,7 +84,7 @@ func (obj *NoopRes) Watch() {
send = false
// only do this on certain types of events
//obj.isStateOK = false // something made state dirty
- Process(obj) // XXX: rename this function
+ processChan <- struct{}{} // trigger process
}
}
}
View
321 pgraph.go

Large diffs are not rendered by default.

Oops, something went wrong.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
9 pkg.go
@@ -19,13 +19,18 @@ package main
import (
//"packagekit" // TODO
+ "encoding/gob"
"errors"
"fmt"
"log"
"path"
"strings"
)
+func init() {
+ gob.Register(&PkgRes{})
+}
+
type PkgRes struct {
BaseRes `yaml:",inline"`
State string `yaml:"state"` // state: installed, uninstalled, newest, <version>
@@ -102,7 +107,7 @@ func (obj *PkgRes) Validate() bool {
// use 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() {
+func (obj *PkgRes) Watch(processChan chan struct{}) {
if obj.IsWatching() {
return
}
@@ -168,7 +173,7 @@ func (obj *PkgRes) Watch() {
dirty = false
obj.isStateOK = false // something made state dirty
}
- Process(obj) // XXX: rename this function
+ processChan <- struct{}{} // trigger process
}
}
}
View
@@ -18,9 +18,11 @@
package main
import (
+ "bytes"
+ "encoding/base64"
+ "encoding/gob"
"fmt"
"log"
- "time"
)
//go:generate stringer -type=resState -output=resstate_stringer.go
@@ -73,27 +75,24 @@ type MetaParams struct {
// everything here only needs to be implemented once, in the BaseRes
type Base interface {
GetName() string // can't be named "Name()" because of struct field
+ SetName(string)
Kind() string
GetMeta() MetaParams
SetVertex(*Vertex)
SetConvergedCallback(ctimeout int, converged chan bool)
- SendEvent(eventName, bool, bool) bool
IsWatching() bool
SetWatching(bool)
GetConvergedState() resConvergedState
SetConvergedState(resConvergedState)
GetState() resState
SetState(resState)
- GetTimestamp() int64
- UpdateTimestamp() int64
- OKTimestamp() bool
- Poke(bool)
- BackPoke()
- GroupCmp(Res) bool // TODO: is there a better name for this?
- GroupRes(Res) error // group resource (arg) into self
- IsGrouped() bool // am I grouped?
- SetGrouped(bool) // set grouped bool
- GetGroup() []Res // return everyone grouped inside me
+ SendEvent(eventName, bool, bool) bool
+ ReadEvent(*Event) (bool, bool) // TODO: optional here?
+ GroupCmp(Res) bool // TODO: is there a better name for this?
+ GroupRes(Res) error // group resource (arg) into self
+ IsGrouped() bool // am I grouped?
+ SetGrouped(bool) // set grouped bool
+ GetGroup() []Res // return everyone grouped inside me
SetGroup([]Res)
}
@@ -103,17 +102,17 @@ type Res interface {
Init()
//Validate() bool // TODO: this might one day be added
GetUUIDs() []ResUUID // most resources only return one
- Watch()
+ Watch(chan struct{}) // send on channel to signal process() events
CheckApply(bool) (bool, error)
AutoEdges() AutoEdge
Compare(Res) bool
+ CollectPattern(string) // XXX: temporary until Res collection is more advanced
}
type BaseRes struct {
Name string `yaml:"name"`
Meta MetaParams `yaml:"meta"` // struct of all the metaparams
kind string
- timestamp int64 // last updated timestamp ?
events chan Event
vertex *Vertex
state resState
@@ -168,11 +167,15 @@ func (obj *BaseRes) Init() {
obj.events = make(chan Event) // unbuffered chan size to avoid stale events
}
-// this method gets used by all the resources, if we have one of (obj NoopRes) it would get overridden in that case!
+// this method gets used by all the resources
func (obj *BaseRes) GetName() string {
return obj.Name
}
+func (obj *BaseRes) SetName(name string) {
+ obj.Name = name
+}
+
// return the kind of resource this is
func (obj *BaseRes) Kind() string {
return obj.kind
@@ -224,87 +227,6 @@ func (obj *BaseRes) SetState(state resState) {
obj.state = state
}
-// GetTimestamp returns the timestamp of a vertex
-func (obj *BaseRes) GetTimestamp() int64 {
- return obj.timestamp
-}
-
-// UpdateTimestamp updates the timestamp on a vertex and returns the new value
-func (obj *BaseRes) UpdateTimestamp() int64 {
- obj.timestamp = time.Now().UnixNano() // update
- return obj.timestamp
-}
-
-// can this element run right now?
-func (obj *BaseRes) OKTimestamp() bool {
- v := obj.GetVertex()
- g := v.GetGraph()
- // these are all the vertices pointing TO v, eg: ??? -> v
- for _, n := range g.IncomingGraphEdges(v) {
- // if the vertex has a greater timestamp than any pre-req (n)
- // then we can't run right now...
- // if they're equal (eg: on init of 0) then we also can't run
- // b/c we should let our pre-req's go first...
- x, y := obj.GetTimestamp(), n.Res.GetTimestamp()
- if DEBUG {
- log.Printf("%v[%v]: OKTimestamp: (%v) >= %v[%v](%v): !%v", obj.Kind(), obj.GetName(), x, n.Kind(), n.GetName(), y, x >= y)
- }
- if x >= y {
- return false
- }
- }
- return true
-}
-
-// notify nodes after me in the dependency graph that they need refreshing...
-// NOTE: this assumes that this can never fail or need to be rescheduled
-func (obj *BaseRes) Poke(activity bool) {
- v := obj.GetVertex()
- g := v.GetGraph()
- // these are all the vertices pointing AWAY FROM v, eg: v -> ???
- for _, n := range g.OutgoingGraphEdges(v) {
- // XXX: if we're in state event and haven't been cancelled by
- // apply, then we can cancel a poke to a child, right? XXX
- // XXX: if n.Res.GetState() != resStateEvent { // is this correct?
- if true { // XXX
- if DEBUG {
- log.Printf("%v[%v]: Poke: %v[%v]", v.Kind(), v.GetName(), n.Kind(), n.GetName())
- }
- n.SendEvent(eventPoke, false, activity) // XXX: can this be switched to sync?
- } else {
- if DEBUG {
- log.Printf("%v[%v]: Poke: %v[%v]: Skipped!", v.Kind(), v.GetName(), n.Kind(), n.GetName())
- }
- }
- }
-}
-
-// poke the pre-requisites that are stale and need to run before I can run...
-func (obj *BaseRes) BackPoke() {
- v := obj.GetVertex()
- g := v.GetGraph()
- // these are all the vertices pointing TO v, eg: ??? -> v
- for _, n := range g.IncomingGraphEdges(v) {
- x, y, s := obj.GetTimestamp(), n.Res.GetTimestamp(), n.Res.GetState()
- // if the parent timestamp needs poking AND it's not in state
- // resStateEvent, then poke it. If the parent is in resStateEvent it
- // means that an event is pending, so we'll be expecting a poke
- // back soon, so we can safely discard the extra parent poke...
- // TODO: implement a stateLT (less than) to tell if something
- // happens earlier in the state cycle and that doesn't wrap nil
- if x >= y && (s != resStateEvent && s != resStateCheckApply) {
- if DEBUG {
- log.Printf("%v[%v]: BackPoke: %v[%v]", v.Kind(), v.GetName(), n.Kind(), n.GetName())
- }
- n.SendEvent(eventBackPoke, false, false) // XXX: can this be switched to sync?
- } else {
- if DEBUG {
- log.Printf("%v[%v]: BackPoke: %v[%v]: Skipped!", v.Kind(), v.GetName(), n.Kind(), n.GetName())
- }
- }
- }
-}
-
// push an event into the message queue for a particular vertex
func (obj *BaseRes) SendEvent(event eventName, sync bool, activity bool) bool {
// TODO: isn't this race-y ?
@@ -394,50 +316,38 @@ func (obj *BaseRes) SetGroup(g []Res) {
obj.grouped = g
}
-// XXX: rename this function
-func Process(obj Res) {
- if DEBUG {
- log.Printf("%v[%v]: Process()", obj.Kind(), obj.GetName())
- }
- obj.SetState(resStateEvent)
- var ok = true
- var apply = false // did we run an apply?
- // is it okay to run dependency wise right now?
- // if not, that's okay because when the dependency runs, it will poke
- // us back and we will run if needed then!
- if obj.OKTimestamp() {
- if DEBUG {
- log.Printf("%v[%v]: OKTimestamp(%v)", obj.Kind(), obj.GetName(), obj.GetTimestamp())
- }
+func (obj *BaseRes) CollectPattern(pattern string) {
+ // XXX: default method is empty
+}
- obj.SetState(resStateCheckApply)
- // if this fails, don't UpdateTimestamp()
- stateok, err := obj.CheckApply(true)
- if stateok && err != nil { // should never return this way
- log.Fatalf("%v[%v]: CheckApply(): %t, %+v", obj.Kind(), obj.GetName(), stateok, err)
- }
- if DEBUG {
- log.Printf("%v[%v]: CheckApply(): %t, %v", obj.Kind(), obj.GetName(), stateok, err)
- }
+// ResToB64 encodes a resource to a base64 encoded string (after serialization)
+func ResToB64(res Res) (string, error) {
+ b := bytes.Buffer{}
+ e := gob.NewEncoder(&b)
+ err := e.Encode(&res) // pass with &
+ if err != nil {
+ return "", fmt.Errorf("Gob failed to encode: %v", err)
+ }
+ return base64.StdEncoding.EncodeToString(b.Bytes()), nil
+}
- if !stateok { // if state *was* not ok, we had to have apply'ed
- if err != nil { // error during check or apply
- ok = false
- } else {
- apply = true
- }
- }
+// B64ToRes decodes a resource from a base64 encoded string (after deserialization)
+func B64ToRes(str string) (Res, error) {
+ var output interface{}
+ bb, err := base64.StdEncoding.DecodeString(str)
+ if err != nil {
+ return nil, fmt.Errorf("Base64 failed to decode: %v", err)
+ }
+ b := bytes.NewBuffer(bb)
+ d := gob.NewDecoder(b)
+ err = d.Decode(&output) // pass with &
+ if err != nil {
+ return nil, fmt.Errorf("Gob failed to decode: %v", err)
+ }
+ res, ok := output.(Res)
+ if !ok {
+ return nil, fmt.Errorf("Output %v is not a Res", res)
- if ok {
- // update this timestamp *before* we poke or the poked
- // nodes might fail due to having a too old timestamp!
- obj.UpdateTimestamp() // this was touched...
- obj.SetState(resStatePoking) // can't cancel parent poke
- obj.Poke(apply)
- }
- // poke at our pre-req's instead since they need to refresh/run...
- } else {
- // only poke at the pre-req's that need to run
- go obj.BackPoke()
}
+ return res, nil
}
View
@@ -0,0 +1,105 @@
+// Mgmt
+// Copyright (C) 2013-2016+ James Shubin and the project contributors
+// Written by James Shubin <james@shubin.ca> and the project contributors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/gob"
+ "testing"
+)
+
+func TestMiscEncodeDecode1(t *testing.T) {
+ var err error
+ //gob.Register( &NoopRes{} ) // happens in noop.go : init()
+ //gob.Register( &FileRes{} ) // happens in file.go : init()
+ // ...
+
+ // encode
+ var input interface{} = &FileRes{}
+ b1 := bytes.Buffer{}
+ e := gob.NewEncoder(&b1)
+ err = e.Encode(&input) // pass with &
+ if err != nil {
+ t.Errorf("Gob failed to Encode: %v", err)
+ }
+ str := base64.StdEncoding.EncodeToString(b1.Bytes())
+
+ // decode
+ var output interface{}
+ bb, err := base64.StdEncoding.DecodeString(str)
+ if err != nil {
+ t.Errorf("Base64 failed to Decode: %v", err)
+ }
+ b2 := bytes.NewBuffer(bb)
+ d := gob.NewDecoder(b2)
+ err = d.Decode(&output) // pass with &
+ if err != nil {
+ t.Errorf("Gob failed to Decode: %v", err)
+ }
+
+ res1, ok := input.(Res)
+ if !ok {
+ t.Errorf("Input %v is not a Res", res1)
+ return
+ }
+ res2, ok := output.(Res)
+ if !ok {
+ t.Errorf("Output %v is not a Res", res2)
+ return
+ }
+ if !res1.Compare(res2) {
+ t.Error("The input and output Res values do not match!")
+ }
+}
+
+func TestMiscEncodeDecode2(t *testing.T) {
+ var err error
+ //gob.Register( &NoopRes{} ) // happens in noop.go : init()
+ //gob.Register( &FileRes{} ) // happens in file.go : init()
+ // ...
+
+ // encode
+ var input Res = &FileRes{}
+
+ b64, err := ResToB64(input)
+ if err != nil {
+ t.Errorf("Can't encode: %v", err)
+ return
+ }
+
+ output, err := B64ToRes(b64)
+ if err != nil {
+ t.Errorf("Can't decode: %v", err)
+ return
+ }
+
+ res1, ok := input.(Res)
+ if !ok {
+ t.Errorf("Input %v is not a Res", res1)
+ return
+ }
+ res2, ok := output.(Res)
+ if !ok {
+ t.Errorf("Output %v is not a Res", res2)
+ return
+ }
+ if !res1.Compare(res2) {
+ t.Error("The input and output Res values do not match!")
+ }
+}
View
11 svc.go
@@ -20,6 +20,7 @@
package main
import (
+ "encoding/gob"
"errors"
"fmt"
systemd "github.com/coreos/go-systemd/dbus" // change namespace
@@ -28,6 +29,10 @@ import (
"log"
)
+func init() {
+ gob.Register(&SvcRes{})
+}
+
type SvcRes struct {
BaseRes `yaml:",inline"`
State string `yaml:"state"` // state: running, stopped, undefined
@@ -62,7 +67,7 @@ func (obj *SvcRes) Validate() bool {
}
// Service watcher
-func (obj *SvcRes) Watch() {
+func (obj *SvcRes) Watch(processChan chan struct{}) {
if obj.IsWatching() {
return
}
@@ -189,7 +194,7 @@ func (obj *SvcRes) Watch() {
case err := <-subErrors:
obj.SetConvergedState(resConvergedNil) // XXX ?
- log.Println("error:", err)
+ log.Printf("error: %v", err)
log.Fatal(err)
//vertex.events <- fmt.Sprintf("svc: %v", "error") // XXX: how should we handle errors?
@@ -210,7 +215,7 @@ func (obj *SvcRes) Watch() {
dirty = false
obj.isStateOK = false // something made state dirty
}
- Process(obj) // XXX: rename this function
+ processChan <- struct{}{} // trigger process
}
}