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