github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/state/migration_export_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"math/rand"
     8  	"time"
     9  
    10  	"github.com/juju/names"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/version"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/constraints"
    16  	"github.com/juju/juju/core/description"
    17  	"github.com/juju/juju/state"
    18  	"github.com/juju/juju/status"
    19  	"github.com/juju/juju/testing/factory"
    20  )
    21  
    22  // Constraints stores megabytes by default for memory and root disk.
    23  const (
    24  	gig uint64 = 1024
    25  
    26  	addedHistoryCount = 5
    27  	// 6 for the one initial + 5 added.
    28  	expectedHistoryCount = addedHistoryCount + 1
    29  )
    30  
    31  var testAnnotations = map[string]string{
    32  	"string":  "value",
    33  	"another": "one",
    34  }
    35  
    36  type MigrationSuite struct {
    37  	ConnSuite
    38  }
    39  
    40  func (s *MigrationSuite) setLatestTools(c *gc.C, latestTools version.Number) {
    41  	dbModel, err := s.State.Model()
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	err = dbModel.UpdateLatestToolsVersion(latestTools)
    44  	c.Assert(err, jc.ErrorIsNil)
    45  }
    46  
    47  func (s *MigrationSuite) setRandSequenceValue(c *gc.C, name string) int {
    48  	var value int
    49  	var err error
    50  	count := rand.Intn(5) + 1
    51  	for i := 0; i < count; i++ {
    52  		value, err = state.Sequence(s.State, name)
    53  		c.Assert(err, jc.ErrorIsNil)
    54  	}
    55  	// The value stored in the doc is one higher than what it returns.
    56  	return value + 1
    57  }
    58  
    59  func (s *MigrationSuite) primeStatusHistory(c *gc.C, entity statusSetter, statusVal status.Status, count int) {
    60  	primeStatusHistory(c, entity, statusVal, count, func(i int) map[string]interface{} {
    61  		return map[string]interface{}{"index": count - i}
    62  	})
    63  }
    64  
    65  func (s *MigrationSuite) makeServiceWithLeader(c *gc.C, serviceName string, count int, leader int) {
    66  	c.Assert(leader < count, jc.IsTrue)
    67  	units := make([]*state.Unit, count)
    68  	service := s.Factory.MakeService(c, &factory.ServiceParams{
    69  		Name: serviceName,
    70  		Charm: s.Factory.MakeCharm(c, &factory.CharmParams{
    71  			Name: serviceName,
    72  		}),
    73  	})
    74  	for i := 0; i < count; i++ {
    75  		units[i] = s.Factory.MakeUnit(c, &factory.UnitParams{
    76  			Service: service,
    77  		})
    78  	}
    79  	err := s.State.LeadershipClaimer().ClaimLeadership(
    80  		service.Name(),
    81  		units[leader].Name(),
    82  		time.Minute)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  }
    85  
    86  type MigrationExportSuite struct {
    87  	MigrationSuite
    88  }
    89  
    90  var _ = gc.Suite(&MigrationExportSuite{})
    91  
    92  func (s *MigrationExportSuite) checkStatusHistory(c *gc.C, history []description.Status, statusVal status.Status) {
    93  	for i, st := range history {
    94  		c.Logf("status history #%d: %s", i, st.Updated())
    95  		c.Check(st.Value(), gc.Equals, string(statusVal))
    96  		c.Check(st.Message(), gc.Equals, "")
    97  		c.Check(st.Data(), jc.DeepEquals, map[string]interface{}{"index": i + 1})
    98  	}
    99  }
   100  
   101  func (s *MigrationExportSuite) TestModelInfo(c *gc.C) {
   102  	stModel, err := s.State.Model()
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	err = s.State.SetAnnotations(stModel, testAnnotations)
   105  	c.Assert(err, jc.ErrorIsNil)
   106  	latestTools := version.MustParse("2.0.1")
   107  	s.setLatestTools(c, latestTools)
   108  	err = s.State.SetModelConstraints(constraints.MustParse("arch=amd64 mem=8G"))
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	machineSeq := s.setRandSequenceValue(c, "machine")
   111  	fooSeq := s.setRandSequenceValue(c, "service-foo")
   112  	s.State.SwitchBlockOn(state.ChangeBlock, "locked down")
   113  
   114  	model, err := s.State.Export()
   115  	c.Assert(err, jc.ErrorIsNil)
   116  
   117  	dbModel, err := s.State.Model()
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	c.Assert(model.Tag(), gc.Equals, dbModel.ModelTag())
   120  	c.Assert(model.Owner(), gc.Equals, dbModel.Owner())
   121  	config, err := dbModel.Config()
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	c.Assert(model.Config(), jc.DeepEquals, config.AllAttrs())
   124  	c.Assert(model.LatestToolsVersion(), gc.Equals, latestTools)
   125  	c.Assert(model.Annotations(), jc.DeepEquals, testAnnotations)
   126  	constraints := model.Constraints()
   127  	c.Assert(constraints, gc.NotNil)
   128  	c.Assert(constraints.Architecture(), gc.Equals, "amd64")
   129  	c.Assert(constraints.Memory(), gc.Equals, 8*gig)
   130  	c.Assert(model.Sequences(), jc.DeepEquals, map[string]int{
   131  		"machine":     machineSeq,
   132  		"service-foo": fooSeq,
   133  		// blocks is added by the switch block on call above.
   134  		"block": 1,
   135  	})
   136  	c.Assert(model.Blocks(), jc.DeepEquals, map[string]string{
   137  		"all-changes": "locked down",
   138  	})
   139  }
   140  
   141  func (s *MigrationExportSuite) TestModelUsers(c *gc.C) {
   142  	// Make sure we have some last connection times for the admin user,
   143  	// and create a few other users.
   144  	lastConnection := state.NowToTheSecond()
   145  	owner, err := s.State.ModelUser(s.Owner)
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	err = state.UpdateModelUserLastConnection(owner, lastConnection)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  
   150  	bobTag := names.NewUserTag("bob@external")
   151  	bob, err := s.State.AddModelUser(state.ModelUserSpec{
   152  		User:      bobTag,
   153  		CreatedBy: s.Owner,
   154  		Access:    state.ModelReadAccess,
   155  	})
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	err = state.UpdateModelUserLastConnection(bob, lastConnection)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  
   160  	model, err := s.State.Export()
   161  	c.Assert(err, jc.ErrorIsNil)
   162  
   163  	users := model.Users()
   164  	c.Assert(users, gc.HasLen, 2)
   165  
   166  	exportedBob := users[0]
   167  	// admin is "test-admin", and results are sorted
   168  	exportedAdmin := users[1]
   169  
   170  	c.Assert(exportedAdmin.Name(), gc.Equals, s.Owner)
   171  	c.Assert(exportedAdmin.DisplayName(), gc.Equals, owner.DisplayName())
   172  	c.Assert(exportedAdmin.CreatedBy(), gc.Equals, s.Owner)
   173  	c.Assert(exportedAdmin.DateCreated(), gc.Equals, owner.DateCreated())
   174  	c.Assert(exportedAdmin.LastConnection(), gc.Equals, lastConnection)
   175  	c.Assert(exportedAdmin.ReadOnly(), jc.IsFalse)
   176  
   177  	c.Assert(exportedBob.Name(), gc.Equals, bobTag)
   178  	c.Assert(exportedBob.DisplayName(), gc.Equals, "")
   179  	c.Assert(exportedBob.CreatedBy(), gc.Equals, s.Owner)
   180  	c.Assert(exportedBob.DateCreated(), gc.Equals, bob.DateCreated())
   181  	c.Assert(exportedBob.LastConnection(), gc.Equals, lastConnection)
   182  	c.Assert(exportedBob.ReadOnly(), jc.IsTrue)
   183  }
   184  
   185  func (s *MigrationExportSuite) TestMachines(c *gc.C) {
   186  	// Add a machine with an LXC container.
   187  	machine1 := s.Factory.MakeMachine(c, &factory.MachineParams{
   188  		Constraints: constraints.MustParse("arch=amd64 mem=8G"),
   189  	})
   190  	nested := s.Factory.MakeMachineNested(c, machine1.Id(), nil)
   191  	err := s.State.SetAnnotations(machine1, testAnnotations)
   192  	c.Assert(err, jc.ErrorIsNil)
   193  	s.primeStatusHistory(c, machine1, status.StatusStarted, addedHistoryCount)
   194  
   195  	model, err := s.State.Export()
   196  	c.Assert(err, jc.ErrorIsNil)
   197  
   198  	machines := model.Machines()
   199  	c.Assert(machines, gc.HasLen, 1)
   200  
   201  	exported := machines[0]
   202  	c.Assert(exported.Tag(), gc.Equals, machine1.MachineTag())
   203  	c.Assert(exported.Series(), gc.Equals, machine1.Series())
   204  	c.Assert(exported.Annotations(), jc.DeepEquals, testAnnotations)
   205  	constraints := exported.Constraints()
   206  	c.Assert(constraints, gc.NotNil)
   207  	c.Assert(constraints.Architecture(), gc.Equals, "amd64")
   208  	c.Assert(constraints.Memory(), gc.Equals, 8*gig)
   209  
   210  	tools, err := machine1.AgentTools()
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	exTools := exported.Tools()
   213  	c.Assert(exTools, gc.NotNil)
   214  	c.Assert(exTools.Version(), jc.DeepEquals, tools.Version)
   215  
   216  	history := exported.StatusHistory()
   217  	c.Assert(history, gc.HasLen, expectedHistoryCount)
   218  	s.checkStatusHistory(c, history[:addedHistoryCount], status.StatusStarted)
   219  
   220  	containers := exported.Containers()
   221  	c.Assert(containers, gc.HasLen, 1)
   222  	container := containers[0]
   223  	c.Assert(container.Tag(), gc.Equals, nested.MachineTag())
   224  }
   225  
   226  func (s *MigrationExportSuite) TestServices(c *gc.C) {
   227  	service := s.Factory.MakeService(c, &factory.ServiceParams{
   228  		Settings: map[string]interface{}{
   229  			"foo": "bar",
   230  		},
   231  		Constraints: constraints.MustParse("arch=amd64 mem=8G"),
   232  	})
   233  	err := service.UpdateLeaderSettings(&goodToken{}, map[string]string{
   234  		"leader": "true",
   235  	})
   236  	c.Assert(err, jc.ErrorIsNil)
   237  	err = service.SetMetricCredentials([]byte("sekrit"))
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	err = s.State.SetAnnotations(service, testAnnotations)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	s.primeStatusHistory(c, service, status.StatusActive, addedHistoryCount)
   242  
   243  	model, err := s.State.Export()
   244  	c.Assert(err, jc.ErrorIsNil)
   245  
   246  	services := model.Services()
   247  	c.Assert(services, gc.HasLen, 1)
   248  
   249  	exported := services[0]
   250  	c.Assert(exported.Name(), gc.Equals, service.Name())
   251  	c.Assert(exported.Tag(), gc.Equals, service.ServiceTag())
   252  	c.Assert(exported.Series(), gc.Equals, service.Series())
   253  	c.Assert(exported.Annotations(), jc.DeepEquals, testAnnotations)
   254  
   255  	c.Assert(exported.Settings(), jc.DeepEquals, map[string]interface{}{
   256  		"foo": "bar",
   257  	})
   258  	c.Assert(exported.SettingsRefCount(), gc.Equals, 1)
   259  	c.Assert(exported.LeadershipSettings(), jc.DeepEquals, map[string]interface{}{
   260  		"leader": "true",
   261  	})
   262  	c.Assert(exported.MetricsCredentials(), jc.DeepEquals, []byte("sekrit"))
   263  
   264  	constraints := exported.Constraints()
   265  	c.Assert(constraints, gc.NotNil)
   266  	c.Assert(constraints.Architecture(), gc.Equals, "amd64")
   267  	c.Assert(constraints.Memory(), gc.Equals, 8*gig)
   268  
   269  	history := exported.StatusHistory()
   270  	c.Assert(history, gc.HasLen, expectedHistoryCount)
   271  	s.checkStatusHistory(c, history[:addedHistoryCount], status.StatusActive)
   272  }
   273  
   274  func (s *MigrationExportSuite) TestMultipleServices(c *gc.C) {
   275  	s.Factory.MakeService(c, &factory.ServiceParams{Name: "first"})
   276  	s.Factory.MakeService(c, &factory.ServiceParams{Name: "second"})
   277  	s.Factory.MakeService(c, &factory.ServiceParams{Name: "third"})
   278  
   279  	model, err := s.State.Export()
   280  	c.Assert(err, jc.ErrorIsNil)
   281  
   282  	services := model.Services()
   283  	c.Assert(services, gc.HasLen, 3)
   284  }
   285  
   286  func (s *MigrationExportSuite) TestUnits(c *gc.C) {
   287  	unit := s.Factory.MakeUnit(c, &factory.UnitParams{
   288  		Constraints: constraints.MustParse("arch=amd64 mem=8G"),
   289  	})
   290  	err := unit.SetMeterStatus("GREEN", "some info")
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	err = s.State.SetAnnotations(unit, testAnnotations)
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	s.primeStatusHistory(c, unit, status.StatusActive, addedHistoryCount)
   295  	s.primeStatusHistory(c, unit.Agent(), status.StatusIdle, addedHistoryCount)
   296  
   297  	model, err := s.State.Export()
   298  	c.Assert(err, jc.ErrorIsNil)
   299  
   300  	services := model.Services()
   301  	c.Assert(services, gc.HasLen, 1)
   302  
   303  	service := services[0]
   304  	units := service.Units()
   305  	c.Assert(units, gc.HasLen, 1)
   306  
   307  	exported := units[0]
   308  
   309  	c.Assert(exported.Name(), gc.Equals, unit.Name())
   310  	c.Assert(exported.Tag(), gc.Equals, unit.UnitTag())
   311  	c.Assert(exported.Validate(), jc.ErrorIsNil)
   312  	c.Assert(exported.MeterStatusCode(), gc.Equals, "GREEN")
   313  	c.Assert(exported.MeterStatusInfo(), gc.Equals, "some info")
   314  	c.Assert(exported.Annotations(), jc.DeepEquals, testAnnotations)
   315  	constraints := exported.Constraints()
   316  	c.Assert(constraints, gc.NotNil)
   317  	c.Assert(constraints.Architecture(), gc.Equals, "amd64")
   318  	c.Assert(constraints.Memory(), gc.Equals, 8*gig)
   319  
   320  	workloadHistory := exported.WorkloadStatusHistory()
   321  	c.Assert(workloadHistory, gc.HasLen, expectedHistoryCount)
   322  	s.checkStatusHistory(c, workloadHistory[:addedHistoryCount], status.StatusActive)
   323  
   324  	agentHistory := exported.AgentStatusHistory()
   325  	c.Assert(agentHistory, gc.HasLen, expectedHistoryCount)
   326  	s.checkStatusHistory(c, agentHistory[:addedHistoryCount], status.StatusIdle)
   327  }
   328  
   329  func (s *MigrationExportSuite) TestServiceLeadership(c *gc.C) {
   330  	s.makeServiceWithLeader(c, "mysql", 2, 1)
   331  	s.makeServiceWithLeader(c, "wordpress", 4, 2)
   332  
   333  	model, err := s.State.Export()
   334  	c.Assert(err, jc.ErrorIsNil)
   335  
   336  	leaders := make(map[string]string)
   337  	for _, service := range model.Services() {
   338  		leaders[service.Name()] = service.Leader()
   339  	}
   340  	c.Assert(leaders, jc.DeepEquals, map[string]string{
   341  		"mysql":     "mysql/1",
   342  		"wordpress": "wordpress/2",
   343  	})
   344  }
   345  
   346  func (s *MigrationExportSuite) TestUnitsOpenPorts(c *gc.C) {
   347  	unit := s.Factory.MakeUnit(c, nil)
   348  	err := unit.OpenPorts("tcp", 1234, 2345)
   349  	c.Assert(err, jc.ErrorIsNil)
   350  
   351  	model, err := s.State.Export()
   352  	c.Assert(err, jc.ErrorIsNil)
   353  
   354  	machines := model.Machines()
   355  	c.Assert(machines, gc.HasLen, 1)
   356  
   357  	ports := machines[0].OpenedPorts()
   358  	c.Assert(ports, gc.HasLen, 1)
   359  
   360  	port := ports[0]
   361  	c.Assert(port.SubnetID(), gc.Equals, "")
   362  	opened := port.OpenPorts()
   363  	c.Assert(opened, gc.HasLen, 1)
   364  	c.Assert(opened[0].UnitName(), gc.Equals, unit.Name())
   365  }
   366  
   367  func (s *MigrationExportSuite) TestRelations(c *gc.C) {
   368  	// Need to remove owner from service.
   369  	ignored := s.Owner
   370  	wordpress := state.AddTestingService(c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"), ignored)
   371  	mysql := state.AddTestingService(c, s.State, "mysql", state.AddTestingCharm(c, s.State, "mysql"), ignored)
   372  	// InferEndpoints will always return provider, requirer
   373  	eps, err := s.State.InferEndpoints("mysql", "wordpress")
   374  	c.Assert(err, jc.ErrorIsNil)
   375  	rel, err := s.State.AddRelation(eps...)
   376  	msEp, wpEp := eps[0], eps[1]
   377  	c.Assert(err, jc.ErrorIsNil)
   378  	wordpress_0 := s.Factory.MakeUnit(c, &factory.UnitParams{Service: wordpress})
   379  	mysql_0 := s.Factory.MakeUnit(c, &factory.UnitParams{Service: mysql})
   380  
   381  	ru, err := rel.Unit(wordpress_0)
   382  	c.Assert(err, jc.ErrorIsNil)
   383  	wordpressSettings := map[string]interface{}{
   384  		"name": "wordpress/0",
   385  	}
   386  	err = ru.EnterScope(wordpressSettings)
   387  	c.Assert(err, jc.ErrorIsNil)
   388  
   389  	ru, err = rel.Unit(mysql_0)
   390  	c.Assert(err, jc.ErrorIsNil)
   391  	mysqlSettings := map[string]interface{}{
   392  		"name": "mysql/0",
   393  	}
   394  	err = ru.EnterScope(mysqlSettings)
   395  	c.Assert(err, jc.ErrorIsNil)
   396  
   397  	model, err := s.State.Export()
   398  	c.Assert(err, jc.ErrorIsNil)
   399  
   400  	rels := model.Relations()
   401  	c.Assert(rels, gc.HasLen, 1)
   402  
   403  	exRel := rels[0]
   404  	c.Assert(exRel.Id(), gc.Equals, rel.Id())
   405  	c.Assert(exRel.Key(), gc.Equals, rel.String())
   406  
   407  	exEps := exRel.Endpoints()
   408  	c.Assert(exEps, gc.HasLen, 2)
   409  
   410  	checkEndpoint := func(
   411  		exEndpoint description.Endpoint,
   412  		unitName string,
   413  		ep state.Endpoint,
   414  		settings map[string]interface{},
   415  	) {
   416  		c.Logf("%#v", exEndpoint)
   417  		c.Check(exEndpoint.ServiceName(), gc.Equals, ep.ServiceName)
   418  		c.Check(exEndpoint.Name(), gc.Equals, ep.Name)
   419  		c.Check(exEndpoint.UnitCount(), gc.Equals, 1)
   420  		c.Check(exEndpoint.Settings(unitName), jc.DeepEquals, settings)
   421  		c.Check(exEndpoint.Role(), gc.Equals, string(ep.Role))
   422  		c.Check(exEndpoint.Interface(), gc.Equals, ep.Interface)
   423  		c.Check(exEndpoint.Optional(), gc.Equals, ep.Optional)
   424  		c.Check(exEndpoint.Limit(), gc.Equals, ep.Limit)
   425  		c.Check(exEndpoint.Scope(), gc.Equals, string(ep.Scope))
   426  	}
   427  	checkEndpoint(exEps[0], mysql_0.Name(), msEp, mysqlSettings)
   428  	checkEndpoint(exEps[1], wordpress_0.Name(), wpEp, wordpressSettings)
   429  }
   430  
   431  type goodToken struct{}
   432  
   433  // Check implements leadership.Token
   434  func (*goodToken) Check(interface{}) error {
   435  	return nil
   436  }