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 }