github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/environs/tools/tools_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package tools_test 5 6 import ( 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 11 "github.com/juju/loggo" 12 gc "launchpad.net/gocheck" 13 14 "launchpad.net/juju-core/environs" 15 "launchpad.net/juju-core/environs/config" 16 "launchpad.net/juju-core/environs/configstore" 17 envtesting "launchpad.net/juju-core/environs/testing" 18 envtools "launchpad.net/juju-core/environs/tools" 19 ttesting "launchpad.net/juju-core/environs/tools/testing" 20 "launchpad.net/juju-core/errors" 21 "launchpad.net/juju-core/provider/dummy" 22 "launchpad.net/juju-core/testing" 23 jc "launchpad.net/juju-core/testing/checkers" 24 "launchpad.net/juju-core/testing/testbase" 25 coretools "launchpad.net/juju-core/tools" 26 "launchpad.net/juju-core/version" 27 ) 28 29 type SimpleStreamsToolsSuite struct { 30 env environs.Environ 31 testbase.LoggingSuite 32 envtesting.ToolsFixture 33 origCurrentVersion version.Binary 34 customToolsDir string 35 publicToolsDir string 36 } 37 38 func setupToolsTests() { 39 gc.Suite(&SimpleStreamsToolsSuite{}) 40 } 41 42 func (s *SimpleStreamsToolsSuite) SetUpSuite(c *gc.C) { 43 s.LoggingSuite.SetUpSuite(c) 44 s.customToolsDir = c.MkDir() 45 s.publicToolsDir = c.MkDir() 46 } 47 48 func (s *SimpleStreamsToolsSuite) SetUpTest(c *gc.C) { 49 s.ToolsFixture.DefaultBaseURL = "file://" + s.publicToolsDir 50 s.LoggingSuite.SetUpTest(c) 51 s.ToolsFixture.SetUpTest(c) 52 s.origCurrentVersion = version.Current 53 s.reset(c, nil) 54 } 55 56 func (s *SimpleStreamsToolsSuite) TearDownTest(c *gc.C) { 57 dummy.Reset() 58 version.Current = s.origCurrentVersion 59 s.ToolsFixture.TearDownTest(c) 60 s.LoggingSuite.TearDownTest(c) 61 } 62 63 func (s *SimpleStreamsToolsSuite) reset(c *gc.C, attrs map[string]interface{}) { 64 final := map[string]interface{}{ 65 "tools-metadata-url": "file://" + s.customToolsDir, 66 } 67 for k, v := range attrs { 68 final[k] = v 69 } 70 s.resetEnv(c, final) 71 } 72 73 func (s *SimpleStreamsToolsSuite) removeTools(c *gc.C) { 74 for _, dir := range []string{s.customToolsDir, s.publicToolsDir} { 75 files, err := ioutil.ReadDir(dir) 76 c.Assert(err, gc.IsNil) 77 for _, f := range files { 78 err := os.RemoveAll(filepath.Join(dir, f.Name())) 79 c.Assert(err, gc.IsNil) 80 } 81 } 82 } 83 84 func (s *SimpleStreamsToolsSuite) uploadCustom(c *gc.C, verses ...version.Binary) map[version.Binary]string { 85 return ttesting.UploadToDirectory(c, s.customToolsDir, verses...) 86 } 87 88 func (s *SimpleStreamsToolsSuite) uploadPublic(c *gc.C, verses ...version.Binary) map[version.Binary]string { 89 return ttesting.UploadToDirectory(c, s.publicToolsDir, verses...) 90 } 91 92 func (s *SimpleStreamsToolsSuite) resetEnv(c *gc.C, attrs map[string]interface{}) { 93 version.Current = s.origCurrentVersion 94 dummy.Reset() 95 cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(attrs)) 96 c.Assert(err, gc.IsNil) 97 env, err := environs.Prepare(cfg, testing.Context(c), configstore.NewMem()) 98 c.Assert(err, gc.IsNil) 99 s.env = env 100 s.removeTools(c) 101 } 102 103 var findToolsTests = []struct { 104 info string 105 major int 106 minor int 107 custom []version.Binary 108 public []version.Binary 109 expect []version.Binary 110 err error 111 }{{ 112 info: "none available anywhere", 113 major: 1, 114 err: envtools.ErrNoTools, 115 }, { 116 info: "custom/private tools only, none matching", 117 major: 1, 118 minor: 2, 119 custom: envtesting.V220all, 120 err: coretools.ErrNoMatches, 121 }, { 122 info: "custom tools found", 123 major: 1, 124 minor: 2, 125 custom: envtesting.VAll, 126 expect: envtesting.V120all, 127 }, { 128 info: "public tools found", 129 major: 1, 130 minor: 1, 131 public: envtesting.VAll, 132 expect: envtesting.V110all, 133 }, { 134 info: "public and custom tools found, only taken from custom", 135 major: 1, 136 minor: 1, 137 custom: envtesting.V110p, 138 public: envtesting.VAll, 139 expect: envtesting.V110p, 140 }, { 141 info: "custom tools completely block public ones", 142 major: 1, 143 minor: -1, 144 custom: envtesting.V220all, 145 public: envtesting.VAll, 146 expect: envtesting.V1all, 147 }, { 148 info: "tools matching major version only", 149 major: 1, 150 minor: -1, 151 public: envtesting.VAll, 152 expect: envtesting.V1all, 153 }} 154 155 func (s *SimpleStreamsToolsSuite) TestFindTools(c *gc.C) { 156 for i, test := range findToolsTests { 157 c.Logf("\ntest %d: %s", i, test.info) 158 s.reset(c, nil) 159 custom := s.uploadCustom(c, test.custom...) 160 public := s.uploadPublic(c, test.public...) 161 actual, err := envtools.FindTools(s.env, test.major, test.minor, coretools.Filter{}, envtools.DoNotAllowRetry) 162 if test.err != nil { 163 if len(actual) > 0 { 164 c.Logf(actual.String()) 165 } 166 c.Check(err, jc.Satisfies, errors.IsNotFoundError) 167 continue 168 } 169 expect := map[version.Binary]string{} 170 for _, expected := range test.expect { 171 // If the tools exist in custom, that's preferred. 172 var ok bool 173 if expect[expected], ok = custom[expected]; !ok { 174 expect[expected] = public[expected] 175 } 176 } 177 c.Check(actual.URLs(), gc.DeepEquals, expect) 178 } 179 } 180 181 func (s *SimpleStreamsToolsSuite) TestFindToolsInControlBucket(c *gc.C) { 182 s.reset(c, nil) 183 custom := ttesting.UploadToStorage(c, s.env.Storage(), envtesting.V110p...) 184 s.uploadPublic(c, envtesting.VAll...) 185 actual, err := envtools.FindTools(s.env, 1, 1, coretools.Filter{}, envtools.DoNotAllowRetry) 186 c.Assert(err, gc.IsNil) 187 expect := map[version.Binary]string{} 188 for _, expected := range envtesting.V110p { 189 expect[expected] = custom[expected] 190 } 191 c.Assert(actual.URLs(), gc.DeepEquals, expect) 192 } 193 194 func (s *SimpleStreamsToolsSuite) TestFindToolsFiltering(c *gc.C) { 195 tw := &loggo.TestWriter{} 196 c.Assert(loggo.RegisterWriter("filter-tester", tw, loggo.DEBUG), gc.IsNil) 197 defer loggo.RemoveWriter("filter-tester") 198 _, err := envtools.FindTools( 199 s.env, 1, -1, coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}}, envtools.DoNotAllowRetry) 200 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 201 // This is slightly overly prescriptive, but feel free to change or add 202 // messages. This still helps to ensure that all log messages are 203 // properly formed. 204 messages := []jc.SimpleMessage{ 205 {loggo.INFO, "reading tools with major version 1"}, 206 {loggo.INFO, "filtering tools by version: \\d+\\.\\d+\\.\\d+"}, 207 {loggo.DEBUG, "no architecture specified when finding tools, looking for any"}, 208 {loggo.DEBUG, "no series specified when finding tools, looking for any"}, 209 } 210 sources, err := envtools.GetMetadataSources(s.env) 211 c.Assert(err, gc.IsNil) 212 for i := 0; i < 2*len(sources); i++ { 213 messages = append(messages, 214 jc.SimpleMessage{loggo.DEBUG, `fetchData failed for .*`}, 215 jc.SimpleMessage{loggo.DEBUG, `cannot load index .*`}) 216 } 217 c.Check(tw.Log, jc.LogMatches, messages) 218 } 219 220 func (s *SimpleStreamsToolsSuite) TestFindBootstrapTools(c *gc.C) { 221 // Remove the default tools URL from the search path, just look in cloud storage. 222 s.PatchValue(&envtools.DefaultBaseURL, "") 223 for i, test := range envtesting.BootstrapToolsTests { 224 c.Logf("\ntest %d: %s", i, test.Info) 225 attrs := map[string]interface{}{ 226 "development": test.Development, 227 } 228 var agentVersion *version.Number 229 if test.AgentVersion != version.Zero { 230 attrs["agent-version"] = test.AgentVersion.String() 231 agentVersion = &test.AgentVersion 232 } 233 s.reset(c, attrs) 234 version.Current = test.CliVersion 235 available := s.uploadCustom(c, test.Available...) 236 237 params := envtools.BootstrapToolsParams{ 238 Version: agentVersion, 239 Series: test.DefaultSeries, 240 Arch: &test.Arch, 241 } 242 actual, err := envtools.FindBootstrapTools(s.env, params) 243 if test.Err != nil { 244 if len(actual) > 0 { 245 c.Logf(actual.String()) 246 } 247 c.Check(err, jc.Satisfies, errors.IsNotFoundError) 248 continue 249 } 250 expect := map[version.Binary]string{} 251 for _, expected := range test.Expect { 252 expect[expected] = available[expected] 253 } 254 c.Check(actual.URLs(), gc.DeepEquals, expect) 255 } 256 } 257 258 var findInstanceToolsTests = []struct { 259 info string 260 available []version.Binary 261 agentVersion version.Number 262 series string 263 arch string 264 expect []version.Binary 265 err error 266 }{{ 267 info: "nothing at all", 268 agentVersion: envtesting.V120, 269 series: "precise", 270 err: envtools.ErrNoTools, 271 }, { 272 info: "nothing matching 1", 273 available: envtesting.V100Xall, 274 agentVersion: envtesting.V120, 275 series: "precise", 276 err: coretools.ErrNoMatches, 277 }, { 278 info: "nothing matching 2", 279 available: envtesting.V120all, 280 agentVersion: envtesting.V110, 281 series: "precise", 282 err: coretools.ErrNoMatches, 283 }, { 284 info: "nothing matching 3", 285 available: envtesting.V120q, 286 agentVersion: envtesting.V120, 287 series: "precise", 288 err: coretools.ErrNoMatches, 289 }, { 290 info: "nothing matching 4", 291 available: envtesting.V120q, 292 agentVersion: envtesting.V120, 293 series: "quantal", 294 arch: "arm", 295 err: coretools.ErrNoMatches, 296 }, { 297 info: "actual match 1", 298 available: envtesting.VAll, 299 agentVersion: envtesting.V1001, 300 series: "precise", 301 expect: []version.Binary{envtesting.V1001p64}, 302 }, { 303 info: "actual match 2", 304 available: envtesting.VAll, 305 agentVersion: envtesting.V120, 306 series: "quantal", 307 expect: []version.Binary{envtesting.V120q64, envtesting.V120q32}, 308 }, { 309 info: "actual match 3", 310 available: envtesting.VAll, 311 agentVersion: envtesting.V110, 312 series: "quantal", 313 arch: "i386", 314 expect: []version.Binary{envtesting.V110q32}, 315 }} 316 317 func (s *SimpleStreamsToolsSuite) TestFindInstanceTools(c *gc.C) { 318 for i, test := range findInstanceToolsTests { 319 c.Logf("\ntest %d: %s", i, test.info) 320 s.reset(c, map[string]interface{}{ 321 "agent-version": test.agentVersion.String(), 322 }) 323 available := s.uploadCustom(c, test.available...) 324 325 agentVersion, _ := s.env.Config().AgentVersion() 326 actual, err := envtools.FindInstanceTools(s.env, agentVersion, test.series, &test.arch) 327 if test.err != nil { 328 if len(actual) > 0 { 329 c.Logf(actual.String()) 330 } 331 c.Check(err, jc.Satisfies, errors.IsNotFoundError) 332 continue 333 } 334 expect := map[version.Binary]string{} 335 for _, expected := range test.expect { 336 expect[expected] = available[expected] 337 } 338 c.Check(actual.URLs(), gc.DeepEquals, expect) 339 } 340 } 341 342 var findExactToolsTests = []struct { 343 info string 344 custom []version.Binary 345 public []version.Binary 346 seek version.Binary 347 err error 348 }{{ 349 info: "nothing available", 350 seek: envtesting.V100p64, 351 err: envtools.ErrNoTools, 352 }, { 353 info: "only non-matches available in custom", 354 custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64), 355 seek: envtesting.V100p64, 356 err: coretools.ErrNoMatches, 357 }, { 358 info: "exact match available in custom", 359 custom: []version.Binary{envtesting.V100p64}, 360 seek: envtesting.V100p64, 361 }, { 362 info: "only non-matches available in public", 363 custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64), 364 seek: envtesting.V100p64, 365 err: coretools.ErrNoMatches, 366 }, { 367 info: "exact match available in public", 368 public: []version.Binary{envtesting.V100p64}, 369 seek: envtesting.V100p64, 370 }, { 371 info: "exact match in public not blocked by custom", 372 custom: envtesting.V110all, 373 public: []version.Binary{envtesting.V100p64}, 374 seek: envtesting.V100p64, 375 }} 376 377 func (s *SimpleStreamsToolsSuite) TestFindExactTools(c *gc.C) { 378 for i, test := range findExactToolsTests { 379 c.Logf("\ntest %d: %s", i, test.info) 380 s.reset(c, nil) 381 custom := s.uploadCustom(c, test.custom...) 382 public := s.uploadPublic(c, test.public...) 383 actual, err := envtools.FindExactTools(s.env, test.seek.Number, test.seek.Series, test.seek.Arch) 384 if test.err == nil { 385 if !c.Check(err, gc.IsNil) { 386 continue 387 } 388 c.Check(actual.Version, gc.Equals, test.seek) 389 if _, ok := custom[actual.Version]; ok { 390 c.Check(actual.URL, gc.DeepEquals, custom[actual.Version]) 391 } else { 392 c.Check(actual.URL, gc.DeepEquals, public[actual.Version]) 393 } 394 } else { 395 c.Check(err, jc.Satisfies, errors.IsNotFoundError) 396 } 397 } 398 } 399 400 // fakeToolsForSeries fakes a Tools object with just enough information for 401 // testing the handling its OS series. 402 func fakeToolsForSeries(series string) *coretools.Tools { 403 return &coretools.Tools{Version: version.Binary{Series: series}} 404 } 405 406 // fakeToolsList fakes a envtools.List containing Tools objects for the given 407 // respective series, in the same number and order. 408 func fakeToolsList(series ...string) coretools.List { 409 list := coretools.List{} 410 for _, name := range series { 411 list = append(list, fakeToolsForSeries(name)) 412 } 413 return list 414 } 415 416 type ToolsListSuite struct{} 417 418 func (s *ToolsListSuite) TestCheckToolsSeriesRequiresTools(c *gc.C) { 419 err := envtools.CheckToolsSeries(fakeToolsList(), "precise") 420 c.Assert(err, gc.NotNil) 421 c.Check(err, gc.ErrorMatches, "expected single series, got \\[\\]") 422 } 423 424 func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsOneSetOfTools(c *gc.C) { 425 names := []string{"precise", "raring"} 426 for _, series := range names { 427 list := fakeToolsList(series) 428 err := envtools.CheckToolsSeries(list, series) 429 c.Check(err, gc.IsNil) 430 } 431 } 432 433 func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsMultipleForSameSeries(c *gc.C) { 434 series := "quantal" 435 list := fakeToolsList(series, series, series) 436 err := envtools.CheckToolsSeries(list, series) 437 c.Check(err, gc.IsNil) 438 } 439 440 func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForOtherSeries(c *gc.C) { 441 list := fakeToolsList("hoary") 442 err := envtools.CheckToolsSeries(list, "warty") 443 c.Assert(err, gc.NotNil) 444 c.Check(err, gc.ErrorMatches, "tools mismatch: expected series warty, got hoary") 445 } 446 447 func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForMixedSeries(c *gc.C) { 448 list := fakeToolsList("precise", "raring") 449 err := envtools.CheckToolsSeries(list, "precise") 450 c.Assert(err, gc.NotNil) 451 c.Check(err, gc.ErrorMatches, "expected single series, got .*") 452 }