github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/machine_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names/v5"
    11  	"github.com/juju/naturalsort"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/apiserver/common"
    16  	"github.com/juju/juju/core/instance"
    17  	"github.com/juju/juju/core/model"
    18  	"github.com/juju/juju/core/status"
    19  	"github.com/juju/juju/rpc/params"
    20  	"github.com/juju/juju/state"
    21  )
    22  
    23  type machineSuite struct{}
    24  
    25  var _ = gc.Suite(&machineSuite{})
    26  
    27  func (s *machineSuite) TestMachineJobFromParams(c *gc.C) {
    28  	var tests = []struct {
    29  		name model.MachineJob
    30  		want state.MachineJob
    31  		err  string
    32  	}{{
    33  		name: model.JobHostUnits,
    34  		want: state.JobHostUnits,
    35  	}, {
    36  		name: model.JobManageModel,
    37  		want: state.JobManageModel,
    38  	}, {
    39  		name: "invalid",
    40  		want: -1,
    41  		err:  `invalid machine job "invalid"`,
    42  	}}
    43  	for _, test := range tests {
    44  		got, err := common.MachineJobFromParams(test.name)
    45  		if err != nil {
    46  			c.Check(err, gc.ErrorMatches, test.err)
    47  		}
    48  		c.Check(got, gc.Equals, test.want)
    49  	}
    50  }
    51  
    52  const (
    53  	dontWait = time.Duration(0)
    54  )
    55  
    56  func (s *machineSuite) TestDestroyMachines(c *gc.C) {
    57  	st := mockState{
    58  		machines: map[string]*mockMachine{
    59  			"1": {},
    60  			"2": {destroyErr: errors.New("unit exists error")},
    61  			"3": {life: state.Dying},
    62  		},
    63  	}
    64  	err := common.MockableDestroyMachines(&st, false, dontWait, "1", "2", "3", "4")
    65  
    66  	c.Assert(st.machines["1"].Life(), gc.Equals, state.Dying)
    67  	c.Assert(st.machines["1"].forceDestroyCalled, jc.IsFalse)
    68  
    69  	c.Assert(st.machines["2"].Life(), gc.Equals, state.Alive)
    70  	c.Assert(st.machines["2"].forceDestroyCalled, jc.IsFalse)
    71  
    72  	c.Assert(st.machines["3"].forceDestroyCalled, jc.IsFalse)
    73  	c.Assert(st.machines["3"].destroyCalled, jc.IsFalse)
    74  
    75  	c.Assert(err, gc.ErrorMatches, "some machines were not destroyed: unit exists error; machine 4 does not exist")
    76  }
    77  
    78  func (s *machineSuite) TestForceDestroyMachines(c *gc.C) {
    79  	st := mockState{
    80  		machines: map[string]*mockMachine{
    81  			"1": {},
    82  			"2": {life: state.Dying},
    83  		},
    84  	}
    85  	err := common.MockableDestroyMachines(&st, true, dontWait, "1", "2")
    86  
    87  	c.Assert(st.machines["1"].Life(), gc.Equals, state.Dying)
    88  	c.Assert(st.machines["1"].forceDestroyCalled, jc.IsTrue)
    89  	c.Assert(st.machines["2"].forceDestroyCalled, jc.IsTrue)
    90  
    91  	c.Assert(err, jc.ErrorIsNil)
    92  }
    93  
    94  func (s *machineSuite) TestMachineHardwareInfo(c *gc.C) {
    95  	one := uint64(1)
    96  	amd64 := "amd64"
    97  	gig := uint64(1024)
    98  	st := mockState{
    99  		machines: map[string]*mockMachine{
   100  			"1": {id: "1", life: state.Alive, containerType: instance.NONE,
   101  				hw: &instance.HardwareCharacteristics{
   102  					Arch:     &amd64,
   103  					Mem:      &gig,
   104  					CpuCores: &one,
   105  					CpuPower: &one,
   106  				}},
   107  			"2": {id: "2", life: state.Alive, containerType: instance.LXD},
   108  			"3": {life: state.Dying},
   109  		},
   110  	}
   111  	info, err := common.ModelMachineInfo(&st)
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{
   114  		{
   115  			Id:          "1",
   116  			DisplayName: "",
   117  			Hardware: &params.MachineHardware{
   118  				Arch:     &amd64,
   119  				Mem:      &gig,
   120  				Cores:    &one,
   121  				CpuPower: &one,
   122  			},
   123  		}, {
   124  			Id:          "2",
   125  			DisplayName: "",
   126  		},
   127  	})
   128  }
   129  
   130  func (s *machineSuite) TestMachineInstanceInfo(c *gc.C) {
   131  	st := mockState{
   132  		machines: map[string]*mockMachine{
   133  			"1": {
   134  				id:     "1",
   135  				instId: "123",
   136  				status: status.Down,
   137  			},
   138  			"2": {
   139  				id:          "2",
   140  				instId:      "456",
   141  				displayName: "two",
   142  				status:      status.Allocating,
   143  			},
   144  		},
   145  		controllerNodes: map[string]*mockControllerNode{
   146  			"1": {
   147  				id:        "1",
   148  				hasVote:   true,
   149  				wantsVote: true,
   150  			},
   151  			"2": {
   152  				id:        "2",
   153  				hasVote:   false,
   154  				wantsVote: true,
   155  			},
   156  		},
   157  	}
   158  	info, err := common.ModelMachineInfo(&st)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{
   161  		{
   162  			Id:         "1",
   163  			InstanceId: "123",
   164  			Status:     "down",
   165  			HasVote:    true,
   166  			WantsVote:  true,
   167  		},
   168  		{
   169  			Id:          "2",
   170  			InstanceId:  "456",
   171  			DisplayName: "two",
   172  			Status:      "allocating",
   173  			HasVote:     false,
   174  			WantsVote:   true,
   175  		},
   176  	})
   177  }
   178  
   179  func (s *machineSuite) TestMachineInstanceInfoWithEmptyDisplayName(c *gc.C) {
   180  	st := mockState{
   181  		machines: map[string]*mockMachine{
   182  			"1": {
   183  				id:          "1",
   184  				instId:      "123",
   185  				displayName: "",
   186  				status:      status.Down,
   187  			},
   188  		},
   189  		controllerNodes: map[string]*mockControllerNode{
   190  			"1": {
   191  				id:        "1",
   192  				hasVote:   true,
   193  				wantsVote: true,
   194  			},
   195  		},
   196  	}
   197  	info, err := common.ModelMachineInfo(&st)
   198  	c.Assert(err, jc.ErrorIsNil)
   199  	c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{
   200  		{
   201  			Id:          "1",
   202  			InstanceId:  "123",
   203  			DisplayName: "",
   204  			Status:      "down",
   205  			HasVote:     true,
   206  			WantsVote:   true,
   207  		},
   208  	})
   209  }
   210  
   211  func (s *machineSuite) TestMachineInstanceInfoWithSetDisplayName(c *gc.C) {
   212  	st := mockState{
   213  		machines: map[string]*mockMachine{
   214  			"1": {
   215  				id:          "1",
   216  				instId:      "123",
   217  				displayName: "snowflake",
   218  				status:      status.Down,
   219  			},
   220  		},
   221  		controllerNodes: map[string]*mockControllerNode{
   222  			"1": {
   223  				id:        "1",
   224  				hasVote:   true,
   225  				wantsVote: true,
   226  			},
   227  		},
   228  	}
   229  	info, err := common.ModelMachineInfo(&st)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{
   232  		{
   233  			Id:          "1",
   234  			InstanceId:  "123",
   235  			DisplayName: "snowflake",
   236  			Status:      "down",
   237  			HasVote:     true,
   238  			WantsVote:   true,
   239  		},
   240  	})
   241  }
   242  
   243  func (s *machineSuite) TestMachineInstanceInfoWithHAPrimary(c *gc.C) {
   244  	st := mockState{
   245  		machines: map[string]*mockMachine{
   246  			"1": {
   247  				id:          "1",
   248  				instId:      "123",
   249  				displayName: "snowflake",
   250  				status:      status.Down,
   251  			},
   252  		},
   253  		controllerNodes: map[string]*mockControllerNode{
   254  			"1": {
   255  				id:        "1",
   256  				hasVote:   true,
   257  				wantsVote: true,
   258  			},
   259  			"2": {
   260  				id:        "1",
   261  				hasVote:   true,
   262  				wantsVote: true,
   263  			},
   264  		},
   265  		haPrimaryMachineF: func() (names.MachineTag, error) {
   266  			return names.NewMachineTag("1"), nil
   267  		},
   268  	}
   269  	info, err := common.ModelMachineInfo(&st)
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	_true := true
   272  	c.Assert(info, jc.DeepEquals, []params.ModelMachineInfo{
   273  		{
   274  			Id:          "1",
   275  			InstanceId:  "123",
   276  			DisplayName: "snowflake",
   277  			Status:      "down",
   278  			HasVote:     true,
   279  			WantsVote:   true,
   280  			HAPrimary:   &_true,
   281  		},
   282  	})
   283  }
   284  
   285  type mockState struct {
   286  	common.ModelManagerBackend
   287  	machines          map[string]*mockMachine
   288  	controllerNodes   map[string]*mockControllerNode
   289  	haPrimaryMachineF func() (names.MachineTag, error)
   290  }
   291  
   292  func (st *mockState) Machine(id string) (common.Machine, error) {
   293  	if m, ok := st.machines[id]; ok {
   294  		return m, nil
   295  	}
   296  	return nil, errors.Errorf("machine %s does not exist", id)
   297  }
   298  
   299  func (st *mockState) AllMachines() (machines []common.Machine, _ error) {
   300  	// Ensure we get machines in id order.
   301  	var ids []string
   302  	for id := range st.machines {
   303  		ids = append(ids, id)
   304  	}
   305  	naturalsort.Sort(ids)
   306  	for _, id := range ids {
   307  		machines = append(machines, st.machines[id])
   308  	}
   309  	return machines, nil
   310  }
   311  
   312  func (st *mockState) ControllerNodes() ([]common.ControllerNode, error) {
   313  	var result []common.ControllerNode
   314  	for _, n := range st.controllerNodes {
   315  		result = append(result, n)
   316  	}
   317  	return result, nil
   318  }
   319  
   320  func (st *mockState) HAPrimaryMachine() (names.MachineTag, error) {
   321  	if st.haPrimaryMachineF == nil {
   322  		return names.MachineTag{}, nil
   323  	}
   324  	return st.haPrimaryMachineF()
   325  }
   326  
   327  type mockControllerNode struct {
   328  	id        string
   329  	hasVote   bool
   330  	wantsVote bool
   331  }
   332  
   333  func (m *mockControllerNode) Id() string {
   334  	return m.id
   335  }
   336  
   337  func (m *mockControllerNode) WantsVote() bool {
   338  	return m.wantsVote
   339  }
   340  
   341  func (m *mockControllerNode) HasVote() bool {
   342  	return m.hasVote
   343  }
   344  
   345  type mockMachine struct {
   346  	state.Machine
   347  	id                 string
   348  	life               state.Life
   349  	containerType      instance.ContainerType
   350  	hw                 *instance.HardwareCharacteristics
   351  	instId             instance.Id
   352  	displayName        string
   353  	status             status.Status
   354  	statusErr          error
   355  	destroyErr         error
   356  	forceDestroyErr    error
   357  	forceDestroyCalled bool
   358  	destroyCalled      bool
   359  }
   360  
   361  func (m *mockMachine) Id() string {
   362  	return m.id
   363  }
   364  
   365  func (m *mockMachine) Life() state.Life {
   366  	return m.life
   367  }
   368  
   369  func (m *mockMachine) InstanceId() (instance.Id, error) {
   370  	return m.instId, nil
   371  }
   372  
   373  func (m *mockMachine) InstanceNames() (instance.Id, string, error) {
   374  	instId, err := m.InstanceId()
   375  	return instId, m.displayName, err
   376  }
   377  
   378  func (m *mockMachine) Status() (status.StatusInfo, error) {
   379  	return status.StatusInfo{
   380  		Status: m.status,
   381  	}, m.statusErr
   382  }
   383  
   384  func (m *mockMachine) HardwareCharacteristics() (*instance.HardwareCharacteristics, error) {
   385  	return m.hw, nil
   386  }
   387  
   388  func (m *mockMachine) ForceDestroy(time.Duration) error {
   389  	m.forceDestroyCalled = true
   390  	if m.forceDestroyErr != nil {
   391  		return m.forceDestroyErr
   392  	}
   393  	m.life = state.Dying
   394  	return nil
   395  }
   396  
   397  func (m *mockMachine) Destroy() error {
   398  	m.destroyCalled = true
   399  	if m.destroyErr != nil {
   400  		return m.destroyErr
   401  	}
   402  	m.life = state.Dying
   403  	return nil
   404  }