github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/jujud/agent/util_test.go (about) 1 // Copyright 2012-2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package agent 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "path/filepath" 10 "reflect" 11 "sync" 12 "time" 13 14 "github.com/juju/cmd" 15 "github.com/juju/collections/set" 16 "github.com/juju/os/series" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils/arch" 19 "github.com/juju/version" 20 gc "gopkg.in/check.v1" 21 "gopkg.in/juju/charmrepo.v3" 22 "gopkg.in/juju/names.v2" 23 "gopkg.in/juju/worker.v1" 24 25 "github.com/juju/juju/agent" 26 "github.com/juju/juju/api" 27 apideployer "github.com/juju/juju/api/deployer" 28 "github.com/juju/juju/cmd/jujud/agent/agenttest" 29 cmdutil "github.com/juju/juju/cmd/jujud/util" 30 "github.com/juju/juju/core/instance" 31 "github.com/juju/juju/environs" 32 "github.com/juju/juju/environs/config" 33 "github.com/juju/juju/environs/context" 34 "github.com/juju/juju/environs/instances" 35 jujutesting "github.com/juju/juju/juju/testing" 36 "github.com/juju/juju/mongo/mongotest" 37 "github.com/juju/juju/network" 38 "github.com/juju/juju/provider/dummy" 39 "github.com/juju/juju/service/upstart" 40 "github.com/juju/juju/state" 41 coretesting "github.com/juju/juju/testing" 42 "github.com/juju/juju/tools" 43 jujuversion "github.com/juju/juju/version" 44 jworker "github.com/juju/juju/worker" 45 "github.com/juju/juju/worker/authenticationworker" 46 "github.com/juju/juju/worker/deployer" 47 "github.com/juju/juju/worker/logsender" 48 ) 49 50 const ( 51 initialMachinePassword = "machine-password-1234567890" 52 initialUnitPassword = "unit-password-1234567890" 53 startWorkerWait = 250 * time.Millisecond 54 ) 55 56 var fastDialOpts = api.DialOpts{ 57 Timeout: coretesting.LongWait, 58 RetryDelay: coretesting.ShortWait, 59 } 60 61 type commonMachineSuite struct { 62 fakeEnsureMongo *agenttest.FakeEnsureMongo 63 AgentSuite 64 } 65 66 func (s *commonMachineSuite) SetUpSuite(c *gc.C) { 67 s.AgentSuite.SetUpSuite(c) 68 s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber) 69 s.PatchValue(&stateWorkerDialOpts, mongotest.DialOpts()) 70 } 71 72 func (s *commonMachineSuite) SetUpTest(c *gc.C) { 73 s.AgentSuite.SetUpTest(c) 74 s.PatchValue(&charmrepo.CacheDir, c.MkDir()) 75 76 // Patch ssh user to avoid touching ~ubuntu/.ssh/authorized_keys. 77 s.PatchValue(&authenticationworker.SSHUser, "") 78 79 testpath := c.MkDir() 80 s.PatchEnvPathPrepend(testpath) 81 // mock out the start method so we can fake install services without sudo 82 fakeCmd(filepath.Join(testpath, "start")) 83 fakeCmd(filepath.Join(testpath, "stop")) 84 85 s.PatchValue(&upstart.InitDir, c.MkDir()) 86 s.fakeEnsureMongo = agenttest.InstallFakeEnsureMongo(s) 87 } 88 89 func (s *commonMachineSuite) assertChannelActive(c *gc.C, aChannel chan struct{}, intent string) { 90 // Wait for channel to be active. 91 select { 92 case <-aChannel: 93 case <-time.After(coretesting.LongWait): 94 c.Fatalf("timeout while waiting for %v", intent) 95 } 96 } 97 98 func (s *commonMachineSuite) assertChannelInactive(c *gc.C, aChannel chan struct{}, intent string) { 99 // Now make sure the channel is not active. 100 select { 101 case <-aChannel: 102 c.Fatalf("%v unexpectedly", intent) 103 case <-time.After(startWorkerWait): 104 } 105 } 106 107 func fakeCmd(path string) { 108 err := ioutil.WriteFile(path, []byte("#!/bin/bash --norc\nexit 0"), 0755) 109 if err != nil { 110 panic(err) 111 } 112 } 113 114 func (s *commonMachineSuite) TearDownTest(c *gc.C) { 115 s.AgentSuite.TearDownTest(c) 116 } 117 118 // primeAgent adds a new Machine to run the given jobs, and sets up the 119 // machine agent's directory. It returns the new machine, the 120 // agent's configuration and the tools currently running. 121 func (s *commonMachineSuite) primeAgent(c *gc.C, jobs ...state.MachineJob) (m *state.Machine, agentConfig agent.ConfigSetterWriter, tools *tools.Tools) { 122 vers := version.Binary{ 123 Number: jujuversion.Current, 124 Arch: arch.HostArch(), 125 Series: series.MustHostSeries(), 126 } 127 return s.primeAgentVersion(c, vers, jobs...) 128 } 129 130 // primeAgentVersion is similar to primeAgent, but permits the 131 // caller to specify the version.Binary to prime with. 132 func (s *commonMachineSuite) primeAgentVersion(c *gc.C, vers version.Binary, jobs ...state.MachineJob) (m *state.Machine, agentConfig agent.ConfigSetterWriter, tools *tools.Tools) { 133 m, err := s.State.AddMachine("quantal", jobs...) 134 c.Assert(err, jc.ErrorIsNil) 135 return s.primeAgentWithMachine(c, m, vers) 136 } 137 138 func (s *commonMachineSuite) primeAgentWithMachine(c *gc.C, m *state.Machine, vers version.Binary) (*state.Machine, agent.ConfigSetterWriter, *tools.Tools) { 139 pinger, err := m.SetAgentPresence() 140 c.Assert(err, jc.ErrorIsNil) 141 s.AddCleanup(func(c *gc.C) { 142 c.Assert(worker.Stop(pinger), jc.ErrorIsNil) 143 }) 144 return s.configureMachine(c, m.Id(), vers) 145 } 146 147 func (s *commonMachineSuite) configureMachine(c *gc.C, machineId string, vers version.Binary) ( 148 machine *state.Machine, agentConfig agent.ConfigSetterWriter, tools *tools.Tools, 149 ) { 150 m, err := s.State.Machine(machineId) 151 c.Assert(err, jc.ErrorIsNil) 152 153 // Add a machine and ensure it is provisioned. 154 inst, md := jujutesting.AssertStartInstance(c, s.Environ, context.NewCloudCallContext(), s.ControllerConfig.ControllerUUID(), machineId) 155 c.Assert(m.SetProvisioned(inst.Id(), "", agent.BootstrapNonce, md), jc.ErrorIsNil) 156 157 // Add an address for the tests in case the initiateMongoServer 158 // codepath is exercised. 159 s.setFakeMachineAddresses(c, m) 160 161 // Set up the new machine. 162 err = m.SetAgentVersion(vers) 163 c.Assert(err, jc.ErrorIsNil) 164 err = m.SetPassword(initialMachinePassword) 165 c.Assert(err, jc.ErrorIsNil) 166 tag := m.Tag() 167 if m.IsManager() { 168 err = m.SetMongoPassword(initialMachinePassword) 169 c.Assert(err, jc.ErrorIsNil) 170 agentConfig, tools = s.PrimeStateAgentVersion(c, tag, initialMachinePassword, vers) 171 info, ok := agentConfig.StateServingInfo() 172 c.Assert(ok, jc.IsTrue) 173 ssi := cmdutil.ParamsStateServingInfoToStateStateServingInfo(info) 174 err = s.State.SetStateServingInfo(ssi) 175 c.Assert(err, jc.ErrorIsNil) 176 } else { 177 agentConfig, tools = s.PrimeAgentVersion(c, tag, initialMachinePassword, vers) 178 } 179 err = agentConfig.Write() 180 c.Assert(err, jc.ErrorIsNil) 181 return m, agentConfig, tools 182 } 183 184 func NewTestMachineAgentFactory( 185 agentConfWriter AgentConfigWriter, 186 bufferedLogger *logsender.BufferedLogWriter, 187 rootDir string, 188 ) func(string) (*MachineAgent, error) { 189 preUpgradeSteps := func(_ *state.StatePool, _ agent.Config, isController, isMaster bool) error { 190 return nil 191 } 192 return func(machineId string) (*MachineAgent, error) { 193 return NewMachineAgent( 194 machineId, 195 agentConfWriter, 196 bufferedLogger, 197 worker.NewRunner(worker.RunnerParams{ 198 IsFatal: cmdutil.IsFatal, 199 MoreImportant: cmdutil.MoreImportant, 200 RestartDelay: jworker.RestartDelay, 201 }), 202 &mockLoopDeviceManager{}, 203 DefaultIntrospectionSocketName, 204 preUpgradeSteps, 205 rootDir, 206 ) 207 } 208 } 209 210 // newAgent returns a new MachineAgent instance 211 func (s *commonMachineSuite) newAgent(c *gc.C, m *state.Machine) *MachineAgent { 212 agentConf := agentConf{dataDir: s.DataDir()} 213 agentConf.ReadConfig(names.NewMachineTag(m.Id()).String()) 214 logger := s.newBufferedLogWriter() 215 machineAgentFactory := NewTestMachineAgentFactory(&agentConf, logger, c.MkDir()) 216 machineAgent, err := machineAgentFactory(m.Id()) 217 c.Assert(err, jc.ErrorIsNil) 218 return machineAgent 219 } 220 221 func (s *commonMachineSuite) newBufferedLogWriter() *logsender.BufferedLogWriter { 222 logger := logsender.NewBufferedLogWriter(1024) 223 s.AddCleanup(func(*gc.C) { logger.Close() }) 224 return logger 225 } 226 227 func patchDeployContext(c *gc.C, st *state.State) (*fakeContext, func()) { 228 ctx := &fakeContext{ 229 inited: newSignal(), 230 deployed: make(set.Strings), 231 } 232 orig := newDeployContext 233 newDeployContext = func(dst *apideployer.State, agentConfig agent.Config) deployer.Context { 234 ctx.st = st 235 ctx.agentConfig = agentConfig 236 ctx.inited.trigger() 237 return ctx 238 } 239 return ctx, func() { newDeployContext = orig } 240 } 241 242 func (s *commonMachineSuite) setFakeMachineAddresses(c *gc.C, machine *state.Machine) { 243 addrs := network.NewAddresses("0.1.2.3") 244 err := machine.SetProviderAddresses(addrs...) 245 c.Assert(err, jc.ErrorIsNil) 246 // Set the addresses in the environ instance as well so that if the instance poller 247 // runs it won't overwrite them. 248 instId, err := machine.InstanceId() 249 c.Assert(err, jc.ErrorIsNil) 250 insts, err := s.Environ.Instances(context.NewCloudCallContext(), []instance.Id{instId}) 251 c.Assert(err, jc.ErrorIsNil) 252 dummy.SetInstanceAddresses(insts[0], addrs) 253 } 254 255 // opRecvTimeout waits for any of the given kinds of operation to 256 // be received from ops, and times out if not. 257 func opRecvTimeout(c *gc.C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation { 258 st.StartSync() 259 timeout := time.After(coretesting.LongWait) 260 for { 261 select { 262 case op := <-opc: 263 for _, k := range kinds { 264 if reflect.TypeOf(op) == reflect.TypeOf(k) { 265 return op 266 } 267 } 268 c.Logf("discarding unknown event %#v", op) 269 case <-time.After(coretesting.ShortWait): 270 st.StartSync() 271 case <-timeout: 272 c.Fatalf("time out wating for operation") 273 } 274 } 275 } 276 277 type mockLoopDeviceManager struct { 278 detachLoopDevicesArgRootfs string 279 detachLoopDevicesArgPrefix string 280 } 281 282 func (m *mockLoopDeviceManager) DetachLoopDevices(rootfs, prefix string) error { 283 m.detachLoopDevicesArgRootfs = rootfs 284 m.detachLoopDevicesArgPrefix = prefix 285 return nil 286 } 287 288 func newSignal() *signal { 289 return &signal{ch: make(chan struct{})} 290 } 291 292 type signal struct { 293 mu sync.Mutex 294 ch chan struct{} 295 } 296 297 func (s *signal) triggered() <-chan struct{} { 298 return s.ch 299 } 300 301 func (s *signal) assertTriggered(c *gc.C, thing string) { 302 select { 303 case <-s.triggered(): 304 case <-time.After(coretesting.LongWait): 305 c.Fatalf("timed out waiting for " + thing) 306 } 307 } 308 309 func (s *signal) assertNotTriggered(c *gc.C, wait time.Duration, thing string) { 310 select { 311 case <-s.triggered(): 312 c.Fatalf("%v unexpectedly", thing) 313 case <-time.After(wait): 314 } 315 } 316 317 func (s *signal) trigger() { 318 s.mu.Lock() 319 defer s.mu.Unlock() 320 321 select { 322 case <-s.ch: 323 // Already closed. 324 default: 325 close(s.ch) 326 } 327 } 328 329 type runner interface { 330 Run(*cmd.Context) error 331 Stop() error 332 } 333 334 // runWithTimeout runs an agent and waits 335 // for it to complete within a reasonable time. 336 func runWithTimeout(r runner) error { 337 done := make(chan error) 338 go func() { 339 done <- r.Run(nil) 340 }() 341 select { 342 case err := <-done: 343 return err 344 case <-time.After(coretesting.LongWait): 345 } 346 err := r.Stop() 347 return fmt.Errorf("timed out waiting for agent to finish; stop error: %v", err) 348 } 349 350 func newDummyWorker() worker.Worker { 351 return jworker.NewSimpleWorker(func(stop <-chan struct{}) error { 352 <-stop 353 return nil 354 }) 355 } 356 357 type FakeConfig struct { 358 agent.ConfigSetter 359 values map[string]string 360 } 361 362 func (FakeConfig) LogDir() string { 363 return filepath.FromSlash("/var/log/juju/") 364 } 365 366 func (FakeConfig) Tag() names.Tag { 367 return names.NewMachineTag("42") 368 } 369 370 func (f FakeConfig) Value(key string) string { 371 if f.values == nil { 372 return "" 373 } 374 return f.values[key] 375 } 376 377 type FakeAgentConfig struct { 378 AgentConf 379 values map[string]string 380 } 381 382 func (FakeAgentConfig) ReadConfig(string) error { return nil } 383 384 func (a FakeAgentConfig) CurrentConfig() agent.Config { 385 return FakeConfig{values: a.values} 386 } 387 388 func (FakeAgentConfig) ChangeConfig(mutate agent.ConfigMutator) error { 389 return mutate(FakeConfig{}) 390 } 391 392 func (FakeAgentConfig) CheckArgs([]string) error { return nil } 393 394 // minModelWorkersEnviron implements just enough of environs.Environ 395 // to allow model workers to run. 396 type minModelWorkersEnviron struct { 397 environs.Environ 398 } 399 400 func (e *minModelWorkersEnviron) Config() *config.Config { 401 attrs := coretesting.FakeConfig() 402 cfg, err := config.New(config.UseDefaults, attrs) 403 if err != nil { 404 panic(err) 405 } 406 return cfg 407 } 408 409 func (e *minModelWorkersEnviron) SetConfig(*config.Config) error { 410 return nil 411 } 412 413 func (e *minModelWorkersEnviron) AllInstances(context.ProviderCallContext) ([]instances.Instance, error) { 414 return nil, nil 415 } 416 417 func (e *minModelWorkersEnviron) Instances(ctx context.ProviderCallContext, ids []instance.Id) ([]instances.Instance, error) { 418 return nil, nil 419 }