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 }