github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/backups/testing/file.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "archive/tar" 8 "bytes" 9 "compress/gzip" 10 "io" 11 "path" 12 "strings" 13 14 "github.com/juju/collections/set" 15 "github.com/juju/errors" 16 17 "github.com/juju/juju/state/backups" 18 ) 19 20 // File represents a file during testing. 21 type File struct { 22 // Name is the path to which the file will be identified in the archive. 23 Name string 24 // Content is the data that will be written to the archive for the file. 25 Content string 26 // IsDir determines if the file is a regular file or a directory. 27 IsDir bool 28 } 29 30 // AddToArchive adds the file to the tar archive. 31 func (f *File) AddToArchive(archive *tar.Writer) error { 32 hdr := &tar.Header{ 33 Name: f.Name, 34 } 35 if f.IsDir { 36 hdr.Typeflag = tar.TypeDir 37 hdr.Mode = 0777 38 } else { 39 hdr.Size = int64(len(f.Content)) 40 hdr.Mode = 0666 41 } 42 43 if err := archive.WriteHeader(hdr); err != nil { 44 return errors.Trace(err) 45 } 46 47 if !f.IsDir { 48 if _, err := archive.Write([]byte(f.Content)); err != nil { 49 return errors.Trace(err) 50 } 51 } 52 53 return nil 54 } 55 56 // NewArchive returns a new archive file containing the files. 57 func NewArchive(meta *backups.Metadata, files, dump []File) (*bytes.Buffer, error) { 58 dirs := set.NewStrings() 59 var sysFiles []File 60 for _, file := range files { 61 var parent string 62 for _, p := range strings.Split(path.Dir(file.Name), "/") { 63 if parent == "" { 64 parent = p 65 } else { 66 parent = path.Join(parent, p) 67 } 68 if !dirs.Contains(parent) { 69 sysFiles = append(sysFiles, File{ 70 Name: parent, 71 IsDir: true, 72 }) 73 dirs.Add(parent) 74 } 75 } 76 if file.IsDir { 77 if !dirs.Contains(file.Name) { 78 sysFiles = append(sysFiles, file) 79 dirs.Add(file.Name) 80 } 81 } else { 82 sysFiles = append(sysFiles, file) 83 } 84 } 85 86 var rootFile bytes.Buffer 87 if err := writeToTar(&rootFile, sysFiles); err != nil { 88 return nil, errors.Trace(err) 89 } 90 91 topfiles := []File{{ 92 Name: "juju-backup", 93 IsDir: true, 94 }} 95 96 topfiles = append(topfiles, File{ 97 Name: "juju-backup/dump", 98 IsDir: true, 99 }) 100 for _, dumpFile := range dump { 101 topfiles = append(topfiles, File{ 102 Name: "juju-backup/dump/" + dumpFile.Name, 103 Content: dumpFile.Content, 104 IsDir: dumpFile.IsDir, 105 }) 106 } 107 108 topfiles = append(topfiles, 109 File{ 110 Name: "juju-backup/root.tar", 111 Content: rootFile.String(), 112 }, 113 ) 114 115 if meta != nil { 116 metaFile, err := meta.AsJSONBuffer() 117 if err != nil { 118 return nil, errors.Trace(err) 119 } 120 topfiles = append(topfiles, 121 File{ 122 Name: "juju-backup/metadata.json", 123 Content: metaFile.(*bytes.Buffer).String(), 124 }, 125 ) 126 } 127 128 var arFile bytes.Buffer 129 compressed := gzip.NewWriter(&arFile) 130 defer compressed.Close() 131 if err := writeToTar(compressed, topfiles); err != nil { 132 return nil, errors.Trace(err) 133 } 134 135 return &arFile, nil 136 } 137 138 // NewArchiveBasic returns a new archive file with a few files provided. 139 func NewArchiveBasic(meta *backups.Metadata) (*bytes.Buffer, error) { 140 files := []File{ 141 { 142 Name: "var/lib/juju/tools/1.21-alpha2.1-trusty-amd64/jujud", 143 Content: "<some binary data goes here>", 144 }, 145 { 146 Name: "var/lib/juju/system-identity", 147 Content: "<an ssh key goes here>", 148 }, 149 } 150 dump := []File{ 151 { 152 Name: "juju/machines.bson", 153 Content: "<BSON data goes here>", 154 }, 155 { 156 Name: "oplog.bson", 157 Content: "<BSON data goes here>", 158 }, 159 } 160 161 arFile, err := NewArchive(meta, files, dump) 162 if err != nil { 163 return nil, errors.Trace(err) 164 } 165 return arFile, nil 166 } 167 168 func writeToTar(archive io.Writer, files []File) error { 169 tarw := tar.NewWriter(archive) 170 defer tarw.Close() 171 172 for _, file := range files { 173 if err := file.AddToArchive(tarw); err != nil { 174 return errors.Trace(err) 175 } 176 } 177 return nil 178 }