github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"github.com/juju/names"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/version"
    14  	gc "gopkg.in/check.v1"
    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{Config: map[string]interface{}{
    52  		"name": "awesome",
    53  		"uuid": "some-uuid",
    54  	}})
    55  	model.UpdateConfig(map[string]interface{}{
    56  		"name": "something else",
    57  		"key":  "value",
    58  	})
    59  	c.Assert(model.Config(), jc.DeepEquals, map[string]interface{}{
    60  		"name": "something else",
    61  		"uuid": "some-uuid",
    62  		"key":  "value",
    63  	})
    64  }
    65  
    66  func (s *ModelSerializationSuite) exportImport(c *gc.C, initial Model) Model {
    67  	bytes, err := Serialize(initial)
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	model, err := Deserialize(bytes)
    70  	c.Assert(err, jc.ErrorIsNil)
    71  	return model
    72  }
    73  
    74  func (s *ModelSerializationSuite) TestParsingYAML(c *gc.C) {
    75  	args := ModelArgs{
    76  		Owner: names.NewUserTag("magic"),
    77  		Config: map[string]interface{}{
    78  			"name": "awesome",
    79  			"uuid": "some-uuid",
    80  		},
    81  		LatestToolsVersion: version.MustParse("2.0.1"),
    82  		Blocks: map[string]string{
    83  			"all-changes": "locked down",
    84  		},
    85  	}
    86  	initial := NewModel(args)
    87  	adminUser := names.NewUserTag("admin@local")
    88  	initial.AddUser(UserArgs{
    89  		Name:        adminUser,
    90  		CreatedBy:   adminUser,
    91  		DateCreated: time.Date(2015, 10, 9, 12, 34, 56, 0, time.UTC),
    92  	})
    93  	addMinimalMachine(initial, "0")
    94  	addMinimalService(initial)
    95  	model := s.exportImport(c, initial)
    96  
    97  	c.Assert(model.Owner(), gc.Equals, args.Owner)
    98  	c.Assert(model.Tag().Id(), gc.Equals, "some-uuid")
    99  	c.Assert(model.Config(), jc.DeepEquals, args.Config)
   100  	c.Assert(model.LatestToolsVersion(), gc.Equals, args.LatestToolsVersion)
   101  	c.Assert(model.Blocks(), jc.DeepEquals, args.Blocks)
   102  	users := model.Users()
   103  	c.Assert(users, gc.HasLen, 1)
   104  	c.Assert(users[0].Name(), gc.Equals, adminUser)
   105  	machines := model.Machines()
   106  	c.Assert(machines, gc.HasLen, 1)
   107  	c.Assert(machines[0].Id(), gc.Equals, "0")
   108  	services := model.Services()
   109  	c.Assert(services, gc.HasLen, 1)
   110  	c.Assert(services[0].Name(), gc.Equals, "ubuntu")
   111  }
   112  
   113  func (s *ModelSerializationSuite) TestParsingOptionals(c *gc.C) {
   114  	args := ModelArgs{
   115  		Owner: names.NewUserTag("magic"),
   116  		Config: map[string]interface{}{
   117  			"name": "awesome",
   118  			"uuid": "some-uuid",
   119  		},
   120  	}
   121  	initial := NewModel(args)
   122  	model := s.exportImport(c, initial)
   123  	c.Assert(model.LatestToolsVersion(), gc.Equals, version.Zero)
   124  }
   125  
   126  func (s *ModelSerializationSuite) TestAnnotations(c *gc.C) {
   127  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   128  	annotations := map[string]string{
   129  		"string":  "value",
   130  		"another": "one",
   131  	}
   132  	initial.SetAnnotations(annotations)
   133  
   134  	bytes, err := yaml.Marshal(initial)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  
   137  	model, err := Deserialize(bytes)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	c.Assert(model.Annotations(), jc.DeepEquals, annotations)
   140  }
   141  
   142  func (s *ModelSerializationSuite) TestSequences(c *gc.C) {
   143  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   144  	initial.SetSequence("machine", 4)
   145  	initial.SetSequence("service-foo", 3)
   146  	initial.SetSequence("service-bar", 1)
   147  	bytes, err := yaml.Marshal(initial)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  
   150  	model, err := Deserialize(bytes)
   151  	c.Assert(err, jc.ErrorIsNil)
   152  
   153  	c.Assert(model.Sequences(), jc.DeepEquals, map[string]int{
   154  		"machine":     4,
   155  		"service-foo": 3,
   156  		"service-bar": 1,
   157  	})
   158  }
   159  
   160  func (s *ModelSerializationSuite) TestConstraints(c *gc.C) {
   161  	initial := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   162  	args := ConstraintsArgs{
   163  		Architecture: "amd64",
   164  		Memory:       8 * gig,
   165  	}
   166  	initial.SetConstraints(args)
   167  
   168  	bytes, err := yaml.Marshal(initial)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  
   171  	model, err := Deserialize(bytes)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	c.Assert(model.Constraints(), jc.DeepEquals, newConstraints(args))
   174  }
   175  
   176  func (*ModelSerializationSuite) TestModelValidation(c *gc.C) {
   177  	model := NewModel(ModelArgs{})
   178  	err := model.Validate()
   179  	c.Assert(err, gc.ErrorMatches, "missing model owner not valid")
   180  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   181  }
   182  
   183  func (*ModelSerializationSuite) TestModelValidationChecksMachines(c *gc.C) {
   184  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   185  	model.AddMachine(MachineArgs{})
   186  	err := model.Validate()
   187  	c.Assert(err, gc.ErrorMatches, "machine missing id not valid")
   188  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   189  }
   190  
   191  func (s *ModelSerializationSuite) addMachineToModel(model Model, id string) Machine {
   192  	machine := model.AddMachine(MachineArgs{Id: names.NewMachineTag(id)})
   193  	machine.SetInstance(CloudInstanceArgs{InstanceId: "magic"})
   194  	machine.SetTools(minimalAgentToolsArgs())
   195  	machine.SetStatus(minimalStatusArgs())
   196  	return machine
   197  }
   198  
   199  func (s *ModelSerializationSuite) TestModelValidationChecksMachinesGood(c *gc.C) {
   200  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   201  	s.addMachineToModel(model, "0")
   202  	err := model.Validate()
   203  	c.Assert(err, jc.ErrorIsNil)
   204  }
   205  
   206  func (s *ModelSerializationSuite) TestModelValidationChecksOpenPortsUnits(c *gc.C) {
   207  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   208  	machine := s.addMachineToModel(model, "0")
   209  	machine.AddOpenedPorts(OpenedPortsArgs{
   210  		OpenedPorts: []PortRangeArgs{
   211  			{
   212  				UnitName: "missing/0",
   213  				FromPort: 8080,
   214  				ToPort:   8080,
   215  				Protocol: "tcp",
   216  			},
   217  		},
   218  	})
   219  	err := model.Validate()
   220  	c.Assert(err.Error(), gc.Equals, "unknown unit names in open ports: [missing/0]")
   221  }
   222  
   223  func (*ModelSerializationSuite) TestModelValidationChecksServices(c *gc.C) {
   224  	model := NewModel(ModelArgs{Owner: names.NewUserTag("owner")})
   225  	model.AddService(ServiceArgs{})
   226  	err := model.Validate()
   227  	c.Assert(err, gc.ErrorMatches, "service missing name not valid")
   228  	c.Assert(err, jc.Satisfies, errors.IsNotValid)
   229  }
   230  
   231  func (s *ModelSerializationSuite) addServiceToModel(model Model, name string, numUnits int) Service {
   232  	service := model.AddService(ServiceArgs{
   233  		Tag:                names.NewServiceTag(name),
   234  		Settings:           map[string]interface{}{},
   235  		LeadershipSettings: map[string]interface{}{},
   236  	})
   237  	service.SetStatus(minimalStatusArgs())
   238  	for i := 0; i < numUnits; i++ {
   239  		// The index i is used as both the machine id and the unit id.
   240  		// A happy coincidence.
   241  		machine := s.addMachineToModel(model, fmt.Sprint(i))
   242  		unit := service.AddUnit(UnitArgs{
   243  			Tag:     names.NewUnitTag(fmt.Sprintf("%s/%d", name, i)),
   244  			Machine: machine.Tag(),
   245  		})
   246  		unit.SetTools(minimalAgentToolsArgs())
   247  		unit.SetAgentStatus(minimalStatusArgs())
   248  		unit.SetWorkloadStatus(minimalStatusArgs())
   249  	}
   250  
   251  	return service
   252  }
   253  
   254  func (s *ModelSerializationSuite) wordpressModel() (Model, Endpoint, Endpoint) {
   255  	model := NewModel(ModelArgs{
   256  		Owner: names.NewUserTag("owner"),
   257  		Config: map[string]interface{}{
   258  			"uuid": "some-uuid",
   259  		}})
   260  	s.addServiceToModel(model, "wordpress", 2)
   261  	s.addServiceToModel(model, "mysql", 1)
   262  
   263  	// Add a relation between wordpress and mysql.
   264  	rel := model.AddRelation(RelationArgs{
   265  		Id:  42,
   266  		Key: "special key",
   267  	})
   268  	wordpressEndpoint := rel.AddEndpoint(EndpointArgs{
   269  		ServiceName: "wordpress",
   270  		Name:        "db",
   271  		// Ignoring other aspects of endpoints.
   272  	})
   273  	mysqlEndpoint := rel.AddEndpoint(EndpointArgs{
   274  		ServiceName: "mysql",
   275  		Name:        "mysql",
   276  		// Ignoring other aspects of endpoints.
   277  	})
   278  	return model, wordpressEndpoint, mysqlEndpoint
   279  }
   280  
   281  func (s *ModelSerializationSuite) wordpressModelWithSettings() Model {
   282  	model, wordpressEndpoint, mysqlEndpoint := s.wordpressModel()
   283  
   284  	wordpressEndpoint.SetUnitSettings("wordpress/0", map[string]interface{}{
   285  		"key": "value",
   286  	})
   287  	wordpressEndpoint.SetUnitSettings("wordpress/1", map[string]interface{}{
   288  		"key": "value",
   289  	})
   290  	mysqlEndpoint.SetUnitSettings("mysql/0", map[string]interface{}{
   291  		"key": "value",
   292  	})
   293  	return model
   294  }
   295  
   296  func (s *ModelSerializationSuite) TestModelValidationChecksRelationsMissingSettings(c *gc.C) {
   297  	model, _, _ := s.wordpressModel()
   298  	err := model.Validate()
   299  	c.Assert(err, gc.ErrorMatches, "missing relation settings for units \\[wordpress/0 wordpress/1\\] in relation 42")
   300  }
   301  
   302  func (s *ModelSerializationSuite) TestModelValidationChecksRelationsMissingSettings2(c *gc.C) {
   303  	model, wordpressEndpoint, _ := s.wordpressModel()
   304  
   305  	wordpressEndpoint.SetUnitSettings("wordpress/0", map[string]interface{}{
   306  		"key": "value",
   307  	})
   308  	wordpressEndpoint.SetUnitSettings("wordpress/1", map[string]interface{}{
   309  		"key": "value",
   310  	})
   311  	err := model.Validate()
   312  	c.Assert(err, gc.ErrorMatches, "missing relation settings for units \\[mysql/0\\] in relation 42")
   313  }
   314  
   315  func (s *ModelSerializationSuite) TestModelValidationChecksRelations(c *gc.C) {
   316  	model := s.wordpressModelWithSettings()
   317  	err := model.Validate()
   318  	c.Assert(err, jc.ErrorIsNil)
   319  }
   320  
   321  func (s *ModelSerializationSuite) TestModelSerializationWithRelations(c *gc.C) {
   322  	initial := s.wordpressModelWithSettings()
   323  	bytes, err := yaml.Marshal(initial)
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	model, err := Deserialize(bytes)
   326  	c.Assert(err, jc.ErrorIsNil)
   327  	c.Assert(model, jc.DeepEquals, initial)
   328  }