github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/environs/testing/tools.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "bytes" 8 "os" 9 "path" 10 "path/filepath" 11 12 "github.com/juju/utils" 13 "github.com/juju/utils/set" 14 gc "launchpad.net/gocheck" 15 16 agenttools "github.com/juju/juju/agent/tools" 17 "github.com/juju/juju/environs" 18 "github.com/juju/juju/environs/bootstrap" 19 "github.com/juju/juju/environs/simplestreams" 20 "github.com/juju/juju/environs/storage" 21 envtools "github.com/juju/juju/environs/tools" 22 "github.com/juju/juju/state" 23 coretesting "github.com/juju/juju/testing" 24 coretools "github.com/juju/juju/tools" 25 "github.com/juju/juju/version" 26 "github.com/juju/juju/worker/upgrader" 27 ) 28 29 // ToolsFixture is used as a fixture to stub out the default tools URL so we 30 // don't hit the real internet during tests. 31 type ToolsFixture struct { 32 origDefaultURL string 33 DefaultBaseURL string 34 35 // UploadArches holds the architectures of tools to 36 // upload in UploadFakeTools. If empty, it will default 37 // to just version.Current.Arch. 38 UploadArches []string 39 } 40 41 func (s *ToolsFixture) SetUpTest(c *gc.C) { 42 s.origDefaultURL = envtools.DefaultBaseURL 43 envtools.DefaultBaseURL = s.DefaultBaseURL 44 } 45 46 func (s *ToolsFixture) TearDownTest(c *gc.C) { 47 envtools.DefaultBaseURL = s.origDefaultURL 48 } 49 50 // UploadFakeTools uploads fake tools of the architectures in 51 // s.UploadArches for each LTS release to the specified storage. 52 func (s *ToolsFixture) UploadFakeTools(c *gc.C, stor storage.Storage) { 53 arches := s.UploadArches 54 if len(arches) == 0 { 55 arches = []string{version.Current.Arch} 56 } 57 var versions []version.Binary 58 for _, arch := range arches { 59 v := version.Current 60 v.Arch = arch 61 for _, series := range bootstrap.ToolsLtsSeries { 62 v.Series = series 63 versions = append(versions, v) 64 } 65 } 66 _, err := UploadFakeToolsVersions(stor, versions...) 67 c.Assert(err, gc.IsNil) 68 } 69 70 // RemoveFakeToolsMetadata deletes the fake simplestreams tools metadata from the supplied storage. 71 func RemoveFakeToolsMetadata(c *gc.C, stor storage.Storage) { 72 files := []string{simplestreams.UnsignedIndex, envtools.ProductMetadataPath} 73 for _, file := range files { 74 toolspath := path.Join("tools", file) 75 err := stor.Remove(toolspath) 76 c.Check(err, gc.IsNil) 77 } 78 } 79 80 // CheckTools ensures the obtained and expected tools are equal, allowing for the fact that 81 // the obtained tools may not have size and checksum set. 82 func CheckTools(c *gc.C, obtained, expected *coretools.Tools) { 83 c.Assert(obtained.Version, gc.Equals, expected.Version) 84 // TODO(dimitern) 2013-10-02 bug #1234217 85 // Are these used at at all? If not we should drop them. 86 if obtained.URL != "" { 87 c.Assert(obtained.URL, gc.Equals, expected.URL) 88 } 89 if obtained.Size > 0 { 90 c.Assert(obtained.Size, gc.Equals, expected.Size) 91 c.Assert(obtained.SHA256, gc.Equals, expected.SHA256) 92 } 93 } 94 95 // CheckUpgraderReadyError ensures the obtained and expected errors are equal. 96 func CheckUpgraderReadyError(c *gc.C, obtained error, expected *upgrader.UpgradeReadyError) { 97 c.Assert(obtained, gc.FitsTypeOf, &upgrader.UpgradeReadyError{}) 98 err := obtained.(*upgrader.UpgradeReadyError) 99 c.Assert(err.AgentName, gc.Equals, expected.AgentName) 100 c.Assert(err.DataDir, gc.Equals, expected.DataDir) 101 c.Assert(err.OldTools, gc.Equals, expected.OldTools) 102 c.Assert(err.NewTools, gc.Equals, expected.NewTools) 103 } 104 105 // PrimeTools sets up the current version of the tools to vers and 106 // makes sure that they're available in the dataDir. 107 func PrimeTools(c *gc.C, stor storage.Storage, dataDir string, vers version.Binary) *coretools.Tools { 108 err := os.RemoveAll(filepath.Join(dataDir, "tools")) 109 c.Assert(err, gc.IsNil) 110 agentTools, err := uploadFakeToolsVersion(stor, vers) 111 c.Assert(err, gc.IsNil) 112 resp, err := utils.GetValidatingHTTPClient().Get(agentTools.URL) 113 c.Assert(err, gc.IsNil) 114 defer resp.Body.Close() 115 err = agenttools.UnpackTools(dataDir, agentTools, resp.Body) 116 c.Assert(err, gc.IsNil) 117 return agentTools 118 } 119 120 func uploadFakeToolsVersion(stor storage.Storage, vers version.Binary) (*coretools.Tools, error) { 121 logger.Infof("uploading FAKE tools %s", vers) 122 tgz, checksum := coretesting.TarGz( 123 coretesting.NewTarFile("jujud", 0777, "jujud contents "+vers.String())) 124 size := int64(len(tgz)) 125 name := envtools.StorageName(vers) 126 if err := stor.Put(name, bytes.NewReader(tgz), size); err != nil { 127 return nil, err 128 } 129 url, err := stor.URL(name) 130 if err != nil { 131 return nil, err 132 } 133 return &coretools.Tools{URL: url, Version: vers, Size: size, SHA256: checksum}, nil 134 } 135 136 // UploadFakeToolsVersions puts fake tools in the supplied storage for the supplied versions. 137 func UploadFakeToolsVersions(stor storage.Storage, versions ...version.Binary) ([]*coretools.Tools, error) { 138 // Leave existing tools alone. 139 existingTools := make(map[version.Binary]*coretools.Tools) 140 existing, _ := envtools.ReadList(stor, 1, -1) 141 for _, tools := range existing { 142 existingTools[tools.Version] = tools 143 } 144 var agentTools coretools.List = make(coretools.List, len(versions)) 145 for i, version := range versions { 146 if tools, ok := existingTools[version]; ok { 147 agentTools[i] = tools 148 } else { 149 t, err := uploadFakeToolsVersion(stor, version) 150 if err != nil { 151 return nil, err 152 } 153 agentTools[i] = t 154 } 155 } 156 if err := envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors); err != nil { 157 return nil, err 158 } 159 return agentTools, nil 160 } 161 162 // AssertUploadFakeToolsVersions puts fake tools in the supplied storage for the supplied versions. 163 func AssertUploadFakeToolsVersions(c *gc.C, stor storage.Storage, versions ...version.Binary) []*coretools.Tools { 164 agentTools, err := UploadFakeToolsVersions(stor, versions...) 165 c.Assert(err, gc.IsNil) 166 err = envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors) 167 c.Assert(err, gc.IsNil) 168 return agentTools 169 } 170 171 // MustUploadFakeToolsVersions acts as UploadFakeToolsVersions, but panics on failure. 172 func MustUploadFakeToolsVersions(stor storage.Storage, versions ...version.Binary) []*coretools.Tools { 173 var agentTools coretools.List = make(coretools.List, len(versions)) 174 for i, version := range versions { 175 t, err := uploadFakeToolsVersion(stor, version) 176 if err != nil { 177 panic(err) 178 } 179 agentTools[i] = t 180 } 181 err := envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors) 182 if err != nil { 183 panic(err) 184 } 185 return agentTools 186 } 187 188 func uploadFakeTools(stor storage.Storage) error { 189 toolsSeries := set.NewStrings(bootstrap.ToolsLtsSeries...) 190 toolsSeries.Add(version.Current.Series) 191 var versions []version.Binary 192 for _, series := range toolsSeries.Values() { 193 vers := version.Current 194 vers.Series = series 195 versions = append(versions, vers) 196 } 197 if _, err := UploadFakeToolsVersions(stor, versions...); err != nil { 198 return err 199 } 200 return nil 201 } 202 203 // UploadFakeTools puts fake tools into the supplied storage with a binary 204 // version matching version.Current; if version.Current's series is different 205 // to coretesting.FakeDefaultSeries, matching fake tools will be uploaded for that 206 // series. This is useful for tests that are kinda casual about specifying 207 // their environment. 208 func UploadFakeTools(c *gc.C, stor storage.Storage) { 209 c.Assert(uploadFakeTools(stor), gc.IsNil) 210 } 211 212 // MustUploadFakeTools acts as UploadFakeTools, but panics on failure. 213 func MustUploadFakeTools(stor storage.Storage) { 214 if err := uploadFakeTools(stor); err != nil { 215 panic(err) 216 } 217 } 218 219 // RemoveFakeTools deletes the fake tools from the supplied storage. 220 func RemoveFakeTools(c *gc.C, stor storage.Storage) { 221 c.Logf("removing fake tools") 222 toolsVersion := version.Current 223 name := envtools.StorageName(toolsVersion) 224 err := stor.Remove(name) 225 c.Check(err, gc.IsNil) 226 defaultSeries := coretesting.FakeDefaultSeries 227 if version.Current.Series != defaultSeries { 228 toolsVersion.Series = defaultSeries 229 name := envtools.StorageName(toolsVersion) 230 err := stor.Remove(name) 231 c.Check(err, gc.IsNil) 232 } 233 RemoveFakeToolsMetadata(c, stor) 234 } 235 236 // RemoveTools deletes all tools from the supplied storage. 237 func RemoveTools(c *gc.C, stor storage.Storage) { 238 names, err := storage.List(stor, "tools/releases/juju-") 239 c.Assert(err, gc.IsNil) 240 c.Logf("removing files: %v", names) 241 for _, name := range names { 242 err = stor.Remove(name) 243 c.Check(err, gc.IsNil) 244 } 245 RemoveFakeToolsMetadata(c, stor) 246 } 247 248 // RemoveAllTools deletes all tools from the supplied environment. 249 func RemoveAllTools(c *gc.C, env environs.Environ) { 250 c.Logf("clearing private storage") 251 RemoveTools(c, env.Storage()) 252 } 253 254 var ( 255 V100 = version.MustParse("1.0.0") 256 V100p64 = version.MustParseBinary("1.0.0-precise-amd64") 257 V100p32 = version.MustParseBinary("1.0.0-precise-i386") 258 V100p = []version.Binary{V100p64, V100p32} 259 260 V100q64 = version.MustParseBinary("1.0.0-quantal-amd64") 261 V100q32 = version.MustParseBinary("1.0.0-quantal-i386") 262 V100q = []version.Binary{V100q64, V100q32} 263 V100all = append(V100p, V100q...) 264 265 V1001 = version.MustParse("1.0.0.1") 266 V1001p64 = version.MustParseBinary("1.0.0.1-precise-amd64") 267 V100Xall = append(V100all, V1001p64) 268 269 V110 = version.MustParse("1.1.0") 270 V110p64 = version.MustParseBinary("1.1.0-precise-amd64") 271 V110p32 = version.MustParseBinary("1.1.0-precise-i386") 272 V110p = []version.Binary{V110p64, V110p32} 273 274 V110q64 = version.MustParseBinary("1.1.0-quantal-amd64") 275 V110q32 = version.MustParseBinary("1.1.0-quantal-i386") 276 V110q = []version.Binary{V110q64, V110q32} 277 V110all = append(V110p, V110q...) 278 279 V1101p64 = version.MustParseBinary("1.1.0.1-precise-amd64") 280 V110Xall = append(V110all, V1101p64) 281 282 V120 = version.MustParse("1.2.0") 283 V120p64 = version.MustParseBinary("1.2.0-precise-amd64") 284 V120p32 = version.MustParseBinary("1.2.0-precise-i386") 285 V120p = []version.Binary{V120p64, V120p32} 286 287 V120q64 = version.MustParseBinary("1.2.0-quantal-amd64") 288 V120q32 = version.MustParseBinary("1.2.0-quantal-i386") 289 V120q = []version.Binary{V120q64, V120q32} 290 V120all = append(V120p, V120q...) 291 V1all = append(V100Xall, append(V110all, V120all...)...) 292 293 V220 = version.MustParse("2.2.0") 294 V220p32 = version.MustParseBinary("2.2.0-precise-i386") 295 V220p64 = version.MustParseBinary("2.2.0-precise-amd64") 296 V220q32 = version.MustParseBinary("2.2.0-quantal-i386") 297 V220q64 = version.MustParseBinary("2.2.0-quantal-amd64") 298 V220all = []version.Binary{V220p64, V220p32, V220q64, V220q32} 299 VAll = append(V1all, V220all...) 300 301 V31d0qppc64 = version.MustParseBinary("3.1-dev0-quantal-ppc64") 302 V31d01qppc64 = version.MustParseBinary("3.1-dev0.1-quantal-ppc64") 303 ) 304 305 type BootstrapToolsTest struct { 306 Info string 307 Available []version.Binary 308 CliVersion version.Binary 309 DefaultSeries string 310 AgentVersion version.Number 311 Development bool 312 Arch string 313 Expect []version.Binary 314 Err string 315 } 316 317 var noToolsMessage = "Juju cannot bootstrap because no tools are available for your environment.*" 318 319 var BootstrapToolsTests = []BootstrapToolsTest{ 320 { 321 Info: "no tools at all", 322 CliVersion: V100p64, 323 DefaultSeries: "precise", 324 Err: noToolsMessage, 325 }, { 326 Info: "released cli: use newest compatible release version", 327 Available: VAll, 328 CliVersion: V100p64, 329 DefaultSeries: "precise", 330 Expect: V100p, 331 }, { 332 Info: "released cli: cli Arch ignored", 333 Available: VAll, 334 CliVersion: V100p32, 335 DefaultSeries: "precise", 336 Expect: V100p, 337 }, { 338 Info: "released cli: cli series ignored", 339 Available: VAll, 340 CliVersion: V100q64, 341 DefaultSeries: "precise", 342 Expect: V100p, 343 }, { 344 Info: "released cli: series taken from default-series", 345 Available: V120all, 346 CliVersion: V120p64, 347 DefaultSeries: "quantal", 348 Expect: V120q, 349 }, { 350 Info: "released cli: ignore close dev match", 351 Available: V100Xall, 352 CliVersion: V100p64, 353 DefaultSeries: "precise", 354 Expect: V100p, 355 }, { 356 Info: "released cli: filter by arch constraints", 357 Available: V120all, 358 CliVersion: V120p64, 359 DefaultSeries: "precise", 360 Arch: "i386", 361 Expect: []version.Binary{V120p32}, 362 }, { 363 Info: "released cli: specific released version", 364 Available: VAll, 365 CliVersion: V100p64, 366 AgentVersion: V100, 367 DefaultSeries: "precise", 368 Expect: V100p, 369 }, { 370 Info: "released cli: specific dev version", 371 Available: VAll, 372 CliVersion: V110p64, 373 AgentVersion: V110, 374 DefaultSeries: "precise", 375 Expect: V110p, 376 }, { 377 Info: "released cli: major upgrades bad", 378 Available: V220all, 379 CliVersion: V100p64, 380 DefaultSeries: "precise", 381 Err: noToolsMessage, 382 }, { 383 Info: "released cli: minor upgrades bad", 384 Available: V120all, 385 CliVersion: V100p64, 386 DefaultSeries: "precise", 387 Err: noToolsMessage, 388 }, { 389 Info: "released cli: major downgrades bad", 390 Available: V100Xall, 391 CliVersion: V220p64, 392 DefaultSeries: "precise", 393 Err: noToolsMessage, 394 }, { 395 Info: "released cli: minor downgrades bad", 396 Available: V100Xall, 397 CliVersion: V120p64, 398 DefaultSeries: "quantal", 399 Err: noToolsMessage, 400 }, { 401 Info: "released cli: no matching series", 402 Available: VAll, 403 CliVersion: V100p64, 404 DefaultSeries: "raring", 405 Err: noToolsMessage, 406 }, { 407 Info: "released cli: no matching arches", 408 Available: VAll, 409 CliVersion: V100p64, 410 DefaultSeries: "precise", 411 Arch: "armhf", 412 Err: noToolsMessage, 413 }, { 414 Info: "released cli: specific bad major 1", 415 Available: VAll, 416 CliVersion: V220p64, 417 AgentVersion: V120, 418 DefaultSeries: "precise", 419 Err: noToolsMessage, 420 }, { 421 Info: "released cli: specific bad major 2", 422 Available: VAll, 423 CliVersion: V120p64, 424 AgentVersion: V220, 425 DefaultSeries: "precise", 426 Err: noToolsMessage, 427 }, { 428 Info: "released cli: ignore dev tools 1", 429 Available: V110all, 430 CliVersion: V100p64, 431 DefaultSeries: "precise", 432 Err: noToolsMessage, 433 }, { 434 Info: "released cli: ignore dev tools 2", 435 Available: V110all, 436 CliVersion: V120p64, 437 DefaultSeries: "precise", 438 Err: noToolsMessage, 439 }, { 440 Info: "released cli: ignore dev tools 3", 441 Available: []version.Binary{V1001p64}, 442 CliVersion: V100p64, 443 DefaultSeries: "precise", 444 Err: noToolsMessage, 445 }, { 446 Info: "released cli with dev setting respects agent-version", 447 Available: VAll, 448 CliVersion: V100q32, 449 AgentVersion: V1001, 450 DefaultSeries: "precise", 451 Development: true, 452 Expect: []version.Binary{V1001p64}, 453 }, { 454 Info: "dev cli respects agent-version", 455 Available: VAll, 456 CliVersion: V100q32, 457 AgentVersion: V1001, 458 DefaultSeries: "precise", 459 Expect: []version.Binary{V1001p64}, 460 }, { 461 Info: "released cli with dev setting respects agent-version", 462 Available: V1all, 463 CliVersion: V100q32, 464 AgentVersion: V1001, 465 DefaultSeries: "precise", 466 Development: true, 467 Expect: []version.Binary{V1001p64}, 468 }, { 469 Info: "dev cli respects agent-version", 470 Available: V1all, 471 CliVersion: V100q32, 472 AgentVersion: V1001, 473 DefaultSeries: "precise", 474 Expect: []version.Binary{V1001p64}, 475 }} 476 477 func SetSSLHostnameVerification(c *gc.C, st *state.State, SSLHostnameVerification bool) { 478 err := st.UpdateEnvironConfig(map[string]interface{}{"ssl-hostname-verification": SSLHostnameVerification}, nil, nil) 479 c.Assert(err, gc.IsNil) 480 }