github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/core/description/machine.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/names"
     9  	"github.com/juju/schema"
    10  	"github.com/juju/version"
    11  )
    12  
    13  type machines struct {
    14  	Version   int        `yaml:"version"`
    15  	Machines_ []*machine `yaml:"machines"`
    16  }
    17  
    18  type machine struct {
    19  	Id_            string         `yaml:"id"`
    20  	Nonce_         string         `yaml:"nonce"`
    21  	PasswordHash_  string         `yaml:"password-hash"`
    22  	Placement_     string         `yaml:"placement,omitempty"`
    23  	Instance_      *cloudInstance `yaml:"instance,omitempty"`
    24  	Series_        string         `yaml:"series"`
    25  	ContainerType_ string         `yaml:"container-type,omitempty"`
    26  
    27  	Status_        *status `yaml:"status"`
    28  	StatusHistory_ `yaml:"status-history"`
    29  
    30  	ProviderAddresses_ []*address `yaml:"provider-addresses,omitempty"`
    31  	MachineAddresses_  []*address `yaml:"machine-addresses,omitempty"`
    32  
    33  	PreferredPublicAddress_  *address `yaml:"preferred-public-address,omitempty"`
    34  	PreferredPrivateAddress_ *address `yaml:"preferred-private-address,omitempty"`
    35  
    36  	Tools_ *agentTools `yaml:"tools"`
    37  	Jobs_  []string    `yaml:"jobs"`
    38  
    39  	SupportedContainers_ *[]string `yaml:"supported-containers,omitempty"`
    40  
    41  	Containers_ []*machine `yaml:"containers"`
    42  
    43  	OpenedPorts_ *versionedOpenedPorts `yaml:"opened-ports,omitempty"`
    44  
    45  	Annotations_ `yaml:"annotations,omitempty"`
    46  
    47  	Constraints_ *constraints `yaml:"constraints,omitempty"`
    48  }
    49  
    50  // MachineArgs is an argument struct used to add a machine to the Model.
    51  type MachineArgs struct {
    52  	Id            names.MachineTag
    53  	Nonce         string
    54  	PasswordHash  string
    55  	Placement     string
    56  	Series        string
    57  	ContainerType string
    58  	Jobs          []string
    59  	// A null value means that we don't yet know which containers
    60  	// are supported. An empty slice means 'no containers are supported'.
    61  	SupportedContainers *[]string
    62  }
    63  
    64  func newMachine(args MachineArgs) *machine {
    65  	var jobs []string
    66  	if count := len(args.Jobs); count > 0 {
    67  		jobs = make([]string, count)
    68  		copy(jobs, args.Jobs)
    69  	}
    70  	m := &machine{
    71  		Id_:            args.Id.Id(),
    72  		Nonce_:         args.Nonce,
    73  		PasswordHash_:  args.PasswordHash,
    74  		Placement_:     args.Placement,
    75  		Series_:        args.Series,
    76  		ContainerType_: args.ContainerType,
    77  		Jobs_:          jobs,
    78  		StatusHistory_: newStatusHistory(),
    79  	}
    80  	if args.SupportedContainers != nil {
    81  		supported := make([]string, len(*args.SupportedContainers))
    82  		copy(supported, *args.SupportedContainers)
    83  		m.SupportedContainers_ = &supported
    84  	}
    85  	return m
    86  }
    87  
    88  // Id implements Machine.
    89  func (m *machine) Id() string {
    90  	return m.Id_
    91  }
    92  
    93  // Tag implements Machine.
    94  func (m *machine) Tag() names.MachineTag {
    95  	return names.NewMachineTag(m.Id_)
    96  }
    97  
    98  // Nonce implements Machine.
    99  func (m *machine) Nonce() string {
   100  	return m.Nonce_
   101  }
   102  
   103  // PasswordHash implements Machine.
   104  func (m *machine) PasswordHash() string {
   105  	return m.PasswordHash_
   106  }
   107  
   108  // Placement implements Machine.
   109  func (m *machine) Placement() string {
   110  	return m.Placement_
   111  }
   112  
   113  // Instance implements Machine.
   114  func (m *machine) Instance() CloudInstance {
   115  	// To avoid typed nils check nil here.
   116  	if m.Instance_ == nil {
   117  		return nil
   118  	}
   119  	return m.Instance_
   120  }
   121  
   122  // SetInstance implements Machine.
   123  func (m *machine) SetInstance(args CloudInstanceArgs) {
   124  	m.Instance_ = newCloudInstance(args)
   125  }
   126  
   127  // Series implements Machine.
   128  func (m *machine) Series() string {
   129  	return m.Series_
   130  }
   131  
   132  // ContainerType implements Machine.
   133  func (m *machine) ContainerType() string {
   134  	return m.ContainerType_
   135  }
   136  
   137  // Status implements Machine.
   138  func (m *machine) Status() Status {
   139  	// To avoid typed nils check nil here.
   140  	if m.Status_ == nil {
   141  		return nil
   142  	}
   143  	return m.Status_
   144  }
   145  
   146  // SetStatus implements Machine.
   147  func (m *machine) SetStatus(args StatusArgs) {
   148  	m.Status_ = newStatus(args)
   149  }
   150  
   151  // ProviderAddresses implements Machine.
   152  func (m *machine) ProviderAddresses() []Address {
   153  	var result []Address
   154  	for _, addr := range m.ProviderAddresses_ {
   155  		result = append(result, addr)
   156  	}
   157  	return result
   158  }
   159  
   160  // MachineAddresses implements Machine.
   161  func (m *machine) MachineAddresses() []Address {
   162  	var result []Address
   163  	for _, addr := range m.MachineAddresses_ {
   164  		result = append(result, addr)
   165  	}
   166  	return result
   167  }
   168  
   169  // SetAddresses implements Machine.
   170  func (m *machine) SetAddresses(margs []AddressArgs, pargs []AddressArgs) {
   171  	m.MachineAddresses_ = nil
   172  	m.ProviderAddresses_ = nil
   173  	for _, args := range margs {
   174  		if args.Value != "" {
   175  			m.MachineAddresses_ = append(m.MachineAddresses_, newAddress(args))
   176  		}
   177  	}
   178  	for _, args := range pargs {
   179  		if args.Value != "" {
   180  			m.ProviderAddresses_ = append(m.ProviderAddresses_, newAddress(args))
   181  		}
   182  	}
   183  }
   184  
   185  // PreferredPublicAddress implements Machine.
   186  func (m *machine) PreferredPublicAddress() Address {
   187  	// To avoid typed nils check nil here.
   188  	if m.PreferredPublicAddress_ == nil {
   189  		return nil
   190  	}
   191  	return m.PreferredPublicAddress_
   192  }
   193  
   194  // PreferredPrivateAddress implements Machine.
   195  func (m *machine) PreferredPrivateAddress() Address {
   196  	// To avoid typed nils check nil here.
   197  	if m.PreferredPrivateAddress_ == nil {
   198  		return nil
   199  	}
   200  	return m.PreferredPrivateAddress_
   201  }
   202  
   203  // SetPreferredAddresses implements Machine.
   204  func (m *machine) SetPreferredAddresses(public AddressArgs, private AddressArgs) {
   205  	if public.Value != "" {
   206  		m.PreferredPublicAddress_ = newAddress(public)
   207  	}
   208  	if private.Value != "" {
   209  		m.PreferredPrivateAddress_ = newAddress(private)
   210  	}
   211  }
   212  
   213  // Tools implements Machine.
   214  func (m *machine) Tools() AgentTools {
   215  	// To avoid a typed nil, check before returning.
   216  	if m.Tools_ == nil {
   217  		return nil
   218  	}
   219  	return m.Tools_
   220  }
   221  
   222  // SetTools implements Machine.
   223  func (m *machine) SetTools(args AgentToolsArgs) {
   224  	m.Tools_ = newAgentTools(args)
   225  }
   226  
   227  // Jobs implements Machine.
   228  func (m *machine) Jobs() []string {
   229  	return m.Jobs_
   230  }
   231  
   232  // SupportedContainers implements Machine.
   233  func (m *machine) SupportedContainers() ([]string, bool) {
   234  	if m.SupportedContainers_ == nil {
   235  		return nil, false
   236  	}
   237  	return *m.SupportedContainers_, true
   238  }
   239  
   240  // Containers implements Machine.
   241  func (m *machine) Containers() []Machine {
   242  	var result []Machine
   243  	for _, container := range m.Containers_ {
   244  		result = append(result, container)
   245  	}
   246  	return result
   247  }
   248  
   249  // AddContainer implements Machine.
   250  func (m *machine) AddContainer(args MachineArgs) Machine {
   251  	container := newMachine(args)
   252  	m.Containers_ = append(m.Containers_, container)
   253  	return container
   254  }
   255  
   256  // OpenedPorts implements Machine.
   257  func (m *machine) OpenedPorts() []OpenedPorts {
   258  	if m.OpenedPorts_ == nil {
   259  		return nil
   260  	}
   261  	var result []OpenedPorts
   262  	for _, ports := range m.OpenedPorts_.OpenedPorts_ {
   263  		result = append(result, ports)
   264  	}
   265  	return result
   266  }
   267  
   268  // AddOpenedPorts implements Machine.
   269  func (m *machine) AddOpenedPorts(args OpenedPortsArgs) OpenedPorts {
   270  	if m.OpenedPorts_ == nil {
   271  		m.OpenedPorts_ = &versionedOpenedPorts{Version: 1}
   272  	}
   273  	ports := newOpenedPorts(args)
   274  	m.OpenedPorts_.OpenedPorts_ = append(m.OpenedPorts_.OpenedPorts_, ports)
   275  	return ports
   276  }
   277  
   278  func (m *machine) setOpenedPorts(portsList []*openedPorts) {
   279  	m.OpenedPorts_ = &versionedOpenedPorts{
   280  		Version:      1,
   281  		OpenedPorts_: portsList,
   282  	}
   283  }
   284  
   285  // Constraints implements HasConstraints.
   286  func (m *machine) Constraints() Constraints {
   287  	if m.Constraints_ == nil {
   288  		return nil
   289  	}
   290  	return m.Constraints_
   291  }
   292  
   293  // SetConstraints implements HasConstraints.
   294  func (m *machine) SetConstraints(args ConstraintsArgs) {
   295  	m.Constraints_ = newConstraints(args)
   296  }
   297  
   298  // Validate implements Machine.
   299  func (m *machine) Validate() error {
   300  	if m.Id_ == "" {
   301  		return errors.NotValidf("machine missing id")
   302  	}
   303  	if m.Status_ == nil {
   304  		return errors.NotValidf("machine %q missing status", m.Id_)
   305  	}
   306  	// Since all exports should be done when machines are stable,
   307  	// there should always be tools and cloud instance.
   308  	if m.Tools_ == nil {
   309  		return errors.NotValidf("machine %q missing tools", m.Id_)
   310  	}
   311  	if m.Instance_ == nil {
   312  		return errors.NotValidf("machine %q missing instance", m.Id_)
   313  	}
   314  	for _, container := range m.Containers_ {
   315  		if err := container.Validate(); err != nil {
   316  			return errors.Trace(err)
   317  		}
   318  	}
   319  
   320  	return nil
   321  }
   322  
   323  func importMachines(source map[string]interface{}) ([]*machine, error) {
   324  	checker := versionedChecker("machines")
   325  	coerced, err := checker.Coerce(source, nil)
   326  	if err != nil {
   327  		return nil, errors.Annotatef(err, "machines version schema check failed")
   328  	}
   329  	valid := coerced.(map[string]interface{})
   330  
   331  	version := int(valid["version"].(int64))
   332  	importFunc, ok := machineDeserializationFuncs[version]
   333  	if !ok {
   334  		return nil, errors.NotValidf("version %d", version)
   335  	}
   336  	sourceList := valid["machines"].([]interface{})
   337  	return importMachineList(sourceList, importFunc)
   338  }
   339  
   340  func importMachineList(sourceList []interface{}, importFunc machineDeserializationFunc) ([]*machine, error) {
   341  	result := make([]*machine, 0, len(sourceList))
   342  	for i, value := range sourceList {
   343  		source, ok := value.(map[string]interface{})
   344  		if !ok {
   345  			return nil, errors.Errorf("unexpected value for machine %d, %T", i, value)
   346  		}
   347  		machine, err := importFunc(source)
   348  		if err != nil {
   349  			return nil, errors.Annotatef(err, "machine %d", i)
   350  		}
   351  		result = append(result, machine)
   352  	}
   353  	return result, nil
   354  }
   355  
   356  type machineDeserializationFunc func(map[string]interface{}) (*machine, error)
   357  
   358  var machineDeserializationFuncs = map[int]machineDeserializationFunc{
   359  	1: importMachineV1,
   360  }
   361  
   362  func importMachineV1(source map[string]interface{}) (*machine, error) {
   363  	fields := schema.Fields{
   364  		"id":                   schema.String(),
   365  		"nonce":                schema.String(),
   366  		"password-hash":        schema.String(),
   367  		"placement":            schema.String(),
   368  		"instance":             schema.StringMap(schema.Any()),
   369  		"series":               schema.String(),
   370  		"container-type":       schema.String(),
   371  		"jobs":                 schema.List(schema.String()),
   372  		"status":               schema.StringMap(schema.Any()),
   373  		"supported-containers": schema.List(schema.String()),
   374  		"tools":                schema.StringMap(schema.Any()),
   375  		"containers":           schema.List(schema.StringMap(schema.Any())),
   376  		"opened-ports":         schema.StringMap(schema.Any()),
   377  
   378  		"provider-addresses":        schema.List(schema.StringMap(schema.Any())),
   379  		"machine-addresses":         schema.List(schema.StringMap(schema.Any())),
   380  		"preferred-public-address":  schema.StringMap(schema.Any()),
   381  		"preferred-private-address": schema.StringMap(schema.Any()),
   382  	}
   383  
   384  	defaults := schema.Defaults{
   385  		"placement":      "",
   386  		"container-type": "",
   387  		// Even though we are expecting instance data for every machine,
   388  		// it isn't strictly necessary, so we allow it to not exist here.
   389  		"instance":                  schema.Omit,
   390  		"supported-containers":      schema.Omit,
   391  		"opened-ports":              schema.Omit,
   392  		"provider-addresses":        schema.Omit,
   393  		"machine-addresses":         schema.Omit,
   394  		"preferred-public-address":  schema.Omit,
   395  		"preferred-private-address": schema.Omit,
   396  	}
   397  	addAnnotationSchema(fields, defaults)
   398  	addConstraintsSchema(fields, defaults)
   399  	addStatusHistorySchema(fields)
   400  	checker := schema.FieldMap(fields, defaults)
   401  
   402  	coerced, err := checker.Coerce(source, nil)
   403  	if err != nil {
   404  		return nil, errors.Annotatef(err, "machine v1 schema check failed")
   405  	}
   406  	valid := coerced.(map[string]interface{})
   407  	// From here we know that the map returned from the schema coercion
   408  	// contains fields of the right type.
   409  	result := &machine{
   410  		Id_:            valid["id"].(string),
   411  		Nonce_:         valid["nonce"].(string),
   412  		PasswordHash_:  valid["password-hash"].(string),
   413  		Placement_:     valid["placement"].(string),
   414  		Series_:        valid["series"].(string),
   415  		ContainerType_: valid["container-type"].(string),
   416  		StatusHistory_: newStatusHistory(),
   417  	}
   418  	result.importAnnotations(valid)
   419  	if err := result.importStatusHistory(valid); err != nil {
   420  		return nil, errors.Trace(err)
   421  	}
   422  
   423  	if constraintsMap, ok := valid["constraints"]; ok {
   424  		constraints, err := importConstraints(constraintsMap.(map[string]interface{}))
   425  		if err != nil {
   426  			return nil, errors.Trace(err)
   427  		}
   428  		result.Constraints_ = constraints
   429  	}
   430  
   431  	if jobs := valid["jobs"].([]interface{}); len(jobs) > 0 {
   432  		for _, job := range jobs {
   433  			result.Jobs_ = append(result.Jobs_, job.(string))
   434  		}
   435  	}
   436  	if supported, ok := valid["supported-containers"]; ok {
   437  		supportedList := supported.([]interface{})
   438  		s := make([]string, len(supportedList))
   439  		for i, containerType := range supportedList {
   440  			s[i] = containerType.(string)
   441  		}
   442  		result.SupportedContainers_ = &s
   443  	}
   444  
   445  	if instanceMap, ok := valid["instance"]; ok {
   446  		instance, err := importCloudInstance(instanceMap.(map[string]interface{}))
   447  		if err != nil {
   448  			return nil, errors.Trace(err)
   449  		}
   450  		result.Instance_ = instance
   451  	}
   452  
   453  	// Tools and status are required, so we expect them to be there.
   454  	tools, err := importAgentTools(valid["tools"].(map[string]interface{}))
   455  	if err != nil {
   456  		return nil, errors.Trace(err)
   457  	}
   458  	result.Tools_ = tools
   459  
   460  	status, err := importStatus(valid["status"].(map[string]interface{}))
   461  	if err != nil {
   462  		return nil, errors.Trace(err)
   463  	}
   464  	result.Status_ = status
   465  
   466  	if addresses, ok := valid["provider-addresses"]; ok {
   467  		providerAddresses, err := importAddresses(addresses.([]interface{}))
   468  		if err != nil {
   469  			return nil, errors.Trace(err)
   470  		}
   471  		result.ProviderAddresses_ = providerAddresses
   472  	}
   473  
   474  	if addresses, ok := valid["machine-addresses"]; ok {
   475  		machineAddresses, err := importAddresses(addresses.([]interface{}))
   476  		if err != nil {
   477  			return nil, errors.Trace(err)
   478  		}
   479  		result.MachineAddresses_ = machineAddresses
   480  	}
   481  
   482  	if address, ok := valid["preferred-public-address"]; ok {
   483  		publicAddress, err := importAddress(address.(map[string]interface{}))
   484  		if err != nil {
   485  			return nil, errors.Trace(err)
   486  		}
   487  		result.PreferredPublicAddress_ = publicAddress
   488  	}
   489  
   490  	if address, ok := valid["preferred-private-address"]; ok {
   491  		privateAddress, err := importAddress(address.(map[string]interface{}))
   492  		if err != nil {
   493  			return nil, errors.Trace(err)
   494  		}
   495  		result.PreferredPrivateAddress_ = privateAddress
   496  	}
   497  
   498  	machineList := valid["containers"].([]interface{})
   499  	machines, err := importMachineList(machineList, importMachineV1)
   500  	if err != nil {
   501  		return nil, errors.Annotatef(err, "containers")
   502  	}
   503  	result.Containers_ = machines
   504  
   505  	if portsMap, ok := valid["opened-ports"]; ok {
   506  		portsList, err := importOpenedPorts(portsMap.(map[string]interface{}))
   507  		if err != nil {
   508  			return nil, errors.Trace(err)
   509  		}
   510  		result.setOpenedPorts(portsList)
   511  	}
   512  
   513  	return result, nil
   514  
   515  }
   516  
   517  // CloudInstanceArgs is an argument struct used to add information about the
   518  // cloud instance to a Machine.
   519  type CloudInstanceArgs struct {
   520  	InstanceId       string
   521  	Status           string
   522  	Architecture     string
   523  	Memory           uint64
   524  	RootDisk         uint64
   525  	CpuCores         uint64
   526  	CpuPower         uint64
   527  	Tags             []string
   528  	AvailabilityZone string
   529  }
   530  
   531  func newCloudInstance(args CloudInstanceArgs) *cloudInstance {
   532  	tags := make([]string, len(args.Tags))
   533  	copy(tags, args.Tags)
   534  	return &cloudInstance{
   535  		Version:           1,
   536  		InstanceId_:       args.InstanceId,
   537  		Status_:           args.Status,
   538  		Architecture_:     args.Architecture,
   539  		Memory_:           args.Memory,
   540  		RootDisk_:         args.RootDisk,
   541  		CpuCores_:         args.CpuCores,
   542  		CpuPower_:         args.CpuPower,
   543  		Tags_:             tags,
   544  		AvailabilityZone_: args.AvailabilityZone,
   545  	}
   546  }
   547  
   548  type cloudInstance struct {
   549  	Version int `yaml:"version"`
   550  
   551  	InstanceId_ string `yaml:"instance-id"`
   552  	Status_     string `yaml:"status"`
   553  	// For all the optional values, empty values make no sense, and
   554  	// it would be better to have them not set rather than set with
   555  	// a nonsense value.
   556  	Architecture_     string   `yaml:"architecture,omitempty"`
   557  	Memory_           uint64   `yaml:"memory,omitempty"`
   558  	RootDisk_         uint64   `yaml:"root-disk,omitempty"`
   559  	CpuCores_         uint64   `yaml:"cpu-cores,omitempty"`
   560  	CpuPower_         uint64   `yaml:"cpu-power,omitempty"`
   561  	Tags_             []string `yaml:"tags,omitempty"`
   562  	AvailabilityZone_ string   `yaml:"availability-zone,omitempty"`
   563  }
   564  
   565  // InstanceId implements CloudInstance.
   566  func (c *cloudInstance) InstanceId() string {
   567  	return c.InstanceId_
   568  }
   569  
   570  // Status implements CloudInstance.
   571  func (c *cloudInstance) Status() string {
   572  	return c.Status_
   573  }
   574  
   575  // Architecture implements CloudInstance.
   576  func (c *cloudInstance) Architecture() string {
   577  	return c.Architecture_
   578  }
   579  
   580  // Memory implements CloudInstance.
   581  func (c *cloudInstance) Memory() uint64 {
   582  	return c.Memory_
   583  }
   584  
   585  // RootDisk implements CloudInstance.
   586  func (c *cloudInstance) RootDisk() uint64 {
   587  	return c.RootDisk_
   588  }
   589  
   590  // CpuCores implements CloudInstance.
   591  func (c *cloudInstance) CpuCores() uint64 {
   592  	return c.CpuCores_
   593  }
   594  
   595  // CpuPower implements CloudInstance.
   596  func (c *cloudInstance) CpuPower() uint64 {
   597  	return c.CpuPower_
   598  }
   599  
   600  // Tags implements CloudInstance.
   601  func (c *cloudInstance) Tags() []string {
   602  	tags := make([]string, len(c.Tags_))
   603  	copy(tags, c.Tags_)
   604  	return tags
   605  }
   606  
   607  // AvailabilityZone implements CloudInstance.
   608  func (c *cloudInstance) AvailabilityZone() string {
   609  	return c.AvailabilityZone_
   610  }
   611  
   612  func importCloudInstance(source map[string]interface{}) (*cloudInstance, error) {
   613  	version, err := getVersion(source)
   614  	if err != nil {
   615  		return nil, errors.Annotate(err, "cloudInstance version schema check failed")
   616  	}
   617  
   618  	importFunc, ok := cloudInstanceDeserializationFuncs[version]
   619  	if !ok {
   620  		return nil, errors.NotValidf("version %d", version)
   621  	}
   622  
   623  	return importFunc(source)
   624  }
   625  
   626  type cloudInstanceDeserializationFunc func(map[string]interface{}) (*cloudInstance, error)
   627  
   628  var cloudInstanceDeserializationFuncs = map[int]cloudInstanceDeserializationFunc{
   629  	1: importCloudInstanceV1,
   630  }
   631  
   632  func importCloudInstanceV1(source map[string]interface{}) (*cloudInstance, error) {
   633  	fields := schema.Fields{
   634  		"instance-id":       schema.String(),
   635  		"status":            schema.String(),
   636  		"architecture":      schema.String(),
   637  		"memory":            schema.Uint(),
   638  		"root-disk":         schema.Uint(),
   639  		"cpu-cores":         schema.Uint(),
   640  		"cpu-power":         schema.Uint(),
   641  		"tags":              schema.List(schema.String()),
   642  		"availability-zone": schema.String(),
   643  	}
   644  	// Some values don't have to be there.
   645  	defaults := schema.Defaults{
   646  		"architecture":      "",
   647  		"memory":            uint64(0),
   648  		"root-disk":         uint64(0),
   649  		"cpu-cores":         uint64(0),
   650  		"cpu-power":         uint64(0),
   651  		"tags":              schema.Omit,
   652  		"availability-zone": "",
   653  	}
   654  	checker := schema.FieldMap(fields, defaults)
   655  
   656  	coerced, err := checker.Coerce(source, nil)
   657  	if err != nil {
   658  		return nil, errors.Annotatef(err, "cloudInstance v1 schema check failed")
   659  	}
   660  	valid := coerced.(map[string]interface{})
   661  	// From here we know that the map returned from the schema coercion
   662  	// contains fields of the right type.
   663  
   664  	return &cloudInstance{
   665  		Version:           1,
   666  		InstanceId_:       valid["instance-id"].(string),
   667  		Status_:           valid["status"].(string),
   668  		Architecture_:     valid["architecture"].(string),
   669  		Memory_:           valid["memory"].(uint64),
   670  		RootDisk_:         valid["root-disk"].(uint64),
   671  		CpuCores_:         valid["cpu-cores"].(uint64),
   672  		CpuPower_:         valid["cpu-power"].(uint64),
   673  		Tags_:             convertToStringSlice(valid["tags"]),
   674  		AvailabilityZone_: valid["availability-zone"].(string),
   675  	}, nil
   676  }
   677  
   678  // AgentToolsArgs is an argument struct used to add information about the
   679  // tools the agent is using to a Machine.
   680  type AgentToolsArgs struct {
   681  	Version version.Binary
   682  	URL     string
   683  	SHA256  string
   684  	Size    int64
   685  }
   686  
   687  func newAgentTools(args AgentToolsArgs) *agentTools {
   688  	return &agentTools{
   689  		Version_:      1,
   690  		ToolsVersion_: args.Version,
   691  		URL_:          args.URL,
   692  		SHA256_:       args.SHA256,
   693  		Size_:         args.Size,
   694  	}
   695  }
   696  
   697  // Keeping the agentTools with the machine code, because we hope
   698  // that one day we will succeed in merging the unit agents with the
   699  // machine agents.
   700  type agentTools struct {
   701  	Version_      int            `yaml:"version"`
   702  	ToolsVersion_ version.Binary `yaml:"tools-version"`
   703  	URL_          string         `yaml:"url"`
   704  	SHA256_       string         `yaml:"sha256"`
   705  	Size_         int64          `yaml:"size"`
   706  }
   707  
   708  // Version implements AgentTools.
   709  func (a *agentTools) Version() version.Binary {
   710  	return a.ToolsVersion_
   711  }
   712  
   713  // URL implements AgentTools.
   714  func (a *agentTools) URL() string {
   715  	return a.URL_
   716  }
   717  
   718  // SHA256 implements AgentTools.
   719  func (a *agentTools) SHA256() string {
   720  	return a.SHA256_
   721  }
   722  
   723  // Size implements AgentTools.
   724  func (a *agentTools) Size() int64 {
   725  	return a.Size_
   726  }
   727  
   728  func importAgentTools(source map[string]interface{}) (*agentTools, error) {
   729  	version, err := getVersion(source)
   730  	if err != nil {
   731  		return nil, errors.Annotate(err, "agentTools version schema check failed")
   732  	}
   733  
   734  	importFunc, ok := agentToolsDeserializationFuncs[version]
   735  	if !ok {
   736  		return nil, errors.NotValidf("version %d", version)
   737  	}
   738  
   739  	return importFunc(source)
   740  }
   741  
   742  type agentToolsDeserializationFunc func(map[string]interface{}) (*agentTools, error)
   743  
   744  var agentToolsDeserializationFuncs = map[int]agentToolsDeserializationFunc{
   745  	1: importAgentToolsV1,
   746  }
   747  
   748  func importAgentToolsV1(source map[string]interface{}) (*agentTools, error) {
   749  	fields := schema.Fields{
   750  		"tools-version": schema.String(),
   751  		"url":           schema.String(),
   752  		"sha256":        schema.String(),
   753  		"size":          schema.Int(),
   754  	}
   755  	checker := schema.FieldMap(fields, nil) // no defaults
   756  
   757  	coerced, err := checker.Coerce(source, nil)
   758  	if err != nil {
   759  		return nil, errors.Annotatef(err, "agentTools v1 schema check failed")
   760  	}
   761  	valid := coerced.(map[string]interface{})
   762  	// From here we know that the map returned from the schema coercion
   763  	// contains fields of the right type.
   764  
   765  	verString := valid["tools-version"].(string)
   766  	toolsVersion, err := version.ParseBinary(verString)
   767  	if err != nil {
   768  		return nil, errors.Annotatef(err, "agentTools tools-version")
   769  	}
   770  
   771  	return &agentTools{
   772  		Version_:      1,
   773  		ToolsVersion_: toolsVersion,
   774  		URL_:          valid["url"].(string),
   775  		SHA256_:       valid["sha256"].(string),
   776  		Size_:         valid["size"].(int64),
   777  	}, nil
   778  }