~rogpeppe/pyjuju/testing2

« back to all changes in this revision

Viewing changes to charm/dir.go

  • Committer: Gustavo Niemeyer
  • Date: 2011-12-19 13:15:01 UTC
  • mfrom: (13.3.4 go-new-revisions)
  • Revision ID: gustavo@niemeyer.net-20111219131501-bx4w15x6ptz0c2zy
charm: must handle new revision schema

Revision handling was moved out of the metadata.yaml file.
The Go port most be updated to handle it.

This branch implements the new schema for charm revision handling
in an independent file, including backwards compatibility with
the previous schema, and also SetVersion methods that enable
bundling and expanding charms with custom revisions
(necessary for store).

R=rog
CC=
https://codereview.appspot.com/5489087

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
import (
4
4
        "archive/zip"
 
5
        "errors"
 
6
        "fmt"
5
7
        "io"
6
8
        "io/ioutil"
7
9
        "os"
8
10
        "path/filepath"
 
11
        "strconv"
 
12
        "syscall"
9
13
)
10
14
 
 
15
// The Dir type encapsulates access to data and operations
 
16
// on a charm directory.
 
17
type Dir struct {
 
18
        Path     string
 
19
        meta     *Meta
 
20
        config   *Config
 
21
        revision int
 
22
}
 
23
 
 
24
// Trick to ensure *Dir implements the Charm interface.
 
25
var _ Charm = (*Dir)(nil)
 
26
 
11
27
// ReadDir returns a Dir representing an expanded charm directory.
12
28
func ReadDir(path string) (dir *Dir, err error) {
13
29
        dir = &Dir{Path: path}
29
45
        if err != nil {
30
46
                return nil, err
31
47
        }
 
48
        if file, err = os.Open(dir.join("revision")); err == nil {
 
49
                _, err = fmt.Fscan(file, &dir.revision)
 
50
                file.Close()
 
51
                if err != nil {
 
52
                        return nil, errors.New("invalid revision file")
 
53
                }
 
54
        } else {
 
55
                dir.revision = dir.meta.OldRevision
 
56
        }
32
57
        return dir, nil
33
58
}
34
59
 
35
 
// The Dir type encapsulates access to data and operations
36
 
// on a charm directory.
37
 
type Dir struct {
38
 
        Path   string
39
 
        meta   *Meta
40
 
        config *Config
41
 
}
42
 
 
43
 
// Trick to ensure *Dir implements the Charm interface.
44
 
var _ Charm = (*Dir)(nil)
45
 
 
46
60
// join builds a path rooted at the charm's expanded directory
47
61
// path and the extra path components provided.
48
62
func (dir *Dir) join(parts ...string) string {
50
64
        return filepath.Join(parts...)
51
65
}
52
66
 
 
67
// Revision returns the revision number for the charm
 
68
// expanded in dir.
 
69
func (dir *Dir) Revision() int {
 
70
        return dir.revision
 
71
}
 
72
 
53
73
// Meta returns the Meta representing the metadata.yaml file
54
74
// for the charm expanded in dir.
55
75
func (dir *Dir) Meta() *Meta {
62
82
        return dir.config
63
83
}
64
84
 
 
85
// SetRevision changes the charm revision number. This affects
 
86
// the revision reported by Revision and the revision of the
 
87
// charm bundled by BundleTo.
 
88
// The revision file in the charm directory is not modified.
 
89
func (dir *Dir) SetRevision(revision int) {
 
90
        dir.revision = revision
 
91
}
 
92
 
 
93
// SetDiskRevision does the same as SetRevision but also changes
 
94
// the revision file in the charm directory.
 
95
func (dir *Dir) SetDiskRevision(revision int) error {
 
96
        dir.SetRevision(revision)
 
97
        file, err := os.OpenFile(dir.join("revision"), os.O_WRONLY|os.O_CREATE, 0644)
 
98
        if err != nil {
 
99
                return err
 
100
        }
 
101
        _, err = file.Write([]byte(strconv.Itoa(revision)))
 
102
        file.Close()
 
103
        return err
 
104
}
 
105
 
65
106
// BundleTo creates a charm file from the charm expanded in dir.
66
107
func (dir *Dir) BundleTo(w io.Writer) (err error) {
67
108
        zipw := zip.NewWriter(w)
68
109
        defer zipw.Close()
69
110
        zp := zipPacker{zipw, dir.Path}
 
111
        zp.AddRevision(dir.revision)
70
112
        return filepath.Walk(dir.Path, zp.WalkFunc())
71
113
}
72
114
 
77
119
 
78
120
func (zp *zipPacker) WalkFunc() filepath.WalkFunc {
79
121
        return func(path string, fi os.FileInfo, err error) error {
80
 
                return zp.Visit(path, fi, err)
81
 
        }
82
 
}
83
 
 
84
 
func (zp *zipPacker) Visit(path string, fi os.FileInfo, err error) error {
 
122
                return zp.visit(path, fi, err)
 
123
        }
 
124
}
 
125
 
 
126
func (zp *zipPacker) AddRevision(revision int) error {
 
127
        h := &zip.FileHeader{Name: "revision"}
 
128
        h.SetMode(syscall.S_IFREG | 0644)
 
129
        w, err := zp.CreateHeader(h)
 
130
        if err == nil {
 
131
                _, err = w.Write([]byte(strconv.Itoa(revision)))
 
132
        }
 
133
        return err
 
134
}
 
135
 
 
136
func (zp *zipPacker) visit(path string, fi os.FileInfo, err error) error {
85
137
        if err != nil {
86
138
                return err
87
139
        }
89
141
        if err != nil {
90
142
                return err
91
143
        }
 
144
        method := zip.Deflate
92
145
        hidden := len(relpath) > 1 && relpath[0] == '.'
93
146
        if fi.IsDir() {
94
147
                if relpath == "build" {
98
151
                        return filepath.SkipDir
99
152
                }
100
153
                relpath += "/"
 
154
                method = zip.Store
101
155
        }
102
 
        if hidden {
 
156
        if hidden || relpath == "revision" {
103
157
                return nil
104
158
        }
105
159
        h := &zip.FileHeader{
106
160
                Name:   relpath,
107
 
                Method: zip.Deflate,
 
161
                Method: method,
108
162
        }
109
163
        h.SetMode(fi.Mode())
110
164
        w, err := zp.CreateHeader(h)