github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/resource/context/internal/content.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package internal 5 6 // TODO(ericsnow) Move this file elsewhere? 7 // (e.g. top-level resource pkg, charm/resource) 8 9 import ( 10 "bytes" 11 "io" 12 13 "github.com/juju/errors" 14 charmresource "gopkg.in/juju/charm.v6-unstable/resource" 15 16 "github.com/juju/juju/resource" 17 ) 18 19 // Content holds a reader for the content of a resource along 20 // with details about that content. 21 type Content struct { 22 // Data holds the resouce content, ready to be read (once). 23 Data io.Reader 24 25 // Size is the byte count of the data. 26 Size int64 27 28 // Fingerprint holds the checksum of the data. 29 Fingerprint charmresource.Fingerprint 30 } 31 32 // verify ensures that the actual resource content details match 33 // the expected ones. 34 func (c Content) Verify(size int64, fp charmresource.Fingerprint) error { 35 if size != c.Size { 36 return errors.Errorf("resource size does not match expected (%d != %d)", size, c.Size) 37 } 38 if !bytes.Equal(fp.Bytes(), c.Fingerprint.Bytes()) { 39 return errors.Errorf("resource fingerprint does not match expected (%q != %q)", fp, c.Fingerprint) 40 } 41 return nil 42 } 43 44 // ContentSource represents the functionality of OpenedResource, 45 // relative to Content. 46 type ContentSource interface { 47 // Content returns the content for the opened resource. 48 Content() Content 49 50 // Info returns the info for the opened resource. 51 Info() resource.Resource 52 } 53 54 // TODO(ericsnow) Need a lockfile around create/write? 55 56 // WriteContent writes the resource file to the target provided 57 // by the deps. 58 func WriteContent(target io.Writer, content Content, deps WriteContentDeps) error { 59 checker := deps.NewChecker(content) 60 source := checker.WrapReader(content.Data) 61 62 if err := deps.Copy(target, source); err != nil { 63 return errors.Annotate(err, "could not write resource to file") 64 } 65 66 if err := checker.Verify(); err != nil { 67 return errors.Trace(err) 68 } 69 70 return nil 71 } 72 73 // WriteContentDeps exposes the external functionality needed by WriteContent. 74 type WriteContentDeps interface { 75 //NewChecker provides a content checker for the given content. 76 NewChecker(Content) ContentChecker 77 78 // Copy copies the data from the reader into the writer. 79 Copy(io.Writer, io.Reader) error 80 } 81 82 // ContentChecker exposes functionality for verifying the data read from a reader. 83 type ContentChecker interface { 84 // WrapReader wraps the provided reader in another reader 85 // that tracks the read data. 86 WrapReader(io.Reader) io.Reader 87 88 // Verify fails if the tracked data does not match 89 // the expected data. 90 Verify() error 91 } 92 93 // Checker provides the functionality for verifying that read data 94 // is correct. 95 type Checker struct { 96 // Content holds the expected content values. 97 Content Content 98 99 // SizeTracker tracks the number of bytes read. 100 SizeTracker SizeTracker 101 102 // ChecksumWriter tracks the checksum of the read bytes. 103 ChecksumWriter ChecksumWriter 104 } 105 106 // NewContentChecker returns a Checker for the provided data. 107 func NewContentChecker(content Content, sizeTracker SizeTracker, checksumWriter ChecksumWriter) *Checker { 108 return &Checker{ 109 Content: content, 110 SizeTracker: sizeTracker, 111 ChecksumWriter: checksumWriter, 112 } 113 } 114 115 // WrapReader implements ContentChecker. 116 func (c Checker) WrapReader(reader io.Reader) io.Reader { 117 hashingReader := io.TeeReader(reader, c.ChecksumWriter) 118 return io.TeeReader(hashingReader, c.SizeTracker) 119 } 120 121 // Verify implements ContentChecker. 122 func (c Checker) Verify() error { 123 size := c.SizeTracker.Size() 124 fp := c.ChecksumWriter.Fingerprint() 125 if err := c.Content.Verify(size, fp); err != nil { 126 return errors.Trace(err) 127 } 128 return nil 129 } 130 131 // NopChecker is a ContentChecker that accepts all data. 132 type NopChecker struct{} 133 134 // WrapReader implements ContentChecker. 135 func (NopChecker) WrapReader(reader io.Reader) io.Reader { 136 return reader 137 } 138 139 // Verify implements ContentChecker. 140 func (NopChecker) Verify() error { 141 return nil 142 } 143 144 // SizeTracker tracks the number of bytes written. 145 type SizeTracker interface { 146 io.Writer 147 148 // Size returns the number of bytes written. 149 Size() int64 150 } 151 152 // ChecksumWriter tracks the checksum of all written bytes. 153 type ChecksumWriter interface { 154 io.Writer 155 156 // Fingerprint is the fingerprint for the tracked checksum. 157 Fingerprint() charmresource.Fingerprint 158 }