agent: First pass at parsing service and check definition

This commit is contained in:
Armon Dadgar 2014-02-03 15:15:35 -08:00
parent db4bf65e2e
commit 1a5158212c
4 changed files with 173 additions and 39 deletions

View File

@ -69,18 +69,7 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
}
// Construct the health check
health := structs.HealthCheck{
Node: s.agent.config.NodeName,
CheckID: args.ID,
Name: args.Name,
Status: structs.HealthUnknown,
Notes: args.Notes,
}
// Fixup the ID if not given
if health.CheckID == "" && health.Name != "" {
health.CheckID = health.Name
}
health := args.HealthCheck(s.agent.config.NodeName)
// Verify the check type
chkType := &args.CheckType
@ -91,7 +80,7 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
}
// Add the check
return s.agent.AddCheck(&health, chkType), nil
return s.agent.AddCheck(health, chkType), nil
}
func (s *HTTPServer) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
@ -132,21 +121,11 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
return nil, nil
}
// Construct the health check
ns := structs.NodeService{
ID: args.ID,
Service: args.Name,
Tag: args.Tag,
Port: args.Port,
}
// Fixup the ID if not given
if ns.ID == "" && ns.Service != "" {
ns.ID = ns.Service
}
// Get the node service
ns := args.NodeService()
// Verify the check type
chkType := args.Check
chkType := args.CheckType()
if chkType != nil && !chkType.Valid() {
resp.WriteHeader(400)
resp.Write([]byte("Must provide TTL or Script and Interval!"))
@ -154,7 +133,7 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
}
// Add the check
return s.agent.AddService(&ns, chkType), nil
return s.agent.AddService(ns, chkType), nil
}
func (s *HTTPServer) AgentDeregisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) {

View File

@ -197,7 +197,25 @@ func (c *Command) Run(args []string) int {
defer c.httpServer.Shutdown()
}
// TODO: Register services/checks
// Register the services
for _, service := range config.Services {
ns := service.NodeService()
chkType := service.CheckType()
if err := c.agent.AddService(ns, chkType); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to register service '%s': %v", service.Name, err))
return 1
}
}
// Register the checks
for _, check := range config.Checks {
health := check.HealthCheck(config.NodeName)
chkType := &check.CheckType
if err := c.agent.AddCheck(health, chkType); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to register check '%s': %v %v", check.Name, err, check))
return 1
}
}
// Let the agent know we've finished registration
c.agent.StartSync()

View File

@ -93,6 +93,12 @@ type Config struct {
// the INT signal. Defaults false. This can be changed on reload.
SkipLeaveOnInt bool `mapstructure:"skip_leave_on_interrupt"`
// Checks holds the provided check definitions
Checks []*CheckDefinition
// Services holds the provided service definitions
Services []*ServiceDefinition
// ConsulConfig can either be provided or a default one created
ConsulConfig *consul.Config
}
@ -124,14 +130,29 @@ func (c *Config) EncryptBytes() ([]byte, error) {
// format and decodes it into a proper Config structure.
func DecodeConfig(r io.Reader) (*Config, error) {
var raw interface{}
var result Config
dec := json.NewDecoder(r)
if err := dec.Decode(&raw); err != nil {
return nil, err
}
// Check the result type
if obj, ok := raw.(map[string]interface{}); ok {
// Check for a "service" or "check" key, meaning
// this is actually a definition entry
if sub, ok := obj["service"]; ok {
service, err := DecodeServiceDefinition(sub)
result.Services = append(result.Services, service)
return &result, err
} else if sub, ok := obj["check"]; ok {
check, err := DecodeCheckDefinition(sub)
result.Checks = append(result.Checks, check)
return &result, err
}
}
// Decode
var md mapstructure.Metadata
var result Config
msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Metadata: &md,
Result: &result,
@ -147,6 +168,85 @@ func DecodeConfig(r io.Reader) (*Config, error) {
return &result, nil
}
// DecodeServiceDefinition is used to decode a service definition
func DecodeServiceDefinition(raw interface{}) (*ServiceDefinition, error) {
var sub interface{}
rawMap, ok := raw.(map[string]interface{})
if !ok {
goto AFTER_FIX
}
sub, ok = rawMap["check"]
if !ok {
goto AFTER_FIX
}
if err := FixupCheckType(sub); err != nil {
return nil, err
}
AFTER_FIX:
var md mapstructure.Metadata
var result ServiceDefinition
msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Metadata: &md,
Result: &result,
})
if err != nil {
return nil, err
}
if err := msdec.Decode(raw); err != nil {
return nil, err
}
return &result, nil
}
func FixupCheckType(raw interface{}) error {
// Handle decoding of time durations
rawMap, ok := raw.(map[string]interface{})
if !ok {
return nil
}
if ttl, ok := rawMap["ttl"]; ok {
ttlS, ok := ttl.(string)
if ok {
if dur, err := time.ParseDuration(ttlS); err != nil {
return err
} else {
rawMap["ttl"] = dur
}
}
}
if interval, ok := rawMap["interval"]; ok {
intervalS, ok := interval.(string)
if ok {
if dur, err := time.ParseDuration(intervalS); err != nil {
return err
} else {
rawMap["interval"] = dur
}
}
}
return nil
}
// DecodeCheckDefinition is used to decode a check definition
func DecodeCheckDefinition(raw interface{}) (*CheckDefinition, error) {
if err := FixupCheckType(raw); err != nil {
return nil, err
}
var md mapstructure.Metadata
var result CheckDefinition
msdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Metadata: &md,
Result: &result,
})
if err != nil {
return nil, err
}
if err := msdec.Decode(raw); err != nil {
return nil, err
}
return &result, nil
}
// MergeConfig merges two configurations together to make a single new
// configuration.
func MergeConfig(a, b *Config) *Config {
@ -210,6 +310,12 @@ func MergeConfig(a, b *Config) *Config {
if b.SkipLeaveOnInt == true {
result.SkipLeaveOnInt = true
}
if b.Checks != nil {
result.Checks = append(result.Checks, b.Checks...)
}
if b.Services != nil {
result.Services = append(result.Services, b.Services...)
}
return &result
}

View File

@ -1,25 +1,56 @@
package agent
import (
"github.com/hashicorp/consul/consul/structs"
)
// ServiceDefinition is used to JSON decode the Service definitions
type ServiceDefinition struct {
ID string
Name string
Tag string
Port int
Check *CheckType
Check CheckType
}
func (s *ServiceDefinition) NodeService() *structs.NodeService {
ns := &structs.NodeService{
ID: s.ID,
Service: s.Name,
Tag: s.Tag,
Port: s.Port,
}
if ns.ID == "" && ns.Service != "" {
ns.ID = ns.Service
}
return ns
}
func (s *ServiceDefinition) CheckType() *CheckType {
if s.Check.Script == "" && s.Check.Interval == 0 && s.Check.TTL == 0 {
return nil
}
return &s.Check
}
// ChecKDefinition is used to JSON decode the Check definitions
type CheckDefinition struct {
ID string
Name string
Notes string
CheckType
ID string
Name string
Notes string
CheckType `mapstructure:",squash"`
}
// UnionDefinition is used to decode when we don't know if
// we are being given a ServiceDefinition or a CheckDefinition
type UnionDefinition struct {
Service *ServiceDefinition
Check *CheckDefinition
func (c *CheckDefinition) HealthCheck(node string) *structs.HealthCheck {
health := &structs.HealthCheck{
Node: node,
CheckID: c.ID,
Name: c.Name,
Status: structs.HealthUnknown,
Notes: c.Notes,
}
if health.CheckID == "" && health.Name != "" {
health.CheckID = health.Name
}
return health
}