github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/errors" 12 "github.com/juju/loggo" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 "github.com/juju/version" 16 gc "gopkg.in/check.v1" 17 18 "github.com/juju/juju/environs" 19 "github.com/juju/juju/environs/bootstrap" 20 sstesting "github.com/juju/juju/environs/simplestreams/testing" 21 envtesting "github.com/juju/juju/environs/testing" 22 envtools "github.com/juju/juju/environs/tools" 23 toolstesting "github.com/juju/juju/environs/tools/testing" 24 "github.com/juju/juju/juju/keys" 25 "github.com/juju/juju/jujuclient/jujuclienttesting" 26 "github.com/juju/juju/provider/dummy" 27 coretesting "github.com/juju/juju/testing" 28 coretools "github.com/juju/juju/tools" 29 jujuversion "github.com/juju/juju/version" 30 ) 31 32 type SimpleStreamsToolsSuite struct { 33 env environs.Environ 34 coretesting.BaseSuite 35 envtesting.ToolsFixture 36 origCurrentVersion version.Number 37 customToolsDir string 38 publicToolsDir string 39 } 40 41 func setupToolsTests() { 42 gc.Suite(&SimpleStreamsToolsSuite{}) 43 } 44 45 func (s *SimpleStreamsToolsSuite) SetUpSuite(c *gc.C) { 46 s.BaseSuite.SetUpSuite(c) 47 s.customToolsDir = c.MkDir() 48 s.publicToolsDir = c.MkDir() 49 s.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey) 50 } 51 52 func (s *SimpleStreamsToolsSuite) SetUpTest(c *gc.C) { 53 s.ToolsFixture.DefaultBaseURL = utils.MakeFileURL(s.publicToolsDir) 54 s.BaseSuite.SetUpTest(c) 55 s.ToolsFixture.SetUpTest(c) 56 s.origCurrentVersion = jujuversion.Current 57 s.reset(c, nil) 58 } 59 60 func (s *SimpleStreamsToolsSuite) TearDownTest(c *gc.C) { 61 dummy.Reset(c) 62 jujuversion.Current = s.origCurrentVersion 63 s.ToolsFixture.TearDownTest(c) 64 s.BaseSuite.TearDownTest(c) 65 } 66 67 func (s *SimpleStreamsToolsSuite) reset(c *gc.C, attrs map[string]interface{}) { 68 final := map[string]interface{}{ 69 "agent-metadata-url": utils.MakeFileURL(s.customToolsDir), 70 "agent-stream": "proposed", 71 } 72 for k, v := range attrs { 73 final[k] = v 74 } 75 s.resetEnv(c, final) 76 } 77 78 func (s *SimpleStreamsToolsSuite) removeTools(c *gc.C) { 79 for _, dir := range []string{s.customToolsDir, s.publicToolsDir} { 80 files, err := ioutil.ReadDir(dir) 81 c.Assert(err, jc.ErrorIsNil) 82 for _, f := range files { 83 err := os.RemoveAll(filepath.Join(dir, f.Name())) 84 c.Assert(err, jc.ErrorIsNil) 85 } 86 } 87 } 88 89 func (s *SimpleStreamsToolsSuite) uploadCustom(c *gc.C, verses ...version.Binary) map[version.Binary]string { 90 return toolstesting.UploadToDirectory(c, "proposed", s.customToolsDir, verses...) 91 } 92 93 func (s *SimpleStreamsToolsSuite) uploadPublic(c *gc.C, verses ...version.Binary) map[version.Binary]string { 94 return toolstesting.UploadToDirectory(c, "proposed", s.publicToolsDir, verses...) 95 } 96 97 func (s *SimpleStreamsToolsSuite) resetEnv(c *gc.C, attrs map[string]interface{}) { 98 jujuversion.Current = s.origCurrentVersion 99 dummy.Reset(c) 100 attrs = dummy.SampleConfig().Merge(attrs) 101 env, err := bootstrap.Prepare(envtesting.BootstrapContext(c), 102 jujuclienttesting.NewMemStore(), 103 bootstrap.PrepareParams{ 104 ControllerConfig: coretesting.FakeControllerConfig(), 105 ControllerName: attrs["name"].(string), 106 ModelConfig: attrs, 107 Cloud: dummy.SampleCloudSpec(), 108 AdminSecret: "admin-secret", 109 }, 110 ) 111 c.Assert(err, jc.ErrorIsNil) 112 s.env = env 113 s.removeTools(c) 114 } 115 116 var findToolsTests = []struct { 117 info string 118 major int 119 minor int 120 custom []version.Binary 121 public []version.Binary 122 expect []version.Binary 123 err error 124 }{{ 125 info: "none available anywhere", 126 major: 1, 127 err: envtools.ErrNoTools, 128 }, { 129 info: "custom/private tools only, none matching", 130 major: 1, 131 minor: 2, 132 custom: envtesting.V220all, 133 err: coretools.ErrNoMatches, 134 }, { 135 info: "custom tools found", 136 major: 1, 137 minor: 2, 138 custom: envtesting.VAll, 139 expect: envtesting.V120all, 140 }, { 141 info: "public tools found", 142 major: 1, 143 minor: 1, 144 public: envtesting.VAll, 145 expect: envtesting.V110all, 146 }, { 147 info: "public and custom tools found, only taken from custom", 148 major: 1, 149 minor: 1, 150 custom: envtesting.V110p, 151 public: envtesting.VAll, 152 expect: envtesting.V110p, 153 }, { 154 info: "custom tools completely block public ones", 155 major: 1, 156 minor: -1, 157 custom: envtesting.V220all, 158 public: envtesting.VAll, 159 expect: envtesting.V1all, 160 }, { 161 info: "tools matching major version only", 162 major: 1, 163 minor: -1, 164 public: envtesting.VAll, 165 expect: envtesting.V1all, 166 }} 167 168 func (s *SimpleStreamsToolsSuite) TestFindTools(c *gc.C) { 169 for i, test := range findToolsTests { 170 c.Logf("\ntest %d: %s", i, test.info) 171 s.reset(c, nil) 172 custom := s.uploadCustom(c, test.custom...) 173 public := s.uploadPublic(c, test.public...) 174 stream := envtools.PreferredStream(&jujuversion.Current, s.env.Config().Development(), s.env.Config().AgentStream()) 175 actual, err := envtools.FindTools(s.env, test.major, test.minor, stream, coretools.Filter{}) 176 if test.err != nil { 177 if len(actual) > 0 { 178 c.Logf(actual.String()) 179 } 180 c.Check(err, jc.Satisfies, errors.IsNotFound) 181 continue 182 } 183 expect := map[version.Binary][]string{} 184 for _, expected := range test.expect { 185 // If the tools exist in custom, that's preferred. 186 url, ok := custom[expected] 187 if !ok { 188 url = public[expected] 189 } 190 expect[expected] = append(expect[expected], url) 191 } 192 c.Check(actual.URLs(), gc.DeepEquals, expect) 193 } 194 } 195 196 func (s *SimpleStreamsToolsSuite) TestFindToolsFiltering(c *gc.C) { 197 var tw loggo.TestWriter 198 c.Assert(loggo.RegisterWriter("filter-tester", &tw), gc.IsNil) 199 defer loggo.RemoveWriter("filter-tester") 200 logger := loggo.GetLogger("juju.environs") 201 defer logger.SetLogLevel(logger.LogLevel()) 202 logger.SetLogLevel(loggo.TRACE) 203 204 _, err := envtools.FindTools( 205 s.env, 1, -1, "released", coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}}) 206 c.Assert(err, jc.Satisfies, errors.IsNotFound) 207 // This is slightly overly prescriptive, but feel free to change or add 208 // messages. This still helps to ensure that all log messages are 209 // properly formed. 210 messages := []jc.SimpleMessage{ 211 {loggo.INFO, "reading agent binaries with major version 1"}, 212 {loggo.INFO, "filtering agent binaries by version: \\d+\\.\\d+\\.\\d+"}, 213 {loggo.TRACE, "no architecture specified when finding agent binaries, looking for "}, 214 {loggo.TRACE, "no series specified when finding agent binaries, looking for \\[.*\\]"}, 215 } 216 sources, err := envtools.GetMetadataSources(s.env) 217 c.Assert(err, jc.ErrorIsNil) 218 for i := 0; i < len(sources); i++ { 219 messages = append(messages, 220 jc.SimpleMessage{loggo.TRACE, `fetchData failed for .*`}, 221 jc.SimpleMessage{loggo.TRACE, `cannot load index .*`}) 222 } 223 c.Check(tw.Log(), jc.LogMatches, messages) 224 } 225 226 var findExactToolsTests = []struct { 227 info string 228 custom []version.Binary 229 public []version.Binary 230 seek version.Binary 231 err error 232 }{{ 233 info: "nothing available", 234 seek: envtesting.V100p64, 235 err: envtools.ErrNoTools, 236 }, { 237 info: "only non-matches available in custom", 238 custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64), 239 seek: envtesting.V100p64, 240 err: coretools.ErrNoMatches, 241 }, { 242 info: "exact match available in custom", 243 custom: []version.Binary{envtesting.V100p64}, 244 seek: envtesting.V100p64, 245 }, { 246 info: "only non-matches available in public", 247 custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64), 248 seek: envtesting.V100p64, 249 err: coretools.ErrNoMatches, 250 }, { 251 info: "exact match available in public", 252 public: []version.Binary{envtesting.V100p64}, 253 seek: envtesting.V100p64, 254 }, { 255 info: "exact match in public not blocked by custom", 256 custom: envtesting.V110all, 257 public: []version.Binary{envtesting.V100p64}, 258 seek: envtesting.V100p64, 259 }} 260 261 func (s *SimpleStreamsToolsSuite) TestFindExactTools(c *gc.C) { 262 for i, test := range findExactToolsTests { 263 c.Logf("\ntest %d: %s", i, test.info) 264 s.reset(c, nil) 265 custom := s.uploadCustom(c, test.custom...) 266 public := s.uploadPublic(c, test.public...) 267 actual, err := envtools.FindExactTools(s.env, test.seek.Number, test.seek.Series, test.seek.Arch) 268 if test.err == nil { 269 if !c.Check(err, jc.ErrorIsNil) { 270 continue 271 } 272 c.Check(actual.Version, gc.Equals, test.seek) 273 if _, ok := custom[actual.Version]; ok { 274 c.Check(actual.URL, gc.DeepEquals, custom[actual.Version]) 275 } else { 276 c.Check(actual.URL, gc.DeepEquals, public[actual.Version]) 277 } 278 } else { 279 c.Check(err, jc.Satisfies, errors.IsNotFound) 280 } 281 } 282 } 283 284 var preferredStreamTests = []struct { 285 explicitVers string 286 currentVers string 287 forceDevel bool 288 streamInConfig string 289 expected string 290 }{{ 291 currentVers: "1.22.0", 292 streamInConfig: "released", 293 expected: "released", 294 }, { 295 currentVers: "1.22.0", 296 streamInConfig: "devel", 297 expected: "devel", 298 }, { 299 currentVers: "1.22.0", 300 expected: "released", 301 }, { 302 currentVers: "1.22-beta1", 303 expected: "devel", 304 }, { 305 currentVers: "1.22-beta1", 306 streamInConfig: "released", 307 expected: "devel", 308 }, { 309 currentVers: "1.22-beta1", 310 streamInConfig: "devel", 311 expected: "devel", 312 }, { 313 currentVers: "1.22.0", 314 forceDevel: true, 315 expected: "devel", 316 }, { 317 currentVers: "1.22.0", 318 explicitVers: "1.22-beta1", 319 expected: "devel", 320 }, { 321 currentVers: "1.22-bta1", 322 explicitVers: "1.22.0", 323 expected: "released", 324 }} 325 326 func (s *SimpleStreamsToolsSuite) TestPreferredStream(c *gc.C) { 327 for i, test := range preferredStreamTests { 328 c.Logf("\ntest %d", i) 329 s.PatchValue(&jujuversion.Current, version.MustParse(test.currentVers)) 330 var vers *version.Number 331 if test.explicitVers != "" { 332 v := version.MustParse(test.explicitVers) 333 vers = &v 334 } 335 obtained := envtools.PreferredStream(vers, test.forceDevel, test.streamInConfig) 336 c.Check(obtained, gc.Equals, test.expected) 337 } 338 } 339 340 // fakeToolsForSeries fakes a Tools object with just enough information for 341 // testing the handling its OS series. 342 func fakeToolsForSeries(series string) *coretools.Tools { 343 return &coretools.Tools{Version: version.Binary{Series: series}} 344 } 345 346 // fakeToolsList fakes a envtools.List containing Tools objects for the given 347 // respective series, in the same number and order. 348 func fakeToolsList(series ...string) coretools.List { 349 list := coretools.List{} 350 for _, name := range series { 351 list = append(list, fakeToolsForSeries(name)) 352 } 353 return list 354 } 355 356 type ToolsListSuite struct{} 357 358 func (s *ToolsListSuite) TestCheckToolsSeriesRequiresTools(c *gc.C) { 359 err := envtools.CheckToolsSeries(fakeToolsList(), "precise") 360 c.Assert(err, gc.NotNil) 361 c.Check(err, gc.ErrorMatches, "expected single series, got \\[\\]") 362 } 363 364 func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsOneSetOfTools(c *gc.C) { 365 names := []string{"precise", "raring"} 366 for _, series := range names { 367 list := fakeToolsList(series) 368 err := envtools.CheckToolsSeries(list, series) 369 c.Check(err, jc.ErrorIsNil) 370 } 371 } 372 373 func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsMultipleForSameSeries(c *gc.C) { 374 series := "quantal" 375 list := fakeToolsList(series, series, series) 376 err := envtools.CheckToolsSeries(list, series) 377 c.Check(err, jc.ErrorIsNil) 378 } 379 380 func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForOtherSeries(c *gc.C) { 381 list := fakeToolsList("hoary") 382 err := envtools.CheckToolsSeries(list, "warty") 383 c.Assert(err, gc.NotNil) 384 c.Check(err, gc.ErrorMatches, "tools mismatch: expected series warty, got hoary") 385 } 386 387 func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForMixedSeries(c *gc.C) { 388 list := fakeToolsList("precise", "raring") 389 err := envtools.CheckToolsSeries(list, "precise") 390 c.Assert(err, gc.NotNil) 391 c.Check(err, gc.ErrorMatches, "expected single series, got .*") 392 }