1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
14
"github.com/juju/errors"
15
jc "github.com/juju/testing/checkers"
16
"github.com/juju/version"
17
gc "gopkg.in/check.v1"
19
agenttools "github.com/juju/juju/agent/tools"
20
"github.com/juju/juju/testing"
21
coretest "github.com/juju/juju/tools"
24
type ToolsSuite struct {
29
var _ = gc.Suite(&ToolsSuite{})
31
func (t *ToolsSuite) SetUpTest(c *gc.C) {
32
t.BaseSuite.SetUpTest(c)
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"),
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,
60
type badDataTest struct {
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))
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"},
81
func (t *ToolsSuite) TestUnpackToolsBadData(c *gc.C) {
82
for i, test := range unpackToolsBadDataTests {
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,
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{})
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)),
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{})
110
func (t *ToolsSuite) toolsDir() string {
111
return filepath.Join(t.dataDir, "tools")
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"),
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)),
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)
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"),
138
data2, checksum2 := testing.TarGz(files2...)
139
tools2 := &coretest.Tools{
141
Version: version.MustParseBinary("1.2.3-quantal-amd64"),
142
Size: int64(len(data2)),
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)
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: .*")
157
dir := agenttools.SharedToolsDir(t.dataDir, vers)
158
err = os.MkdirAll(dir, agenttools.DirPerm)
159
c.Assert(err, jc.ErrorIsNil)
161
err = ioutil.WriteFile(filepath.Join(dir, agenttools.ToolsFile), []byte(" \t\n"), 0644)
162
c.Assert(err, jc.ErrorIsNil)
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 .*")
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)
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)
181
err = ioutil.WriteFile(filepath.Join(dir, agenttools.GUIArchiveFile), []byte(" \t\n"), 0644)
182
c.Assert(err, jc.ErrorIsNil)
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)
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)
194
expectGUI := coretest.GUIArchive{
195
Version: version.MustParse("2.0.42"),
196
URL: "file:///path/to/gui",
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)
205
gui, err := agenttools.ReadGUIArchive(t.dataDir)
206
c.Assert(err, jc.ErrorIsNil)
207
c.Assert(*gui, gc.Equals, expectGUI)
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"),
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)),
222
err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data))
223
c.Assert(err, jc.ErrorIsNil)
225
gotTools, err := agenttools.ChangeAgentTools(t.dataDir, "testagent", testTools.Version)
226
c.Assert(err, jc.ErrorIsNil)
227
c.Assert(*gotTools, gc.Equals, *testTools)
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})
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"),
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)),
244
err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2))
245
c.Assert(err, jc.ErrorIsNil)
247
gotTools, err = agenttools.ChangeAgentTools(t.dataDir, "testagent", tools2.Version)
248
c.Assert(err, jc.ErrorIsNil)
249
c.Assert(*gotTools, gc.Equals, *tools2)
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})
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")
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")
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)
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)
281
gotTools, err := agenttools.ReadTools(t.dataDir, testTools.Version)
282
c.Assert(err, jc.ErrorIsNil)
283
c.Assert(*gotTools, gc.Equals, *testTools)
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)
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)
304
dnames, err := f.Readdirnames(0)
305
c.Assert(err, jc.ErrorIsNil)
308
c.Assert(dnames, gc.DeepEquals, names)