github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/environs/tools/tools_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package tools_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "launchpad.net/gocheck"
    15  
    16  	"github.com/juju/juju/environs"
    17  	"github.com/juju/juju/environs/config"
    18  	"github.com/juju/juju/environs/configstore"
    19  	envtesting "github.com/juju/juju/environs/testing"
    20  	envtools "github.com/juju/juju/environs/tools"
    21  	toolstesting "github.com/juju/juju/environs/tools/testing"
    22  	"github.com/juju/juju/provider/dummy"
    23  	"github.com/juju/juju/testing"
    24  	coretesting "github.com/juju/juju/testing"
    25  	coretools "github.com/juju/juju/tools"
    26  	"github.com/juju/juju/version"
    27  )
    28  
    29  type SimpleStreamsToolsSuite struct {
    30  	env environs.Environ
    31  	coretesting.BaseSuite
    32  	envtesting.ToolsFixture
    33  	origCurrentVersion version.Binary
    34  	customToolsDir     string
    35  	publicToolsDir     string
    36  }
    37  
    38  func setupToolsTests() {
    39  	gc.Suite(&SimpleStreamsToolsSuite{})
    40  }
    41  
    42  func (s *SimpleStreamsToolsSuite) SetUpSuite(c *gc.C) {
    43  	s.BaseSuite.SetUpSuite(c)
    44  	s.customToolsDir = c.MkDir()
    45  	s.publicToolsDir = c.MkDir()
    46  }
    47  
    48  func (s *SimpleStreamsToolsSuite) SetUpTest(c *gc.C) {
    49  	s.ToolsFixture.DefaultBaseURL = "file://" + s.publicToolsDir
    50  	s.BaseSuite.SetUpTest(c)
    51  	s.ToolsFixture.SetUpTest(c)
    52  	s.origCurrentVersion = version.Current
    53  	s.reset(c, nil)
    54  }
    55  
    56  func (s *SimpleStreamsToolsSuite) TearDownTest(c *gc.C) {
    57  	dummy.Reset()
    58  	version.Current = s.origCurrentVersion
    59  	s.ToolsFixture.TearDownTest(c)
    60  	s.BaseSuite.TearDownTest(c)
    61  }
    62  
    63  func (s *SimpleStreamsToolsSuite) reset(c *gc.C, attrs map[string]interface{}) {
    64  	final := map[string]interface{}{
    65  		"tools-metadata-url": "file://" + s.customToolsDir,
    66  	}
    67  	for k, v := range attrs {
    68  		final[k] = v
    69  	}
    70  	s.resetEnv(c, final)
    71  }
    72  
    73  func (s *SimpleStreamsToolsSuite) removeTools(c *gc.C) {
    74  	for _, dir := range []string{s.customToolsDir, s.publicToolsDir} {
    75  		files, err := ioutil.ReadDir(dir)
    76  		c.Assert(err, gc.IsNil)
    77  		for _, f := range files {
    78  			err := os.RemoveAll(filepath.Join(dir, f.Name()))
    79  			c.Assert(err, gc.IsNil)
    80  		}
    81  	}
    82  }
    83  
    84  func (s *SimpleStreamsToolsSuite) uploadCustom(c *gc.C, verses ...version.Binary) map[version.Binary]string {
    85  	return toolstesting.UploadToDirectory(c, s.customToolsDir, verses...)
    86  }
    87  
    88  func (s *SimpleStreamsToolsSuite) uploadPublic(c *gc.C, verses ...version.Binary) map[version.Binary]string {
    89  	return toolstesting.UploadToDirectory(c, s.publicToolsDir, verses...)
    90  }
    91  
    92  func (s *SimpleStreamsToolsSuite) resetEnv(c *gc.C, attrs map[string]interface{}) {
    93  	version.Current = s.origCurrentVersion
    94  	dummy.Reset()
    95  	cfg, err := config.New(config.NoDefaults, dummy.SampleConfig().Merge(attrs))
    96  	c.Assert(err, gc.IsNil)
    97  	env, err := environs.Prepare(cfg, testing.Context(c), configstore.NewMem())
    98  	c.Assert(err, gc.IsNil)
    99  	s.env = env
   100  	s.removeTools(c)
   101  }
   102  
   103  var findToolsTests = []struct {
   104  	info   string
   105  	major  int
   106  	minor  int
   107  	custom []version.Binary
   108  	public []version.Binary
   109  	expect []version.Binary
   110  	err    error
   111  }{{
   112  	info:  "none available anywhere",
   113  	major: 1,
   114  	err:   envtools.ErrNoTools,
   115  }, {
   116  	info:   "custom/private tools only, none matching",
   117  	major:  1,
   118  	minor:  2,
   119  	custom: envtesting.V220all,
   120  	err:    coretools.ErrNoMatches,
   121  }, {
   122  	info:   "custom tools found",
   123  	major:  1,
   124  	minor:  2,
   125  	custom: envtesting.VAll,
   126  	expect: envtesting.V120all,
   127  }, {
   128  	info:   "public tools found",
   129  	major:  1,
   130  	minor:  1,
   131  	public: envtesting.VAll,
   132  	expect: envtesting.V110all,
   133  }, {
   134  	info:   "public and custom tools found, only taken from custom",
   135  	major:  1,
   136  	minor:  1,
   137  	custom: envtesting.V110p,
   138  	public: envtesting.VAll,
   139  	expect: envtesting.V110p,
   140  }, {
   141  	info:   "custom tools completely block public ones",
   142  	major:  1,
   143  	minor:  -1,
   144  	custom: envtesting.V220all,
   145  	public: envtesting.VAll,
   146  	expect: envtesting.V1all,
   147  }, {
   148  	info:   "tools matching major version only",
   149  	major:  1,
   150  	minor:  -1,
   151  	public: envtesting.VAll,
   152  	expect: envtesting.V1all,
   153  }}
   154  
   155  func (s *SimpleStreamsToolsSuite) TestFindTools(c *gc.C) {
   156  	for i, test := range findToolsTests {
   157  		c.Logf("\ntest %d: %s", i, test.info)
   158  		s.reset(c, nil)
   159  		custom := s.uploadCustom(c, test.custom...)
   160  		public := s.uploadPublic(c, test.public...)
   161  		actual, err := envtools.FindTools(s.env, test.major, test.minor, coretools.Filter{}, envtools.DoNotAllowRetry)
   162  		if test.err != nil {
   163  			if len(actual) > 0 {
   164  				c.Logf(actual.String())
   165  			}
   166  			c.Check(err, jc.Satisfies, errors.IsNotFound)
   167  			continue
   168  		}
   169  		expect := map[version.Binary]string{}
   170  		for _, expected := range test.expect {
   171  			// If the tools exist in custom, that's preferred.
   172  			var ok bool
   173  			if expect[expected], ok = custom[expected]; !ok {
   174  				expect[expected] = public[expected]
   175  			}
   176  		}
   177  		c.Check(actual.URLs(), gc.DeepEquals, expect)
   178  	}
   179  }
   180  
   181  func (s *SimpleStreamsToolsSuite) TestFindToolsInControlBucket(c *gc.C) {
   182  	s.reset(c, nil)
   183  	custom := toolstesting.UploadToStorage(c, s.env.Storage(), envtesting.V110p...)
   184  	s.uploadPublic(c, envtesting.VAll...)
   185  	actual, err := envtools.FindTools(s.env, 1, 1, coretools.Filter{}, envtools.DoNotAllowRetry)
   186  	c.Assert(err, gc.IsNil)
   187  	expect := map[version.Binary]string{}
   188  	for _, expected := range envtesting.V110p {
   189  		expect[expected] = custom[expected]
   190  	}
   191  	c.Assert(actual.URLs(), gc.DeepEquals, expect)
   192  }
   193  
   194  func (s *SimpleStreamsToolsSuite) TestFindToolsFiltering(c *gc.C) {
   195  	tw := &loggo.TestWriter{}
   196  	c.Assert(loggo.RegisterWriter("filter-tester", tw, loggo.DEBUG), gc.IsNil)
   197  	defer loggo.RemoveWriter("filter-tester")
   198  	_, err := envtools.FindTools(
   199  		s.env, 1, -1, coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}}, envtools.DoNotAllowRetry)
   200  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   201  	// This is slightly overly prescriptive, but feel free to change or add
   202  	// messages. This still helps to ensure that all log messages are
   203  	// properly formed.
   204  	messages := []jc.SimpleMessage{
   205  		{loggo.INFO, "reading tools with major version 1"},
   206  		{loggo.INFO, "filtering tools by version: \\d+\\.\\d+\\.\\d+"},
   207  		{loggo.DEBUG, "no architecture specified when finding tools, looking for any"},
   208  		{loggo.DEBUG, "no series specified when finding tools, looking for any"},
   209  	}
   210  	sources, err := envtools.GetMetadataSources(s.env)
   211  	c.Assert(err, gc.IsNil)
   212  	for i := 0; i < 2*len(sources); i++ {
   213  		messages = append(messages,
   214  			jc.SimpleMessage{loggo.DEBUG, `fetchData failed for .*`},
   215  			jc.SimpleMessage{loggo.DEBUG, `cannot load index .*`})
   216  	}
   217  	c.Check(tw.Log, jc.LogMatches, messages)
   218  }
   219  
   220  func (s *SimpleStreamsToolsSuite) TestFindBootstrapTools(c *gc.C) {
   221  	// Remove the default tools URL from the search path, just look in cloud storage.
   222  	s.PatchValue(&envtools.DefaultBaseURL, "")
   223  	for i, test := range envtesting.BootstrapToolsTests {
   224  		c.Logf("\ntest %d: %s", i, test.Info)
   225  		attrs := map[string]interface{}{
   226  			"development": test.Development,
   227  		}
   228  		var agentVersion *version.Number
   229  		if test.AgentVersion != version.Zero {
   230  			attrs["agent-version"] = test.AgentVersion.String()
   231  			agentVersion = &test.AgentVersion
   232  		}
   233  		s.reset(c, attrs)
   234  		version.Current = test.CliVersion
   235  		available := s.uploadCustom(c, test.Available...)
   236  
   237  		params := envtools.BootstrapToolsParams{
   238  			Version: agentVersion,
   239  			Series:  test.DefaultSeries,
   240  			Arch:    &test.Arch,
   241  		}
   242  		actual, err := envtools.FindBootstrapTools(s.env, params)
   243  		if test.Err != "" {
   244  			if len(actual) > 0 {
   245  				c.Logf(actual.String())
   246  			}
   247  			c.Check(err, jc.Satisfies, errors.IsNotFound)
   248  			continue
   249  		}
   250  		expect := map[version.Binary]string{}
   251  		for _, expected := range test.Expect {
   252  			expect[expected] = available[expected]
   253  		}
   254  		c.Check(actual.URLs(), gc.DeepEquals, expect)
   255  	}
   256  }
   257  
   258  var findInstanceToolsTests = []struct {
   259  	info         string
   260  	available    []version.Binary
   261  	agentVersion version.Number
   262  	series       string
   263  	arch         string
   264  	expect       []version.Binary
   265  	err          error
   266  }{{
   267  	info:         "nothing at all",
   268  	agentVersion: envtesting.V120,
   269  	series:       "precise",
   270  	err:          envtools.ErrNoTools,
   271  }, {
   272  	info:         "nothing matching 1",
   273  	available:    envtesting.V100Xall,
   274  	agentVersion: envtesting.V120,
   275  	series:       "precise",
   276  	err:          coretools.ErrNoMatches,
   277  }, {
   278  	info:         "nothing matching 2",
   279  	available:    envtesting.V120all,
   280  	agentVersion: envtesting.V110,
   281  	series:       "precise",
   282  	err:          coretools.ErrNoMatches,
   283  }, {
   284  	info:         "nothing matching 3",
   285  	available:    envtesting.V120q,
   286  	agentVersion: envtesting.V120,
   287  	series:       "precise",
   288  	err:          coretools.ErrNoMatches,
   289  }, {
   290  	info:         "nothing matching 4",
   291  	available:    envtesting.V120q,
   292  	agentVersion: envtesting.V120,
   293  	series:       "quantal",
   294  	arch:         "arm",
   295  	err:          coretools.ErrNoMatches,
   296  }, {
   297  	info:         "actual match 1",
   298  	available:    envtesting.VAll,
   299  	agentVersion: envtesting.V1001,
   300  	series:       "precise",
   301  	expect:       []version.Binary{envtesting.V1001p64},
   302  }, {
   303  	info:         "actual match 2",
   304  	available:    envtesting.VAll,
   305  	agentVersion: envtesting.V120,
   306  	series:       "quantal",
   307  	expect:       []version.Binary{envtesting.V120q64, envtesting.V120q32},
   308  }, {
   309  	info:         "actual match 3",
   310  	available:    envtesting.VAll,
   311  	agentVersion: envtesting.V110,
   312  	series:       "quantal",
   313  	arch:         "i386",
   314  	expect:       []version.Binary{envtesting.V110q32},
   315  }}
   316  
   317  func (s *SimpleStreamsToolsSuite) TestFindInstanceTools(c *gc.C) {
   318  	for i, test := range findInstanceToolsTests {
   319  		c.Logf("\ntest %d: %s", i, test.info)
   320  		s.reset(c, map[string]interface{}{
   321  			"agent-version": test.agentVersion.String(),
   322  		})
   323  		available := s.uploadCustom(c, test.available...)
   324  
   325  		agentVersion, _ := s.env.Config().AgentVersion()
   326  		actual, err := envtools.FindInstanceTools(s.env, agentVersion, test.series, &test.arch)
   327  		if test.err != nil {
   328  			if len(actual) > 0 {
   329  				c.Logf(actual.String())
   330  			}
   331  			c.Check(err, jc.Satisfies, errors.IsNotFound)
   332  			continue
   333  		}
   334  		expect := map[version.Binary]string{}
   335  		for _, expected := range test.expect {
   336  			expect[expected] = available[expected]
   337  		}
   338  		c.Check(actual.URLs(), gc.DeepEquals, expect)
   339  	}
   340  }
   341  
   342  var findExactToolsTests = []struct {
   343  	info   string
   344  	custom []version.Binary
   345  	public []version.Binary
   346  	seek   version.Binary
   347  	err    error
   348  }{{
   349  	info: "nothing available",
   350  	seek: envtesting.V100p64,
   351  	err:  envtools.ErrNoTools,
   352  }, {
   353  	info:   "only non-matches available in custom",
   354  	custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64),
   355  	seek:   envtesting.V100p64,
   356  	err:    coretools.ErrNoMatches,
   357  }, {
   358  	info:   "exact match available in custom",
   359  	custom: []version.Binary{envtesting.V100p64},
   360  	seek:   envtesting.V100p64,
   361  }, {
   362  	info:   "only non-matches available in public",
   363  	custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64),
   364  	seek:   envtesting.V100p64,
   365  	err:    coretools.ErrNoMatches,
   366  }, {
   367  	info:   "exact match available in public",
   368  	public: []version.Binary{envtesting.V100p64},
   369  	seek:   envtesting.V100p64,
   370  }, {
   371  	info:   "exact match in public not blocked by custom",
   372  	custom: envtesting.V110all,
   373  	public: []version.Binary{envtesting.V100p64},
   374  	seek:   envtesting.V100p64,
   375  }}
   376  
   377  func (s *SimpleStreamsToolsSuite) TestFindExactTools(c *gc.C) {
   378  	for i, test := range findExactToolsTests {
   379  		c.Logf("\ntest %d: %s", i, test.info)
   380  		s.reset(c, nil)
   381  		custom := s.uploadCustom(c, test.custom...)
   382  		public := s.uploadPublic(c, test.public...)
   383  		actual, err := envtools.FindExactTools(s.env, test.seek.Number, test.seek.Series, test.seek.Arch)
   384  		if test.err == nil {
   385  			if !c.Check(err, gc.IsNil) {
   386  				continue
   387  			}
   388  			c.Check(actual.Version, gc.Equals, test.seek)
   389  			if _, ok := custom[actual.Version]; ok {
   390  				c.Check(actual.URL, gc.DeepEquals, custom[actual.Version])
   391  			} else {
   392  				c.Check(actual.URL, gc.DeepEquals, public[actual.Version])
   393  			}
   394  		} else {
   395  			c.Check(err, jc.Satisfies, errors.IsNotFound)
   396  		}
   397  	}
   398  }
   399  
   400  // fakeToolsForSeries fakes a Tools object with just enough information for
   401  // testing the handling its OS series.
   402  func fakeToolsForSeries(series string) *coretools.Tools {
   403  	return &coretools.Tools{Version: version.Binary{Series: series}}
   404  }
   405  
   406  // fakeToolsList fakes a envtools.List containing Tools objects for the given
   407  // respective series, in the same number and order.
   408  func fakeToolsList(series ...string) coretools.List {
   409  	list := coretools.List{}
   410  	for _, name := range series {
   411  		list = append(list, fakeToolsForSeries(name))
   412  	}
   413  	return list
   414  }
   415  
   416  type ToolsListSuite struct{}
   417  
   418  func (s *ToolsListSuite) TestCheckToolsSeriesRequiresTools(c *gc.C) {
   419  	err := envtools.CheckToolsSeries(fakeToolsList(), "precise")
   420  	c.Assert(err, gc.NotNil)
   421  	c.Check(err, gc.ErrorMatches, "expected single series, got \\[\\]")
   422  }
   423  
   424  func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsOneSetOfTools(c *gc.C) {
   425  	names := []string{"precise", "raring"}
   426  	for _, series := range names {
   427  		list := fakeToolsList(series)
   428  		err := envtools.CheckToolsSeries(list, series)
   429  		c.Check(err, gc.IsNil)
   430  	}
   431  }
   432  
   433  func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsMultipleForSameSeries(c *gc.C) {
   434  	series := "quantal"
   435  	list := fakeToolsList(series, series, series)
   436  	err := envtools.CheckToolsSeries(list, series)
   437  	c.Check(err, gc.IsNil)
   438  }
   439  
   440  func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForOtherSeries(c *gc.C) {
   441  	list := fakeToolsList("hoary")
   442  	err := envtools.CheckToolsSeries(list, "warty")
   443  	c.Assert(err, gc.NotNil)
   444  	c.Check(err, gc.ErrorMatches, "tools mismatch: expected series warty, got hoary")
   445  }
   446  
   447  func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForMixedSeries(c *gc.C) {
   448  	list := fakeToolsList("precise", "raring")
   449  	err := envtools.CheckToolsSeries(list, "precise")
   450  	c.Assert(err, gc.NotNil)
   451  	c.Check(err, gc.ErrorMatches, "expected single series, got .*")
   452  }