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