github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/environs/sync/sync_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package sync_test 5 6 import ( 7 "bytes" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "os" 12 "os/exec" 13 "path" 14 "path/filepath" 15 "runtime" 16 "sort" 17 "testing" 18 19 "github.com/juju/errors" 20 gitjujutesting "github.com/juju/testing" 21 jc "github.com/juju/testing/checkers" 22 "github.com/juju/utils" 23 "github.com/juju/utils/series" 24 gc "gopkg.in/check.v1" 25 26 "github.com/juju/juju/environs" 27 "github.com/juju/juju/environs/filestorage" 28 "github.com/juju/juju/environs/simplestreams" 29 "github.com/juju/juju/environs/storage" 30 "github.com/juju/juju/environs/sync" 31 envtesting "github.com/juju/juju/environs/testing" 32 envtools "github.com/juju/juju/environs/tools" 33 toolstesting "github.com/juju/juju/environs/tools/testing" 34 coretesting "github.com/juju/juju/testing" 35 coretools "github.com/juju/juju/tools" 36 "github.com/juju/juju/version" 37 ) 38 39 func TestPackage(t *testing.T) { 40 gc.TestingT(t) 41 } 42 43 type syncSuite struct { 44 coretesting.FakeJujuHomeSuite 45 envtesting.ToolsFixture 46 storage storage.Storage 47 localStorage string 48 } 49 50 var _ = gc.Suite(&syncSuite{}) 51 var _ = gc.Suite(&uploadSuite{}) 52 var _ = gc.Suite(&badBuildSuite{}) 53 54 func (s *syncSuite) setUpTest(c *gc.C) { 55 if runtime.GOOS == "windows" { 56 c.Skip("issue 1403084: Currently does not work because of jujud problems") 57 } 58 s.FakeJujuHomeSuite.SetUpTest(c) 59 s.ToolsFixture.SetUpTest(c) 60 61 // It's important that this be v1.8.x to match the test data. 62 s.PatchValue(&version.Current.Number, version.MustParse("1.8.3")) 63 64 // Create a source storage. 65 baseDir := c.MkDir() 66 stor, err := filestorage.NewFileStorageWriter(baseDir) 67 c.Assert(err, jc.ErrorIsNil) 68 s.storage = stor 69 70 // Create a local tools directory. 71 s.localStorage = c.MkDir() 72 73 // Populate both local and default tools locations with the public tools. 74 versionStrings := make([]string, len(vAll)) 75 for i, vers := range vAll { 76 versionStrings[i] = vers.String() 77 } 78 toolstesting.MakeTools(c, baseDir, "released", versionStrings) 79 toolstesting.MakeTools(c, s.localStorage, "released", versionStrings) 80 81 // Switch the default tools location. 82 baseURL, err := s.storage.URL(storage.BaseToolsPath) 83 c.Assert(err, jc.ErrorIsNil) 84 s.PatchValue(&envtools.DefaultBaseURL, baseURL) 85 } 86 87 func (s *syncSuite) tearDownTest(c *gc.C) { 88 s.ToolsFixture.TearDownTest(c) 89 s.FakeJujuHomeSuite.TearDownTest(c) 90 } 91 92 var tests = []struct { 93 description string 94 ctx *sync.SyncContext 95 source bool 96 tools []version.Binary 97 version version.Number 98 major int 99 minor int 100 }{ 101 { 102 description: "copy newest from the filesystem", 103 ctx: &sync.SyncContext{}, 104 source: true, 105 tools: v180all, 106 }, 107 { 108 description: "copy newest from the dummy environment", 109 ctx: &sync.SyncContext{}, 110 tools: v180all, 111 }, 112 { 113 description: "copy matching dev from the dummy environment", 114 ctx: &sync.SyncContext{}, 115 version: version.MustParse("1.9.3"), 116 tools: v190all, 117 }, 118 { 119 description: "copy matching major, minor from the dummy environment", 120 ctx: &sync.SyncContext{}, 121 major: 3, 122 minor: 2, 123 tools: []version.Binary{v320p64}, 124 }, 125 { 126 description: "copy matching major, minor dev from the dummy environment", 127 ctx: &sync.SyncContext{}, 128 major: 3, 129 minor: 1, 130 tools: []version.Binary{v310p64}, 131 }, 132 { 133 description: "copy all from the dummy environment", 134 ctx: &sync.SyncContext{ 135 AllVersions: true, 136 }, 137 tools: v1all, 138 }, 139 } 140 141 func (s *syncSuite) TestSyncing(c *gc.C) { 142 for i, test := range tests { 143 // Perform all tests in a "clean" environment. 144 func() { 145 s.setUpTest(c) 146 defer s.tearDownTest(c) 147 148 c.Logf("test %d: %s", i, test.description) 149 150 if test.source { 151 test.ctx.Source = s.localStorage 152 } 153 if test.version != version.Zero { 154 version.Current.Number = test.version 155 } 156 if test.major > 0 { 157 test.ctx.MajorVersion = test.major 158 test.ctx.MinorVersion = test.minor 159 } 160 uploader := fakeToolsUploader{ 161 uploaded: make(map[version.Binary]bool), 162 } 163 test.ctx.TargetToolsFinder = mockToolsFinder{} 164 test.ctx.TargetToolsUploader = &uploader 165 166 err := sync.SyncTools(test.ctx) 167 c.Assert(err, jc.ErrorIsNil) 168 169 var uploaded []version.Binary 170 for v := range uploader.uploaded { 171 uploaded = append(uploaded, v) 172 } 173 c.Assert(uploaded, jc.SameContents, test.tools) 174 }() 175 } 176 } 177 178 type fakeToolsUploader struct { 179 uploaded map[version.Binary]bool 180 } 181 182 func (u *fakeToolsUploader) UploadTools(toolsDir, stream string, tools *coretools.Tools, data []byte) error { 183 u.uploaded[tools.Version] = true 184 return nil 185 } 186 187 var ( 188 v100p64 = version.MustParseBinary("1.0.0-precise-amd64") 189 v100q64 = version.MustParseBinary("1.0.0-quantal-amd64") 190 v100q32 = version.MustParseBinary("1.0.0-quantal-i386") 191 v100all = []version.Binary{v100p64, v100q64, v100q32} 192 v180q64 = version.MustParseBinary("1.8.0-quantal-amd64") 193 v180p32 = version.MustParseBinary("1.8.0-precise-i386") 194 v180all = []version.Binary{v180q64, v180p32} 195 v190q64 = version.MustParseBinary("1.9.0-quantal-amd64") 196 v190p32 = version.MustParseBinary("1.9.0-precise-i386") 197 v190all = []version.Binary{v190q64, v190p32} 198 v1all = append(append(v100all, v180all...), v190all...) 199 v200p64 = version.MustParseBinary("2.0.0-precise-amd64") 200 v310p64 = version.MustParseBinary("3.1.0-precise-amd64") 201 v320p64 = version.MustParseBinary("3.2.0-precise-amd64") 202 vAll = append(append(v1all, v200p64), v310p64, v320p64) 203 ) 204 205 type uploadSuite struct { 206 env environs.Environ 207 coretesting.FakeJujuHomeSuite 208 envtesting.ToolsFixture 209 targetStorage storage.Storage 210 } 211 212 func (s *uploadSuite) SetUpTest(c *gc.C) { 213 if runtime.GOOS == "windows" { 214 c.Skip("issue 1403084: Currently does not work because of jujud problems") 215 } 216 s.FakeJujuHomeSuite.SetUpTest(c) 217 s.ToolsFixture.SetUpTest(c) 218 219 // Create a target storage. 220 stor, err := filestorage.NewFileStorageWriter(c.MkDir()) 221 c.Assert(err, jc.ErrorIsNil) 222 s.targetStorage = stor 223 224 // Mock out building of tools. Sync should not care about the contents 225 // of tools archives, other than that they hash correctly. 226 s.PatchValue(&envtools.BundleTools, toolstesting.GetMockBundleTools(c)) 227 } 228 229 func (s *uploadSuite) TearDownTest(c *gc.C) { 230 s.ToolsFixture.TearDownTest(c) 231 s.FakeJujuHomeSuite.TearDownTest(c) 232 } 233 234 func (s *uploadSuite) TestUpload(c *gc.C) { 235 t, err := sync.Upload(s.targetStorage, "released", nil) 236 c.Assert(err, jc.ErrorIsNil) 237 c.Assert(t.Version, gc.Equals, version.Current) 238 c.Assert(t.URL, gc.Not(gc.Equals), "") 239 s.assertUploadedTools(c, t, []string{series.HostSeries()}, "released") 240 } 241 242 func (s *uploadSuite) TestUploadFakeSeries(c *gc.C) { 243 seriesToUpload := "precise" 244 if seriesToUpload == series.HostSeries() { 245 seriesToUpload = "raring" 246 } 247 t, err := sync.Upload(s.targetStorage, "released", nil, "quantal", seriesToUpload) 248 c.Assert(err, jc.ErrorIsNil) 249 s.assertUploadedTools(c, t, []string{seriesToUpload, "quantal", series.HostSeries()}, "released") 250 } 251 252 func (s *uploadSuite) TestUploadAndForceVersion(c *gc.C) { 253 // This test actually tests three things: 254 // the writing of the FORCE-VERSION file; 255 // the reading of the FORCE-VERSION file by the version package; 256 // and the reading of the version from jujud. 257 vers := version.Current 258 vers.Patch++ 259 t, err := sync.Upload(s.targetStorage, "released", &vers.Number) 260 c.Assert(err, jc.ErrorIsNil) 261 c.Assert(t.Version, gc.Equals, vers) 262 } 263 264 func (s *uploadSuite) TestSyncTools(c *gc.C) { 265 builtTools, err := sync.BuildToolsTarball(nil, "released") 266 c.Assert(err, jc.ErrorIsNil) 267 t, err := sync.SyncBuiltTools(s.targetStorage, "released", builtTools) 268 c.Assert(err, jc.ErrorIsNil) 269 c.Assert(t.Version, gc.Equals, version.Current) 270 c.Assert(t.URL, gc.Not(gc.Equals), "") 271 } 272 273 func (s *uploadSuite) TestSyncToolsFakeSeries(c *gc.C) { 274 seriesToUpload := "precise" 275 if seriesToUpload == series.HostSeries() { 276 seriesToUpload = "raring" 277 } 278 builtTools, err := sync.BuildToolsTarball(nil, "testing") 279 c.Assert(err, jc.ErrorIsNil) 280 281 t, err := sync.SyncBuiltTools(s.targetStorage, "testing", builtTools, "quantal", seriesToUpload) 282 c.Assert(err, jc.ErrorIsNil) 283 s.assertUploadedTools(c, t, []string{seriesToUpload, "quantal", series.HostSeries()}, "testing") 284 } 285 286 func (s *uploadSuite) TestSyncAndForceVersion(c *gc.C) { 287 // This test actually tests three things: 288 // the writing of the FORCE-VERSION file; 289 // the reading of the FORCE-VERSION file by the version package; 290 // and the reading of the version from jujud. 291 vers := version.Current 292 vers.Patch++ 293 builtTools, err := sync.BuildToolsTarball(&vers.Number, "released") 294 c.Assert(err, jc.ErrorIsNil) 295 t, err := sync.SyncBuiltTools(s.targetStorage, "released", builtTools) 296 c.Assert(err, jc.ErrorIsNil) 297 c.Assert(t.Version, gc.Equals, vers) 298 } 299 300 func (s *uploadSuite) assertUploadedTools(c *gc.C, t *coretools.Tools, expectSeries []string, stream string) { 301 c.Assert(t.Version, gc.Equals, version.Current) 302 expectRaw := downloadToolsRaw(c, t) 303 304 list, err := envtools.ReadList(s.targetStorage, stream, version.Current.Major, version.Current.Minor) 305 c.Assert(err, jc.ErrorIsNil) 306 c.Assert(list.AllSeries(), jc.SameContents, expectSeries) 307 sort.Strings(expectSeries) 308 c.Assert(list.AllSeries(), gc.DeepEquals, expectSeries) 309 for _, t := range list { 310 c.Logf("checking %s", t.URL) 311 c.Assert(t.Version.Number, gc.Equals, version.Current.Number) 312 actualRaw := downloadToolsRaw(c, t) 313 c.Assert(string(actualRaw), gc.Equals, string(expectRaw)) 314 } 315 metadata, err := envtools.ReadMetadata(s.targetStorage, stream) 316 c.Assert(err, jc.ErrorIsNil) 317 c.Assert(metadata, gc.HasLen, 0) 318 } 319 320 // downloadToolsRaw downloads the supplied tools and returns the raw bytes. 321 func downloadToolsRaw(c *gc.C, t *coretools.Tools) []byte { 322 resp, err := utils.GetValidatingHTTPClient().Get(t.URL) 323 c.Assert(err, jc.ErrorIsNil) 324 defer resp.Body.Close() 325 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 326 var buf bytes.Buffer 327 _, err = io.Copy(&buf, resp.Body) 328 c.Assert(err, jc.ErrorIsNil) 329 return buf.Bytes() 330 } 331 332 func bundleTools(c *gc.C) (version.Binary, string, error) { 333 f, err := ioutil.TempFile("", "juju-tgz") 334 c.Assert(err, jc.ErrorIsNil) 335 defer f.Close() 336 defer os.Remove(f.Name()) 337 338 return envtools.BundleTools(f, &version.Current.Number) 339 } 340 341 type badBuildSuite struct { 342 env environs.Environ 343 gitjujutesting.LoggingSuite 344 gitjujutesting.CleanupSuite 345 envtesting.ToolsFixture 346 } 347 348 var badGo = ` 349 #!/bin/bash --norc 350 exit 1 351 `[1:] 352 353 func (s *badBuildSuite) SetUpSuite(c *gc.C) { 354 if runtime.GOOS == "windows" { 355 c.Skip("issue 1403084: Currently does not work because of jujud problems") 356 } 357 s.CleanupSuite.SetUpSuite(c) 358 s.LoggingSuite.SetUpSuite(c) 359 } 360 361 func (s *badBuildSuite) TearDownSuite(c *gc.C) { 362 s.LoggingSuite.TearDownSuite(c) 363 s.CleanupSuite.TearDownSuite(c) 364 } 365 366 func (s *badBuildSuite) SetUpTest(c *gc.C) { 367 s.CleanupSuite.SetUpTest(c) 368 s.LoggingSuite.SetUpTest(c) 369 s.ToolsFixture.SetUpTest(c) 370 371 // Mock go cmd 372 testPath := c.MkDir() 373 s.PatchEnvPathPrepend(testPath) 374 path := filepath.Join(testPath, "go") 375 err := ioutil.WriteFile(path, []byte(badGo), 0755) 376 c.Assert(err, jc.ErrorIsNil) 377 378 // Check mocked go cmd errors 379 out, err := exec.Command("go").CombinedOutput() 380 c.Assert(err, gc.ErrorMatches, "exit status 1") 381 c.Assert(string(out), gc.Equals, "") 382 } 383 384 func (s *badBuildSuite) TearDownTest(c *gc.C) { 385 s.ToolsFixture.TearDownTest(c) 386 s.LoggingSuite.TearDownTest(c) 387 s.CleanupSuite.TearDownTest(c) 388 } 389 390 func (s *badBuildSuite) TestBundleToolsBadBuild(c *gc.C) { 391 // Test that original bundleTools Func fails as expected 392 vers, sha256Hash, err := bundleTools(c) 393 c.Assert(vers, gc.DeepEquals, version.Binary{}) 394 c.Assert(sha256Hash, gc.Equals, "") 395 c.Assert(err, gc.ErrorMatches, `build command "go" failed: exit status 1; `) 396 397 s.PatchValue(&envtools.BundleTools, toolstesting.GetMockBundleTools(c)) 398 399 // Test that BundleTools func passes after it is 400 // mocked out 401 vers, sha256Hash, err = bundleTools(c) 402 c.Assert(err, jc.ErrorIsNil) 403 c.Assert(vers.Number, gc.Equals, version.Current.Number) 404 c.Assert(sha256Hash, gc.Equals, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") 405 } 406 407 func (s *badBuildSuite) TestUploadToolsBadBuild(c *gc.C) { 408 stor, err := filestorage.NewFileStorageWriter(c.MkDir()) 409 c.Assert(err, jc.ErrorIsNil) 410 411 // Test that original Upload Func fails as expected 412 t, err := sync.Upload(stor, "released", nil) 413 c.Assert(t, gc.IsNil) 414 c.Assert(err, gc.ErrorMatches, `build command "go" failed: exit status 1; `) 415 416 // Test that Upload func passes after BundleTools func is mocked out 417 s.PatchValue(&envtools.BundleTools, toolstesting.GetMockBundleTools(c)) 418 t, err = sync.Upload(stor, "released", nil) 419 c.Assert(err, jc.ErrorIsNil) 420 c.Assert(t.Version, gc.Equals, version.Current) 421 c.Assert(t.URL, gc.Not(gc.Equals), "") 422 } 423 424 func (s *badBuildSuite) TestBuildToolsBadBuild(c *gc.C) { 425 // Test that original BuildToolsTarball fails 426 builtTools, err := sync.BuildToolsTarball(nil, "released") 427 c.Assert(err, gc.ErrorMatches, `build command "go" failed: exit status 1; `) 428 c.Assert(builtTools, gc.IsNil) 429 430 // Test that BuildToolsTarball func passes after BundleTools func is 431 // mocked out 432 s.PatchValue(&envtools.BundleTools, toolstesting.GetMockBundleTools(c)) 433 builtTools, err = sync.BuildToolsTarball(nil, "released") 434 c.Assert(builtTools.Version, gc.Equals, version.Current) 435 c.Assert(err, jc.ErrorIsNil) 436 } 437 438 func (s *uploadSuite) TestMockBundleTools(c *gc.C) { 439 var ( 440 writer io.Writer 441 forceVersion *version.Number 442 n int 443 p bytes.Buffer 444 ) 445 p.WriteString("Hello World") 446 447 s.PatchValue(&envtools.BundleTools, func(writerArg io.Writer, forceVersionArg *version.Number) (vers version.Binary, sha256Hash string, err error) { 448 writer = writerArg 449 n, err = writer.Write(p.Bytes()) 450 c.Assert(err, jc.ErrorIsNil) 451 forceVersion = forceVersionArg 452 return 453 }) 454 455 _, err := sync.BuildToolsTarball(&version.Current.Number, "released") 456 c.Assert(err, jc.ErrorIsNil) 457 c.Assert(*forceVersion, gc.Equals, version.Current.Number) 458 c.Assert(writer, gc.NotNil) 459 c.Assert(n, gc.Equals, len(p.Bytes())) 460 } 461 462 func (s *uploadSuite) TestMockBuildTools(c *gc.C) { 463 s.PatchValue(&version.Current, version.MustParseBinary("1.9.1-trusty-amd64")) 464 buildToolsFunc := toolstesting.GetMockBuildTools(c) 465 builtTools, err := buildToolsFunc(nil, "released") 466 c.Assert(err, jc.ErrorIsNil) 467 468 builtTools.Dir = "" 469 470 expectedBuiltTools := &sync.BuiltTools{ 471 StorageName: "name", 472 Version: version.Current, 473 Size: 127, 474 Sha256Hash: "6a19d08ca4913382ca86508aa38eb8ee5b9ae2d74333fe8d862c0f9e29b82c39", 475 } 476 c.Assert(builtTools, gc.DeepEquals, expectedBuiltTools) 477 478 vers := version.MustParseBinary("1.5.3-trusty-amd64") 479 builtTools, err = buildToolsFunc(&vers.Number, "released") 480 c.Assert(err, jc.ErrorIsNil) 481 builtTools.Dir = "" 482 expectedBuiltTools = &sync.BuiltTools{ 483 StorageName: "name", 484 Version: vers, 485 Size: 127, 486 Sha256Hash: "cad8ccedab8f26807ff379ddc2f2f78d9a7cac1276e001154cee5e39b9ddcc38", 487 } 488 c.Assert(builtTools, gc.DeepEquals, expectedBuiltTools) 489 } 490 491 func (s *uploadSuite) TestStorageToolsUploaderWriteMirrors(c *gc.C) { 492 s.testStorageToolsUploaderWriteMirrors(c, envtools.WriteMirrors) 493 } 494 495 func (s *uploadSuite) TestStorageToolsUploaderDontWriteMirrors(c *gc.C) { 496 s.testStorageToolsUploaderWriteMirrors(c, envtools.DoNotWriteMirrors) 497 } 498 499 func (s *uploadSuite) testStorageToolsUploaderWriteMirrors(c *gc.C, writeMirrors envtools.ShouldWriteMirrors) { 500 storageDir := c.MkDir() 501 stor, err := filestorage.NewFileStorageWriter(storageDir) 502 c.Assert(err, jc.ErrorIsNil) 503 504 uploader := &sync.StorageToolsUploader{ 505 Storage: stor, 506 WriteMetadata: true, 507 WriteMirrors: writeMirrors, 508 } 509 510 err = uploader.UploadTools( 511 "released", 512 "released", 513 &coretools.Tools{ 514 Version: version.Current, 515 Size: 7, 516 SHA256: "ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73", 517 }, []byte("content")) 518 c.Assert(err, jc.ErrorIsNil) 519 520 mirrorsPath := simplestreams.MirrorsPath(envtools.StreamsVersionV1) + simplestreams.UnsignedSuffix 521 r, err := stor.Get(path.Join(storage.BaseToolsPath, mirrorsPath)) 522 if writeMirrors == envtools.WriteMirrors { 523 c.Assert(err, jc.ErrorIsNil) 524 data, err := ioutil.ReadAll(r) 525 r.Close() 526 c.Assert(err, jc.ErrorIsNil) 527 c.Assert(string(data), jc.Contains, `"mirrors":`) 528 } else { 529 c.Assert(err, jc.Satisfies, errors.IsNotFound) 530 } 531 } 532 533 type mockToolsFinder struct{} 534 535 func (mockToolsFinder) FindTools(major int, stream string) (coretools.List, error) { 536 return nil, coretools.ErrNoMatches 537 }