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: ¶ms.Error{Code: params.CodeNotProvisioned}, 60 replaceErr: worker.ErrTerminateAgent, 61 }, { 62 openErr: ¶ms.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, ¶ms.Error{Code: params.CodeUnauthorized} 84 } 85 return nil, ¶ms.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, ¶ms.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{} }