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