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  }