gitlab.com/creichlin/pentaconta@v0.1.1-0.20170921154330-ffd669064217/declaration/declaration.go (about)

     1  package declaration
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"github.com/creichlin/goschema"
     8  	"github.com/creichlin/gutil"
     9  	"github.com/mitchellh/mapstructure"
    10  )
    11  
    12  type Service struct {
    13  	Executable string
    14  	WorkingDir string `mapstructure:"working-dir"`
    15  	Arguments  []string
    16  }
    17  
    18  type FSTrigger struct {
    19  	Path     string
    20  	Services []string
    21  	Signal   string
    22  }
    23  
    24  type Stats struct {
    25  	File    string
    26  	Seconds int
    27  }
    28  
    29  type Root struct {
    30  	Stats      *Stats
    31  	Services   map[string]*Service
    32  	FSTriggers map[string]*FSTrigger `mapstructure:"fs-triggers"`
    33  }
    34  
    35  func Doc() string {
    36  	return goschema.Doc(buildSchema())
    37  }
    38  
    39  func Parse(data interface{}) (*Root, error) {
    40  	schema := buildSchema()
    41  	errors := goschema.ValidateGO(schema, data)
    42  	if errors.Has() {
    43  		return nil, errors
    44  	}
    45  
    46  	root := &Root{}
    47  	err := mapstructure.Decode(data, root)
    48  	errors = validate(root)
    49  	if errors.Has() {
    50  		return nil, errors
    51  	}
    52  	return root, err
    53  }
    54  
    55  func validate(r *Root) *gutil.ErrorCollector {
    56  	ec := gutil.NewErrorCollector()
    57  
    58  	keys := []string{}
    59  	for name, _ := range r.FSTriggers {
    60  		keys = append(keys, name)
    61  	}
    62  	sort.Strings(keys)
    63  
    64  	for _, name := range keys {
    65  		for _, serviceName := range r.FSTriggers[name].Services {
    66  			if _, contains := r.Services[serviceName]; !contains {
    67  				ec.Add(fmt.Errorf("fs-trigger %v has unknown service %v as target", name, serviceName))
    68  			}
    69  		}
    70  	}
    71  
    72  	return ec
    73  }
    74  
    75  func buildSchema() goschema.Type {
    76  	return goschema.NewObjectType("Pentaconta service declaration", func(o goschema.ObjectType) {
    77  		o.Optional("services").Map(func(m goschema.MapType) {
    78  			m.Object("Services start executables and restart them when terminated/crashed", func(o goschema.ObjectType) {
    79  				o.Attribute("executable").String(
    80  					"path or name, if not absolute will use PATH env var to find binary")
    81  				o.Optional("working-dir").String(
    82  					"path, absolute or relative to current working dir")
    83  				o.Optional("arguments").List(func(l goschema.ListType) {
    84  					l.String("arguments")
    85  				})
    86  			})
    87  		})
    88  		o.Optional("fs-triggers").Map(func(m goschema.MapType) {
    89  			m.Object(
    90  				"filesystem watchers trigger termination (and implicit restart) of services", func(o goschema.ObjectType) {
    91  					o.Attribute("path").String(
    92  						"Folder or file to watch. Folder must exists. If it's a file, parent folder must exist.")
    93  					o.Attribute("services").List(func(l goschema.ListType) {
    94  						l.String("name of service to restart")
    95  					})
    96  					o.Optional("signal").String("Name of the signal to send. If omitted sigint and sigkill are used.")
    97  				})
    98  		})
    99  		o.Optional("stats").Object("statistics writer", func(o goschema.ObjectType) {
   100  			o.Attribute("seconds").Int("defines in which interval stats are written").Min(5).Max(60)
   101  			o.Attribute("file").String("where to write the stats to")
   102  		})
   103  	})
   104  }