github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/unit_assignment_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	jc "github.com/juju/testing/checkers"
     9  	gc "gopkg.in/check.v1"
    10  
    11  	"github.com/juju/juju/core/constraints"
    12  	"github.com/juju/juju/core/instance"
    13  	"github.com/juju/juju/state"
    14  )
    15  
    16  type UnitAssignmentSuite struct {
    17  	ConnSuite
    18  }
    19  
    20  var _ = gc.Suite(&UnitAssignmentSuite{})
    21  
    22  func (s *UnitAssignmentSuite) testAddApplicationUnitAssignment(c *gc.C) (*state.Application, []state.UnitAssignment) {
    23  	charm := s.AddTestingCharm(c, "dummy")
    24  	app, err := s.State.AddApplication(state.AddApplicationArgs{
    25  		Name: "dummy", Charm: charm, NumUnits: 2,
    26  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
    27  			OS:      "ubuntu",
    28  			Channel: "22.04/stable",
    29  		}},
    30  		Placement: []*instance.Placement{{s.State.ModelUUID(), "abc"}},
    31  	})
    32  	c.Assert(err, jc.ErrorIsNil)
    33  	units, err := app.AllUnits()
    34  	c.Assert(err, jc.ErrorIsNil)
    35  	c.Assert(units, gc.HasLen, 2)
    36  	for _, u := range units {
    37  		_, err := u.AssignedMachineId()
    38  		c.Assert(err, jc.Satisfies, errors.IsNotAssigned)
    39  	}
    40  
    41  	assignments, err := s.State.AllUnitAssignments()
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	c.Assert(assignments, jc.SameContents, []state.UnitAssignment{
    44  		{Unit: "dummy/0", Scope: s.State.ModelUUID(), Directive: "abc"},
    45  		{Unit: "dummy/1"},
    46  	})
    47  	return app, assignments
    48  }
    49  
    50  func (s *UnitAssignmentSuite) TestAddApplicationUnitAssignment(c *gc.C) {
    51  	s.testAddApplicationUnitAssignment(c)
    52  }
    53  
    54  func (s *UnitAssignmentSuite) TestAssignStagedUnits(c *gc.C) {
    55  	app, _ := s.testAddApplicationUnitAssignment(c)
    56  
    57  	results, err := s.State.AssignStagedUnits([]string{
    58  		"dummy/0", "dummy/1",
    59  	})
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	c.Assert(results, jc.SameContents, []state.UnitAssignmentResult{
    62  		{Unit: "dummy/0"},
    63  		{Unit: "dummy/1"},
    64  	})
    65  
    66  	units, err := app.AllUnits()
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	c.Assert(units, gc.HasLen, 2)
    69  	for _, u := range units {
    70  		_, err := u.AssignedMachineId()
    71  		c.Assert(err, jc.ErrorIsNil)
    72  	}
    73  
    74  	// There should be no staged assignments now.
    75  	assignments, err := s.State.AllUnitAssignments()
    76  	c.Assert(err, jc.ErrorIsNil)
    77  	c.Assert(assignments, gc.HasLen, 0)
    78  }
    79  
    80  func (s *UnitAssignmentSuite) TestAssignUnitWithPlacementMakesContainerInNewMachine(c *gc.C) {
    81  	// Enables juju deploy <charm> --to <container-type>
    82  	// It creates a new machine with a new container of that type.
    83  	// https://bugs.launchpad.net/juju-core/+bug/1590960
    84  	charm := s.AddTestingCharm(c, "dummy")
    85  	placement := instance.Placement{Scope: "lxd"}
    86  	app, err := s.State.AddApplication(state.AddApplicationArgs{
    87  		Name:  "dummy",
    88  		Charm: charm,
    89  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
    90  			OS:      "ubuntu",
    91  			Channel: "22.04/stable",
    92  		}},
    93  		NumUnits:  1,
    94  		Placement: []*instance.Placement{&placement},
    95  	})
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	units, err := app.AllUnits()
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	c.Assert(units, gc.HasLen, 1)
   100  	unit := units[0]
   101  
   102  	err = s.State.AssignUnitWithPlacement(unit, &placement)
   103  	c.Assert(err, jc.ErrorIsNil)
   104  
   105  	machineId, err := unit.AssignedMachineId()
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	machine, err := s.State.Machine(machineId)
   108  	c.Assert(err, jc.ErrorIsNil)
   109  	parentId, isContainer := machine.ParentId()
   110  	c.Assert(isContainer, jc.IsTrue)
   111  	_, err = s.State.Machine(parentId)
   112  	c.Assert(err, jc.ErrorIsNil)
   113  }
   114  
   115  func (s *UnitAssignmentSuite) TestAssignUnitWithPlacementNewMachinesHaveBindingsAsConstraints(c *gc.C) {
   116  	specialSpace, err := s.State.AddSpace("special-space", "", nil, false)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  
   119  	charm := s.AddTestingCharm(c, "dummy")
   120  	placement := instance.Placement{Scope: "lxd"}
   121  	app, err := s.State.AddApplication(state.AddApplicationArgs{
   122  		Name:  "dummy",
   123  		Charm: charm,
   124  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   125  			OS:      "ubuntu",
   126  			Channel: "22.04/stable",
   127  		}},
   128  		NumUnits:  1,
   129  		Placement: []*instance.Placement{&placement},
   130  		EndpointBindings: map[string]string{
   131  			"": specialSpace.Id(),
   132  		},
   133  	})
   134  	c.Assert(err, jc.ErrorIsNil)
   135  
   136  	units, err := app.AllUnits()
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	c.Assert(units, gc.HasLen, 1)
   139  	unit := units[0]
   140  
   141  	err = s.State.AssignUnitWithPlacement(unit, &placement)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  
   144  	guestID, err := unit.AssignedMachineId()
   145  	c.Assert(err, jc.ErrorIsNil)
   146  
   147  	guest, err := s.State.Machine(guestID)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  
   150  	hostID, _ := guest.ParentId()
   151  	host, err := s.State.Machine(hostID)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  
   154  	for _, m := range []*state.Machine{guest, host} {
   155  		cons, err := m.Constraints()
   156  		c.Assert(err, jc.ErrorIsNil)
   157  		c.Assert(cons.IncludeSpaces(), gc.DeepEquals, []string{"special-space"})
   158  	}
   159  }
   160  
   161  func (s *UnitAssignmentSuite) TestAssignUnitWithPlacementNewMachinesHaveBindingsAsConstraintsMerged(c *gc.C) {
   162  	boundSpace, err := s.State.AddSpace("bound-space", "", nil, false)
   163  	c.Assert(err, jc.ErrorIsNil)
   164  
   165  	constrainedSpace, err := s.State.AddSpace("constrained-space", "", nil, false)
   166  	c.Assert(err, jc.ErrorIsNil)
   167  
   168  	charm := s.AddTestingCharm(c, "dummy")
   169  	placement := instance.Placement{Scope: "lxd"}
   170  	app, err := s.State.AddApplication(state.AddApplicationArgs{
   171  		Name:  "dummy",
   172  		Charm: charm,
   173  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   174  			OS:      "ubuntu",
   175  			Channel: "22.04/stable",
   176  		}},
   177  		NumUnits:  1,
   178  		Placement: []*instance.Placement{&placement},
   179  		// Same space used in both bindings and constraints to test merging.
   180  		Constraints: constraints.MustParse("spaces=bound-space,constrained-space"),
   181  		EndpointBindings: map[string]string{
   182  			"": boundSpace.Id(),
   183  		},
   184  	})
   185  	c.Assert(err, jc.ErrorIsNil)
   186  
   187  	units, err := app.AllUnits()
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	c.Assert(units, gc.HasLen, 1)
   190  	unit := units[0]
   191  
   192  	err = s.State.AssignUnitWithPlacement(unit, &placement)
   193  	c.Assert(err, jc.ErrorIsNil)
   194  
   195  	guestID, err := unit.AssignedMachineId()
   196  	c.Assert(err, jc.ErrorIsNil)
   197  
   198  	guest, err := s.State.Machine(guestID)
   199  	c.Assert(err, jc.ErrorIsNil)
   200  
   201  	hostID, _ := guest.ParentId()
   202  	host, err := s.State.Machine(hostID)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  
   205  	for _, m := range []*state.Machine{guest, host} {
   206  		cons, err := m.Constraints()
   207  		c.Assert(err, jc.ErrorIsNil)
   208  		c.Assert(cons.IncludeSpaces(), jc.SameContents, []string{boundSpace.Name(), constrainedSpace.Name()})
   209  	}
   210  }
   211  
   212  func (s *UnitAssignmentSuite) TestAssignUnitWithPlacementDirective(c *gc.C) {
   213  	// Enables juju deploy <charm> --to <container-type>
   214  	// It creates a new machine with a new container of that type.
   215  	// https://bugs.launchpad.net/juju-core/+bug/1590960
   216  	charm := s.AddTestingCharm(c, "dummy")
   217  	placement := instance.Placement{Scope: s.State.ModelUUID(), Directive: "zone=test"}
   218  	app, err := s.State.AddApplication(state.AddApplicationArgs{
   219  		Name:  "dummy",
   220  		Charm: charm,
   221  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   222  			OS:      "ubuntu",
   223  			Channel: "22.04/stable",
   224  		}},
   225  		NumUnits:  1,
   226  		Placement: []*instance.Placement{&placement},
   227  	})
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	units, err := app.AllUnits()
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	c.Assert(units, gc.HasLen, 1)
   232  	unit := units[0]
   233  
   234  	err = s.State.AssignUnitWithPlacement(unit, &placement)
   235  	c.Assert(err, jc.ErrorIsNil)
   236  
   237  	machineId, err := unit.AssignedMachineId()
   238  	c.Assert(err, jc.ErrorIsNil)
   239  	machine, err := s.State.Machine(machineId)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(machine.Placement(), gc.Equals, "zone=test")
   242  }
   243  
   244  func (s *UnitAssignmentSuite) TestAssignUnitCleanMachineUpgradeSeriesLockError(c *gc.C) {
   245  	s.addLockedMachine(c, true)
   246  
   247  	charm := s.AddTestingCharm(c, "dummy")
   248  	app, err := s.State.AddApplication(state.AddApplicationArgs{
   249  		Name:  "dummy",
   250  		Charm: charm,
   251  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   252  			OS:      "ubuntu",
   253  			Channel: "22.04/stable",
   254  		}},
   255  		NumUnits: 1,
   256  	})
   257  	c.Assert(err, jc.ErrorIsNil)
   258  	units, err := app.AllUnits()
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	c.Assert(units, gc.HasLen, 1)
   261  
   262  	unit := units[0]
   263  	_, err = unit.AssignToCleanEmptyMachine()
   264  	c.Assert(err, gc.ErrorMatches, eligibleMachinesInUse)
   265  }
   266  
   267  func (s *UnitAssignmentSuite) TestAssignUnitMachinePlacementUpgradeSeriesLockError(c *gc.C) {
   268  	machine, _ := s.addLockedMachine(c, false)
   269  	// As in --to 0
   270  	s.testPlacementUpgradeSeriesLockError(c, &instance.Placement{Scope: "#", Directive: machine.Id()})
   271  }
   272  
   273  func (s *UnitAssignmentSuite) TestAssignUnitContainerOnMachinePlacementUpgradeSeriesLockError(c *gc.C) {
   274  	machine, _ := s.addLockedMachine(c, false)
   275  	// As in --to lxd:0
   276  	s.testPlacementUpgradeSeriesLockError(c, &instance.Placement{Scope: "lxd", Directive: machine.Id()})
   277  }
   278  
   279  func (s *UnitAssignmentSuite) TestAssignUnitExtantContainerOnMachinePlacementUpgradeSeriesLockError(c *gc.C) {
   280  	_, child := s.addLockedMachine(c, true)
   281  
   282  	// As in --to 0/lxd/0
   283  	s.testPlacementUpgradeSeriesLockError(c, &instance.Placement{Scope: "#", Directive: child.Id()})
   284  }
   285  
   286  func (s *UnitAssignmentSuite) testPlacementUpgradeSeriesLockError(c *gc.C, placement *instance.Placement) {
   287  	charm := s.AddTestingCharm(c, "dummy")
   288  	app, err := s.State.AddApplication(state.AddApplicationArgs{
   289  		Name:  "dummy",
   290  		Charm: charm,
   291  		CharmOrigin: &state.CharmOrigin{Platform: &state.Platform{
   292  			OS:      "ubuntu",
   293  			Channel: "12.10/stable",
   294  		}},
   295  		NumUnits:  1,
   296  		Placement: []*instance.Placement{placement},
   297  	})
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	units, err := app.AllUnits()
   300  	c.Assert(err, jc.ErrorIsNil)
   301  	c.Assert(units, gc.HasLen, 1)
   302  
   303  	unit := units[0]
   304  	err = s.State.AssignUnitWithPlacement(unit, placement)
   305  	c.Assert(err, gc.ErrorMatches, ".* is locked for series upgrade")
   306  }
   307  
   308  func (s *UnitAssignmentSuite) addLockedMachine(c *gc.C, addContainer bool) (*state.Machine, *state.Machine) {
   309  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   310  	c.Assert(err, jc.ErrorIsNil)
   311  
   312  	var child *state.Machine
   313  	if addContainer {
   314  		template := state.MachineTemplate{
   315  			Base: state.UbuntuBase("12.10"),
   316  			Jobs: []state.MachineJob{state.JobHostUnits},
   317  		}
   318  		child, err = s.State.AddMachineInsideMachine(template, machine.Id(), "lxd")
   319  		c.Assert(err, jc.ErrorIsNil)
   320  	}
   321  
   322  	c.Assert(machine.CreateUpgradeSeriesLock(nil, state.UbuntuBase("22.04")), jc.ErrorIsNil)
   323  	return machine, child
   324  }