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