github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/container/lxd/initialisation_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 //go:build linux 5 6 package lxd 7 8 import ( 9 "os/exec" 10 11 lxd "github.com/canonical/lxd/client" 12 "github.com/canonical/lxd/shared/api" 13 "github.com/juju/packaging/v3/commands" 14 "github.com/juju/packaging/v3/manager" 15 "github.com/juju/proxy" 16 "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "go.uber.org/mock/gomock" 19 gc "gopkg.in/check.v1" 20 21 "github.com/juju/juju/container/lxd/mocks" 22 lxdtesting "github.com/juju/juju/container/lxd/testing" 23 "github.com/juju/juju/core/base" 24 coretesting "github.com/juju/juju/testing" 25 ) 26 27 type initialiserTestSuite struct { 28 coretesting.BaseSuite 29 testing.PatchExecHelper 30 } 31 32 // patchDF100GB ensures that df always returns 100GB. 33 func (s *initialiserTestSuite) patchDF100GB() { 34 df100 := func(path string) (uint64, error) { 35 return 100 * 1024 * 1024 * 1024, nil 36 } 37 s.PatchValue(&df, df100) 38 } 39 40 type InitialiserSuite struct { 41 initialiserTestSuite 42 calledCmds []string 43 } 44 45 var _ = gc.Suite(&InitialiserSuite{}) 46 47 const lxdSnapChannel = "latest/stable" 48 49 func (s *InitialiserSuite) SetUpTest(c *gc.C) { 50 coretesting.SkipLXDNotSupported(c) 51 s.initialiserTestSuite.SetUpTest(c) 52 s.calledCmds = []string{} 53 s.PatchValue(&manager.RunCommandWithRetry, getMockRunCommandWithRetry(&s.calledCmds)) 54 55 nonRandomizedOctetRange := func() []int { 56 // chosen by fair dice roll 57 // guaranteed to be random :) 58 // intentionally not random to allow for deterministic tests 59 return []int{4, 5, 6, 7, 8} 60 } 61 s.PatchValue(&randomizedOctetRange, nonRandomizedOctetRange) 62 // Fake the lxc executable for all the tests. 63 testing.PatchExecutableAsEchoArgs(c, s, "lxc") 64 testing.PatchExecutableAsEchoArgs(c, s, "lxd") 65 } 66 67 // getMockRunCommandWithRetry is a helper function which returns a function 68 // with an identical signature to manager.RunCommandWithRetry which saves each 69 // command it receives in a slice and always returns no output, error code 0 70 // and a nil error. 71 func getMockRunCommandWithRetry(calledCmds *[]string) func(string, manager.Retryable, manager.RetryPolicy) (string, int, error) { 72 return func(cmd string, _ manager.Retryable, _ manager.RetryPolicy) (string, int, error) { 73 *calledCmds = append(*calledCmds, cmd) 74 return "", 0, nil 75 } 76 } 77 78 func (s *initialiserTestSuite) containerInitialiser(svr lxd.InstanceServer, lxdIsRunning bool, containerNetworkingMethod string) *containerInitialiser { 79 result := NewContainerInitialiser(lxdSnapChannel, containerNetworkingMethod).(*containerInitialiser) 80 result.configureLxdProxies = func(proxy.Settings, func() (bool, error), func() (*Server, error)) error { return nil } 81 result.newLocalServer = func() (*Server, error) { return NewServer(svr) } 82 result.isRunningLocally = func() (bool, error) { 83 return lxdIsRunning, nil 84 } 85 return result 86 } 87 88 func (s *InitialiserSuite) TestSnapInstalled(c *gc.C) { 89 PatchLXDViaSnap(s, true) 90 PatchHostBase(s, base.MustParseBaseFromString("ubuntu@22.04")) 91 92 ctrl := gomock.NewController(c) 93 defer ctrl.Finish() 94 95 mgr := mocks.NewMockSnapManager(ctrl) 96 mgr.EXPECT().InstalledChannel("lxd").Return("latest/stable") 97 PatchGetSnapManager(s, mgr) 98 99 err := s.containerInitialiser(nil, true, "local").Initialise() 100 c.Assert(err, jc.ErrorIsNil) 101 102 c.Assert(s.calledCmds, gc.DeepEquals, []string{}) 103 } 104 105 func (s *InitialiserSuite) TestSnapChannelMismatch(c *gc.C) { 106 PatchLXDViaSnap(s, true) 107 PatchHostBase(s, base.MustParseBaseFromString("ubuntu@20.04")) 108 109 ctrl := gomock.NewController(c) 110 defer ctrl.Finish() 111 112 mgr := mocks.NewMockSnapManager(ctrl) 113 gomock.InOrder( 114 mgr.EXPECT().InstalledChannel("lxd").Return("3.2/stable"), 115 mgr.EXPECT().ChangeChannel("lxd", lxdSnapChannel), 116 ) 117 PatchGetSnapManager(s, mgr) 118 119 err := s.containerInitialiser(nil, true, "local").Initialise() 120 c.Assert(err, jc.ErrorIsNil) 121 } 122 123 func (s *InitialiserSuite) TestSnapChannelPrefixMatch(c *gc.C) { 124 PatchLXDViaSnap(s, true) 125 PatchHostBase(s, base.MustParseBaseFromString("ubuntu@20.04")) 126 127 ctrl := gomock.NewController(c) 128 defer ctrl.Finish() 129 130 mgr := mocks.NewMockSnapManager(ctrl) 131 gomock.InOrder( 132 // The channel for the installed lxd snap also includes the 133 // branch for the focal release. The "track/risk" prefix is 134 // the same however so the container manager should not attempt 135 // to change the channel. 136 mgr.EXPECT().InstalledChannel("lxd").Return("latest/stable/ubuntu-20.04"), 137 ) 138 PatchGetSnapManager(s, mgr) 139 140 err := s.containerInitialiser(nil, true, "local").Initialise() 141 c.Assert(err, jc.ErrorIsNil) 142 } 143 144 func (s *InitialiserSuite) TestInstallViaSnap(c *gc.C) { 145 PatchLXDViaSnap(s, false) 146 147 PatchHostBase(s, base.MustParseBaseFromString("ubuntu@20.04")) 148 149 paccmder := commands.NewSnapPackageCommander() 150 151 err := s.containerInitialiser(nil, true, "local").Initialise() 152 c.Assert(err, jc.ErrorIsNil) 153 154 c.Assert(s.calledCmds, gc.DeepEquals, []string{ 155 paccmder.InstallCmd("--classic --channel latest/stable lxd"), 156 }) 157 } 158 159 func (s *InitialiserSuite) TestLXDAlreadyInitialized(c *gc.C) { 160 s.patchDF100GB() 161 PatchHostBase(s, base.MustParseBaseFromString("ubuntu@20.04")) 162 163 ci := s.containerInitialiser(nil, true, "local") 164 ci.getExecCommand = s.PatchExecHelper.GetExecCommand(testing.PatchExecConfig{ 165 Stderr: `error: You have existing containers or images. lxd init requires an empty LXD.`, 166 ExitCode: 1, 167 }) 168 169 // the above error should be ignored by the code that calls lxd init. 170 err := ci.Initialise() 171 c.Assert(err, jc.ErrorIsNil) 172 } 173 174 func (s *InitialiserSuite) TestInitializeSetsProxies(c *gc.C) { 175 PatchHostBase(s, base.MustParseBaseFromString("ubuntu@20.04")) 176 177 ctrl := gomock.NewController(c) 178 defer ctrl.Finish() 179 cSvr := lxdtesting.NewMockInstanceServer(ctrl) 180 181 s.PatchEnvironment("http_proxy", "http://test.local/http/proxy") 182 s.PatchEnvironment("https_proxy", "http://test.local/https/proxy") 183 s.PatchEnvironment("no_proxy", "test.local,localhost") 184 185 var calls []string 186 updateReq := api.ServerPut{Config: map[string]interface{}{ 187 "core.proxy_http": "http://test.local/http/proxy", 188 "core.proxy_https": "http://test.local/https/proxy", 189 "core.proxy_ignore_hosts": "test.local,localhost", 190 }} 191 gomock.InOrder( 192 cSvr.EXPECT().GetServer().Return(&api.Server{}, lxdtesting.ETag, nil).Times(2), 193 cSvr.EXPECT().UpdateServer(updateReq, lxdtesting.ETag).DoAndReturn(func(_ api.ServerPut, _ string) error { 194 calls = append(calls, "update server") 195 return nil 196 }), 197 ) 198 199 ci := s.containerInitialiser(cSvr, true, "local") 200 ci.configureLxdProxies = internalConfigureLXDProxies 201 ci.getExecCommand = func(cmd string, args ...string) *exec.Cmd { 202 calls = append(calls, "exec command") 203 return exec.Command(cmd, args...) 204 } 205 err := ci.Initialise() 206 c.Assert(err, jc.ErrorIsNil) 207 208 // We want update server to ve called last, after the lxd init command is run. 209 c.Assert(calls, jc.DeepEquals, []string{ 210 "exec command", 211 "update server", 212 }) 213 } 214 215 func (s *InitialiserSuite) TestConfigureProxiesLXDNotRunning(c *gc.C) { 216 ctrl := gomock.NewController(c) 217 defer ctrl.Finish() 218 cSvr := lxdtesting.NewMockInstanceServer(ctrl) 219 220 s.PatchEnvironment("http_proxy", "http://test.local/http/proxy") 221 s.PatchEnvironment("https_proxy", "http://test.local/https/proxy") 222 s.PatchEnvironment("no_proxy", "test.local,localhost") 223 224 // No expected calls. 225 ci := s.containerInitialiser(cSvr, false, "local") 226 err := ci.Initialise() 227 c.Assert(err, jc.ErrorIsNil) 228 } 229 230 type ConfigureInitialiserSuite struct { 231 initialiserTestSuite 232 testing.PatchExecHelper 233 } 234 235 var _ = gc.Suite(&ConfigureInitialiserSuite{}) 236 237 func (s *ConfigureInitialiserSuite) SetUpTest(c *gc.C) { 238 s.initialiserTestSuite.SetUpTest(c) 239 // Fake the lxc executable for all the tests. 240 testing.PatchExecutableAsEchoArgs(c, s, "lxc") 241 testing.PatchExecutableAsEchoArgs(c, s, "lxd") 242 }