github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	"github.com/juju/utils"
    15  	"github.com/juju/version"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/environs/bootstrap"
    20  	sstesting "github.com/juju/juju/environs/simplestreams/testing"
    21  	envtesting "github.com/juju/juju/environs/testing"
    22  	envtools "github.com/juju/juju/environs/tools"
    23  	toolstesting "github.com/juju/juju/environs/tools/testing"
    24  	"github.com/juju/juju/juju/keys"
    25  	"github.com/juju/juju/jujuclient/jujuclienttesting"
    26  	"github.com/juju/juju/provider/dummy"
    27  	coretesting "github.com/juju/juju/testing"
    28  	coretools "github.com/juju/juju/tools"
    29  	jujuversion "github.com/juju/juju/version"
    30  )
    31  
    32  type SimpleStreamsToolsSuite struct {
    33  	env environs.Environ
    34  	coretesting.BaseSuite
    35  	envtesting.ToolsFixture
    36  	origCurrentVersion version.Number
    37  	customToolsDir     string
    38  	publicToolsDir     string
    39  }
    40  
    41  func setupToolsTests() {
    42  	gc.Suite(&SimpleStreamsToolsSuite{})
    43  }
    44  
    45  func (s *SimpleStreamsToolsSuite) SetUpSuite(c *gc.C) {
    46  	s.BaseSuite.SetUpSuite(c)
    47  	s.customToolsDir = c.MkDir()
    48  	s.publicToolsDir = c.MkDir()
    49  	s.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
    50  }
    51  
    52  func (s *SimpleStreamsToolsSuite) SetUpTest(c *gc.C) {
    53  	s.ToolsFixture.DefaultBaseURL = utils.MakeFileURL(s.publicToolsDir)
    54  	s.BaseSuite.SetUpTest(c)
    55  	s.ToolsFixture.SetUpTest(c)
    56  	s.origCurrentVersion = jujuversion.Current
    57  	s.reset(c, nil)
    58  }
    59  
    60  func (s *SimpleStreamsToolsSuite) TearDownTest(c *gc.C) {
    61  	dummy.Reset(c)
    62  	jujuversion.Current = s.origCurrentVersion
    63  	s.ToolsFixture.TearDownTest(c)
    64  	s.BaseSuite.TearDownTest(c)
    65  }
    66  
    67  func (s *SimpleStreamsToolsSuite) reset(c *gc.C, attrs map[string]interface{}) {
    68  	final := map[string]interface{}{
    69  		"agent-metadata-url": utils.MakeFileURL(s.customToolsDir),
    70  		"agent-stream":       "proposed",
    71  	}
    72  	for k, v := range attrs {
    73  		final[k] = v
    74  	}
    75  	s.resetEnv(c, final)
    76  }
    77  
    78  func (s *SimpleStreamsToolsSuite) removeTools(c *gc.C) {
    79  	for _, dir := range []string{s.customToolsDir, s.publicToolsDir} {
    80  		files, err := ioutil.ReadDir(dir)
    81  		c.Assert(err, jc.ErrorIsNil)
    82  		for _, f := range files {
    83  			err := os.RemoveAll(filepath.Join(dir, f.Name()))
    84  			c.Assert(err, jc.ErrorIsNil)
    85  		}
    86  	}
    87  }
    88  
    89  func (s *SimpleStreamsToolsSuite) uploadCustom(c *gc.C, verses ...version.Binary) map[version.Binary]string {
    90  	return toolstesting.UploadToDirectory(c, "proposed", s.customToolsDir, verses...)
    91  }
    92  
    93  func (s *SimpleStreamsToolsSuite) uploadPublic(c *gc.C, verses ...version.Binary) map[version.Binary]string {
    94  	return toolstesting.UploadToDirectory(c, "proposed", s.publicToolsDir, verses...)
    95  }
    96  
    97  func (s *SimpleStreamsToolsSuite) resetEnv(c *gc.C, attrs map[string]interface{}) {
    98  	jujuversion.Current = s.origCurrentVersion
    99  	dummy.Reset(c)
   100  	attrs = dummy.SampleConfig().Merge(attrs)
   101  	env, err := bootstrap.Prepare(envtesting.BootstrapContext(c),
   102  		jujuclienttesting.NewMemStore(),
   103  		bootstrap.PrepareParams{
   104  			ControllerConfig: coretesting.FakeControllerConfig(),
   105  			ControllerName:   attrs["name"].(string),
   106  			ModelConfig:      attrs,
   107  			Cloud:            dummy.SampleCloudSpec(),
   108  			AdminSecret:      "admin-secret",
   109  		},
   110  	)
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	s.env = env
   113  	s.removeTools(c)
   114  }
   115  
   116  var findToolsTests = []struct {
   117  	info   string
   118  	major  int
   119  	minor  int
   120  	custom []version.Binary
   121  	public []version.Binary
   122  	expect []version.Binary
   123  	err    error
   124  }{{
   125  	info:  "none available anywhere",
   126  	major: 1,
   127  	err:   envtools.ErrNoTools,
   128  }, {
   129  	info:   "custom/private tools only, none matching",
   130  	major:  1,
   131  	minor:  2,
   132  	custom: envtesting.V220all,
   133  	err:    coretools.ErrNoMatches,
   134  }, {
   135  	info:   "custom tools found",
   136  	major:  1,
   137  	minor:  2,
   138  	custom: envtesting.VAll,
   139  	expect: envtesting.V120all,
   140  }, {
   141  	info:   "public tools found",
   142  	major:  1,
   143  	minor:  1,
   144  	public: envtesting.VAll,
   145  	expect: envtesting.V110all,
   146  }, {
   147  	info:   "public and custom tools found, only taken from custom",
   148  	major:  1,
   149  	minor:  1,
   150  	custom: envtesting.V110p,
   151  	public: envtesting.VAll,
   152  	expect: envtesting.V110p,
   153  }, {
   154  	info:   "custom tools completely block public ones",
   155  	major:  1,
   156  	minor:  -1,
   157  	custom: envtesting.V220all,
   158  	public: envtesting.VAll,
   159  	expect: envtesting.V1all,
   160  }, {
   161  	info:   "tools matching major version only",
   162  	major:  1,
   163  	minor:  -1,
   164  	public: envtesting.VAll,
   165  	expect: envtesting.V1all,
   166  }}
   167  
   168  func (s *SimpleStreamsToolsSuite) TestFindTools(c *gc.C) {
   169  	for i, test := range findToolsTests {
   170  		c.Logf("\ntest %d: %s", i, test.info)
   171  		s.reset(c, nil)
   172  		custom := s.uploadCustom(c, test.custom...)
   173  		public := s.uploadPublic(c, test.public...)
   174  		stream := envtools.PreferredStream(&jujuversion.Current, s.env.Config().Development(), s.env.Config().AgentStream())
   175  		actual, err := envtools.FindTools(s.env, test.major, test.minor, stream, coretools.Filter{})
   176  		if test.err != nil {
   177  			if len(actual) > 0 {
   178  				c.Logf(actual.String())
   179  			}
   180  			c.Check(err, jc.Satisfies, errors.IsNotFound)
   181  			continue
   182  		}
   183  		expect := map[version.Binary][]string{}
   184  		for _, expected := range test.expect {
   185  			// If the tools exist in custom, that's preferred.
   186  			url, ok := custom[expected]
   187  			if !ok {
   188  				url = public[expected]
   189  			}
   190  			expect[expected] = append(expect[expected], url)
   191  		}
   192  		c.Check(actual.URLs(), gc.DeepEquals, expect)
   193  	}
   194  }
   195  
   196  func (s *SimpleStreamsToolsSuite) TestFindToolsFiltering(c *gc.C) {
   197  	var tw loggo.TestWriter
   198  	c.Assert(loggo.RegisterWriter("filter-tester", &tw), gc.IsNil)
   199  	defer loggo.RemoveWriter("filter-tester")
   200  	logger := loggo.GetLogger("juju.environs")
   201  	defer logger.SetLogLevel(logger.LogLevel())
   202  	logger.SetLogLevel(loggo.TRACE)
   203  
   204  	_, err := envtools.FindTools(
   205  		s.env, 1, -1, "released", coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}})
   206  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   207  	// This is slightly overly prescriptive, but feel free to change or add
   208  	// messages. This still helps to ensure that all log messages are
   209  	// properly formed.
   210  	messages := []jc.SimpleMessage{
   211  		{loggo.INFO, "reading agent binaries with major version 1"},
   212  		{loggo.INFO, "filtering agent binaries by version: \\d+\\.\\d+\\.\\d+"},
   213  		{loggo.TRACE, "no architecture specified when finding agent binaries, looking for "},
   214  		{loggo.TRACE, "no series specified when finding agent binaries, looking for \\[.*\\]"},
   215  	}
   216  	sources, err := envtools.GetMetadataSources(s.env)
   217  	c.Assert(err, jc.ErrorIsNil)
   218  	for i := 0; i < len(sources); i++ {
   219  		messages = append(messages,
   220  			jc.SimpleMessage{loggo.TRACE, `fetchData failed for .*`},
   221  			jc.SimpleMessage{loggo.TRACE, `cannot load index .*`})
   222  	}
   223  	c.Check(tw.Log(), jc.LogMatches, messages)
   224  }
   225  
   226  var findExactToolsTests = []struct {
   227  	info   string
   228  	custom []version.Binary
   229  	public []version.Binary
   230  	seek   version.Binary
   231  	err    error
   232  }{{
   233  	info: "nothing available",
   234  	seek: envtesting.V100p64,
   235  	err:  envtools.ErrNoTools,
   236  }, {
   237  	info:   "only non-matches available in custom",
   238  	custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64),
   239  	seek:   envtesting.V100p64,
   240  	err:    coretools.ErrNoMatches,
   241  }, {
   242  	info:   "exact match available in custom",
   243  	custom: []version.Binary{envtesting.V100p64},
   244  	seek:   envtesting.V100p64,
   245  }, {
   246  	info:   "only non-matches available in public",
   247  	custom: append(envtesting.V110all, envtesting.V100p32, envtesting.V100q64, envtesting.V1001p64),
   248  	seek:   envtesting.V100p64,
   249  	err:    coretools.ErrNoMatches,
   250  }, {
   251  	info:   "exact match available in public",
   252  	public: []version.Binary{envtesting.V100p64},
   253  	seek:   envtesting.V100p64,
   254  }, {
   255  	info:   "exact match in public not blocked by custom",
   256  	custom: envtesting.V110all,
   257  	public: []version.Binary{envtesting.V100p64},
   258  	seek:   envtesting.V100p64,
   259  }}
   260  
   261  func (s *SimpleStreamsToolsSuite) TestFindExactTools(c *gc.C) {
   262  	for i, test := range findExactToolsTests {
   263  		c.Logf("\ntest %d: %s", i, test.info)
   264  		s.reset(c, nil)
   265  		custom := s.uploadCustom(c, test.custom...)
   266  		public := s.uploadPublic(c, test.public...)
   267  		actual, err := envtools.FindExactTools(s.env, test.seek.Number, test.seek.Series, test.seek.Arch)
   268  		if test.err == nil {
   269  			if !c.Check(err, jc.ErrorIsNil) {
   270  				continue
   271  			}
   272  			c.Check(actual.Version, gc.Equals, test.seek)
   273  			if _, ok := custom[actual.Version]; ok {
   274  				c.Check(actual.URL, gc.DeepEquals, custom[actual.Version])
   275  			} else {
   276  				c.Check(actual.URL, gc.DeepEquals, public[actual.Version])
   277  			}
   278  		} else {
   279  			c.Check(err, jc.Satisfies, errors.IsNotFound)
   280  		}
   281  	}
   282  }
   283  
   284  var preferredStreamTests = []struct {
   285  	explicitVers   string
   286  	currentVers    string
   287  	forceDevel     bool
   288  	streamInConfig string
   289  	expected       string
   290  }{{
   291  	currentVers:    "1.22.0",
   292  	streamInConfig: "released",
   293  	expected:       "released",
   294  }, {
   295  	currentVers:    "1.22.0",
   296  	streamInConfig: "devel",
   297  	expected:       "devel",
   298  }, {
   299  	currentVers: "1.22.0",
   300  	expected:    "released",
   301  }, {
   302  	currentVers: "1.22-beta1",
   303  	expected:    "devel",
   304  }, {
   305  	currentVers:    "1.22-beta1",
   306  	streamInConfig: "released",
   307  	expected:       "devel",
   308  }, {
   309  	currentVers:    "1.22-beta1",
   310  	streamInConfig: "devel",
   311  	expected:       "devel",
   312  }, {
   313  	currentVers: "1.22.0",
   314  	forceDevel:  true,
   315  	expected:    "devel",
   316  }, {
   317  	currentVers:  "1.22.0",
   318  	explicitVers: "1.22-beta1",
   319  	expected:     "devel",
   320  }, {
   321  	currentVers:  "1.22-bta1",
   322  	explicitVers: "1.22.0",
   323  	expected:     "released",
   324  }}
   325  
   326  func (s *SimpleStreamsToolsSuite) TestPreferredStream(c *gc.C) {
   327  	for i, test := range preferredStreamTests {
   328  		c.Logf("\ntest %d", i)
   329  		s.PatchValue(&jujuversion.Current, version.MustParse(test.currentVers))
   330  		var vers *version.Number
   331  		if test.explicitVers != "" {
   332  			v := version.MustParse(test.explicitVers)
   333  			vers = &v
   334  		}
   335  		obtained := envtools.PreferredStream(vers, test.forceDevel, test.streamInConfig)
   336  		c.Check(obtained, gc.Equals, test.expected)
   337  	}
   338  }
   339  
   340  // fakeToolsForSeries fakes a Tools object with just enough information for
   341  // testing the handling its OS series.
   342  func fakeToolsForSeries(series string) *coretools.Tools {
   343  	return &coretools.Tools{Version: version.Binary{Series: series}}
   344  }
   345  
   346  // fakeToolsList fakes a envtools.List containing Tools objects for the given
   347  // respective series, in the same number and order.
   348  func fakeToolsList(series ...string) coretools.List {
   349  	list := coretools.List{}
   350  	for _, name := range series {
   351  		list = append(list, fakeToolsForSeries(name))
   352  	}
   353  	return list
   354  }
   355  
   356  type ToolsListSuite struct{}
   357  
   358  func (s *ToolsListSuite) TestCheckToolsSeriesRequiresTools(c *gc.C) {
   359  	err := envtools.CheckToolsSeries(fakeToolsList(), "precise")
   360  	c.Assert(err, gc.NotNil)
   361  	c.Check(err, gc.ErrorMatches, "expected single series, got \\[\\]")
   362  }
   363  
   364  func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsOneSetOfTools(c *gc.C) {
   365  	names := []string{"precise", "raring"}
   366  	for _, series := range names {
   367  		list := fakeToolsList(series)
   368  		err := envtools.CheckToolsSeries(list, series)
   369  		c.Check(err, jc.ErrorIsNil)
   370  	}
   371  }
   372  
   373  func (s *ToolsListSuite) TestCheckToolsSeriesAcceptsMultipleForSameSeries(c *gc.C) {
   374  	series := "quantal"
   375  	list := fakeToolsList(series, series, series)
   376  	err := envtools.CheckToolsSeries(list, series)
   377  	c.Check(err, jc.ErrorIsNil)
   378  }
   379  
   380  func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForOtherSeries(c *gc.C) {
   381  	list := fakeToolsList("hoary")
   382  	err := envtools.CheckToolsSeries(list, "warty")
   383  	c.Assert(err, gc.NotNil)
   384  	c.Check(err, gc.ErrorMatches, "tools mismatch: expected series warty, got hoary")
   385  }
   386  
   387  func (s *ToolsListSuite) TestCheckToolsSeriesRejectsToolsForMixedSeries(c *gc.C) {
   388  	list := fakeToolsList("precise", "raring")
   389  	err := envtools.CheckToolsSeries(list, "precise")
   390  	c.Assert(err, gc.NotNil)
   391  	c.Check(err, gc.ErrorMatches, "expected single series, got .*")
   392  }