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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package description
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/version"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/names.v2"
    15  	"gopkg.in/yaml.v2"
    16  
    17  	"github.com/juju/juju/testing"
    18  )
    19  
    20  type ModelSerializationSuite struct {
    21  	testing.BaseSuite
    22  }
    23  
    24  var _ = gc.Suite(&ModelSerializationSuite{})
    25  
    26  func (*ModelSerializationSuite) TestNil(c *gc.C) {
    27  	_, err := importModel(nil)
    28  	c.Check(err, gc.ErrorMatches, "version: expected int, got nothing")
    29  }
    30  
    31  func (*ModelSerializationSuite) TestMissingVersion(c *gc.C) {
    32  	_, err := importModel(map[string]interface{}{})
    33  	c.Check(err, gc.ErrorMatches, "version: expected int, got nothing")
    34  }
    35  
    36  func (*ModelSerializationSuite) TestNonIntVersion(c *gc.C) {
    37  	_, err := importModel(map[string]interface{}{
    38  		"version": "hello",
    39  	})
    40  	c.Check(err.Error(), gc.Equals, `version: expected int, got string("hello")`)
    41  }
    42  
    43  func (*ModelSerializationSuite) TestUnknownVersion(c *gc.C) {
    44  	_, err := importModel(map[string]interface{}{
    45  		"version": 42,
    46  	})
    47  	c.Check(err.Error(), gc.Equals, `version 42 not valid`)
    48  }
    49  
    50  func (*ModelSerializationSuite) TestUpdateConfig(c *gc.C) {
    51  	model := NewModel(ModelArgs{
    52  		Config: map[string]interface{}{
    53  			"name": "awesome",
    54  			"uuid": "some-uuid",
    55  		},
    56  		CloudRegion: "some-region",
    57  	})
    58  	model.UpdateConfig(map[string]interface{}{
    59  		"name": "something else",
    60  		"key":  "value",
    61  	})
    62  	c.Assert(model.Config(), jc.DeepEquals, map[string]interface{}{
    63  		"name": "something else",
    64  		"uuid": "some-uuid",
    65  		"key":  "value",
    66  	})
    67  }
    68  
    69  func (s *ModelSerializationSuite) exportImport(c *gc.C, initial Model) Model {
    70  	bytes, err := Serialize(initial)
    71  	c.Assert(err, jc.ErrorIsNil)
    72  	model, err := Deserialize(bytes)
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	return model
    75  }
    76  
    77  func (s *ModelSerializationSuite) TestParsingYAML(c *gc.C) {
    78  	args := ModelArgs{
    79  		Owner: names.NewUserTag("magic"),
    80  		Config: map[string]interface{}{
    81  			"name": "awesome",
    82  			"uuid": "some-uuid",
    83  		},
    84  		LatestToolsVersion: version.MustParse("2.0.1"),
    85  		Blocks: map[string]string{
    86  			"all-changes": "locked down",
    87  		},
    88  	}
    89  	initial := NewModel(args)
    90  	adminUser := names.NewUserTag("admin@local")
    91  	initial.AddUser(UserArgs{
    92  		Name:        adminUser,
    93  		CreatedBy:   adminUser,
    94  		DateCreated: time.Date(2015, 10, 9, 12, 34, 56, 0, time.UTC),
    95  	})
    96  	addMinimalMachine(initial, "0")
    97  	addMinimalApplication(initial)
    98  	model := s.exportImport(c, initial)
    99  
   100  	c.Assert(model.Owner(), gc.Equals, args.Owner)
   101  	c.Assert(model.Tag().Id(), gc.Equals, "some-uuid")
   102  	c.Assert(model.Config(), jc.DeepEquals, args.Config)
   103  	c.Assert(model.LatestToolsVersion(), gc.Equals, args.LatestToolsVersion)
   104  	c.Assert(model.Blocks(), jc.DeepEquals, args.Blocks)
   105  	users := model.Users()
   106  	c.Assert(users, gc.HasLen, 1)
   107  	c.Assert(users[0].Name(), gc.Equals, adminUser)
   108  	machines := model.Machines()
   109  	c.Assert(machines, gc.HasLen, 1)
   110  	c.Assert(machines[0].Id(), gc.Equals, "0")
   111  	applications := model.Applications()
   112  	c.Assert(applications, gc.HasLen, 1)
   113  	c.Assert(applications[0].Name(), gc.Equals, "ubuntu")
   114  }
   115  
   116  func (s *ModelSerializationSuite) TestParsingOptionals(c *gc.C) {
   117  	args := ModelArgs{
   118  		Owner: names.NewUserTag("magic"),
   119  		Config: map[string]interface{}{
   120  			"name": "awesome",
   121  			"uuid": "some-uuid",
   122  		},
   123  	}
   124  	initial := NewModel(args)
   125  	model := s.exportImport(c, initial)
   126  	c.Assert(model.LatestToolsVersion(), gc.Equals, version.Zero)
   127  }
   128  
   129  func (s *ModelSerializationSuite) TestAnnotations(c *gc.C) {
   130  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   131  	annotations := map[string]string{
   132  		"string":  "value",
   133  		"another": "one",
   134  	}
   135  	initial.SetAnnotations(annotations)
   136  
   137  	bytes, err := yaml.Marshal(initial)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  
   140  	model, err := Deserialize(bytes)
   141  	c.Assert(err, jc.ErrorIsNil)
   142  	c.Assert(model.Annotations(), jc.DeepEquals, annotations)
   143  }
   144  
   145  func (s *ModelSerializationSuite) TestSequences(c *gc.C) {
   146  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   147  	initial.SetSequence("machine", 4)
   148  	initial.SetSequence("application-foo", 3)
   149  	initial.SetSequence("application-bar", 1)
   150  	bytes, err := yaml.Marshal(initial)
   151  	c.Assert(err, jc.ErrorIsNil)
   152  
   153  	model, err := Deserialize(bytes)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  
   156  	c.Assert(model.Sequences(), jc.DeepEquals, map[string]int{
   157  		"machine":         4,
   158  		"application-foo": 3,
   159  		"application-bar": 1,
   160  	})
   161  }
   162  
   163  func (s *ModelSerializationSuite) TestConstraints(c *gc.C) {
   164  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   165  	args := ConstraintsArgs{
   166  		Architecture: "amd64",
   167  		Memory:       8 * gig,
   168  	}
   169  	initial.SetConstraints(args)
   170  
   171  	bytes, err := yaml.Marshal(initial)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  
   174  	model, err := Deserialize(bytes)
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	c.Assert(model.Constraints(), jc.DeepEquals, newConstraints(args))
   177  }
   178  
   179  func (*ModelSerializationSuite) TestModelValidation(c *gc.C) {
   180  	model := NewModel(ModelArgs{})
   181  	err := model.Validate()
   182  	c.Assert(err, gc.ErrorMatches, "missing model owner not valid")
   183  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   184  }
   185  
   186  func (*ModelSerializationSuite) TestModelValidationChecksMachines(c *gc.C) {
   187  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner"), CloudRegion: "some-region"})
   188  	model.AddMachine(MachineArgs{})
   189  	err := model.Validate()
   190  	c.Assert(err, gc.ErrorMatches, "machine missing id not valid")
   191  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   192  }
   193  
   194  func (s *ModelSerializationSuite) addMachineToModel(model Model, id string) Machine {
   195  	machine := model.AddMachine(MachineArgs{Id: names.NewMachineTag(id)})
   196  	machine.SetInstance(CloudInstanceArgs{InstanceId: "magic"})
   197  	machine.SetTools(minimalAgentToolsArgs())
   198  	machine.SetStatus(minimalStatusArgs())
   199  	return machine
   200  }
   201  
   202  func (s *ModelSerializationSuite) TestModelValidationChecksMachinesGood(c *gc.C) {
   203  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner"), CloudRegion: "some-region"})
   204  	s.addMachineToModel(model, "0")
   205  	err := model.Validate()
   206  	c.Assert(err, jc.ErrorIsNil)
   207  }
   208  
   209  func (s *ModelSerializationSuite) TestModelValidationChecksOpenPortsUnits(c *gc.C) {
   210  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner"), CloudRegion: "some-region"})
   211  	machine := s.addMachineToModel(model, "0")
   212  	machine.AddOpenedPorts(OpenedPortsArgs{
   213  		OpenedPorts: []PortRangeArgs{
   214  			{
   215  				UnitName: "missing/0",
   216  				FromPort: 8080,
   217  				ToPort:   8080,
   218  				Protocol: "tcp",
   219  			},
   220  		},
   221  	})
   222  	err := model.Validate()
   223  	c.Assert(err.Error(), gc.Equals, "unknown unit names in open ports: [missing/0]")
   224  }
   225  
   226  func (*ModelSerializationSuite) TestModelValidationChecksApplications(c *gc.C) {
   227  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner"), CloudRegion: "some-region"})
   228  	model.AddApplication(ApplicationArgs{})
   229  	err := model.Validate()
   230  	c.Assert(err, gc.ErrorMatches, "application missing name not valid")
   231  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   232  }
   233  
   234  func (s *ModelSerializationSuite) addApplicationToModel(model Model, name string, numUnits int) Application {
   235  	application := model.AddApplication(ApplicationArgs{
   236  		Tag:                names.NewApplicationTag(name),
   237  		Settings:           map[string]interface{}{},
   238  		LeadershipSettings: map[string]interface{}{},
   239  	})
   240  	application.SetStatus(minimalStatusArgs())
   241  	for i := 0; i < numUnits; i++ {
   242  		// The index i is used as both the machine id and the unit id.
   243  		// A happy coincidence.
   244  		machine := s.addMachineToModel(model, fmt.Sprint(i))
   245  		unit := application.AddUnit(UnitArgs{
   246  			Tag:     names.NewUnitTag(fmt.Sprintf("%s/%d", name, i)),
   247  			Machine: machine.Tag(),
   248  		})
   249  		unit.SetTools(minimalAgentToolsArgs())
   250  		unit.SetAgentStatus(minimalStatusArgs())
   251  		unit.SetWorkloadStatus(minimalStatusArgs())
   252  	}
   253  
   254  	return application
   255  }
   256  
   257  func (s *ModelSerializationSuite) wordpressModel() (Model, Endpoint, Endpoint) {
   258  	model := NewModel(ModelArgs{
   259  		Owner: names.NewUserTag("owner"),
   260  		Config: map[string]interface{}{
   261  			"uuid": "some-uuid",
   262  		},
   263  		CloudRegion: "some-region",
   264  	})
   265  	s.addApplicationToModel(model, "wordpress", 2)
   266  	s.addApplicationToModel(model, "mysql", 1)
   267  
   268  	// Add a relation between wordpress and mysql.
   269  	rel := model.AddRelation(RelationArgs{
   270  		Id:  42,
   271  		Key: "special key",
   272  	})
   273  	wordpressEndpoint := rel.AddEndpoint(EndpointArgs{
   274  		ApplicationName: "wordpress",
   275  		Name:            "db",
   276  		// Ignoring other aspects of endpoints.
   277  	})
   278  	mysqlEndpoint := rel.AddEndpoint(EndpointArgs{
   279  		ApplicationName: "mysql",
   280  		Name:            "mysql",
   281  		// Ignoring other aspects of endpoints.
   282  	})
   283  	return model, wordpressEndpoint, mysqlEndpoint
   284  }
   285  
   286  func (s *ModelSerializationSuite) wordpressModelWithSettings() Model {
   287  	model, wordpressEndpoint, mysqlEndpoint := s.wordpressModel()
   288  
   289  	wordpressEndpoint.SetUnitSettings("wordpress/0", map[string]interface{}{
   290  		"key": "value",
   291  	})
   292  	wordpressEndpoint.SetUnitSettings("wordpress/1", map[string]interface{}{
   293  		"key": "value",
   294  	})
   295  	mysqlEndpoint.SetUnitSettings("mysql/0", map[string]interface{}{
   296  		"key": "value",
   297  	})
   298  	return model
   299  }
   300  
   301  func (s *ModelSerializationSuite) TestModelValidationChecksRelationsMissingSettings(c *gc.C) {
   302  	model, _, _ := s.wordpressModel()
   303  	err := model.Validate()
   304  	c.Assert(err, gc.ErrorMatches, "missing relation settings for units \\[wordpress/0 wordpress/1\\] in relation 42")
   305  }
   306  
   307  func (s *ModelSerializationSuite) TestModelValidationChecksRelationsMissingSettings2(c *gc.C) {
   308  	model, wordpressEndpoint, _ := s.wordpressModel()
   309  
   310  	wordpressEndpoint.SetUnitSettings("wordpress/0", map[string]interface{}{
   311  		"key": "value",
   312  	})
   313  	wordpressEndpoint.SetUnitSettings("wordpress/1", map[string]interface{}{
   314  		"key": "value",
   315  	})
   316  	err := model.Validate()
   317  	c.Assert(err, gc.ErrorMatches, "missing relation settings for units \\[mysql/0\\] in relation 42")
   318  }
   319  
   320  func (s *ModelSerializationSuite) TestModelValidationChecksRelations(c *gc.C) {
   321  	model := s.wordpressModelWithSettings()
   322  	err := model.Validate()
   323  	c.Assert(err, jc.ErrorIsNil)
   324  }
   325  
   326  func (s *ModelSerializationSuite) TestModelSerializationWithRelations(c *gc.C) {
   327  	initial := s.wordpressModelWithSettings()
   328  	bytes, err := yaml.Marshal(initial)
   329  	c.Assert(err, jc.ErrorIsNil)
   330  	model, err := Deserialize(bytes)
   331  	c.Assert(err, jc.ErrorIsNil)
   332  	c.Assert(model, jc.DeepEquals, initial)
   333  }
   334  
   335  func (s *ModelSerializationSuite) TestModelValidationChecksSubnets(c *gc.C) {
   336  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   337  	model.AddSubnet(SubnetArgs{CIDR: "10.0.0.0/24", SpaceName: "foo"})
   338  	model.AddSubnet(SubnetArgs{CIDR: "10.0.1.0/24"})
   339  	err := model.Validate()
   340  	c.Assert(err, gc.ErrorMatches, `subnet "10.0.0.0/24" references non-existent space "foo"`)
   341  	model.AddSpace(SpaceArgs{Name: "foo"})
   342  	err = model.Validate()
   343  	c.Assert(err, jc.ErrorIsNil)
   344  }
   345  
   346  func (s *ModelSerializationSuite) TestModelValidationChecksAddressMachineID(c *gc.C) {
   347  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   348  	model.AddIPAddress(IPAddressArgs{Value: "192.168.1.0", MachineID: "42"})
   349  	err := model.Validate()
   350  	c.Assert(err, gc.ErrorMatches, `ip address "192.168.1.0" references non-existent machine "42"`)
   351  }
   352  
   353  func (s *ModelSerializationSuite) TestModelValidationChecksAddressDeviceName(c *gc.C) {
   354  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   355  	args := IPAddressArgs{Value: "192.168.1.0", MachineID: "42", DeviceName: "foo"}
   356  	model.AddIPAddress(args)
   357  	s.addMachineToModel(model, "42")
   358  	err := model.Validate()
   359  	c.Assert(err, gc.ErrorMatches, `ip address "192.168.1.0" references non-existent device "foo"`)
   360  }
   361  
   362  func (s *ModelSerializationSuite) TestModelValidationChecksAddressValueEmpty(c *gc.C) {
   363  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   364  	args := IPAddressArgs{MachineID: "42", DeviceName: "foo"}
   365  	model.AddIPAddress(args)
   366  	s.addMachineToModel(model, "42")
   367  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   368  	err := model.Validate()
   369  	c.Assert(err, gc.ErrorMatches, `ip address has invalid value ""`)
   370  }
   371  
   372  func (s *ModelSerializationSuite) TestModelValidationChecksAddressValueInvalid(c *gc.C) {
   373  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   374  	args := IPAddressArgs{MachineID: "42", DeviceName: "foo", Value: "foobar"}
   375  	model.AddIPAddress(args)
   376  	s.addMachineToModel(model, "42")
   377  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   378  	err := model.Validate()
   379  	c.Assert(err, gc.ErrorMatches, `ip address has invalid value "foobar"`)
   380  }
   381  
   382  func (s *ModelSerializationSuite) TestModelValidationChecksAddressSubnetEmpty(c *gc.C) {
   383  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   384  	args := IPAddressArgs{MachineID: "42", DeviceName: "foo", Value: "192.168.1.1"}
   385  	model.AddIPAddress(args)
   386  	s.addMachineToModel(model, "42")
   387  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   388  	err := model.Validate()
   389  	c.Assert(err, gc.ErrorMatches, `ip address "192.168.1.1" has empty subnet CIDR`)
   390  }
   391  
   392  func (s *ModelSerializationSuite) TestModelValidationChecksAddressSubnetInvalid(c *gc.C) {
   393  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   394  	args := IPAddressArgs{
   395  		MachineID:  "42",
   396  		DeviceName: "foo",
   397  		Value:      "192.168.1.1",
   398  		SubnetCIDR: "foo",
   399  	}
   400  	model.AddIPAddress(args)
   401  	s.addMachineToModel(model, "42")
   402  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   403  	err := model.Validate()
   404  	c.Assert(err, gc.ErrorMatches, `ip address "192.168.1.1" has invalid subnet CIDR "foo"`)
   405  }
   406  
   407  func (s *ModelSerializationSuite) TestModelValidationChecksAddressSucceeds(c *gc.C) {
   408  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   409  	args := IPAddressArgs{
   410  		MachineID:  "42",
   411  		DeviceName: "foo",
   412  		Value:      "192.168.1.1",
   413  		SubnetCIDR: "192.168.1.0/24",
   414  	}
   415  	model.AddIPAddress(args)
   416  	s.addMachineToModel(model, "42")
   417  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   418  	err := model.Validate()
   419  	c.Assert(err, jc.ErrorIsNil)
   420  }
   421  
   422  func (s *ModelSerializationSuite) TestModelValidationChecksAddressGatewayAddressInvalid(c *gc.C) {
   423  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   424  	args := IPAddressArgs{
   425  		MachineID:      "42",
   426  		DeviceName:     "foo",
   427  		Value:          "192.168.1.1",
   428  		SubnetCIDR:     "192.168.1.0/24",
   429  		GatewayAddress: "foo",
   430  	}
   431  	model.AddIPAddress(args)
   432  	s.addMachineToModel(model, "42")
   433  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   434  	err := model.Validate()
   435  	c.Assert(err, gc.ErrorMatches, `ip address "192.168.1.1" has invalid gateway address "foo"`)
   436  }
   437  
   438  func (s *ModelSerializationSuite) TestModelValidationChecksAddressGatewayAddressValid(c *gc.C) {
   439  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   440  	args := IPAddressArgs{
   441  		MachineID:      "42",
   442  		DeviceName:     "foo",
   443  		Value:          "192.168.1.2",
   444  		SubnetCIDR:     "192.168.1.0/24",
   445  		GatewayAddress: "192.168.1.1",
   446  	}
   447  	model.AddIPAddress(args)
   448  	s.addMachineToModel(model, "42")
   449  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   450  	err := model.Validate()
   451  	c.Assert(err, jc.ErrorIsNil)
   452  }
   453  
   454  func (s *ModelSerializationSuite) TestModelValidationChecksLinkLayerDeviceMachineId(c *gc.C) {
   455  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   456  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo", MachineID: "42"})
   457  	err := model.Validate()
   458  	c.Assert(err, gc.ErrorMatches, `device "foo" references non-existent machine "42"`)
   459  	s.addMachineToModel(model, "42")
   460  	err = model.Validate()
   461  	c.Assert(err, jc.ErrorIsNil)
   462  }
   463  
   464  func (s *ModelSerializationSuite) TestModelValidationChecksLinkLayerName(c *gc.C) {
   465  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   466  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{MachineID: "42"})
   467  	s.addMachineToModel(model, "42")
   468  	err := model.Validate()
   469  	c.Assert(err, gc.ErrorMatches, "device has empty name.*")
   470  }
   471  
   472  func (s *ModelSerializationSuite) TestModelValidationChecksLinkLayerMACAddress(c *gc.C) {
   473  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   474  	args := LinkLayerDeviceArgs{MachineID: "42", Name: "foo", MACAddress: "DEADBEEF"}
   475  	model.AddLinkLayerDevice(args)
   476  	s.addMachineToModel(model, "42")
   477  	err := model.Validate()
   478  	c.Assert(err, gc.ErrorMatches, `device "foo" has invalid MACAddress "DEADBEEF"`)
   479  }
   480  
   481  func (s *ModelSerializationSuite) TestModelValidationChecksParentExists(c *gc.C) {
   482  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   483  	args := LinkLayerDeviceArgs{MachineID: "42", Name: "foo", ParentName: "bar", MACAddress: "01:23:45:67:89:ab"}
   484  	model.AddLinkLayerDevice(args)
   485  	s.addMachineToModel(model, "42")
   486  	err := model.Validate()
   487  	c.Assert(err, gc.ErrorMatches, `device "foo" has non-existent parent "bar"`)
   488  	model.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "bar", MachineID: "42"})
   489  	err = model.Validate()
   490  	c.Assert(err, jc.ErrorIsNil)
   491  }
   492  
   493  func (s *ModelSerializationSuite) TestModelValidationChecksParentIsNotItself(c *gc.C) {
   494  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   495  	args := LinkLayerDeviceArgs{MachineID: "42", Name: "foo", ParentName: "foo"}
   496  	model.AddLinkLayerDevice(args)
   497  	s.addMachineToModel(model, "42")
   498  	err := model.Validate()
   499  	c.Assert(err, gc.ErrorMatches, `device "foo" is its own parent`)
   500  }
   501  
   502  func (s *ModelSerializationSuite) TestModelValidationChecksParentIsABridge(c *gc.C) {
   503  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   504  	args := LinkLayerDeviceArgs{MachineID: "42", Name: "foo", ParentName: "m#43#d#bar"}
   505  	model.AddLinkLayerDevice(args)
   506  	args2 := LinkLayerDeviceArgs{MachineID: "43", Name: "bar"}
   507  	model.AddLinkLayerDevice(args2)
   508  	s.addMachineToModel(model, "42")
   509  	s.addMachineToModel(model, "43")
   510  	err := model.Validate()
   511  	c.Assert(err, gc.ErrorMatches, `device "foo" on a container but not a bridge`)
   512  }
   513  
   514  func (s *ModelSerializationSuite) TestModelValidationChecksChildDeviceContained(c *gc.C) {
   515  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   516  	args := LinkLayerDeviceArgs{MachineID: "42", Name: "foo", ParentName: "m#43#d#bar"}
   517  	model.AddLinkLayerDevice(args)
   518  	args2 := LinkLayerDeviceArgs{MachineID: "43", Name: "bar", Type: "bridge"}
   519  	model.AddLinkLayerDevice(args2)
   520  	s.addMachineToModel(model, "42")
   521  	s.addMachineToModel(model, "43")
   522  	err := model.Validate()
   523  	c.Assert(err, gc.ErrorMatches, `ParentName "m#43#d#bar" for non-container machine "42"`)
   524  }
   525  
   526  func (s *ModelSerializationSuite) TestModelValidationChecksParentOnHost(c *gc.C) {
   527  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   528  	args := LinkLayerDeviceArgs{MachineID: "41/lxd/0", Name: "foo", ParentName: "m#43#d#bar"}
   529  	model.AddLinkLayerDevice(args)
   530  	args2 := LinkLayerDeviceArgs{MachineID: "43", Name: "bar", Type: "bridge"}
   531  	model.AddLinkLayerDevice(args2)
   532  	machine := s.addMachineToModel(model, "41")
   533  	container := machine.AddContainer(MachineArgs{Id: names.NewMachineTag("41/lxd/0")})
   534  	container.SetInstance(CloudInstanceArgs{InstanceId: "magic"})
   535  	container.SetTools(minimalAgentToolsArgs())
   536  	container.SetStatus(minimalStatusArgs())
   537  	s.addMachineToModel(model, "43")
   538  	err := model.Validate()
   539  	c.Assert(err, gc.ErrorMatches, `parent machine of device "foo" not host machine "41"`)
   540  }
   541  
   542  func (s *ModelSerializationSuite) TestModelValidationLinkLayerDeviceContainer(c *gc.C) {
   543  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   544  	args := LinkLayerDeviceArgs{MachineID: "43/lxd/0", Name: "foo", ParentName: "m#43#d#bar"}
   545  	model.AddLinkLayerDevice(args)
   546  	args2 := LinkLayerDeviceArgs{MachineID: "43", Name: "bar", Type: "bridge"}
   547  	model.AddLinkLayerDevice(args2)
   548  	machine := s.addMachineToModel(model, "43")
   549  	container := machine.AddContainer(MachineArgs{Id: names.NewMachineTag("43/lxd/0")})
   550  	container.SetInstance(CloudInstanceArgs{InstanceId: "magic"})
   551  	container.SetTools(minimalAgentToolsArgs())
   552  	container.SetStatus(minimalStatusArgs())
   553  	err := model.Validate()
   554  	c.Assert(err, jc.ErrorIsNil)
   555  }
   556  
   557  func (s *ModelSerializationSuite) TestSpaces(c *gc.C) {
   558  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   559  	space := initial.AddSpace(SpaceArgs{Name: "special"})
   560  	c.Assert(space.Name(), gc.Equals, "special")
   561  	spaces := initial.Spaces()
   562  	c.Assert(spaces, gc.HasLen, 1)
   563  	c.Assert(spaces[0], gc.Equals, space)
   564  
   565  	bytes, err := yaml.Marshal(initial)
   566  	c.Assert(err, jc.ErrorIsNil)
   567  
   568  	model, err := Deserialize(bytes)
   569  	c.Assert(err, jc.ErrorIsNil)
   570  	c.Assert(model.Spaces(), jc.DeepEquals, spaces)
   571  
   572  }
   573  
   574  func (s *ModelSerializationSuite) TestLinkLayerDevice(c *gc.C) {
   575  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   576  	device := initial.AddLinkLayerDevice(LinkLayerDeviceArgs{Name: "foo"})
   577  	c.Assert(device.Name(), gc.Equals, "foo")
   578  	devices := initial.LinkLayerDevices()
   579  	c.Assert(devices, gc.HasLen, 1)
   580  	c.Assert(devices[0], jc.DeepEquals, device)
   581  
   582  	bytes, err := yaml.Marshal(initial)
   583  	c.Assert(err, jc.ErrorIsNil)
   584  
   585  	model, err := Deserialize(bytes)
   586  	c.Assert(err, jc.ErrorIsNil)
   587  	c.Assert(model.LinkLayerDevices(), jc.DeepEquals, devices)
   588  }
   589  
   590  func (s *ModelSerializationSuite) TestSubnets(c *gc.C) {
   591  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   592  	subnet := initial.AddSubnet(SubnetArgs{CIDR: "10.0.0.0/24"})
   593  	c.Assert(subnet.CIDR(), gc.Equals, "10.0.0.0/24")
   594  	subnets := initial.Subnets()
   595  	c.Assert(subnets, gc.HasLen, 1)
   596  	c.Assert(subnets[0], jc.DeepEquals, subnet)
   597  
   598  	bytes, err := yaml.Marshal(initial)
   599  	c.Assert(err, jc.ErrorIsNil)
   600  
   601  	model, err := Deserialize(bytes)
   602  	c.Assert(err, jc.ErrorIsNil)
   603  	c.Assert(model.Subnets(), jc.DeepEquals, subnets)
   604  }
   605  
   606  func (s *ModelSerializationSuite) TestIPAddress(c *gc.C) {
   607  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   608  	addr := initial.AddIPAddress(IPAddressArgs{Value: "10.0.0.4"})
   609  	c.Assert(addr.Value(), gc.Equals, "10.0.0.4")
   610  	addresses := initial.IPAddresses()
   611  	c.Assert(addresses, gc.HasLen, 1)
   612  	c.Assert(addresses[0], jc.DeepEquals, addr)
   613  
   614  	bytes, err := yaml.Marshal(initial)
   615  	c.Assert(err, jc.ErrorIsNil)
   616  
   617  	model, err := Deserialize(bytes)
   618  	c.Assert(err, jc.ErrorIsNil)
   619  	c.Assert(model.IPAddresses(), jc.DeepEquals, addresses)
   620  }
   621  
   622  func (s *ModelSerializationSuite) TestSSHHostKey(c *gc.C) {
   623  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   624  	key := initial.AddSSHHostKey(SSHHostKeyArgs{MachineID: "foo"})
   625  	c.Assert(key.MachineID(), gc.Equals, "foo")
   626  	keys := initial.SSHHostKeys()
   627  	c.Assert(keys, gc.HasLen, 1)
   628  	c.Assert(keys[0], jc.DeepEquals, key)
   629  
   630  	bytes, err := yaml.Marshal(initial)
   631  	c.Assert(err, jc.ErrorIsNil)
   632  
   633  	model, err := Deserialize(bytes)
   634  	c.Assert(err, jc.ErrorIsNil)
   635  	c.Assert(model.SSHHostKeys(), jc.DeepEquals, keys)
   636  }
   637  
   638  func (s *ModelSerializationSuite) TestCloudImageMetadata(c *gc.C) {
   639  	storageSize := uint64(3)
   640  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   641  	image := initial.AddCloudImageMetadata(CloudImageMetadataArgs{
   642  		Stream:          "stream",
   643  		Region:          "region-test",
   644  		Version:         "14.04",
   645  		Series:          "trusty",
   646  		Arch:            "arch",
   647  		VirtType:        "virtType-test",
   648  		RootStorageType: "rootStorageType-test",
   649  		RootStorageSize: &storageSize,
   650  		Source:          "test",
   651  		Priority:        2,
   652  		ImageId:         "1",
   653  		DateCreated:     2,
   654  	})
   655  	c.Assert(image.Stream(), gc.Equals, "stream")
   656  	c.Assert(image.Region(), gc.Equals, "region-test")
   657  	c.Assert(image.Version(), gc.Equals, "14.04")
   658  	c.Assert(image.Arch(), gc.Equals, "arch")
   659  	c.Assert(image.VirtType(), gc.Equals, "virtType-test")
   660  	c.Assert(image.RootStorageType(), gc.Equals, "rootStorageType-test")
   661  	value, ok := image.RootStorageSize()
   662  	c.Assert(ok, jc.IsTrue)
   663  	c.Assert(value, gc.Equals, uint64(3))
   664  	c.Assert(image.Source(), gc.Equals, "test")
   665  	c.Assert(image.Priority(), gc.Equals, 2)
   666  	c.Assert(image.ImageId(), gc.Equals, "1")
   667  	c.Assert(image.DateCreated(), gc.Equals, int64(2))
   668  
   669  	metadata := initial.CloudImageMetadata()
   670  	c.Assert(metadata, gc.HasLen, 1)
   671  	c.Assert(metadata[0], jc.DeepEquals, image)
   672  
   673  	bytes, err := yaml.Marshal(initial)
   674  	c.Assert(err, jc.ErrorIsNil)
   675  
   676  	model, err := Deserialize(bytes)
   677  	c.Assert(err, jc.ErrorIsNil)
   678  	c.Assert(model.CloudImageMetadata(), jc.DeepEquals, metadata)
   679  }
   680  
   681  func (s *ModelSerializationSuite) TestAction(c *gc.C) {
   682  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   683  	enqueued := time.Now().UTC()
   684  	action := initial.AddAction(ActionArgs{
   685  		Name:       "foo",
   686  		Enqueued:   enqueued,
   687  		Parameters: map[string]interface{}{},
   688  		Results:    map[string]interface{}{},
   689  	})
   690  	c.Assert(action.Name(), gc.Equals, "foo")
   691  	c.Assert(action.Enqueued(), gc.Equals, enqueued)
   692  	actions := initial.Actions()
   693  	c.Assert(actions, gc.HasLen, 1)
   694  	c.Assert(actions[0], jc.DeepEquals, action)
   695  
   696  	bytes, err := yaml.Marshal(initial)
   697  	c.Assert(err, jc.ErrorIsNil)
   698  
   699  	model, err := Deserialize(bytes)
   700  	c.Assert(err, jc.ErrorIsNil)
   701  	c.Assert(model.Actions(), jc.DeepEquals, actions)
   702  }
   703  
   704  func (s *ModelSerializationSuite) TestVolumeValidation(c *gc.C) {
   705  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   706  	model.AddVolume(testVolumeArgs())
   707  	err := model.Validate()
   708  	c.Assert(err, gc.ErrorMatches, `volume\[0\]: volume "1234" missing status not valid`)
   709  }
   710  
   711  func (s *ModelSerializationSuite) TestVolumes(c *gc.C) {
   712  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   713  	volume := initial.AddVolume(testVolumeArgs())
   714  	volume.SetStatus(minimalStatusArgs())
   715  	volumes := initial.Volumes()
   716  	c.Assert(volumes, gc.HasLen, 1)
   717  	c.Assert(volumes[0], gc.Equals, volume)
   718  
   719  	bytes, err := yaml.Marshal(initial)
   720  	c.Assert(err, jc.ErrorIsNil)
   721  
   722  	model, err := Deserialize(bytes)
   723  	c.Assert(err, jc.ErrorIsNil)
   724  	c.Assert(model.Volumes(), jc.DeepEquals, volumes)
   725  }
   726  
   727  func (s *ModelSerializationSuite) TestFilesystemValidation(c *gc.C) {
   728  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   729  	model.AddFilesystem(testFilesystemArgs())
   730  	err := model.Validate()
   731  	c.Assert(err, gc.ErrorMatches, `filesystem\[0\]: filesystem "1234" missing status not valid`)
   732  }
   733  
   734  func (s *ModelSerializationSuite) TestFilesystems(c *gc.C) {
   735  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   736  	filesystem := initial.AddFilesystem(testFilesystemArgs())
   737  	filesystem.SetStatus(minimalStatusArgs())
   738  	filesystem.AddAttachment(testFilesystemAttachmentArgs())
   739  	filesystems := initial.Filesystems()
   740  	c.Assert(filesystems, gc.HasLen, 1)
   741  	c.Assert(filesystems[0], gc.Equals, filesystem)
   742  
   743  	bytes, err := yaml.Marshal(initial)
   744  	c.Assert(err, jc.ErrorIsNil)
   745  
   746  	model, err := Deserialize(bytes)
   747  	c.Assert(err, jc.ErrorIsNil)
   748  	c.Assert(model.Filesystems(), jc.DeepEquals, filesystems)
   749  }
   750  
   751  func (s *ModelSerializationSuite) TestStorage(c *gc.C) {
   752  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   753  	storage := initial.AddStorage(testStorageArgs())
   754  	storages := initial.Storages()
   755  	c.Assert(storages, gc.HasLen, 1)
   756  	c.Assert(storages[0], jc.DeepEquals, storage)
   757  
   758  	bytes, err := yaml.Marshal(initial)
   759  	c.Assert(err, jc.ErrorIsNil)
   760  
   761  	model, err := Deserialize(bytes)
   762  	c.Assert(err, jc.ErrorIsNil)
   763  	c.Assert(model.Storages(), jc.DeepEquals, storages)
   764  }
   765  
   766  func (s *ModelSerializationSuite) TestStoragePools(c *gc.C) {
   767  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   768  	poolOne := map[string]interface{}{
   769  		"foo":   42,
   770  		"value": true,
   771  	}
   772  	poolTwo := map[string]interface{}{
   773  		"value": "spanner",
   774  	}
   775  	initial.AddStoragePool(StoragePoolArgs{
   776  		Name: "one", Provider: "sparkles", Attributes: poolOne})
   777  	initial.AddStoragePool(StoragePoolArgs{
   778  		Name: "two", Provider: "spanner", Attributes: poolTwo})
   779  
   780  	pools := initial.StoragePools()
   781  	c.Assert(pools, gc.HasLen, 2)
   782  	one, two := pools[0], pools[1]
   783  	c.Check(one.Name(), gc.Equals, "one")
   784  	c.Check(one.Provider(), gc.Equals, "sparkles")
   785  	c.Check(one.Attributes(), jc.DeepEquals, poolOne)
   786  	c.Check(two.Name(), gc.Equals, "two")
   787  	c.Check(two.Provider(), gc.Equals, "spanner")
   788  	c.Check(two.Attributes(), jc.DeepEquals, poolTwo)
   789  
   790  	bytes, err := yaml.Marshal(initial)
   791  	c.Assert(err, jc.ErrorIsNil)
   792  
   793  	model, err := Deserialize(bytes)
   794  	c.Assert(err, jc.ErrorIsNil)
   795  
   796  	pools = model.StoragePools()
   797  	c.Assert(pools, gc.HasLen, 2)
   798  	one, two = pools[0], pools[1]
   799  	c.Check(one.Name(), gc.Equals, "one")
   800  	c.Check(one.Provider(), gc.Equals, "sparkles")
   801  	c.Check(one.Attributes(), jc.DeepEquals, poolOne)
   802  	c.Check(two.Name(), gc.Equals, "two")
   803  	c.Check(two.Provider(), gc.Equals, "spanner")
   804  	c.Check(two.Attributes(), jc.DeepEquals, poolTwo)
   805  }