main: Libify mgmt with a golang API
This is an initial implementation of a possible golang API. In this particular version, the *gconfig.GraphConfig data structures are emitted, instead of possibly building a pgraph. As long as we can represent any local graph as the data structure, then this is fine! Is there a way to merge the gconfig Vertex and the pgraph Vertex?
This commit is contained in:
110
examples/lib/libmgmt1.go
Normal file
110
examples/lib/libmgmt1.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// libmgmt example
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/purpleidea/mgmt/gconfig"
|
||||||
|
mgmt "github.com/purpleidea/mgmt/mgmtmain"
|
||||||
|
"github.com/purpleidea/mgmt/resources"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateGraphConfig() *gconfig.GraphConfig {
|
||||||
|
|
||||||
|
n1, err := resources.NewNoopRes("noop1")
|
||||||
|
if err != nil {
|
||||||
|
return nil // error
|
||||||
|
}
|
||||||
|
|
||||||
|
gc := &gconfig.GraphConfig{
|
||||||
|
Graph: "libmgmt",
|
||||||
|
Resources: gconfig.Resources{ // must redefine anonymous struct :(
|
||||||
|
// in alphabetical order
|
||||||
|
Exec: []*resources.ExecRes{},
|
||||||
|
File: []*resources.FileRes{},
|
||||||
|
Msg: []*resources.MsgRes{},
|
||||||
|
Noop: []*resources.NoopRes{n1},
|
||||||
|
Pkg: []*resources.PkgRes{},
|
||||||
|
Svc: []*resources.SvcRes{},
|
||||||
|
Timer: []*resources.TimerRes{},
|
||||||
|
Virt: []*resources.VirtRes{},
|
||||||
|
},
|
||||||
|
//Collector: []collectorResConfig{},
|
||||||
|
//Edges: []Edge{},
|
||||||
|
Comment: "comment!",
|
||||||
|
//Hostname: "???",
|
||||||
|
//Remote: "???",
|
||||||
|
}
|
||||||
|
return gc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs an embedded mgmt server.
|
||||||
|
func Run() error {
|
||||||
|
|
||||||
|
obj := &mgmt.Main{}
|
||||||
|
obj.Program = "mgmtlib" // TODO: set on compilation
|
||||||
|
obj.Version = "0.0.1" // TODO: set on compilation
|
||||||
|
obj.TmpPrefix = true
|
||||||
|
obj.IdealClusterSize = -1
|
||||||
|
obj.ConvergedTimeout = -1
|
||||||
|
obj.Noop = true
|
||||||
|
|
||||||
|
obj.GAPI = generateGraphConfig // graph API function
|
||||||
|
|
||||||
|
if err := obj.Init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
log.Printf("Generating new graph...")
|
||||||
|
obj.Switch(generateGraphConfig) // pass in function to run...
|
||||||
|
|
||||||
|
time.Sleep(15 * time.Second) // XXX: arbitrarily change graph every 30 seconds
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// install the exit signal handler
|
||||||
|
exit := make(chan struct{})
|
||||||
|
defer close(exit)
|
||||||
|
go func() {
|
||||||
|
signals := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||||
|
//signal.Notify(signals, os.Kill) // catch signals
|
||||||
|
signal.Notify(signals, syscall.SIGTERM)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sig := <-signals: // any signal will do
|
||||||
|
if sig == os.Interrupt {
|
||||||
|
log.Println("Interrupted by ^C")
|
||||||
|
obj.Exit(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Interrupted by signal")
|
||||||
|
obj.Exit(fmt.Errorf("Killed by %v", sig))
|
||||||
|
return
|
||||||
|
case <-exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := obj.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Hello!")
|
||||||
|
if err := Run(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Goodbye!")
|
||||||
|
}
|
||||||
@@ -41,33 +41,38 @@ type collectorResConfig struct {
|
|||||||
Pattern string `yaml:"pattern"` // XXX: Not Implemented
|
Pattern string `yaml:"pattern"` // XXX: Not Implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
type vertexConfig struct {
|
// Vertex is the data structure of a vertex.
|
||||||
|
type Vertex struct {
|
||||||
Kind string `yaml:"kind"`
|
Kind string `yaml:"kind"`
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type edgeConfig struct {
|
// Edge is the data structure of an edge.
|
||||||
Name string `yaml:"name"`
|
type Edge struct {
|
||||||
From vertexConfig `yaml:"from"`
|
Name string `yaml:"name"`
|
||||||
To vertexConfig `yaml:"to"`
|
From Vertex `yaml:"from"`
|
||||||
|
To Vertex `yaml:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources is the data structure of the set of resources.
|
||||||
|
type Resources struct {
|
||||||
|
// in alphabetical order
|
||||||
|
Exec []*resources.ExecRes `yaml:"exec"`
|
||||||
|
File []*resources.FileRes `yaml:"file"`
|
||||||
|
Msg []*resources.MsgRes `yaml:"msg"`
|
||||||
|
Noop []*resources.NoopRes `yaml:"noop"`
|
||||||
|
Pkg []*resources.PkgRes `yaml:"pkg"`
|
||||||
|
Svc []*resources.SvcRes `yaml:"svc"`
|
||||||
|
Timer []*resources.TimerRes `yaml:"timer"`
|
||||||
|
Virt []*resources.VirtRes `yaml:"virt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphConfig is the data structure that describes a single graph to run.
|
// GraphConfig is the data structure that describes a single graph to run.
|
||||||
type GraphConfig struct {
|
type GraphConfig struct {
|
||||||
Graph string `yaml:"graph"`
|
Graph string `yaml:"graph"`
|
||||||
Resources struct {
|
Resources Resources `yaml:"resources"`
|
||||||
// in alphabetical order
|
|
||||||
Exec []*resources.ExecRes `yaml:"exec"`
|
|
||||||
File []*resources.FileRes `yaml:"file"`
|
|
||||||
Msg []*resources.MsgRes `yaml:"msg"`
|
|
||||||
Noop []*resources.NoopRes `yaml:"noop"`
|
|
||||||
Pkg []*resources.PkgRes `yaml:"pkg"`
|
|
||||||
Svc []*resources.SvcRes `yaml:"svc"`
|
|
||||||
Timer []*resources.TimerRes `yaml:"timer"`
|
|
||||||
Virt []*resources.VirtRes `yaml:"virt"`
|
|
||||||
} `yaml:"resources"`
|
|
||||||
Collector []collectorResConfig `yaml:"collect"`
|
Collector []collectorResConfig `yaml:"collect"`
|
||||||
Edges []edgeConfig `yaml:"edges"`
|
Edges []Edge `yaml:"edges"`
|
||||||
Comment string `yaml:"comment"`
|
Comment string `yaml:"comment"`
|
||||||
Hostname string `yaml:"hostname"` // uuid for the host
|
Hostname string `yaml:"hostname"` // uuid for the host
|
||||||
Remote string `yaml:"remote"`
|
Remote string `yaml:"remote"`
|
||||||
@@ -152,9 +157,6 @@ func (c *GraphConfig) NewGraphFromConfig(g *pgraph.Graph, embdEtcd *etcd.EmbdEtc
|
|||||||
// XXX: should we export based on a @@ prefix, or a metaparam
|
// XXX: should we export based on a @@ prefix, or a metaparam
|
||||||
// like exported => true || exported => (host pattern)||(other pattern?)
|
// like exported => true || exported => (host pattern)||(other pattern?)
|
||||||
if !strings.HasPrefix(res.GetName(), "@@") { // not exported resource
|
if !strings.HasPrefix(res.GetName(), "@@") { // not exported resource
|
||||||
// XXX: we don't have a way of knowing if any of the
|
|
||||||
// metaparams are undefined, and as a result to set the
|
|
||||||
// defaults that we want! I hate the go yaml parser!!!
|
|
||||||
v := graph.GetVertexMatch(res)
|
v := graph.GetVertexMatch(res)
|
||||||
if v == nil { // no match found
|
if v == nil { // no match found
|
||||||
res.Init()
|
res.Init()
|
||||||
|
|||||||
@@ -49,10 +49,11 @@ type Main struct {
|
|||||||
|
|
||||||
Hostname *string // hostname to use; nil if undefined
|
Hostname *string // hostname to use; nil if undefined
|
||||||
|
|
||||||
File *string // graph file to run; nil if undefined
|
File *string // graph file to run; nil if undefined
|
||||||
Puppet *string // puppet mode to run; nil if undefined
|
Puppet *string // puppet mode to run; nil if undefined
|
||||||
PuppetConf string // the path to an alternate puppet.conf file
|
PuppetConf string // the path to an alternate puppet.conf file
|
||||||
Remotes []string // list of remote graph definitions to run
|
GAPI func() *gconfig.GraphConfig // graph API; nil if undefined
|
||||||
|
Remotes []string // list of remote graph definitions to run
|
||||||
|
|
||||||
NoWatch bool // do not update graph on watched graph definition file changes
|
NoWatch bool // do not update graph on watched graph definition file changes
|
||||||
Noop bool // globally force all resources into no-op mode
|
Noop bool // globally force all resources into no-op mode
|
||||||
@@ -80,12 +81,18 @@ type Main struct {
|
|||||||
clientURLs etcdtypes.URLs // processed client urls value
|
clientURLs etcdtypes.URLs // processed client urls value
|
||||||
serverURLs etcdtypes.URLs // processed server urls value
|
serverURLs etcdtypes.URLs // processed server urls value
|
||||||
idealClusterSize uint16 // processed ideal cluster size value
|
idealClusterSize uint16 // processed ideal cluster size value
|
||||||
exit chan error // exit signal
|
|
||||||
|
exit chan error // exit signal
|
||||||
|
switchChan chan func() *gconfig.GraphConfig // graph switches
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the main struct after it performs some validation.
|
// Init initializes the main struct after it performs some validation.
|
||||||
func (obj *Main) Init() error {
|
func (obj *Main) Init() error {
|
||||||
|
|
||||||
|
if obj.Program == "" || obj.Version == "" {
|
||||||
|
return fmt.Errorf("You must set the Program and Version strings!")
|
||||||
|
}
|
||||||
|
|
||||||
if obj.Prefix != nil && obj.TmpPrefix {
|
if obj.Prefix != nil && obj.TmpPrefix {
|
||||||
return fmt.Errorf("Choosing a prefix and the request for a tmp prefix is illogical!")
|
return fmt.Errorf("Choosing a prefix and the request for a tmp prefix is illogical!")
|
||||||
}
|
}
|
||||||
@@ -145,6 +152,7 @@ func (obj *Main) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
obj.exit = make(chan error)
|
obj.exit = make(chan error)
|
||||||
|
obj.switchChan = make(chan func() *gconfig.GraphConfig)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +161,14 @@ func (obj *Main) Exit(err error) {
|
|||||||
obj.exit <- err // trigger an exit!
|
obj.exit <- err // trigger an exit!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch causes mgmt try to switch the currently running graph to a new one.
|
||||||
|
// The function passed in will usually be called immediately, but it can also
|
||||||
|
// happen after a delay, and more often than this Switch function is called!
|
||||||
|
func (obj *Main) Switch(f func() *gconfig.GraphConfig) {
|
||||||
|
obj.switchChan <- f
|
||||||
|
// TODO: should we get an ACK() and pass back a return value ?
|
||||||
|
}
|
||||||
|
|
||||||
// Run is the main execution entrypoint to run mgmt.
|
// Run is the main execution entrypoint to run mgmt.
|
||||||
func (obj *Main) Run() error {
|
func (obj *Main) Run() error {
|
||||||
|
|
||||||
@@ -273,13 +289,14 @@ func (obj *Main) Run() error {
|
|||||||
go func() {
|
go func() {
|
||||||
startchan := make(chan struct{}) // start signal
|
startchan := make(chan struct{}) // start signal
|
||||||
go func() { startchan <- struct{}{} }()
|
go func() { startchan <- struct{}{} }()
|
||||||
var configchan chan error
|
var configChan chan error
|
||||||
var puppetchan <-chan time.Time
|
var puppetChan <-chan time.Time
|
||||||
|
var customFunc = obj.GAPI // default
|
||||||
if !obj.NoWatch && obj.File != nil {
|
if !obj.NoWatch && obj.File != nil {
|
||||||
configchan = recwatch.ConfigWatch(*obj.File)
|
configChan = recwatch.ConfigWatch(*obj.File)
|
||||||
} else if obj.Puppet != nil {
|
} else if obj.Puppet != nil {
|
||||||
interval := puppet.PuppetInterval(obj.PuppetConf)
|
interval := puppet.PuppetInterval(obj.PuppetConf)
|
||||||
puppetchan = time.Tick(time.Duration(interval) * time.Second)
|
puppetChan = time.Tick(time.Duration(interval) * time.Second)
|
||||||
}
|
}
|
||||||
log.Println("Etcd: Starting...")
|
log.Println("Etcd: Starting...")
|
||||||
etcdchan := etcd.EtcdWatch(EmbdEtcd)
|
etcdchan := etcd.EtcdWatch(EmbdEtcd)
|
||||||
@@ -296,10 +313,14 @@ func (obj *Main) Run() error {
|
|||||||
}
|
}
|
||||||
// everything else passes through to cause a compile!
|
// everything else passes through to cause a compile!
|
||||||
|
|
||||||
case <-puppetchan:
|
case customFunc = <-obj.switchChan:
|
||||||
|
// handle a graph switch with a new custom function
|
||||||
|
obj.GAPI = customFunc
|
||||||
|
|
||||||
|
case <-puppetChan:
|
||||||
// nothing, just go on
|
// nothing, just go on
|
||||||
|
|
||||||
case e := <-configchan:
|
case e := <-configChan:
|
||||||
if obj.NoWatch {
|
if obj.NoWatch {
|
||||||
continue // not ready to read config
|
continue // not ready to read config
|
||||||
}
|
}
|
||||||
@@ -319,7 +340,10 @@ func (obj *Main) Run() error {
|
|||||||
config = gconfig.ParseConfigFromFile(*obj.File)
|
config = gconfig.ParseConfigFromFile(*obj.File)
|
||||||
} else if obj.Puppet != nil {
|
} else if obj.Puppet != nil {
|
||||||
config = puppet.ParseConfigFromPuppet(*obj.Puppet, obj.PuppetConf)
|
config = puppet.ParseConfigFromPuppet(*obj.Puppet, obj.PuppetConf)
|
||||||
|
} else if obj.GAPI != nil {
|
||||||
|
config = obj.GAPI()
|
||||||
}
|
}
|
||||||
|
|
||||||
if config == nil {
|
if config == nil {
|
||||||
log.Printf("Config: Parse failure")
|
log.Printf("Config: Parse failure")
|
||||||
continue
|
continue
|
||||||
@@ -337,7 +361,7 @@ func (obj *Main) Run() error {
|
|||||||
G.Pause() // sync
|
G.Pause() // sync
|
||||||
}
|
}
|
||||||
|
|
||||||
// build graph from yaml file on events (eg: from etcd)
|
// build graph from config struct on events, eg: etcd...
|
||||||
// we need the vertices to be paused to work on them
|
// we need the vertices to be paused to work on them
|
||||||
if newFullgraph, err := config.NewGraphFromConfig(fullGraph, EmbdEtcd, obj.Noop); err == nil { // keep references to all original elements
|
if newFullgraph, err := config.NewGraphFromConfig(fullGraph, EmbdEtcd, obj.Noop); err == nil { // keep references to all original elements
|
||||||
fullGraph = newFullgraph
|
fullGraph = newFullgraph
|
||||||
@@ -420,7 +444,7 @@ func (obj *Main) Run() error {
|
|||||||
// wait for etcd to be running before we remote in, which we do above!
|
// wait for etcd to be running before we remote in, which we do above!
|
||||||
go remotes.Run()
|
go remotes.Run()
|
||||||
|
|
||||||
if obj.File == nil && obj.Puppet == nil {
|
if obj.File == nil && obj.Puppet == nil && obj.GAPI == nil {
|
||||||
converger.Start() // better start this for empty graphs
|
converger.Start() // better start this for empty graphs
|
||||||
}
|
}
|
||||||
log.Println("Main: Running...")
|
log.Println("Main: Running...")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ done < "$FILE"
|
|||||||
cd "${ROOT}"
|
cd "${ROOT}"
|
||||||
|
|
||||||
find_files() {
|
find_files() {
|
||||||
git ls-files | grep '\.go$'
|
git ls-files | grep '\.go$' | grep -v '^examples/'
|
||||||
}
|
}
|
||||||
|
|
||||||
bad_files=$(
|
bad_files=$(
|
||||||
|
|||||||
Reference in New Issue
Block a user