diff --git a/Gopkg.lock b/Gopkg.lock index a9dbf6b..cbb2234 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -7,12 +7,6 @@ revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" -[[projects]] - branch = "master" - name = "github.com/jasonlvhit/gocron" - packages = ["."] - revision = "54194c9749d48b073c9aeb2c787b6882e95dd0cd" - [[projects]] name = "github.com/pmezard/go-difflib" packages = ["difflib"] @@ -52,6 +46,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "b9da9d3b3191ba4c945dfa6abbe96970ac2b93586b1bfdc0ae04a01578e447c5" + inputs-digest = "645c0b81f09f2272ad583d044face9056cd37b1975c7cf84f6506c91cbebea9f" solver-name = "gps-cdcl" solver-version = 1 diff --git a/config/config.go b/config/config.go index bfdab02..0fe17ae 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "sync" "time" "github.com/sirupsen/logrus" @@ -23,8 +24,37 @@ var ( DisableTimestamp: false, TimestampFormat: time.RFC3339, } + + globalConfig *Config + + cfgLock = sync.RWMutex{} ) +// GetConfig will return a global instance of the configuration if it has ever been loaded through +// LoadConfig. It can also return the default config if LoadConfig has never been called. +func GetConfig() *Config { + cfgLock.RLock() + defer cfgLock.RUnlock() + + if globalConfig == nil { + return &DefaultConfig + } + + return globalConfig +} + +// GetEmailConfig will the EmailConfig portion of the global config object. +func GetEmailConfig() *EmailConfig { + cfgLock.RLock() + defer cfgLock.RUnlock() + + if globalConfig == nil { + return &DefaultConfig.EmailConfig + } + + return &globalConfig.EmailConfig +} + // LoadConfig loads the configuration based on the input file. Errors can occur for various // i/o or marshalling related reasons. Defaults will be returned for primitive types, like strings. func LoadConfig(filename string) (*Config, error) { @@ -48,6 +78,10 @@ func LoadConfig(filename string) (*Config, error) { config.loggers = make(map[string]*logrus.Logger) } + cfgLock.Lock() + defer cfgLock.Unlock() + + globalConfig = config return config, nil } @@ -69,10 +103,9 @@ func (c *Config) GetLogger(name string) *logrus.Logger { var logger *logrus.Logger var found bool - c.logLock.Lock() //locking strategy probably a bit aggressive - defer c.logLock.Unlock() - if logger, found = c.loggers[name]; !found { + c.modLock.Lock() //locking strategy probably a bit aggressive + defer c.modLock.Unlock() logger := logrus.New() logger.Formatter = formatter diff --git a/config/testdata/file_with_loggers.yml b/config/testdata/file_with_loggers.yml index 6a760d0..e696bd3 100644 --- a/config/testdata/file_with_loggers.yml +++ b/config/testdata/file_with_loggers.yml @@ -1,8 +1,8 @@ storage: "file" -fileconfig: +file_config: directory: "/tmp/deadline" -serverconfig: +server_config: port: "8082" logs: manager: "debug" diff --git a/config/testdata/goodfile.yml b/config/testdata/goodfile.yml index 11442ea..55935f8 100644 --- a/config/testdata/goodfile.yml +++ b/config/testdata/goodfile.yml @@ -1,8 +1,8 @@ storage: "file" -fileconfig: +file_config: directory: "../server/" -dbconfig: +db_config: connection_string: "N/A" -serverconfig: +server_config: port: "8081" diff --git a/config/types.go b/config/types.go index 7d00920..c79dc87 100644 --- a/config/types.go +++ b/config/types.go @@ -27,15 +27,15 @@ const ( // Config represents the configuration struct for the entire deadline application type Config struct { - FileConfig FileConfig `yaml:"fileconfig"` - DBConfig DBConfig `yaml:"dbconfig"` + FileConfig FileConfig `yaml:"file_config"` + DBConfig DBConfig `yaml:"db_config"` Storage string `yaml:"storage"` EvalTime string `yaml:"eval_timing"` - Server ServerConfig `yaml:"serverconfig"` - EmailConfig EmailConfig `yaml:"emailconfig"` + Server ServerConfig `yaml:"server_config"` + EmailConfig EmailConfig `yaml:"email_config"` Logconfig map[string]string `yaml:"logs"` loggers map[string]*logrus.Logger - logLock sync.Mutex + modLock sync.RWMutex } // FileConfig is the configuration type for file storage diff --git a/notifier/notifier.go b/notifier/notifier.go index 6ac67d9..4646f2f 100644 --- a/notifier/notifier.go +++ b/notifier/notifier.go @@ -1,29 +1,24 @@ package notifier -func (w Webhook) Send(msg string) { - // var str string - // str = msg - // jv, err := json.Marshal(str) - // if err != nil { - // common.CheckError(err) - // } - // _ , err = http.Post(w.Addr,"application/json", bytes.NewBuffer(jv)) - // common.CheckError(err) +import ( + "sync" -} + "github.com/att/deadline/config" +) + +var once sync.Once +var notifier *Notifier -// func NewNotifyHandler(handlerType string, addr string) NotifyHandler { +// GetInstance gets the current running instance of a Notifier class +func GetInstance(cfg *config.Config) *Notifier { + once.Do(func() { + notifier = &Notifier{} + }) -// switch handlerType { -// case "WEBHOOK": + return notifier +} -// w := &Webhook{ -// Addr: addr, -// } -// w.TH.Name = handlerType +// Notify is the main API to notify some entity with a message of some kind +func (notifier *Notifier) Notify(notification Notification) { -// return w -// } -// common.Info.Println("Did not give a valid handler.") -// return &Webhook{} -// } +} diff --git a/notifier/types.go b/notifier/types.go index 6b99653..994ff77 100644 --- a/notifier/types.go +++ b/notifier/types.go @@ -1,18 +1,6 @@ -package notifier -import ( - "net/http" -) +package notifier -type NotifyHandler interface { - Send(string) +type Notifier struct { } -type TypeHandler struct { - Name string -} -type Webhook struct { - TH TypeHandler - Addr string - Handler http.Handler - -} +type Notification map[string]string diff --git a/schedule/handlers.go b/schedule/handlers.go index 8f2d0b9..4cc0d63 100644 --- a/schedule/handlers.go +++ b/schedule/handlers.go @@ -1,16 +1,21 @@ package schedule // Handle is the email handler's implementation of the Handler interface. -func (h EmailHandlerNode) Handle(ctx *Context) { +func (handler EmailHandlerNode) Handle(ctx *Context) { + // cfg := config.GetEmailConfig() + + // var client *smtp.Client } // Name is the email handlers implementation of the Node interface. -func (h EmailHandlerNode) Name() string { - return h.name +func (handler EmailHandlerNode) Name() string { + return handler.name } -// Next defines what's after this node completes. -func (h EmailHandlerNode) Next() ([]*NodeInstance, *Context) { - return nil, nil +// Next for this type is simply defined. There's no logic computed. It +// return nil context. +func (handler EmailHandlerNode) Next() ([]*NodeInstance, *Context) { + var ret []*NodeInstance + return append(ret, handler.to), nil } diff --git a/schedule/node.go b/schedule/node.go index 946a828..ed2fc34 100644 --- a/schedule/node.go +++ b/schedule/node.go @@ -24,36 +24,39 @@ func (node *EventNode) Next() ([]*NodeInstance, *Context) { next := make([]*NodeInstance, 0) var successful bool var failureReason string - var ctx *Context + var ctx = newContext() var pastDue = time.Now().Unix() > node.constraints.ReceiveBy var received = node.event != nil if !received && !pastDue { - return next, nil + return nil, &ctx } if received { successful, failureReason = node.event.IsSuccessful(node.constraints) } else if pastDue { // not received and past due + + // logline here for debugging bc we can't currently see the schedule state through + // the api. it will probably be redundant/much less useful when we can. log.WithFields(logrus.Fields{ "node": node.name, "reason": "event never arrived", "recieve-by": time.Unix(node.constraints.ReceiveBy, 0).Format(time.RFC3339), }).Debug("node failed") - ctx = newContext(node.name, EventNeverArrived) + ctx = newFailedContext(node.name, EventNeverArrived) next = append(next, node.errorTo) } if successful { next = append(next, node.okTo) } else { - ctx = newContext(node.name, failureReason) + ctx = newFailedContext(node.name, failureReason) next = append(next, node.errorTo) } - return next, ctx + return next, &ctx } // AddEvent adds an event to the EventNode @@ -66,7 +69,7 @@ func (node *EndNode) Name() string { return node.name } -// Next for an end node returns nil for both array and error +// Next for an end node returns nil for both parameters func (node *EndNode) Next() ([]*NodeInstance, *Context) { return nil, nil } @@ -83,10 +86,20 @@ func (node *StartNode) Next() ([]*NodeInstance, *Context) { return next, nil } -func newContext(name string, reason string) *Context { - return &Context{ - FailedNoded: name, - FailureReason: reason, - FailureTime: time.Now(), +func newFailedContext(name string, reason string) Context { + return Context{ + Successful: false, + FailureContext: &FailureContext{ + Node: name, + Reason: reason, + Time: time.Now(), + }, + } +} + +func newContext() Context { + return Context{ + Successful: true, + FailureContext: nil, } } diff --git a/schedule/node_test.go b/schedule/node_test.go index 92ecfb0..d8c2fa7 100644 --- a/schedule/node_test.go +++ b/schedule/node_test.go @@ -57,12 +57,14 @@ func TestEventOKTo(test *testing.T) { } else { node.AddEvent(&e) - next, err := node.Next() + next, ctx := node.Next() - assert.Nil(test, err, "") + assert.NotNil(test, ctx, "") + assert.Equal(test, true, ctx.Successful) assert.Equal(test, len(next), 1) assert.Equal(test, next[0], secondEventNode) } + } func TestEventErrorTo(test *testing.T) { @@ -80,6 +82,6 @@ func TestEventErrorTo(test *testing.T) { assert.Equal(test, len(next), 1) assert.Equal(test, next[0], endNode) assert.NotNil(test, ctx) - assert.Equal(test, com.LateEvent, ctx.FailureReason) + assert.Equal(test, com.LateEvent, ctx.FailureContext.Reason) } } diff --git a/schedule/schedule.go b/schedule/schedule.go index cd49f09..55e0e74 100644 --- a/schedule/schedule.go +++ b/schedule/schedule.go @@ -10,30 +10,30 @@ import ( // Evaluate the schedule completely. func (schedule *Schedule) Evaluate() State { - schedule.walk(schedule.Start, nil) + schedule.walk(schedule.Start) return schedule.state } -func (schedule *Schedule) walk(instance *NodeInstance, context *Context) { +func (schedule *Schedule) walk(instance *NodeInstance) { switch node := instance.value.(type) { case *StartNode: - schedule.walk(node.to, nil) + schedule.walk(node.to) case *EventNode: - next, ctx := node.Next() + next, _ := node.Next() if next != nil && len(next) > 0 { if next[0] == node.errorTo { schedule.state = Failed } - schedule.walk(next[0], ctx) + schedule.walk(next[0]) } case *EmailHandlerNode: - go node.Handle(context) - schedule.walk(node.to, nil) + //go node.Handle(context) + schedule.walk(node.to) case *EndNode: if schedule.state != Failed { schedule.state = Ended @@ -306,9 +306,6 @@ func checkEmptyFields(blueprint *com.ScheduleBlueprint) error { } } -// func contextFromNode(node *EventNode) Context { -// ctx := Context{ -// FailedNoded: node.name, -// FailureTime: time.Now(), -// } -// } +func addFailureContext(ctx FailureContext) { + +} diff --git a/schedule/types.go b/schedule/types.go index 196a9b6..6fead97 100644 --- a/schedule/types.go +++ b/schedule/types.go @@ -43,7 +43,7 @@ var ( "hourly": "1h", } - // StateStringLookup is a lookuptable for the State iota which + // StateStringLookup is a lookuptable for the State iota StateStringLookup = map[State]string{ Running: "running", Ended: "ended", @@ -59,7 +59,8 @@ func (state State) String() string { return "unknown" } -// Schedule is the type that represents a running schedule +// Schedule is the workhorse struct of this application. It *is* the runtime representation +// of the schedule that is being evaluated. type Schedule struct { Name string `json:"name,attr,omitempty" db:"name"` StartTime time.Time @@ -83,11 +84,17 @@ type Manager struct { evalTicker *time.Ticker } -// Context is the way to pass state between different nodes in a schedule +// Context is the way to pass state of what occurs in a node back to the caller. type Context struct { - FailedNoded string - FailureReason string - FailureTime time.Time + Successful bool + FailureContext *FailureContext +} + +// FailureContext describes a node failure +type FailureContext struct { + Node string + Reason string + Time time.Time } // Node is the interface for nodes in the schedules and provides ways to see what they are and how they connect