github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/constraints.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package description
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/schema"
     9  )
    10  
    11  // ConstraintsArgs is an argument struct to construct Constraints.
    12  type ConstraintsArgs struct {
    13  	Architecture string
    14  	Container    string
    15  	CpuCores     uint64
    16  	CpuPower     uint64
    17  	InstanceType string
    18  	Memory       uint64
    19  	RootDisk     uint64
    20  
    21  	Spaces []string
    22  	Tags   []string
    23  
    24  	VirtType string
    25  }
    26  
    27  func newConstraints(args ConstraintsArgs) *constraints {
    28  	// If the ConstraintsArgs are all empty, then we return
    29  	// nil to indicate that there are no constraints.
    30  	if args.empty() {
    31  		return nil
    32  	}
    33  
    34  	tags := make([]string, len(args.Tags))
    35  	copy(tags, args.Tags)
    36  	spaces := make([]string, len(args.Spaces))
    37  	copy(spaces, args.Spaces)
    38  	return &constraints{
    39  		Version:       1,
    40  		Architecture_: args.Architecture,
    41  		Container_:    args.Container,
    42  		CpuCores_:     args.CpuCores,
    43  		CpuPower_:     args.CpuPower,
    44  		InstanceType_: args.InstanceType,
    45  		Memory_:       args.Memory,
    46  		RootDisk_:     args.RootDisk,
    47  		Spaces_:       spaces,
    48  		Tags_:         tags,
    49  		VirtType_:     args.VirtType,
    50  	}
    51  }
    52  
    53  type constraints struct {
    54  	Version int `yaml:"version"`
    55  
    56  	Architecture_ string `yaml:"architecture,omitempty"`
    57  	Container_    string `yaml:"container,omitempty"`
    58  	CpuCores_     uint64 `yaml:"cores,omitempty"`
    59  	CpuPower_     uint64 `yaml:"cpu-power,omitempty"`
    60  	InstanceType_ string `yaml:"instance-type,omitempty"`
    61  	Memory_       uint64 `yaml:"memory,omitempty"`
    62  	RootDisk_     uint64 `yaml:"root-disk,omitempty"`
    63  
    64  	Spaces_ []string `yaml:"spaces,omitempty"`
    65  	Tags_   []string `yaml:"tags,omitempty"`
    66  
    67  	VirtType_ string `yaml:"virt-type,omitempty"`
    68  }
    69  
    70  // Architecture implements Constraints.
    71  func (c *constraints) Architecture() string {
    72  	return c.Architecture_
    73  }
    74  
    75  // Container implements Constraints.
    76  func (c *constraints) Container() string {
    77  	return c.Container_
    78  }
    79  
    80  // CpuCores implements Constraints.
    81  func (c *constraints) CpuCores() uint64 {
    82  	return c.CpuCores_
    83  }
    84  
    85  // CpuPower implements Constraints.
    86  func (c *constraints) CpuPower() uint64 {
    87  	return c.CpuPower_
    88  }
    89  
    90  // InstanceType implements Constraints.
    91  func (c *constraints) InstanceType() string {
    92  	return c.InstanceType_
    93  }
    94  
    95  // Memory implements Constraints.
    96  func (c *constraints) Memory() uint64 {
    97  	return c.Memory_
    98  }
    99  
   100  // RootDisk implements Constraints.
   101  func (c *constraints) RootDisk() uint64 {
   102  	return c.RootDisk_
   103  }
   104  
   105  // Spaces implements Constraints.
   106  func (c *constraints) Spaces() []string {
   107  	var spaces []string
   108  	if count := len(c.Spaces_); count > 0 {
   109  		spaces = make([]string, count)
   110  		copy(spaces, c.Spaces_)
   111  	}
   112  	return spaces
   113  }
   114  
   115  // Tags implements Constraints.
   116  func (c *constraints) Tags() []string {
   117  	var tags []string
   118  	if count := len(c.Tags_); count > 0 {
   119  		tags = make([]string, count)
   120  		copy(tags, c.Tags_)
   121  	}
   122  	return tags
   123  }
   124  
   125  // VirtType implements Constraints.
   126  func (c *constraints) VirtType() string {
   127  	return c.VirtType_
   128  }
   129  
   130  func importConstraints(source map[string]interface{}) (*constraints, error) {
   131  	version, err := getVersion(source)
   132  	if err != nil {
   133  		return nil, errors.Annotate(err, "constraints version schema check failed")
   134  	}
   135  
   136  	importFunc, ok := constraintsDeserializationFuncs[version]
   137  	if !ok {
   138  		return nil, errors.NotValidf("version %d", version)
   139  	}
   140  
   141  	return importFunc(source)
   142  }
   143  
   144  type constraintsDeserializationFunc func(map[string]interface{}) (*constraints, error)
   145  
   146  var constraintsDeserializationFuncs = map[int]constraintsDeserializationFunc{
   147  	1: importConstraintsV1,
   148  }
   149  
   150  func importConstraintsV1(source map[string]interface{}) (*constraints, error) {
   151  	fields := schema.Fields{
   152  		"architecture":  schema.String(),
   153  		"container":     schema.String(),
   154  		"cpu-cores":     schema.ForceUint(),
   155  		"cores":         schema.ForceUint(),
   156  		"cpu-power":     schema.ForceUint(),
   157  		"instance-type": schema.String(),
   158  		"memory":        schema.ForceUint(),
   159  		"root-disk":     schema.ForceUint(),
   160  
   161  		"spaces": schema.List(schema.String()),
   162  		"tags":   schema.List(schema.String()),
   163  
   164  		"virt-type": schema.String(),
   165  	}
   166  	// Some values don't have to be there.
   167  	defaults := schema.Defaults{
   168  		"architecture":  "",
   169  		"container":     "",
   170  		"cpu-cores":     schema.Omit,
   171  		"cores":         schema.Omit,
   172  		"cpu-power":     uint64(0),
   173  		"instance-type": "",
   174  		"memory":        uint64(0),
   175  		"root-disk":     uint64(0),
   176  
   177  		"spaces": schema.Omit,
   178  		"tags":   schema.Omit,
   179  
   180  		"virt-type": "",
   181  	}
   182  	checker := schema.FieldMap(fields, defaults)
   183  
   184  	coerced, err := checker.Coerce(source, nil)
   185  	if err != nil {
   186  		return nil, errors.Annotatef(err, "constraints v1 schema check failed")
   187  	}
   188  
   189  	valid := coerced.(map[string]interface{})
   190  	_, hasCPU := valid["cpu-cores"]
   191  	_, hasCores := valid["cores"]
   192  	if hasCPU && hasCores {
   193  		return nil, errors.Errorf("can not specify both cores and cores constraints")
   194  	}
   195  
   196  	var cores uint64
   197  	if hasCPU {
   198  		cores = valid["cpu-cores"].(uint64)
   199  	}
   200  	if hasCores {
   201  		cores = valid["cores"].(uint64)
   202  	}
   203  
   204  	// From here we know that the map returned from the schema coercion
   205  	// contains fields of the right type.
   206  
   207  	return &constraints{
   208  		Version:       1,
   209  		Architecture_: valid["architecture"].(string),
   210  		Container_:    valid["container"].(string),
   211  		CpuCores_:     cores,
   212  		CpuPower_:     valid["cpu-power"].(uint64),
   213  		InstanceType_: valid["instance-type"].(string),
   214  		Memory_:       valid["memory"].(uint64),
   215  		RootDisk_:     valid["root-disk"].(uint64),
   216  
   217  		Spaces_: convertToStringSlice(valid["spaces"]),
   218  		Tags_:   convertToStringSlice(valid["tags"]),
   219  
   220  		VirtType_: valid["virt-type"].(string),
   221  	}, nil
   222  }
   223  
   224  func addConstraintsSchema(fields schema.Fields, defaults schema.Defaults) {
   225  	fields["constraints"] = schema.StringMap(schema.Any())
   226  	defaults["constraints"] = schema.Omit
   227  }
   228  
   229  func (c ConstraintsArgs) empty() bool {
   230  	return c.Architecture == "" &&
   231  		c.Container == "" &&
   232  		c.CpuCores == 0 &&
   233  		c.CpuPower == 0 &&
   234  		c.InstanceType == "" &&
   235  		c.Memory == 0 &&
   236  		c.RootDisk == 0 &&
   237  		c.Spaces == nil &&
   238  		c.Tags == nil &&
   239  		c.VirtType == ""
   240  }