LEFT | RIGHT |
1 // Copyright 2013 Canonical Ltd. | 1 // Copyright 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 // The tools package supports locating, parsing, and filtering Ubuntu tools meta
data in simplestreams format. | 4 // The tools package supports locating, parsing, and filtering Ubuntu tools meta
data in simplestreams format. |
5 // See http://launchpad.net/simplestreams and in particular the doc/README file
in that project for more information | 5 // See http://launchpad.net/simplestreams and in particular the doc/README file
in that project for more information |
6 // about the file formats. | 6 // about the file formats. |
7 package tools | 7 package tools |
8 | 8 |
9 import ( | 9 import ( |
10 "bytes" | 10 "bytes" |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
207 FileType: "tar.gz", | 207 FileType: "tar.gz", |
208 Size: t.Size, | 208 Size: t.Size, |
209 SHA256: t.SHA256, | 209 SHA256: t.SHA256, |
210 } | 210 } |
211 } | 211 } |
212 return metadata | 212 return metadata |
213 } | 213 } |
214 | 214 |
215 // ResolveMetadata resolves incomplete metadata | 215 // ResolveMetadata resolves incomplete metadata |
216 // by fetching the tools from storage and computing | 216 // by fetching the tools from storage and computing |
217 // the metadata locally. | 217 // the size and hash locally. |
218 func ResolveMetadata(stor storage.StorageReader, metadata []*ToolsMetadata) erro
r { | 218 func ResolveMetadata(stor storage.StorageReader, metadata []*ToolsMetadata) erro
r { |
219 for _, md := range metadata { | 219 for _, md := range metadata { |
220 » » if md.Size == 0 { | 220 » » if md.Size != 0 { |
221 » » » binary := md.binary() | 221 » » » continue |
222 » » » logger.Infof("Fetching tools to generate hash: %v", bina
ry) | 222 » » } |
223 » » » var sha256hash hash.Hash | 223 » » binary := md.binary() |
224 » » » size, sha256hash, err := fetchToolsHash(stor, binary) | 224 » » logger.Infof("Fetching tools to generate hash: %v", binary) |
225 » » » if err != nil { | 225 » » var sha256hash hash.Hash |
226 » » » » return err | 226 » » size, sha256hash, err := fetchToolsHash(stor, binary) |
227 » » » } | 227 » » if err != nil { |
228 » » » md.Size = size | 228 » » » return err |
229 » » » md.SHA256 = fmt.Sprintf("%x", sha256hash.Sum(nil)) | 229 » » } |
230 » » } | 230 » » md.Size = size |
| 231 » » md.SHA256 = fmt.Sprintf("%x", sha256hash.Sum(nil)) |
231 } | 232 } |
232 return nil | 233 return nil |
233 } | 234 } |
234 | 235 |
235 // MergeMetadata merges the given tools metadata. | 236 // MergeMetadata merges the given tools metadata. |
236 // If metadata for the same tools version exists in both lists, | 237 // If metadata for the same tools version exists in both lists, |
237 // an entry with non-empty size/SHA256 takes precedence. If | 238 // an entry with non-empty size/SHA256 takes precedence; if |
238 // both entries have information, prefer the entry from | 239 // the two entries have different sizes/hashes, then an error is |
239 // "newMetadata". | 240 // returned. |
240 func MergeMetadata(newMetadata, oldMetadata []*ToolsMetadata) []*ToolsMetadata { | 241 func MergeMetadata(tmlist1, tmlist2 []*ToolsMetadata) ([]*ToolsMetadata, error)
{ |
241 merged := make(map[version.Binary]*ToolsMetadata) | 242 merged := make(map[version.Binary]*ToolsMetadata) |
242 » for _, tm := range newMetadata { | 243 » for _, tm := range tmlist1 { |
243 merged[tm.binary()] = tm | 244 merged[tm.binary()] = tm |
244 } | 245 } |
245 » for _, tm := range oldMetadata { | 246 » for _, tm := range tmlist2 { |
246 binary := tm.binary() | 247 binary := tm.binary() |
247 » » if existing, ok := merged[binary]; !ok || existing.Size == 0 { | 248 » » if existing, ok := merged[binary]; ok { |
| 249 » » » if tm.Size != 0 { |
| 250 » » » » if existing.Size == 0 { |
| 251 » » » » » merged[binary] = tm |
| 252 » » » » } else if existing.Size != tm.Size || existing.S
HA256 != tm.SHA256 { |
| 253 » » » » » return nil, fmt.Errorf( |
| 254 » » » » » » "metadata mismatch for %s: sizes
=(%v,%v) sha256=(%v,%v)", |
| 255 » » » » » » binary.String(), |
| 256 » » » » » » existing.Size, tm.Size, |
| 257 » » » » » » existing.SHA256, tm.SHA256, |
| 258 » » » » » ) |
| 259 » » » » } |
| 260 » » » } |
| 261 » » } else { |
248 merged[binary] = tm | 262 merged[binary] = tm |
249 } | 263 } |
250 } | 264 } |
251 list := make([]*ToolsMetadata, 0, len(merged)) | 265 list := make([]*ToolsMetadata, 0, len(merged)) |
252 for _, metadata := range merged { | 266 for _, metadata := range merged { |
253 list = append(list, metadata) | 267 list = append(list, metadata) |
254 } | 268 } |
255 » return list | 269 » return list, nil |
256 } | 270 } |
257 | 271 |
258 // ReadMetadata returns the tools metadata from the given storage. | 272 // ReadMetadata returns the tools metadata from the given storage. |
259 func ReadMetadata(store storage.StorageReader) ([]*ToolsMetadata, error) { | 273 func ReadMetadata(store storage.StorageReader) ([]*ToolsMetadata, error) { |
260 dataSource := storage.NewStorageSimpleStreamsDataSource(store, "tools") | 274 dataSource := storage.NewStorageSimpleStreamsDataSource(store, "tools") |
261 toolsConstraint, err := makeToolsConstraint(simplestreams.CloudSpec{}, -
1, -1, coretools.Filter{}) | 275 toolsConstraint, err := makeToolsConstraint(simplestreams.CloudSpec{}, -
1, -1, coretools.Filter{}) |
262 if err != nil { | 276 if err != nil { |
263 return nil, err | 277 return nil, err |
264 } | 278 } |
265 metadata, err := Fetch([]simplestreams.DataSource{dataSource}, simplestr
eams.DefaultIndexPath, toolsConstraint, false) | 279 metadata, err := Fetch([]simplestreams.DataSource{dataSource}, simplestr
eams.DefaultIndexPath, toolsConstraint, false) |
(...skipping 16 matching lines...) Expand all Loading... |
282 for _, md := range metadataInfo { | 296 for _, md := range metadataInfo { |
283 logger.Infof("Writing %s", "tools/"+md.Path) | 297 logger.Infof("Writing %s", "tools/"+md.Path) |
284 err = stor.Put("tools/"+md.Path, bytes.NewReader(md.Data), int64
(len(md.Data))) | 298 err = stor.Put("tools/"+md.Path, bytes.NewReader(md.Data), int64
(len(md.Data))) |
285 if err != nil { | 299 if err != nil { |
286 return err | 300 return err |
287 } | 301 } |
288 } | 302 } |
289 return nil | 303 return nil |
290 } | 304 } |
291 | 305 |
292 type ResolveFlag bool | |
293 | |
294 const ( | |
295 DontResolve ResolveFlag = false | |
296 Resolve ResolveFlag = true | |
297 ) | |
298 | |
299 // MergeAndWriteMetadata reads the existing metadata from storage (if any), | 306 // MergeAndWriteMetadata reads the existing metadata from storage (if any), |
300 // and merges it with metadata generated from the given tools list. | 307 // and merges it with metadata generated from the given tools list. The |
301 // If resolve is true, incomplete metadata is resolved by fetching the tools | 308 // resulting metadata is written to storage. |
302 // from the target storage. Finally, the resulting metadata is written to | 309 func MergeAndWriteMetadata(stor storage.Storage, tools coretools.List) error { |
303 // storage. | |
304 func MergeAndWriteMetadata(stor storage.Storage, targetTools coretools.List, res
olve ResolveFlag) error { | |
305 existing, err := ReadMetadata(stor) | 310 existing, err := ReadMetadata(stor) |
306 if err != nil { | 311 if err != nil { |
307 return err | 312 return err |
308 } | 313 } |
309 » metadata := MetadataFromTools(targetTools) | 314 » metadata := MetadataFromTools(tools) |
310 » metadata = MergeMetadata(metadata, existing) | 315 » if metadata, err = MergeMetadata(metadata, existing); err != nil { |
311 » if resolve { | 316 » » return err |
312 » » if err = ResolveMetadata(stor, metadata); err != nil { | |
313 » » » return err | |
314 » » } | |
315 } | 317 } |
316 return WriteMetadata(stor, metadata) | 318 return WriteMetadata(stor, metadata) |
317 } | 319 } |
318 | 320 |
319 // fetchToolsHash fetches the tools from storage and calculates | 321 // fetchToolsHash fetches the tools from storage and calculates |
320 // its size in bytes and computes a SHA256 hash of its contents. | 322 // its size in bytes and computes a SHA256 hash of its contents. |
321 func fetchToolsHash(stor storage.StorageReader, ver version.Binary) (size int64,
sha256hash hash.Hash, err error) { | 323 func fetchToolsHash(stor storage.StorageReader, ver version.Binary) (size int64,
sha256hash hash.Hash, err error) { |
322 r, err := storage.Get(stor, StorageName(ver)) | 324 r, err := storage.Get(stor, StorageName(ver)) |
323 if err != nil { | 325 if err != nil { |
324 return 0, nil, err | 326 return 0, nil, err |
325 } | 327 } |
326 defer r.Close() | 328 defer r.Close() |
327 sha256hash = sha256.New() | 329 sha256hash = sha256.New() |
328 size, err = io.Copy(sha256hash, r) | 330 size, err = io.Copy(sha256hash, r) |
329 return size, sha256hash, err | 331 return size, sha256hash, err |
330 } | 332 } |
LEFT | RIGHT |