OLD | NEW |
1 // Copyright 2011, 2012, 2013 Canonical Ltd. | 1 // Copyright 2011, 2012, 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package charm | 4 package charm |
5 | 5 |
6 import ( | 6 import ( |
7 "archive/zip" | 7 "archive/zip" |
8 "errors" | 8 "errors" |
9 "fmt" | 9 "fmt" |
10 "io" | 10 "io" |
11 "io/ioutil" | 11 "io/ioutil" |
12 "os" | 12 "os" |
13 "path/filepath" | 13 "path/filepath" |
14 "strconv" | 14 "strconv" |
15 | 15 |
16 "launchpad.net/juju-core/utils/set" | 16 "launchpad.net/juju-core/utils/set" |
17 ziputil "launchpad.net/juju-core/utils/zip" | 17 ziputil "launchpad.net/juju-core/utils/zip" |
18 ) | 18 ) |
19 | 19 |
20 // The Bundle type encapsulates access to data and operations | 20 // The Bundle type encapsulates access to data and operations |
21 // on a charm bundle. | 21 // on a charm bundle. |
22 type Bundle struct { | 22 type Bundle struct { |
23 » Path string // May be empty if Bundle wasn't read from a file | 23 » Path string // May be empty if Bundle wasn't read from a file |
24 » meta *Meta | 24 » meta *Meta |
25 » config *Config | 25 » config *Config |
26 » revision int | 26 » actionsSpec *ActionsSpec |
27 » r io.ReaderAt | 27 » revision int |
28 » size int64 | 28 » r io.ReaderAt |
| 29 » size int64 |
29 } | 30 } |
30 | 31 |
31 // Trick to ensure *Bundle implements the Charm interface. | 32 // Trick to ensure *Bundle implements the Charm interface. |
32 var _ Charm = (*Bundle)(nil) | 33 var _ Charm = (*Bundle)(nil) |
33 | 34 |
34 // ReadBundle returns a Bundle for the charm in path. | 35 // ReadBundle returns a Bundle for the charm in path. |
35 func ReadBundle(path string) (bundle *Bundle, err error) { | 36 func ReadBundle(path string) (bundle *Bundle, err error) { |
36 f, err := os.Open(path) | 37 f, err := os.Open(path) |
37 if err != nil { | 38 if err != nil { |
38 return | 39 return |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
78 } else if err != nil { | 79 } else if err != nil { |
79 return nil, err | 80 return nil, err |
80 } else { | 81 } else { |
81 b.config, err = ReadConfig(reader) | 82 b.config, err = ReadConfig(reader) |
82 reader.Close() | 83 reader.Close() |
83 if err != nil { | 84 if err != nil { |
84 return nil, err | 85 return nil, err |
85 } | 86 } |
86 } | 87 } |
87 | 88 |
| 89 reader, err = zipOpen(zipr, "actions.yaml") |
| 90 if _, ok := err.(*noBundleFile); ok { |
| 91 b.actionsSpec = NewActionsSpec() |
| 92 } else if err != nil { |
| 93 return nil, err |
| 94 } else { |
| 95 b.actionsSpec, err = ReadActionsSpec(reader) |
| 96 reader.Close() |
| 97 if err != nil { |
| 98 return nil, err |
| 99 } |
| 100 } |
| 101 |
88 reader, err = zipOpen(zipr, "revision") | 102 reader, err = zipOpen(zipr, "revision") |
89 if err != nil { | 103 if err != nil { |
90 if _, ok := err.(*noBundleFile); !ok { | 104 if _, ok := err.(*noBundleFile); !ok { |
91 return | 105 return |
92 } | 106 } |
93 b.revision = b.meta.OldRevision | 107 b.revision = b.meta.OldRevision |
94 } else { | 108 } else { |
95 _, err = fmt.Fscan(reader, &b.revision) | 109 _, err = fmt.Fscan(reader, &b.revision) |
96 if err != nil { | 110 if err != nil { |
97 return nil, errors.New("invalid revision file") | 111 return nil, errors.New("invalid revision file") |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 func (b *Bundle) Meta() *Meta { | 149 func (b *Bundle) Meta() *Meta { |
136 return b.meta | 150 return b.meta |
137 } | 151 } |
138 | 152 |
139 // Config returns the Config representing the config.yaml file | 153 // Config returns the Config representing the config.yaml file |
140 // for the charm bundle. | 154 // for the charm bundle. |
141 func (b *Bundle) Config() *Config { | 155 func (b *Bundle) Config() *Config { |
142 return b.config | 156 return b.config |
143 } | 157 } |
144 | 158 |
| 159 // Actions returns the ActionSpec representing the actions.yaml file |
| 160 // for the charm bundle. |
| 161 func (b *Bundle) ActionsSpec() *ActionsSpec { |
| 162 return b.actionsSpec |
| 163 } |
| 164 |
145 type zipReadCloser struct { | 165 type zipReadCloser struct { |
146 io.Closer | 166 io.Closer |
147 *zip.Reader | 167 *zip.Reader |
148 } | 168 } |
149 | 169 |
150 // zipOpen returns a zipReadCloser. | 170 // zipOpen returns a zipReadCloser. |
151 func (b *Bundle) zipOpen() (*zipReadCloser, error) { | 171 func (b *Bundle) zipOpen() (*zipReadCloser, error) { |
152 // If we don't have a Path, try to use the original ReaderAt. | 172 // If we don't have a Path, try to use the original ReaderAt. |
153 if b.Path == "" { | 173 if b.Path == "" { |
154 r, err := zip.NewReader(b.r, b.size) | 174 r, err := zip.NewReader(b.r, b.size) |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 return nil | 259 return nil |
240 } | 260 } |
241 } | 261 } |
242 | 262 |
243 // FWIW, being able to do this is awesome. | 263 // FWIW, being able to do this is awesome. |
244 type readAtBytes []byte | 264 type readAtBytes []byte |
245 | 265 |
246 func (b readAtBytes) ReadAt(out []byte, off int64) (n int, err error) { | 266 func (b readAtBytes) ReadAt(out []byte, off int64) (n int, err error) { |
247 return copy(out, b[off:]), nil | 267 return copy(out, b[off:]), nil |
248 } | 268 } |
OLD | NEW |