github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/environs/testing/tools.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"bytes"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  
    12  	"github.com/juju/utils"
    13  	"github.com/juju/utils/set"
    14  	gc "launchpad.net/gocheck"
    15  
    16  	agenttools "github.com/juju/juju/agent/tools"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/environs/bootstrap"
    19  	"github.com/juju/juju/environs/simplestreams"
    20  	"github.com/juju/juju/environs/storage"
    21  	envtools "github.com/juju/juju/environs/tools"
    22  	"github.com/juju/juju/state"
    23  	coretesting "github.com/juju/juju/testing"
    24  	coretools "github.com/juju/juju/tools"
    25  	"github.com/juju/juju/version"
    26  	"github.com/juju/juju/worker/upgrader"
    27  )
    28  
    29  // ToolsFixture is used as a fixture to stub out the default tools URL so we
    30  // don't hit the real internet during tests.
    31  type ToolsFixture struct {
    32  	origDefaultURL string
    33  	DefaultBaseURL string
    34  
    35  	// UploadArches holds the architectures of tools to
    36  	// upload in UploadFakeTools. If empty, it will default
    37  	// to just version.Current.Arch.
    38  	UploadArches []string
    39  }
    40  
    41  func (s *ToolsFixture) SetUpTest(c *gc.C) {
    42  	s.origDefaultURL = envtools.DefaultBaseURL
    43  	envtools.DefaultBaseURL = s.DefaultBaseURL
    44  }
    45  
    46  func (s *ToolsFixture) TearDownTest(c *gc.C) {
    47  	envtools.DefaultBaseURL = s.origDefaultURL
    48  }
    49  
    50  // UploadFakeTools uploads fake tools of the architectures in
    51  // s.UploadArches for each LTS release to the specified storage.
    52  func (s *ToolsFixture) UploadFakeTools(c *gc.C, stor storage.Storage) {
    53  	arches := s.UploadArches
    54  	if len(arches) == 0 {
    55  		arches = []string{version.Current.Arch}
    56  	}
    57  	var versions []version.Binary
    58  	for _, arch := range arches {
    59  		v := version.Current
    60  		v.Arch = arch
    61  		for _, series := range bootstrap.ToolsLtsSeries {
    62  			v.Series = series
    63  			versions = append(versions, v)
    64  		}
    65  	}
    66  	_, err := UploadFakeToolsVersions(stor, versions...)
    67  	c.Assert(err, gc.IsNil)
    68  }
    69  
    70  // RemoveFakeToolsMetadata deletes the fake simplestreams tools metadata from the supplied storage.
    71  func RemoveFakeToolsMetadata(c *gc.C, stor storage.Storage) {
    72  	files := []string{simplestreams.UnsignedIndex, envtools.ProductMetadataPath}
    73  	for _, file := range files {
    74  		toolspath := path.Join("tools", file)
    75  		err := stor.Remove(toolspath)
    76  		c.Check(err, gc.IsNil)
    77  	}
    78  }
    79  
    80  // CheckTools ensures the obtained and expected tools are equal, allowing for the fact that
    81  // the obtained tools may not have size and checksum set.
    82  func CheckTools(c *gc.C, obtained, expected *coretools.Tools) {
    83  	c.Assert(obtained.Version, gc.Equals, expected.Version)
    84  	// TODO(dimitern) 2013-10-02 bug #1234217
    85  	// Are these used at at all? If not we should drop them.
    86  	if obtained.URL != "" {
    87  		c.Assert(obtained.URL, gc.Equals, expected.URL)
    88  	}
    89  	if obtained.Size > 0 {
    90  		c.Assert(obtained.Size, gc.Equals, expected.Size)
    91  		c.Assert(obtained.SHA256, gc.Equals, expected.SHA256)
    92  	}
    93  }
    94  
    95  // CheckUpgraderReadyError ensures the obtained and expected errors are equal.
    96  func CheckUpgraderReadyError(c *gc.C, obtained error, expected *upgrader.UpgradeReadyError) {
    97  	c.Assert(obtained, gc.FitsTypeOf, &upgrader.UpgradeReadyError{})
    98  	err := obtained.(*upgrader.UpgradeReadyError)
    99  	c.Assert(err.AgentName, gc.Equals, expected.AgentName)
   100  	c.Assert(err.DataDir, gc.Equals, expected.DataDir)
   101  	c.Assert(err.OldTools, gc.Equals, expected.OldTools)
   102  	c.Assert(err.NewTools, gc.Equals, expected.NewTools)
   103  }
   104  
   105  // PrimeTools sets up the current version of the tools to vers and
   106  // makes sure that they're available in the dataDir.
   107  func PrimeTools(c *gc.C, stor storage.Storage, dataDir string, vers version.Binary) *coretools.Tools {
   108  	err := os.RemoveAll(filepath.Join(dataDir, "tools"))
   109  	c.Assert(err, gc.IsNil)
   110  	agentTools, err := uploadFakeToolsVersion(stor, vers)
   111  	c.Assert(err, gc.IsNil)
   112  	resp, err := utils.GetValidatingHTTPClient().Get(agentTools.URL)
   113  	c.Assert(err, gc.IsNil)
   114  	defer resp.Body.Close()
   115  	err = agenttools.UnpackTools(dataDir, agentTools, resp.Body)
   116  	c.Assert(err, gc.IsNil)
   117  	return agentTools
   118  }
   119  
   120  func uploadFakeToolsVersion(stor storage.Storage, vers version.Binary) (*coretools.Tools, error) {
   121  	logger.Infof("uploading FAKE tools %s", vers)
   122  	tgz, checksum := coretesting.TarGz(
   123  		coretesting.NewTarFile("jujud", 0777, "jujud contents "+vers.String()))
   124  	size := int64(len(tgz))
   125  	name := envtools.StorageName(vers)
   126  	if err := stor.Put(name, bytes.NewReader(tgz), size); err != nil {
   127  		return nil, err
   128  	}
   129  	url, err := stor.URL(name)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return &coretools.Tools{URL: url, Version: vers, Size: size, SHA256: checksum}, nil
   134  }
   135  
   136  // UploadFakeToolsVersions puts fake tools in the supplied storage for the supplied versions.
   137  func UploadFakeToolsVersions(stor storage.Storage, versions ...version.Binary) ([]*coretools.Tools, error) {
   138  	// Leave existing tools alone.
   139  	existingTools := make(map[version.Binary]*coretools.Tools)
   140  	existing, _ := envtools.ReadList(stor, 1, -1)
   141  	for _, tools := range existing {
   142  		existingTools[tools.Version] = tools
   143  	}
   144  	var agentTools coretools.List = make(coretools.List, len(versions))
   145  	for i, version := range versions {
   146  		if tools, ok := existingTools[version]; ok {
   147  			agentTools[i] = tools
   148  		} else {
   149  			t, err := uploadFakeToolsVersion(stor, version)
   150  			if err != nil {
   151  				return nil, err
   152  			}
   153  			agentTools[i] = t
   154  		}
   155  	}
   156  	if err := envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors); err != nil {
   157  		return nil, err
   158  	}
   159  	return agentTools, nil
   160  }
   161  
   162  // AssertUploadFakeToolsVersions puts fake tools in the supplied storage for the supplied versions.
   163  func AssertUploadFakeToolsVersions(c *gc.C, stor storage.Storage, versions ...version.Binary) []*coretools.Tools {
   164  	agentTools, err := UploadFakeToolsVersions(stor, versions...)
   165  	c.Assert(err, gc.IsNil)
   166  	err = envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors)
   167  	c.Assert(err, gc.IsNil)
   168  	return agentTools
   169  }
   170  
   171  // MustUploadFakeToolsVersions acts as UploadFakeToolsVersions, but panics on failure.
   172  func MustUploadFakeToolsVersions(stor storage.Storage, versions ...version.Binary) []*coretools.Tools {
   173  	var agentTools coretools.List = make(coretools.List, len(versions))
   174  	for i, version := range versions {
   175  		t, err := uploadFakeToolsVersion(stor, version)
   176  		if err != nil {
   177  			panic(err)
   178  		}
   179  		agentTools[i] = t
   180  	}
   181  	err := envtools.MergeAndWriteMetadata(stor, agentTools, envtools.DoNotWriteMirrors)
   182  	if err != nil {
   183  		panic(err)
   184  	}
   185  	return agentTools
   186  }
   187  
   188  func uploadFakeTools(stor storage.Storage) error {
   189  	toolsSeries := set.NewStrings(bootstrap.ToolsLtsSeries...)
   190  	toolsSeries.Add(version.Current.Series)
   191  	var versions []version.Binary
   192  	for _, series := range toolsSeries.Values() {
   193  		vers := version.Current
   194  		vers.Series = series
   195  		versions = append(versions, vers)
   196  	}
   197  	if _, err := UploadFakeToolsVersions(stor, versions...); err != nil {
   198  		return err
   199  	}
   200  	return nil
   201  }
   202  
   203  // UploadFakeTools puts fake tools into the supplied storage with a binary
   204  // version matching version.Current; if version.Current's series is different
   205  // to coretesting.FakeDefaultSeries, matching fake tools will be uploaded for that
   206  // series.  This is useful for tests that are kinda casual about specifying
   207  // their environment.
   208  func UploadFakeTools(c *gc.C, stor storage.Storage) {
   209  	c.Assert(uploadFakeTools(stor), gc.IsNil)
   210  }
   211  
   212  // MustUploadFakeTools acts as UploadFakeTools, but panics on failure.
   213  func MustUploadFakeTools(stor storage.Storage) {
   214  	if err := uploadFakeTools(stor); err != nil {
   215  		panic(err)
   216  	}
   217  }
   218  
   219  // RemoveFakeTools deletes the fake tools from the supplied storage.
   220  func RemoveFakeTools(c *gc.C, stor storage.Storage) {
   221  	c.Logf("removing fake tools")
   222  	toolsVersion := version.Current
   223  	name := envtools.StorageName(toolsVersion)
   224  	err := stor.Remove(name)
   225  	c.Check(err, gc.IsNil)
   226  	defaultSeries := coretesting.FakeDefaultSeries
   227  	if version.Current.Series != defaultSeries {
   228  		toolsVersion.Series = defaultSeries
   229  		name := envtools.StorageName(toolsVersion)
   230  		err := stor.Remove(name)
   231  		c.Check(err, gc.IsNil)
   232  	}
   233  	RemoveFakeToolsMetadata(c, stor)
   234  }
   235  
   236  // RemoveTools deletes all tools from the supplied storage.
   237  func RemoveTools(c *gc.C, stor storage.Storage) {
   238  	names, err := storage.List(stor, "tools/releases/juju-")
   239  	c.Assert(err, gc.IsNil)
   240  	c.Logf("removing files: %v", names)
   241  	for _, name := range names {
   242  		err = stor.Remove(name)
   243  		c.Check(err, gc.IsNil)
   244  	}
   245  	RemoveFakeToolsMetadata(c, stor)
   246  }
   247  
   248  // RemoveAllTools deletes all tools from the supplied environment.
   249  func RemoveAllTools(c *gc.C, env environs.Environ) {
   250  	c.Logf("clearing private storage")
   251  	RemoveTools(c, env.Storage())
   252  }
   253  
   254  var (
   255  	V100    = version.MustParse("1.0.0")
   256  	V100p64 = version.MustParseBinary("1.0.0-precise-amd64")
   257  	V100p32 = version.MustParseBinary("1.0.0-precise-i386")
   258  	V100p   = []version.Binary{V100p64, V100p32}
   259  
   260  	V100q64 = version.MustParseBinary("1.0.0-quantal-amd64")
   261  	V100q32 = version.MustParseBinary("1.0.0-quantal-i386")
   262  	V100q   = []version.Binary{V100q64, V100q32}
   263  	V100all = append(V100p, V100q...)
   264  
   265  	V1001    = version.MustParse("1.0.0.1")
   266  	V1001p64 = version.MustParseBinary("1.0.0.1-precise-amd64")
   267  	V100Xall = append(V100all, V1001p64)
   268  
   269  	V110    = version.MustParse("1.1.0")
   270  	V110p64 = version.MustParseBinary("1.1.0-precise-amd64")
   271  	V110p32 = version.MustParseBinary("1.1.0-precise-i386")
   272  	V110p   = []version.Binary{V110p64, V110p32}
   273  
   274  	V110q64 = version.MustParseBinary("1.1.0-quantal-amd64")
   275  	V110q32 = version.MustParseBinary("1.1.0-quantal-i386")
   276  	V110q   = []version.Binary{V110q64, V110q32}
   277  	V110all = append(V110p, V110q...)
   278  
   279  	V1101p64 = version.MustParseBinary("1.1.0.1-precise-amd64")
   280  	V110Xall = append(V110all, V1101p64)
   281  
   282  	V120    = version.MustParse("1.2.0")
   283  	V120p64 = version.MustParseBinary("1.2.0-precise-amd64")
   284  	V120p32 = version.MustParseBinary("1.2.0-precise-i386")
   285  	V120p   = []version.Binary{V120p64, V120p32}
   286  
   287  	V120q64 = version.MustParseBinary("1.2.0-quantal-amd64")
   288  	V120q32 = version.MustParseBinary("1.2.0-quantal-i386")
   289  	V120q   = []version.Binary{V120q64, V120q32}
   290  	V120all = append(V120p, V120q...)
   291  	V1all   = append(V100Xall, append(V110all, V120all...)...)
   292  
   293  	V220    = version.MustParse("2.2.0")
   294  	V220p32 = version.MustParseBinary("2.2.0-precise-i386")
   295  	V220p64 = version.MustParseBinary("2.2.0-precise-amd64")
   296  	V220q32 = version.MustParseBinary("2.2.0-quantal-i386")
   297  	V220q64 = version.MustParseBinary("2.2.0-quantal-amd64")
   298  	V220all = []version.Binary{V220p64, V220p32, V220q64, V220q32}
   299  	VAll    = append(V1all, V220all...)
   300  
   301  	V31d0qppc64  = version.MustParseBinary("3.1-dev0-quantal-ppc64")
   302  	V31d01qppc64 = version.MustParseBinary("3.1-dev0.1-quantal-ppc64")
   303  )
   304  
   305  type BootstrapToolsTest struct {
   306  	Info          string
   307  	Available     []version.Binary
   308  	CliVersion    version.Binary
   309  	DefaultSeries string
   310  	AgentVersion  version.Number
   311  	Development   bool
   312  	Arch          string
   313  	Expect        []version.Binary
   314  	Err           string
   315  }
   316  
   317  var noToolsMessage = "Juju cannot bootstrap because no tools are available for your environment.*"
   318  
   319  var BootstrapToolsTests = []BootstrapToolsTest{
   320  	{
   321  		Info:          "no tools at all",
   322  		CliVersion:    V100p64,
   323  		DefaultSeries: "precise",
   324  		Err:           noToolsMessage,
   325  	}, {
   326  		Info:          "released cli: use newest compatible release version",
   327  		Available:     VAll,
   328  		CliVersion:    V100p64,
   329  		DefaultSeries: "precise",
   330  		Expect:        V100p,
   331  	}, {
   332  		Info:          "released cli: cli Arch ignored",
   333  		Available:     VAll,
   334  		CliVersion:    V100p32,
   335  		DefaultSeries: "precise",
   336  		Expect:        V100p,
   337  	}, {
   338  		Info:          "released cli: cli series ignored",
   339  		Available:     VAll,
   340  		CliVersion:    V100q64,
   341  		DefaultSeries: "precise",
   342  		Expect:        V100p,
   343  	}, {
   344  		Info:          "released cli: series taken from default-series",
   345  		Available:     V120all,
   346  		CliVersion:    V120p64,
   347  		DefaultSeries: "quantal",
   348  		Expect:        V120q,
   349  	}, {
   350  		Info:          "released cli: ignore close dev match",
   351  		Available:     V100Xall,
   352  		CliVersion:    V100p64,
   353  		DefaultSeries: "precise",
   354  		Expect:        V100p,
   355  	}, {
   356  		Info:          "released cli: filter by arch constraints",
   357  		Available:     V120all,
   358  		CliVersion:    V120p64,
   359  		DefaultSeries: "precise",
   360  		Arch:          "i386",
   361  		Expect:        []version.Binary{V120p32},
   362  	}, {
   363  		Info:          "released cli: specific released version",
   364  		Available:     VAll,
   365  		CliVersion:    V100p64,
   366  		AgentVersion:  V100,
   367  		DefaultSeries: "precise",
   368  		Expect:        V100p,
   369  	}, {
   370  		Info:          "released cli: specific dev version",
   371  		Available:     VAll,
   372  		CliVersion:    V110p64,
   373  		AgentVersion:  V110,
   374  		DefaultSeries: "precise",
   375  		Expect:        V110p,
   376  	}, {
   377  		Info:          "released cli: major upgrades bad",
   378  		Available:     V220all,
   379  		CliVersion:    V100p64,
   380  		DefaultSeries: "precise",
   381  		Err:           noToolsMessage,
   382  	}, {
   383  		Info:          "released cli: minor upgrades bad",
   384  		Available:     V120all,
   385  		CliVersion:    V100p64,
   386  		DefaultSeries: "precise",
   387  		Err:           noToolsMessage,
   388  	}, {
   389  		Info:          "released cli: major downgrades bad",
   390  		Available:     V100Xall,
   391  		CliVersion:    V220p64,
   392  		DefaultSeries: "precise",
   393  		Err:           noToolsMessage,
   394  	}, {
   395  		Info:          "released cli: minor downgrades bad",
   396  		Available:     V100Xall,
   397  		CliVersion:    V120p64,
   398  		DefaultSeries: "quantal",
   399  		Err:           noToolsMessage,
   400  	}, {
   401  		Info:          "released cli: no matching series",
   402  		Available:     VAll,
   403  		CliVersion:    V100p64,
   404  		DefaultSeries: "raring",
   405  		Err:           noToolsMessage,
   406  	}, {
   407  		Info:          "released cli: no matching arches",
   408  		Available:     VAll,
   409  		CliVersion:    V100p64,
   410  		DefaultSeries: "precise",
   411  		Arch:          "armhf",
   412  		Err:           noToolsMessage,
   413  	}, {
   414  		Info:          "released cli: specific bad major 1",
   415  		Available:     VAll,
   416  		CliVersion:    V220p64,
   417  		AgentVersion:  V120,
   418  		DefaultSeries: "precise",
   419  		Err:           noToolsMessage,
   420  	}, {
   421  		Info:          "released cli: specific bad major 2",
   422  		Available:     VAll,
   423  		CliVersion:    V120p64,
   424  		AgentVersion:  V220,
   425  		DefaultSeries: "precise",
   426  		Err:           noToolsMessage,
   427  	}, {
   428  		Info:          "released cli: ignore dev tools 1",
   429  		Available:     V110all,
   430  		CliVersion:    V100p64,
   431  		DefaultSeries: "precise",
   432  		Err:           noToolsMessage,
   433  	}, {
   434  		Info:          "released cli: ignore dev tools 2",
   435  		Available:     V110all,
   436  		CliVersion:    V120p64,
   437  		DefaultSeries: "precise",
   438  		Err:           noToolsMessage,
   439  	}, {
   440  		Info:          "released cli: ignore dev tools 3",
   441  		Available:     []version.Binary{V1001p64},
   442  		CliVersion:    V100p64,
   443  		DefaultSeries: "precise",
   444  		Err:           noToolsMessage,
   445  	}, {
   446  		Info:          "released cli with dev setting respects agent-version",
   447  		Available:     VAll,
   448  		CliVersion:    V100q32,
   449  		AgentVersion:  V1001,
   450  		DefaultSeries: "precise",
   451  		Development:   true,
   452  		Expect:        []version.Binary{V1001p64},
   453  	}, {
   454  		Info:          "dev cli respects agent-version",
   455  		Available:     VAll,
   456  		CliVersion:    V100q32,
   457  		AgentVersion:  V1001,
   458  		DefaultSeries: "precise",
   459  		Expect:        []version.Binary{V1001p64},
   460  	}, {
   461  		Info:          "released cli with dev setting respects agent-version",
   462  		Available:     V1all,
   463  		CliVersion:    V100q32,
   464  		AgentVersion:  V1001,
   465  		DefaultSeries: "precise",
   466  		Development:   true,
   467  		Expect:        []version.Binary{V1001p64},
   468  	}, {
   469  		Info:          "dev cli respects agent-version",
   470  		Available:     V1all,
   471  		CliVersion:    V100q32,
   472  		AgentVersion:  V1001,
   473  		DefaultSeries: "precise",
   474  		Expect:        []version.Binary{V1001p64},
   475  	}}
   476  
   477  func SetSSLHostnameVerification(c *gc.C, st *state.State, SSLHostnameVerification bool) {
   478  	err := st.UpdateEnvironConfig(map[string]interface{}{"ssl-hostname-verification": SSLHostnameVerification}, nil, nil)
   479  	c.Assert(err, gc.IsNil)
   480  }