launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/agent/tools/tools_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package tools_test
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"sort"
    13  
    14  	gc "launchpad.net/gocheck"
    15  
    16  	agenttools "launchpad.net/juju-core/agent/tools"
    17  	"launchpad.net/juju-core/testing"
    18  	"launchpad.net/juju-core/testing/testbase"
    19  	coretest "launchpad.net/juju-core/tools"
    20  	"launchpad.net/juju-core/version"
    21  )
    22  
    23  type ToolsSuite struct {
    24  	testbase.LoggingSuite
    25  	dataDir string
    26  }
    27  
    28  var _ = gc.Suite(&ToolsSuite{})
    29  
    30  func (t *ToolsSuite) SetUpTest(c *gc.C) {
    31  	t.LoggingSuite.SetUpTest(c)
    32  	t.dataDir = c.MkDir()
    33  }
    34  
    35  func (t *ToolsSuite) TestPackageDependencies(c *gc.C) {
    36  	// This test is to ensure we don't bring in dependencies on state, environ
    37  	// or any of the other bigger packages that'll drag in yet more dependencies.
    38  	// Only imports that start with "launchpad.net/juju-core" are checked, and the
    39  	// resulting slice has that prefix removed to keep the output short.
    40  	c.Assert(testbase.FindJujuCoreImports(c, "launchpad.net/juju-core/agent/tools"),
    41  		gc.DeepEquals,
    42  		[]string{"tools", "utils/set", "version"})
    43  }
    44  
    45  const toolsFile = "downloaded-tools.txt"
    46  
    47  // gzyesses holds the result of running:
    48  // yes | head -17000 | gzip
    49  var gzyesses = []byte{
    50  	0x1f, 0x8b, 0x08, 0x00, 0x29, 0xae, 0x1a, 0x50,
    51  	0x00, 0x03, 0xed, 0xc2, 0x31, 0x0d, 0x00, 0x00,
    52  	0x00, 0x02, 0xa0, 0xdf, 0xc6, 0xb6, 0xb7, 0x87,
    53  	0x63, 0xd0, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
    54  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    55  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    56  	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    57  	0x00, 0x00, 0x00, 0x38, 0x31, 0x53, 0xad, 0x03,
    58  	0x8d, 0xd0, 0x84, 0x00, 0x00,
    59  }
    60  
    61  type badDataTest struct {
    62  	data     []byte
    63  	checksum string
    64  	err      string
    65  }
    66  
    67  func initBadDataTest(name string, mode os.FileMode, contents string, err string) badDataTest {
    68  	var result badDataTest
    69  	result.data, result.checksum = testing.TarGz(testing.NewTarFile(name, mode, contents))
    70  	result.err = err
    71  	return result
    72  }
    73  
    74  var unpackToolsBadDataTests = []badDataTest{
    75  	initBadDataTest("bar", os.ModeDir, "", "bad file type.*"),
    76  	initBadDataTest("../../etc/passwd", 0755, "", "bad name.*"),
    77  	initBadDataTest(`\ini.sys`, 0755, "", "bad name.*"),
    78  	badDataTest{[]byte("x"), "2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881", "unexpected EOF"},
    79  	badDataTest{gzyesses, "8d900c68a1a847aae4e95edcb29fcecd142c9b88ca4fe63209c216edbed546e1", "archive/tar: invalid tar header"},
    80  }
    81  
    82  func (t *ToolsSuite) TestUnpackToolsBadData(c *gc.C) {
    83  	for i, test := range unpackToolsBadDataTests {
    84  		c.Logf("test %d", i)
    85  		testTools := &coretest.Tools{
    86  			URL:     "http://foo/bar",
    87  			Version: version.MustParseBinary("1.2.3-foo-bar"),
    88  			Size:    int64(len(test.data)),
    89  			SHA256:  test.checksum,
    90  		}
    91  		err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(test.data))
    92  		c.Assert(err, gc.ErrorMatches, test.err)
    93  		assertDirNames(c, t.toolsDir(), []string{})
    94  	}
    95  }
    96  
    97  func (t *ToolsSuite) TestUnpackToolsBadChecksum(c *gc.C) {
    98  	data, _ := testing.TarGz(testing.NewTarFile("tools", 0755, "some data"))
    99  	testTools := &coretest.Tools{
   100  		URL:     "http://foo/bar",
   101  		Version: version.MustParseBinary("1.2.3-foo-bar"),
   102  		Size:    int64(len(data)),
   103  		SHA256:  "1234",
   104  	}
   105  	err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data))
   106  	c.Assert(err, gc.ErrorMatches, "tarball sha256 mismatch, expected 1234, got .*")
   107  	_, err = os.Stat(t.toolsDir())
   108  	c.Assert(err, gc.FitsTypeOf, &os.PathError{})
   109  }
   110  
   111  func (t *ToolsSuite) toolsDir() string {
   112  	return filepath.Join(t.dataDir, "tools")
   113  }
   114  
   115  func (t *ToolsSuite) TestUnpackToolsContents(c *gc.C) {
   116  	files := []*testing.TarFile{
   117  		testing.NewTarFile("bar", 0755, "bar contents"),
   118  		testing.NewTarFile("foo", 0755, "foo contents"),
   119  	}
   120  	data, checksum := testing.TarGz(files...)
   121  	testTools := &coretest.Tools{
   122  		URL:     "http://foo/bar",
   123  		Version: version.MustParseBinary("1.2.3-foo-bar"),
   124  		Size:    int64(len(data)),
   125  		SHA256:  checksum,
   126  	}
   127  
   128  	err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data))
   129  	c.Assert(err, gc.IsNil)
   130  	assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"})
   131  	t.assertToolsContents(c, testTools, files)
   132  
   133  	// Try to unpack the same version of tools again - it should succeed,
   134  	// leaving the original version around.
   135  	files2 := []*testing.TarFile{
   136  		testing.NewTarFile("bar", 0755, "bar2 contents"),
   137  		testing.NewTarFile("x", 0755, "x contents"),
   138  	}
   139  	data2, checksum2 := testing.TarGz(files2...)
   140  	tools2 := &coretest.Tools{
   141  		URL:     "http://arble",
   142  		Version: version.MustParseBinary("1.2.3-foo-bar"),
   143  		Size:    int64(len(data2)),
   144  		SHA256:  checksum2,
   145  	}
   146  	err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2))
   147  	c.Assert(err, gc.IsNil)
   148  	assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"})
   149  	t.assertToolsContents(c, testTools, files)
   150  }
   151  
   152  func (t *ToolsSuite) TestReadToolsErrors(c *gc.C) {
   153  	vers := version.MustParseBinary("1.2.3-precise-amd64")
   154  	testTools, err := agenttools.ReadTools(t.dataDir, vers)
   155  	c.Assert(testTools, gc.IsNil)
   156  	c.Assert(err, gc.ErrorMatches, "cannot read tools metadata in tools directory: .*")
   157  
   158  	dir := agenttools.SharedToolsDir(t.dataDir, vers)
   159  	err = os.MkdirAll(dir, 0755)
   160  	c.Assert(err, gc.IsNil)
   161  
   162  	err = ioutil.WriteFile(filepath.Join(dir, toolsFile), []byte(" \t\n"), 0644)
   163  	c.Assert(err, gc.IsNil)
   164  
   165  	testTools, err = agenttools.ReadTools(t.dataDir, vers)
   166  	c.Assert(testTools, gc.IsNil)
   167  	c.Assert(err, gc.ErrorMatches, "invalid tools metadata in tools directory .*")
   168  }
   169  
   170  func (t *ToolsSuite) TestChangeAgentTools(c *gc.C) {
   171  	files := []*testing.TarFile{
   172  		testing.NewTarFile("jujuc", 0755, "juju executable"),
   173  		testing.NewTarFile("jujud", 0755, "jujuc executable"),
   174  	}
   175  	data, checksum := testing.TarGz(files...)
   176  	testTools := &coretest.Tools{
   177  		URL:     "http://foo/bar1",
   178  		Version: version.MustParseBinary("1.2.3-foo-bar"),
   179  		Size:    int64(len(data)),
   180  		SHA256:  checksum,
   181  	}
   182  	err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data))
   183  	c.Assert(err, gc.IsNil)
   184  
   185  	gotTools, err := agenttools.ChangeAgentTools(t.dataDir, "testagent", testTools.Version)
   186  	c.Assert(err, gc.IsNil)
   187  	c.Assert(*gotTools, gc.Equals, *testTools)
   188  
   189  	assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "testagent"})
   190  	assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"jujuc", "jujud", toolsFile})
   191  
   192  	// Upgrade again to check that the link replacement logic works ok.
   193  	files2 := []*testing.TarFile{
   194  		testing.NewTarFile("foo", 0755, "foo content"),
   195  		testing.NewTarFile("bar", 0755, "bar content"),
   196  	}
   197  	data2, checksum2 := testing.TarGz(files2...)
   198  	tools2 := &coretest.Tools{
   199  		URL:     "http://foo/bar2",
   200  		Version: version.MustParseBinary("1.2.4-foo-bar"),
   201  		Size:    int64(len(data2)),
   202  		SHA256:  checksum2,
   203  	}
   204  	err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2))
   205  	c.Assert(err, gc.IsNil)
   206  
   207  	gotTools, err = agenttools.ChangeAgentTools(t.dataDir, "testagent", tools2.Version)
   208  	c.Assert(err, gc.IsNil)
   209  	c.Assert(*gotTools, gc.Equals, *tools2)
   210  
   211  	assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "1.2.4-foo-bar", "testagent"})
   212  	assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"foo", "bar", toolsFile})
   213  }
   214  
   215  func (t *ToolsSuite) TestSharedToolsDir(c *gc.C) {
   216  	dir := agenttools.SharedToolsDir("/var/lib/juju", version.MustParseBinary("1.2.3-precise-amd64"))
   217  	c.Assert(dir, gc.Equals, "/var/lib/juju/tools/1.2.3-precise-amd64")
   218  }
   219  
   220  // assertToolsContents asserts that the directory for the tools
   221  // has the given contents.
   222  func (t *ToolsSuite) assertToolsContents(c *gc.C, testTools *coretest.Tools, files []*testing.TarFile) {
   223  	var wantNames []string
   224  	for _, f := range files {
   225  		wantNames = append(wantNames, f.Header.Name)
   226  	}
   227  	wantNames = append(wantNames, toolsFile)
   228  	dir := agenttools.SharedToolsDir(t.dataDir, testTools.Version)
   229  	assertDirNames(c, dir, wantNames)
   230  	expectedURLFileContents, err := json.Marshal(testTools)
   231  	c.Assert(err, gc.IsNil)
   232  	assertFileContents(c, dir, toolsFile, string(expectedURLFileContents), 0200)
   233  	for _, f := range files {
   234  		assertFileContents(c, dir, f.Header.Name, f.Contents, 0400)
   235  	}
   236  	gotTools, err := agenttools.ReadTools(t.dataDir, testTools.Version)
   237  	c.Assert(err, gc.IsNil)
   238  	c.Assert(*gotTools, gc.Equals, *testTools)
   239  }
   240  
   241  // assertFileContents asserts that the given file in the
   242  // given directory has the given contents.
   243  func assertFileContents(c *gc.C, dir, file, contents string, mode os.FileMode) {
   244  	file = filepath.Join(dir, file)
   245  	info, err := os.Stat(file)
   246  	c.Assert(err, gc.IsNil)
   247  	c.Assert(info.Mode()&(os.ModeType|mode), gc.Equals, mode)
   248  	data, err := ioutil.ReadFile(file)
   249  	c.Assert(err, gc.IsNil)
   250  	c.Assert(string(data), gc.Equals, contents)
   251  }
   252  
   253  // assertDirNames asserts that the given directory
   254  // holds the given file or directory names.
   255  func assertDirNames(c *gc.C, dir string, names []string) {
   256  	f, err := os.Open(dir)
   257  	c.Assert(err, gc.IsNil)
   258  	defer f.Close()
   259  	dnames, err := f.Readdirnames(0)
   260  	c.Assert(err, gc.IsNil)
   261  	sort.Strings(dnames)
   262  	sort.Strings(names)
   263  	c.Assert(dnames, gc.DeepEquals, names)
   264  }