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