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: ¶ms.Error{Code: params.CodeNotProvisioned}, 62 replaceErr: worker.ErrTerminateAgent, 63 }, { 64 openErr: ¶ms.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, ¶ms.Error{Code: params.CodeUnauthorized} 86 } 87 return nil, ¶ms.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, ¶ms.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{} }