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