github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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 "github.com/juju/juju/agent/tools" 17 "github.com/juju/juju/testing" 18 coretest "github.com/juju/juju/tools" 19 "github.com/juju/juju/version" 20 ) 21 22 type ToolsSuite struct { 23 testing.BaseSuite 24 dataDir string 25 } 26 27 var _ = gc.Suite(&ToolsSuite{}) 28 29 func (t *ToolsSuite) SetUpTest(c *gc.C) { 30 t.BaseSuite.SetUpTest(c) 31 t.dataDir = c.MkDir() 32 } 33 34 func (t *ToolsSuite) TestPackageDependencies(c *gc.C) { 35 // This test is to ensure we don't bring in dependencies on state, environ 36 // or any of the other bigger packages that'll drag in yet more dependencies. 37 // Only imports that start with "github.com/juju/juju" are checked, and the 38 // resulting slice has that prefix removed to keep the output short. 39 c.Assert(testing.FindJujuCoreImports(c, "github.com/juju/juju/agent/tools"), 40 gc.DeepEquals, 41 []string{"juju/arch", "tools", "version"}) 42 } 43 44 const toolsFile = "downloaded-tools.txt" 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", 0755, "", "bad name.*"), 76 initBadDataTest(`\ini.sys`, 0755, "", "bad name.*"), 77 badDataTest{[]byte("x"), "2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881", "unexpected EOF"}, 78 badDataTest{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-foo-bar"), 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", 0755, "some data")) 98 testTools := &coretest.Tools{ 99 URL: "http://foo/bar", 100 Version: version.MustParseBinary("1.2.3-foo-bar"), 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", 0755, "bar contents"), 117 testing.NewTarFile("foo", 0755, "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-foo-bar"), 123 Size: int64(len(data)), 124 SHA256: checksum, 125 } 126 127 err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data)) 128 c.Assert(err, gc.IsNil) 129 assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) 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", 0755, "bar2 contents"), 136 testing.NewTarFile("x", 0755, "x contents"), 137 } 138 data2, checksum2 := testing.TarGz(files2...) 139 tools2 := &coretest.Tools{ 140 URL: "http://arble", 141 Version: version.MustParseBinary("1.2.3-foo-bar"), 142 Size: int64(len(data2)), 143 SHA256: checksum2, 144 } 145 err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2)) 146 c.Assert(err, gc.IsNil) 147 assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar"}) 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, 0755) 159 c.Assert(err, gc.IsNil) 160 161 err = ioutil.WriteFile(filepath.Join(dir, toolsFile), []byte(" \t\n"), 0644) 162 c.Assert(err, gc.IsNil) 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) TestChangeAgentTools(c *gc.C) { 170 files := []*testing.TarFile{ 171 testing.NewTarFile("jujuc", 0755, "juju executable"), 172 testing.NewTarFile("jujud", 0755, "jujuc executable"), 173 } 174 data, checksum := testing.TarGz(files...) 175 testTools := &coretest.Tools{ 176 URL: "http://foo/bar1", 177 Version: version.MustParseBinary("1.2.3-foo-bar"), 178 Size: int64(len(data)), 179 SHA256: checksum, 180 } 181 err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data)) 182 c.Assert(err, gc.IsNil) 183 184 gotTools, err := agenttools.ChangeAgentTools(t.dataDir, "testagent", testTools.Version) 185 c.Assert(err, gc.IsNil) 186 c.Assert(*gotTools, gc.Equals, *testTools) 187 188 assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "testagent"}) 189 assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"jujuc", "jujud", toolsFile}) 190 191 // Upgrade again to check that the link replacement logic works ok. 192 files2 := []*testing.TarFile{ 193 testing.NewTarFile("foo", 0755, "foo content"), 194 testing.NewTarFile("bar", 0755, "bar content"), 195 } 196 data2, checksum2 := testing.TarGz(files2...) 197 tools2 := &coretest.Tools{ 198 URL: "http://foo/bar2", 199 Version: version.MustParseBinary("1.2.4-foo-bar"), 200 Size: int64(len(data2)), 201 SHA256: checksum2, 202 } 203 err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2)) 204 c.Assert(err, gc.IsNil) 205 206 gotTools, err = agenttools.ChangeAgentTools(t.dataDir, "testagent", tools2.Version) 207 c.Assert(err, gc.IsNil) 208 c.Assert(*gotTools, gc.Equals, *tools2) 209 210 assertDirNames(c, t.toolsDir(), []string{"1.2.3-foo-bar", "1.2.4-foo-bar", "testagent"}) 211 assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"foo", "bar", toolsFile}) 212 } 213 214 func (t *ToolsSuite) TestSharedToolsDir(c *gc.C) { 215 dir := agenttools.SharedToolsDir("/var/lib/juju", version.MustParseBinary("1.2.3-precise-amd64")) 216 c.Assert(dir, gc.Equals, "/var/lib/juju/tools/1.2.3-precise-amd64") 217 } 218 219 // assertToolsContents asserts that the directory for the tools 220 // has the given contents. 221 func (t *ToolsSuite) assertToolsContents(c *gc.C, testTools *coretest.Tools, files []*testing.TarFile) { 222 var wantNames []string 223 for _, f := range files { 224 wantNames = append(wantNames, f.Header.Name) 225 } 226 wantNames = append(wantNames, toolsFile) 227 dir := agenttools.SharedToolsDir(t.dataDir, testTools.Version) 228 assertDirNames(c, dir, wantNames) 229 expectedURLFileContents, err := json.Marshal(testTools) 230 c.Assert(err, gc.IsNil) 231 assertFileContents(c, dir, toolsFile, string(expectedURLFileContents), 0200) 232 for _, f := range files { 233 assertFileContents(c, dir, f.Header.Name, f.Contents, 0400) 234 } 235 gotTools, err := agenttools.ReadTools(t.dataDir, testTools.Version) 236 c.Assert(err, gc.IsNil) 237 c.Assert(*gotTools, gc.Equals, *testTools) 238 } 239 240 // assertFileContents asserts that the given file in the 241 // given directory has the given contents. 242 func assertFileContents(c *gc.C, dir, file, contents string, mode os.FileMode) { 243 file = filepath.Join(dir, file) 244 info, err := os.Stat(file) 245 c.Assert(err, gc.IsNil) 246 c.Assert(info.Mode()&(os.ModeType|mode), gc.Equals, mode) 247 data, err := ioutil.ReadFile(file) 248 c.Assert(err, gc.IsNil) 249 c.Assert(string(data), gc.Equals, contents) 250 } 251 252 // assertDirNames asserts that the given directory 253 // holds the given file or directory names. 254 func assertDirNames(c *gc.C, dir string, names []string) { 255 f, err := os.Open(dir) 256 c.Assert(err, gc.IsNil) 257 defer f.Close() 258 dnames, err := f.Readdirnames(0) 259 c.Assert(err, gc.IsNil) 260 sort.Strings(dnames) 261 sort.Strings(names) 262 c.Assert(dnames, gc.DeepEquals, names) 263 }