resources: Add a Default method to the resource API
This provides sensible defaults for when they're not the zero value.
This commit is contained in:
@@ -30,10 +30,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
1. [Overview](#overview)
|
||||
2. [Theory - Resource theory in mgmt](#theory)
|
||||
3. [Resource API - Getting started with mgmt](#resource-api)
|
||||
* [Default - Get an empty resource with defaults](#default)
|
||||
* [Init - Initialize the resource](#init)
|
||||
* [CheckApply - Check and apply resource state](#checkapply)
|
||||
* [Watch - Detect resource changes](#watch)
|
||||
* [Compare - Compare resource with another](#compare)
|
||||
* [(UnmarshalYAML) - Optional, sets the YAML defaults](#unmarshalyaml)
|
||||
4. [Further considerations - More information about resource writing](#further-considerations)
|
||||
5. [Automatic edges - Adding automatic resources dependencies](#automatic-edges)
|
||||
6. [Automatic grouping - Grouping multiple resources into one](#automatic-grouping)
|
||||
@@ -69,6 +71,25 @@ To implement a resource in `mgmt` it must satisfy the
|
||||
interface. What follows are each of the method signatures and a description of
|
||||
each.
|
||||
|
||||
###Default
|
||||
```golang
|
||||
Default() Res
|
||||
```
|
||||
|
||||
This returns a populated resource struct as a `Res`. It shouldn't populate any
|
||||
values which already have the correct default as the golang zero value. In
|
||||
general it is preferable if the zero values make for the correct defaults.
|
||||
|
||||
####Example
|
||||
```golang
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *FooRes) Default() Res {
|
||||
return &FooRes{
|
||||
Answer: 42, // sometimes, defaults shouldn't be the zero value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
###Init
|
||||
```golang
|
||||
Init() error
|
||||
@@ -378,6 +399,42 @@ CollectPattern() string
|
||||
|
||||
This is currently a stub and will be updated once the DSL is further along.
|
||||
|
||||
###UnmarshalYAML
|
||||
```golang
|
||||
UnmarshalYAML(unmarshal func(interface{}) error) error // optional
|
||||
```
|
||||
|
||||
This is optional, but recommended for any resource that will have a YAML
|
||||
accessible struct, and an entry in the `GraphConfig` struct. It is not required
|
||||
because to do so would mean that third-party or custom resources (such as those
|
||||
someone writes to use with `libmgmt`) would have to implement this needlessly.
|
||||
|
||||
The signature intentionally matches what is required to satisfy the `go-yaml`
|
||||
[Unmarshaler](https://godoc.org/gopkg.in/yaml.v2#Unmarshaler) interface.
|
||||
|
||||
####Example
|
||||
```golang
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *FooRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes FooRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*FooRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to FooRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = FooRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
##Further considerations
|
||||
There is some additional information that any resource writer will need to know.
|
||||
Each issue is listed separately below!
|
||||
@@ -419,6 +476,9 @@ type GraphConfig struct {
|
||||
}
|
||||
```
|
||||
|
||||
It's also recommended that you add the [UnmarshalYAML](#unmarshalyaml) method to
|
||||
your resources so that unspecified values are given sane defaults.
|
||||
|
||||
###Gob registration
|
||||
All resources must be registered with the `golang` _gob_ module so that they can
|
||||
be encoded and decoded. Make sure to include the following code snippet for this
|
||||
|
||||
@@ -69,6 +69,11 @@ func NewExecRes(name, cmd, shell string, timeout int, watchcmd, watchshell, ifcm
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *ExecRes) Default() Res {
|
||||
return &ExecRes{}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *ExecRes) Init() error {
|
||||
obj.BaseRes.kind = "Exec"
|
||||
@@ -433,3 +438,23 @@ func (obj *ExecRes) Compare(res Res) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *ExecRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes ExecRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*ExecRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to ExecRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = ExecRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -77,6 +77,13 @@ func NewFileRes(name, path, dirname, basename string, content *string, source, s
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *FileRes) Default() Res {
|
||||
return &FileRes{
|
||||
State: "exists",
|
||||
}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *FileRes) Init() error {
|
||||
obj.sha256sum = ""
|
||||
@@ -804,3 +811,23 @@ 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
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *FileRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes FileRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*FileRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to FileRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = FileRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@ func NewHostnameRes(name, staticHostname, transientHostname, prettyHostname stri
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *HostnameRes) Default() Res {
|
||||
return &HostnameRes{}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *HostnameRes) Init() error {
|
||||
obj.BaseRes.kind = "Hostname"
|
||||
@@ -293,3 +298,23 @@ func (obj *HostnameRes) Compare(res Res) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *HostnameRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes HostnameRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*HostnameRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to HostnameRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = HostnameRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ func NewMsgRes(name, body, priority string, journal, syslog bool, fields map[str
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *MsgRes) Default() Res {
|
||||
return &MsgRes{}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *MsgRes) Init() error {
|
||||
obj.BaseRes.kind = "Msg"
|
||||
@@ -259,3 +264,23 @@ func (obj *MsgRes) Compare(res Res) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *MsgRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes MsgRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*MsgRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to MsgRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = MsgRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package resources
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/purpleidea/mgmt/event"
|
||||
@@ -45,6 +46,11 @@ func NewNoopRes(name string) (*NoopRes, error) {
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *NoopRes) Default() Res {
|
||||
return &NoopRes{}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *NoopRes) Init() error {
|
||||
obj.BaseRes.kind = "Noop"
|
||||
@@ -153,3 +159,23 @@ func (obj *NoopRes) Compare(res Res) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *NoopRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes NoopRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*NoopRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to NoopRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = NoopRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -57,6 +57,13 @@ type NspawnRes struct {
|
||||
svc *SvcRes
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *NspawnRes) Default() Res {
|
||||
return &NspawnRes{
|
||||
State: running,
|
||||
}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource
|
||||
func (obj *NspawnRes) Init() error {
|
||||
var serviceName = fmt.Sprintf(nspawnServiceTmpl, obj.GetName())
|
||||
@@ -83,6 +90,7 @@ func NewNspawnRes(name string, state string) (*NspawnRes, error) {
|
||||
|
||||
// Validate params
|
||||
func (obj *NspawnRes) Validate() error {
|
||||
// TODO: validStates should be an enum!
|
||||
validStates := map[string]struct{}{
|
||||
stopped: {},
|
||||
running: {},
|
||||
@@ -304,3 +312,23 @@ func (obj *NspawnRes) Compare(res Res) bool {
|
||||
func (obj *NspawnRes) AutoEdges() AutoEdge {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *NspawnRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes NspawnRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*NspawnRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to NspawnRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = NspawnRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -67,6 +67,13 @@ func NewPasswordRes(name string, length uint16) (*PasswordRes, error) {
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *PasswordRes) Default() Res {
|
||||
return &PasswordRes{
|
||||
Length: 64, // safe default
|
||||
}
|
||||
}
|
||||
|
||||
// Init generates a new password for this resource if one was not provided. It
|
||||
// will save this into a local file. It will load it back in from previous runs.
|
||||
func (obj *PasswordRes) Init() error {
|
||||
@@ -363,3 +370,23 @@ func (obj *PasswordRes) Compare(res Res) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *PasswordRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes PasswordRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*PasswordRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to PasswordRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = PasswordRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -60,6 +60,13 @@ func NewPkgRes(name, state string, allowuntrusted, allownonfree, allowunsupporte
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *PkgRes) Default() Res {
|
||||
return &PkgRes{
|
||||
State: "installed", // i think this is preferable to "latest"
|
||||
}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *PkgRes) Init() error {
|
||||
obj.BaseRes.kind = "Pkg"
|
||||
@@ -544,3 +551,23 @@ func ReturnSvcInFileList(fileList []string) []string {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *PkgRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes PkgRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*PkgRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to PkgRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = PkgRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -162,7 +162,8 @@ type Base interface {
|
||||
|
||||
// Res is the minimum interface you need to implement to define a new resource.
|
||||
type Res interface {
|
||||
Base // include everything from the Base interface
|
||||
Base // include everything from the Base interface
|
||||
Default() Res // return a struct with sane defaults as a Res
|
||||
Init() error
|
||||
//Validate() error // TODO: this might one day be added
|
||||
GetUIDs() []ResUID // most resources only return one
|
||||
@@ -171,6 +172,7 @@ type Res interface {
|
||||
AutoEdges() AutoEdge
|
||||
Compare(Res) bool
|
||||
CollectPattern(string) // XXX: temporary until Res collection is more advanced
|
||||
//UnmarshalYAML(unmarshal func(interface{}) error) error // optional
|
||||
}
|
||||
|
||||
// BaseRes is the base struct that gets used in every resource.
|
||||
|
||||
@@ -56,6 +56,11 @@ func NewSvcRes(name, state, startup string) (*SvcRes, error) {
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *SvcRes) Default() Res {
|
||||
return &SvcRes{}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *SvcRes) Init() error {
|
||||
obj.BaseRes.kind = "Svc"
|
||||
@@ -454,3 +459,23 @@ func (obj *SvcRes) Compare(res Res) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *SvcRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes SvcRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*SvcRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to SvcRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = SvcRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package resources
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
@@ -54,6 +55,11 @@ func NewTimerRes(name string, interval uint32) (*TimerRes, error) {
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *TimerRes) Default() Res {
|
||||
return &TimerRes{}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *TimerRes) Init() error {
|
||||
obj.BaseRes.kind = "Timer"
|
||||
@@ -165,3 +171,23 @@ func (obj *TimerRes) Compare(res Res) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *TimerRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes TimerRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*TimerRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to TimerRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = TimerRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -91,6 +91,11 @@ func NewVirtRes(name string, uri, state string, transient bool, cpus uint, memor
|
||||
return obj, obj.Init()
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *VirtRes) Default() Res {
|
||||
return &VirtRes{}
|
||||
}
|
||||
|
||||
// Init runs some startup code for this resource.
|
||||
func (obj *VirtRes) Init() error {
|
||||
if !libvirtInitialized {
|
||||
@@ -765,6 +770,26 @@ func (obj *VirtRes) Compare(res Res) bool {
|
||||
func (obj *VirtRes) CollectPattern(string) {
|
||||
}
|
||||
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct.
|
||||
// It is primarily useful for setting the defaults.
|
||||
func (obj *VirtRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes VirtRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*VirtRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to VirtRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = VirtRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
// randMAC returns a random mac address in the libvirt range.
|
||||
func randMAC() string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
Reference in New Issue
Block a user