github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/jujud/agent/agent_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agent
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/cmd"
    11  	"github.com/juju/names"
    12  	gitjujutesting "github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/agent"
    18  	agenttools "github.com/juju/juju/agent/tools"
    19  	"github.com/juju/juju/api"
    20  	apienvironment "github.com/juju/juju/api/environment"
    21  	"github.com/juju/juju/apiserver/params"
    22  	agenttesting "github.com/juju/juju/cmd/jujud/agent/testing"
    23  	cmdutil "github.com/juju/juju/cmd/jujud/util"
    24  	"github.com/juju/juju/environs/filestorage"
    25  	envtesting "github.com/juju/juju/environs/testing"
    26  	"github.com/juju/juju/mongo"
    27  	"github.com/juju/juju/network"
    28  	"github.com/juju/juju/state"
    29  	"github.com/juju/juju/state/multiwatcher"
    30  	coretesting "github.com/juju/juju/testing"
    31  	coretools "github.com/juju/juju/tools"
    32  	"github.com/juju/juju/version"
    33  	"github.com/juju/juju/worker"
    34  	"github.com/juju/juju/worker/proxyupdater"
    35  )
    36  
    37  var (
    38  	_ = gc.Suite(&apiOpenSuite{})
    39  )
    40  
    41  type apiOpenSuite struct{ coretesting.BaseSuite }
    42  
    43  func (s *apiOpenSuite) SetUpTest(c *gc.C) {
    44  	s.PatchValue(&checkProvisionedStrategy, utils.AttemptStrategy{})
    45  }
    46  
    47  func (s *apiOpenSuite) TestOpenAPIStateReplaceErrors(c *gc.C) {
    48  	type replaceErrors struct {
    49  		openErr    error
    50  		replaceErr error
    51  	}
    52  	var apiError error
    53  	s.PatchValue(&apiOpen, func(info *api.Info, opts api.DialOpts) (*api.State, error) {
    54  		return nil, apiError
    55  	})
    56  	errReplacePairs := []replaceErrors{{
    57  		fmt.Errorf("blah"), nil,
    58  	}, {
    59  		openErr:    &params.Error{Code: params.CodeNotProvisioned},
    60  		replaceErr: worker.ErrTerminateAgent,
    61  	}, {
    62  		openErr:    &params.Error{Code: params.CodeUnauthorized},
    63  		replaceErr: worker.ErrTerminateAgent,
    64  	}}
    65  	for i, test := range errReplacePairs {
    66  		c.Logf("test %d", i)
    67  		apiError = test.openErr
    68  		_, _, err := OpenAPIState(fakeAPIOpenConfig{}, nil)
    69  		if test.replaceErr == nil {
    70  			c.Check(err, gc.Equals, test.openErr)
    71  		} else {
    72  			c.Check(err, gc.Equals, test.replaceErr)
    73  		}
    74  	}
    75  }
    76  
    77  func (s *apiOpenSuite) TestOpenAPIStateWaitsProvisioned(c *gc.C) {
    78  	s.PatchValue(&checkProvisionedStrategy.Min, 5)
    79  	var called int
    80  	s.PatchValue(&apiOpen, func(info *api.Info, opts api.DialOpts) (*api.State, error) {
    81  		called++
    82  		if called == checkProvisionedStrategy.Min-1 {
    83  			return nil, &params.Error{Code: params.CodeUnauthorized}
    84  		}
    85  		return nil, &params.Error{Code: params.CodeNotProvisioned}
    86  	})
    87  	_, _, err := OpenAPIState(fakeAPIOpenConfig{}, nil)
    88  	c.Assert(err, gc.Equals, worker.ErrTerminateAgent)
    89  	c.Assert(called, gc.Equals, checkProvisionedStrategy.Min-1)
    90  }
    91  
    92  func (s *apiOpenSuite) TestOpenAPIStateWaitsProvisionedGivesUp(c *gc.C) {
    93  	s.PatchValue(&checkProvisionedStrategy.Min, 5)
    94  	var called int
    95  	s.PatchValue(&apiOpen, func(info *api.Info, opts api.DialOpts) (*api.State, error) {
    96  		called++
    97  		return nil, &params.Error{Code: params.CodeNotProvisioned}
    98  	})
    99  	_, _, err := OpenAPIState(fakeAPIOpenConfig{}, nil)
   100  	c.Assert(err, gc.Equals, worker.ErrTerminateAgent)
   101  	// +1 because we always attempt at least once outside the attempt strategy
   102  	// (twice if the API server initially returns CodeUnauthorized.)
   103  	c.Assert(called, gc.Equals, checkProvisionedStrategy.Min+1)
   104  }
   105  
   106  type acCreator func() (cmd.Command, *AgentConf)
   107  
   108  // CheckAgentCommand is a utility function for verifying that common agent
   109  // options are handled by a Command; it returns an instance of that
   110  // command pre-parsed, with any mandatory flags added.
   111  func CheckAgentCommand(c *gc.C, create acCreator, args []string) cmd.Command {
   112  	com, conf := create()
   113  	err := coretesting.InitCommand(com, args)
   114  	c.Assert(conf.DataDir, gc.Equals, "/var/lib/juju")
   115  	badArgs := append(args, "--data-dir", "")
   116  	com, _ = create()
   117  	err = coretesting.InitCommand(com, badArgs)
   118  	c.Assert(err, gc.ErrorMatches, "--data-dir option must be set")
   119  
   120  	args = append(args, "--data-dir", "jd")
   121  	com, conf = create()
   122  	c.Assert(coretesting.InitCommand(com, args), gc.IsNil)
   123  	c.Assert(conf.DataDir, gc.Equals, "jd")
   124  	return com
   125  }
   126  
   127  // ParseAgentCommand is a utility function that inserts the always-required args
   128  // before parsing an agent command and returning the result.
   129  func ParseAgentCommand(ac cmd.Command, args []string) error {
   130  	common := []string{
   131  		"--data-dir", "jd",
   132  	}
   133  	return coretesting.InitCommand(ac, append(common, args...))
   134  }
   135  
   136  // AgentSuite is a fixture to be used by agent test suites.
   137  type AgentSuite struct {
   138  	agenttesting.AgentSuite
   139  	oldRestartDelay time.Duration
   140  }
   141  
   142  func (s *AgentSuite) SetUpSuite(c *gc.C) {
   143  	s.JujuConnSuite.SetUpSuite(c)
   144  
   145  	s.oldRestartDelay = worker.RestartDelay
   146  	// We could use testing.ShortWait, but this thrashes quite
   147  	// a bit when some tests are restarting every 50ms for 10 seconds,
   148  	// so use a slightly more friendly delay.
   149  	worker.RestartDelay = 250 * time.Millisecond
   150  	s.PatchValue(&cmdutil.EnsureMongoServer, func(mongo.EnsureServerParams) error {
   151  		return nil
   152  	})
   153  }
   154  
   155  func (s *AgentSuite) TearDownSuite(c *gc.C) {
   156  	s.JujuConnSuite.TearDownSuite(c)
   157  	worker.RestartDelay = s.oldRestartDelay
   158  }
   159  
   160  func (s *AgentSuite) SetUpTest(c *gc.C) {
   161  	s.JujuConnSuite.SetUpTest(c)
   162  	// Set API host ports so FindTools/Tools API calls succeed.
   163  	hostPorts := [][]network.HostPort{{{
   164  		Address: network.NewAddress("0.1.2.3", network.ScopeUnknown),
   165  		Port:    1234,
   166  	}}}
   167  	err := s.State.SetAPIHostPorts(hostPorts)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	s.PatchValue(&proxyupdater.New, func(*apienvironment.Facade, bool) worker.Worker {
   170  		return newDummyWorker()
   171  	})
   172  }
   173  
   174  func (s *AgentSuite) primeAPIHostPorts(c *gc.C) {
   175  	apiInfo := s.APIInfo(c)
   176  
   177  	c.Assert(apiInfo.Addrs, gc.HasLen, 1)
   178  	hostPort, err := agenttesting.ParseHostPort(apiInfo.Addrs[0])
   179  	c.Assert(err, jc.ErrorIsNil)
   180  
   181  	err = s.State.SetAPIHostPorts([][]network.HostPort{{hostPort}})
   182  	c.Assert(err, jc.ErrorIsNil)
   183  
   184  	logger.Debugf("api host ports primed %#v", hostPort)
   185  }
   186  
   187  // primeStateAgent writes the configuration file and tools with version vers
   188  // for an agent with the given entity name.  It returns the agent's configuration
   189  // and the current tools.
   190  func (s *AgentSuite) PrimeStateAgent(
   191  	c *gc.C, tag names.Tag, password string, vers version.Binary) (agent.ConfigSetterWriter, *coretools.Tools) {
   192  
   193  	stor, err := filestorage.NewFileStorageWriter(c.MkDir())
   194  	c.Assert(err, jc.ErrorIsNil)
   195  	agentTools := envtesting.PrimeTools(c, stor, s.DataDir(), "released", vers)
   196  	tools1, err := agenttools.ChangeAgentTools(s.DataDir(), tag.String(), vers)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Assert(tools1, gc.DeepEquals, agentTools)
   199  
   200  	stateInfo := s.MongoInfo(c)
   201  	conf := writeStateAgentConfig(c, stateInfo, s.DataDir(), tag, password, vers, s.State.EnvironTag())
   202  	s.primeAPIHostPorts(c)
   203  	return conf, agentTools
   204  }
   205  
   206  func (s *AgentSuite) RunTestOpenAPIState(c *gc.C, ent state.AgentEntity, agentCmd Agent, initialPassword string) {
   207  	conf, err := agent.ReadConfig(agent.ConfigPath(s.DataDir(), ent.Tag()))
   208  	c.Assert(err, jc.ErrorIsNil)
   209  
   210  	conf.SetPassword("")
   211  	err = conf.Write()
   212  	c.Assert(err, jc.ErrorIsNil)
   213  
   214  	// Check that it starts initially and changes the password
   215  	assertOpen := func(conf agent.Config) {
   216  		st, gotEnt, err := OpenAPIState(conf, agentCmd)
   217  		c.Assert(err, jc.ErrorIsNil)
   218  		c.Assert(st, gc.NotNil)
   219  		st.Close()
   220  		c.Assert(gotEnt.Tag(), gc.Equals, ent.Tag().String())
   221  	}
   222  	assertOpen(conf)
   223  
   224  	// Check that the initial password is no longer valid.
   225  	err = ent.Refresh()
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	c.Assert(ent.PasswordValid(initialPassword), jc.IsFalse)
   228  
   229  	// Read the configuration and check that we can connect with it.
   230  	conf, err = agent.ReadConfig(agent.ConfigPath(conf.DataDir(), conf.Tag()))
   231  	c.Assert(err, gc.IsNil)
   232  	// Check we can open the API with the new configuration.
   233  	assertOpen(conf)
   234  }
   235  
   236  // writeStateAgentConfig creates and writes a state agent config.
   237  func writeStateAgentConfig(
   238  	c *gc.C, stateInfo *mongo.MongoInfo, dataDir string, tag names.Tag,
   239  	password string, vers version.Binary, envTag names.EnvironTag) agent.ConfigSetterWriter {
   240  	port := gitjujutesting.FindTCPPort()
   241  	apiAddr := []string{fmt.Sprintf("localhost:%d", port)}
   242  	conf, err := agent.NewStateMachineConfig(
   243  		agent.AgentConfigParams{
   244  			DataDir:           dataDir,
   245  			Tag:               tag,
   246  			UpgradedToVersion: vers.Number,
   247  			Password:          password,
   248  			Nonce:             agent.BootstrapNonce,
   249  			StateAddresses:    stateInfo.Addrs,
   250  			APIAddresses:      apiAddr,
   251  			CACert:            stateInfo.CACert,
   252  			Environment:       envTag,
   253  		},
   254  		params.StateServingInfo{
   255  			Cert:         coretesting.ServerCert,
   256  			PrivateKey:   coretesting.ServerKey,
   257  			CAPrivateKey: coretesting.CAKey,
   258  			StatePort:    gitjujutesting.MgoServer.Port(),
   259  			APIPort:      port,
   260  		})
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	conf.SetPassword(password)
   263  	c.Assert(conf.Write(), gc.IsNil)
   264  	return conf
   265  }
   266  
   267  type fakeAPIOpenConfig struct{ agent.Config }
   268  
   269  func (fakeAPIOpenConfig) APIInfo() *api.Info              { return &api.Info{} }
   270  func (fakeAPIOpenConfig) OldPassword() string             { return "old" }
   271  func (fakeAPIOpenConfig) Jobs() []multiwatcher.MachineJob { return []multiwatcher.MachineJob{} }