~juju-qa/ubuntu/yakkety/juju/2.0-rc3-again

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/charm/bundle_test.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-04-24 22:34:47 UTC
  • Revision ID: package-import@ubuntu.com-20130424223447-f0qdji7ubnyo0s71
Tags: upstream-1.10.0.1
ImportĀ upstreamĀ versionĀ 1.10.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package charm_test
 
2
 
 
3
import (
 
4
        "archive/zip"
 
5
        "bytes"
 
6
        "fmt"
 
7
        "io/ioutil"
 
8
        . "launchpad.net/gocheck"
 
9
        "launchpad.net/goyaml"
 
10
        "launchpad.net/juju-core/charm"
 
11
        "launchpad.net/juju-core/testing"
 
12
        "os"
 
13
        "os/exec"
 
14
        "path/filepath"
 
15
        "strconv"
 
16
        "syscall"
 
17
)
 
18
 
 
19
type BundleSuite struct {
 
20
        repo       *testing.Repo
 
21
        bundlePath string
 
22
}
 
23
 
 
24
var _ = Suite(&BundleSuite{})
 
25
 
 
26
func (s *BundleSuite) SetUpSuite(c *C) {
 
27
        s.bundlePath = testing.Charms.BundlePath(c.MkDir(), "dummy")
 
28
}
 
29
 
 
30
func (s *BundleSuite) TestReadBundle(c *C) {
 
31
        bundle, err := charm.ReadBundle(s.bundlePath)
 
32
        c.Assert(err, IsNil)
 
33
        checkDummy(c, bundle, s.bundlePath)
 
34
}
 
35
 
 
36
func (s *BundleSuite) TestReadBundleWithoutConfig(c *C) {
 
37
        path := testing.Charms.BundlePath(c.MkDir(), "varnish")
 
38
        bundle, err := charm.ReadBundle(path)
 
39
        c.Assert(err, IsNil)
 
40
 
 
41
        // A lacking config.yaml file still causes a proper
 
42
        // Config value to be returned.
 
43
        c.Assert(bundle.Config().Options, HasLen, 0)
 
44
}
 
45
 
 
46
func (s *BundleSuite) TestReadBundleBytes(c *C) {
 
47
        data, err := ioutil.ReadFile(s.bundlePath)
 
48
        c.Assert(err, IsNil)
 
49
 
 
50
        bundle, err := charm.ReadBundleBytes(data)
 
51
        c.Assert(err, IsNil)
 
52
        checkDummy(c, bundle, "")
 
53
}
 
54
 
 
55
func (s *BundleSuite) TestExpandTo(c *C) {
 
56
        bundle, err := charm.ReadBundle(s.bundlePath)
 
57
        c.Assert(err, IsNil)
 
58
 
 
59
        path := filepath.Join(c.MkDir(), "charm")
 
60
        err = bundle.ExpandTo(path)
 
61
        c.Assert(err, IsNil)
 
62
 
 
63
        dir, err := charm.ReadDir(path)
 
64
        c.Assert(err, IsNil)
 
65
        checkDummy(c, dir, path)
 
66
}
 
67
 
 
68
func (s *BundleSuite) prepareBundle(c *C, charmDir *charm.Dir, bundlePath string) {
 
69
        file, err := os.Create(bundlePath)
 
70
        c.Assert(err, IsNil)
 
71
        defer file.Close()
 
72
        zipw := zip.NewWriter(file)
 
73
        defer zipw.Close()
 
74
 
 
75
        h := &zip.FileHeader{Name: "revision"}
 
76
        h.SetMode(syscall.S_IFREG | 0644)
 
77
        w, err := zipw.CreateHeader(h)
 
78
        c.Assert(err, IsNil)
 
79
        _, err = w.Write([]byte(strconv.Itoa(charmDir.Revision())))
 
80
 
 
81
        h = &zip.FileHeader{Name: "metadata.yaml", Method: zip.Deflate}
 
82
        h.SetMode(0644)
 
83
        w, err = zipw.CreateHeader(h)
 
84
        c.Assert(err, IsNil)
 
85
        data, err := goyaml.Marshal(charmDir.Meta())
 
86
        c.Assert(err, IsNil)
 
87
        _, err = w.Write(data)
 
88
        c.Assert(err, IsNil)
 
89
 
 
90
        for name := range charmDir.Meta().Hooks() {
 
91
                hookName := filepath.Join("hooks", name)
 
92
                h = &zip.FileHeader{
 
93
                        Name:   hookName,
 
94
                        Method: zip.Deflate,
 
95
                }
 
96
                // Force it non-executable
 
97
                h.SetMode(0644)
 
98
                w, err := zipw.CreateHeader(h)
 
99
                c.Assert(err, IsNil)
 
100
                _, err = w.Write([]byte("not important"))
 
101
                c.Assert(err, IsNil)
 
102
        }
 
103
}
 
104
 
 
105
func (s *BundleSuite) TestExpandToSetsHooksExecutable(c *C) {
 
106
        charmDir := testing.Charms.ClonedDir(c.MkDir(), "all-hooks")
 
107
        // Bundle manually, so we can check ExpandTo(), unaffected
 
108
        // by BundleTo()'s behavior
 
109
        bundlePath := filepath.Join(c.MkDir(), "bundle.charm")
 
110
        s.prepareBundle(c, charmDir, bundlePath)
 
111
        bundle, err := charm.ReadBundle(bundlePath)
 
112
        c.Assert(err, IsNil)
 
113
 
 
114
        path := filepath.Join(c.MkDir(), "charm")
 
115
        err = bundle.ExpandTo(path)
 
116
        c.Assert(err, IsNil)
 
117
 
 
118
        _, err = charm.ReadDir(path)
 
119
        c.Assert(err, IsNil)
 
120
 
 
121
        for name := range bundle.Meta().Hooks() {
 
122
                hookName := string(name)
 
123
                info, err := os.Stat(filepath.Join(path, "hooks", hookName))
 
124
                c.Assert(err, IsNil)
 
125
                perm := info.Mode() & 0777
 
126
                c.Assert(perm&0100 != 0, Equals, true, Commentf("hook %q is not executable", hookName))
 
127
        }
 
128
}
 
129
 
 
130
func (s *BundleSuite) TestBundleFileModes(c *C) {
 
131
        // Apply subtler mode differences than can be expressed in Bazaar.
 
132
        srcPath := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
 
133
        modes := []struct {
 
134
                path string
 
135
                mode os.FileMode
 
136
        }{
 
137
                {"hooks/install", 0751},
 
138
                {"empty", 0750},
 
139
                {"src/hello.c", 0614},
 
140
        }
 
141
        for _, m := range modes {
 
142
                err := os.Chmod(filepath.Join(srcPath, m.path), m.mode)
 
143
                c.Assert(err, IsNil)
 
144
        }
 
145
        var haveSymlinks = true
 
146
        if err := os.Symlink("../target", filepath.Join(srcPath, "hooks/symlink")); err != nil {
 
147
                haveSymlinks = false
 
148
        }
 
149
 
 
150
        // Bundle and extract the charm to a new directory.
 
151
        dir, err := charm.ReadDir(srcPath)
 
152
        c.Assert(err, IsNil)
 
153
        buf := new(bytes.Buffer)
 
154
        err = dir.BundleTo(buf)
 
155
        c.Assert(err, IsNil)
 
156
        bundle, err := charm.ReadBundleBytes(buf.Bytes())
 
157
        c.Assert(err, IsNil)
 
158
        path := c.MkDir()
 
159
        err = bundle.ExpandTo(path)
 
160
        c.Assert(err, IsNil)
 
161
 
 
162
        // Check sensible file modes once round-tripped.
 
163
        info, err := os.Stat(filepath.Join(path, "src", "hello.c"))
 
164
        c.Assert(err, IsNil)
 
165
        c.Assert(info.Mode()&0777, Equals, os.FileMode(0644))
 
166
        c.Assert(info.Mode()&os.ModeType, Equals, os.FileMode(0))
 
167
 
 
168
        info, err = os.Stat(filepath.Join(path, "hooks", "install"))
 
169
        c.Assert(err, IsNil)
 
170
        c.Assert(info.Mode()&0777, Equals, os.FileMode(0755))
 
171
        c.Assert(info.Mode()&os.ModeType, Equals, os.FileMode(0))
 
172
 
 
173
        info, err = os.Stat(filepath.Join(path, "empty"))
 
174
        c.Assert(err, IsNil)
 
175
        c.Assert(info.Mode()&0777, Equals, os.FileMode(0755))
 
176
 
 
177
        if haveSymlinks {
 
178
                target, err := os.Readlink(filepath.Join(path, "hooks", "symlink"))
 
179
                c.Assert(err, IsNil)
 
180
                c.Assert(target, Equals, "../target")
 
181
        }
 
182
}
 
183
 
 
184
func (s *BundleSuite) TestBundleRevisionFile(c *C) {
 
185
        charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
 
186
        revPath := filepath.Join(charmDir, "revision")
 
187
 
 
188
        // Missing revision file
 
189
        err := os.Remove(revPath)
 
190
        c.Assert(err, IsNil)
 
191
 
 
192
        bundle, err := charm.ReadBundle(extBundleDir(c, charmDir))
 
193
        c.Assert(err, IsNil)
 
194
        c.Assert(bundle.Revision(), Equals, 0)
 
195
 
 
196
        // Missing revision file with old revision in metadata
 
197
        file, err := os.OpenFile(filepath.Join(charmDir, "metadata.yaml"), os.O_WRONLY|os.O_APPEND, 0)
 
198
        c.Assert(err, IsNil)
 
199
        _, err = file.Write([]byte("\nrevision: 1234\n"))
 
200
        c.Assert(err, IsNil)
 
201
 
 
202
        bundle, err = charm.ReadBundle(extBundleDir(c, charmDir))
 
203
        c.Assert(err, IsNil)
 
204
        c.Assert(bundle.Revision(), Equals, 1234)
 
205
 
 
206
        // Revision file with bad content
 
207
        err = ioutil.WriteFile(revPath, []byte("garbage"), 0666)
 
208
        c.Assert(err, IsNil)
 
209
 
 
210
        bundle, err = charm.ReadBundle(extBundleDir(c, charmDir))
 
211
        c.Assert(err, ErrorMatches, "invalid revision file")
 
212
        c.Assert(bundle, IsNil)
 
213
}
 
214
 
 
215
func (s *BundleSuite) TestBundleSetRevision(c *C) {
 
216
        bundle, err := charm.ReadBundle(s.bundlePath)
 
217
        c.Assert(err, IsNil)
 
218
 
 
219
        c.Assert(bundle.Revision(), Equals, 1)
 
220
        bundle.SetRevision(42)
 
221
        c.Assert(bundle.Revision(), Equals, 42)
 
222
 
 
223
        path := filepath.Join(c.MkDir(), "charm")
 
224
        err = bundle.ExpandTo(path)
 
225
        c.Assert(err, IsNil)
 
226
 
 
227
        dir, err := charm.ReadDir(path)
 
228
        c.Assert(err, IsNil)
 
229
        c.Assert(dir.Revision(), Equals, 42)
 
230
}
 
231
 
 
232
func (s *BundleSuite) TestExpandToWithBadLink(c *C) {
 
233
        charmDir := testing.Charms.ClonedDirPath(c.MkDir(), "dummy")
 
234
        badLink := filepath.Join(charmDir, "hooks", "badlink")
 
235
 
 
236
        // Symlink targeting a path outside of the charm.
 
237
        err := os.Symlink("../../target", badLink)
 
238
        c.Assert(err, IsNil)
 
239
 
 
240
        bundle, err := charm.ReadBundle(extBundleDir(c, charmDir))
 
241
        c.Assert(err, IsNil)
 
242
 
 
243
        path := filepath.Join(c.MkDir(), "charm")
 
244
        err = bundle.ExpandTo(path)
 
245
        c.Assert(err, ErrorMatches, `symlink "hooks/badlink" links out of charm: "../../target"`)
 
246
 
 
247
        // Symlink targeting an absolute path.
 
248
        os.Remove(badLink)
 
249
        err = os.Symlink("/target", badLink)
 
250
        c.Assert(err, IsNil)
 
251
 
 
252
        bundle, err = charm.ReadBundle(extBundleDir(c, charmDir))
 
253
        c.Assert(err, IsNil)
 
254
 
 
255
        path = filepath.Join(c.MkDir(), "charm")
 
256
        err = bundle.ExpandTo(path)
 
257
        c.Assert(err, ErrorMatches, `symlink "hooks/badlink" is absolute: "/target"`)
 
258
}
 
259
 
 
260
func extBundleDir(c *C, dirpath string) (path string) {
 
261
        path = filepath.Join(c.MkDir(), "bundle.charm")
 
262
        cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("cd %s; zip --fifo --symlinks -r %s .", dirpath, path))
 
263
        output, err := cmd.CombinedOutput()
 
264
        c.Assert(err, IsNil, Commentf("Command output: %s", output))
 
265
        return path
 
266
}