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