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