github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/imagetool/addImageCommon.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "bufio" 6 "compress/gzip" 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "os/exec" 12 "strings" 13 "syscall" 14 "time" 15 16 "github.com/Cloud-Foundations/Dominator/imageserver/client" 17 "github.com/Cloud-Foundations/Dominator/lib/filesystem" 18 "github.com/Cloud-Foundations/Dominator/lib/filesystem/scanner" 19 "github.com/Cloud-Foundations/Dominator/lib/filesystem/untar" 20 "github.com/Cloud-Foundations/Dominator/lib/filter" 21 "github.com/Cloud-Foundations/Dominator/lib/format" 22 "github.com/Cloud-Foundations/Dominator/lib/hash" 23 "github.com/Cloud-Foundations/Dominator/lib/image" 24 "github.com/Cloud-Foundations/Dominator/lib/log" 25 "github.com/Cloud-Foundations/Dominator/lib/mbr" 26 objectclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 27 "github.com/Cloud-Foundations/Dominator/lib/srpc" 28 "github.com/Cloud-Foundations/Dominator/lib/srpc/setupclient" 29 "github.com/Cloud-Foundations/Dominator/lib/triggers" 30 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 31 ) 32 33 type hasher struct { 34 objQ *objectclient.ObjectAdderQueue 35 } 36 37 func addImage(imageSClient *srpc.Client, name string, img *image.Image, 38 logger log.DebugLogger) error { 39 if *expiresIn > 0 { 40 img.ExpiresAt = time.Now().Add(*expiresIn) 41 } else { 42 img.ExpiresAt = time.Time{} 43 } 44 if err := img.Verify(); err != nil { 45 return err 46 } 47 if err := img.VerifyRequiredPaths(requiredPaths); err != nil { 48 return err 49 } 50 startTime := time.Now() 51 if err := client.AddImage(imageSClient, name, img); err != nil { 52 return errors.New("remote error: " + err.Error()) 53 } 54 logger.Debugf(0, "Uploaded image: %s in %s\n", 55 name, format.Duration(time.Since(startTime))) 56 return nil 57 } 58 59 func (h *hasher) Hash(reader io.Reader, length uint64) ( 60 hash.Hash, error) { 61 hash, err := h.objQ.Add(reader, length) 62 if err != nil { 63 return hash, errors.New("error sending image data: " + err.Error()) 64 } 65 return hash, nil 66 } 67 68 func buildImage(imageSClient *srpc.Client, filter *filter.Filter, 69 imageFilename string, 70 logger log.DebugLogger) (*filesystem.FileSystem, error) { 71 var h hasher 72 var err error 73 h.objQ, err = objectclient.NewObjectAdderQueue(imageSClient) 74 if err != nil { 75 return nil, err 76 } 77 startTime := time.Now() 78 fs, err := buildImageWithHasher(imageSClient, filter, imageFilename, &h) 79 if err != nil { 80 h.objQ.Close() 81 return nil, err 82 } 83 err = h.objQ.Close() 84 if err != nil { 85 return nil, err 86 } 87 duration := time.Since(startTime) 88 speed := uint64(float64(fs.TotalDataBytes) / duration.Seconds()) 89 logger.Debugf(0, 90 "Scanned file-system and uploaded %d objects (%s) in %s (%s/s)\n", 91 fs.NumRegularInodes, format.FormatBytes(fs.TotalDataBytes), 92 format.Duration(duration), format.FormatBytes(speed)) 93 return fs, nil 94 } 95 96 func buildImageWithHasher(imageSClient *srpc.Client, filter *filter.Filter, 97 imageFilename string, h scanner.Hasher) (*filesystem.FileSystem, error) { 98 fi, err := os.Lstat(imageFilename) 99 if err != nil { 100 return nil, err 101 } 102 if fi.IsDir() { 103 sfs, err := scanner.ScanFileSystem(imageFilename, nil, filter, nil, h, 104 nil) 105 if err != nil { 106 return nil, err 107 } 108 return &sfs.FileSystem, nil 109 } 110 imageFile, err := os.Open(imageFilename) 111 if err != nil { 112 return nil, errors.New("error opening image file: " + err.Error()) 113 } 114 defer imageFile.Close() 115 if partitionTable, err := mbr.Decode(imageFile); err != nil { 116 if err != io.EOF { 117 return nil, err 118 } // Else perhaps a tiny tarfile, definitely not a partition table. 119 } else if partitionTable != nil { 120 return buildImageFromRaw(imageSClient, filter, imageFile, 121 partitionTable, h) 122 } 123 var imageReader io.Reader 124 if strings.HasSuffix(imageFilename, ".tar") { 125 imageReader = imageFile 126 } else if strings.HasSuffix(imageFilename, ".tar.gz") || 127 strings.HasSuffix(imageFilename, ".tgz") { 128 gzipReader, err := gzip.NewReader(imageFile) 129 if err != nil { 130 return nil, errors.New("error creating gzip reader: " + err.Error()) 131 } 132 defer gzipReader.Close() 133 imageReader = gzipReader 134 } else { 135 return nil, errors.New("unrecognised image type") 136 } 137 tarReader := tar.NewReader(imageReader) 138 fs, err := untar.Decode(tarReader, h, filter) 139 if err != nil { 140 return nil, errors.New("error building image: " + err.Error()) 141 } 142 return fs, nil 143 } 144 145 func buildImageFromRaw(imageSClient *srpc.Client, filter *filter.Filter, 146 imageFile *os.File, partitionTable *mbr.Mbr, 147 h scanner.Hasher) (*filesystem.FileSystem, error) { 148 var index uint 149 var offsetOfLargest, sizeOfLargest uint64 150 numPartitions := partitionTable.GetNumPartitions() 151 for index = 0; index < numPartitions; index++ { 152 offset := partitionTable.GetPartitionOffset(index) 153 size := partitionTable.GetPartitionSize(index) 154 if size > sizeOfLargest { 155 offsetOfLargest = offset 156 sizeOfLargest = size 157 } 158 } 159 if sizeOfLargest < 1 { 160 return nil, errors.New("unable to find largest partition") 161 } 162 if err := wsyscall.UnshareMountNamespace(); err != nil { 163 if os.IsPermission(err) { 164 // Try again with sudo(8). 165 args := make([]string, 0, len(os.Args)+1) 166 if sudoPath, err := exec.LookPath("sudo"); err != nil { 167 return nil, err 168 } else { 169 args = append(args, sudoPath) 170 } 171 if myPath, err := exec.LookPath(os.Args[0]); err != nil { 172 return nil, err 173 } else { 174 args = append(args, myPath) 175 } 176 args = append(args, fmt.Sprintf("-certDirectory=%s", 177 setupclient.GetCertDirectory())) 178 args = append(args, os.Args[1:]...) 179 if err := syscall.Exec(args[0], args, os.Environ()); err != nil { 180 return nil, errors.New("unable to Exec: " + err.Error()) 181 } 182 } 183 return nil, errors.New( 184 "error unsharing mount namespace: " + err.Error()) 185 } 186 cmd := exec.Command("mount", "-o", 187 fmt.Sprintf("loop,offset=%d", offsetOfLargest), imageFile.Name(), 188 "/mnt") 189 cmd.Stdin = os.Stdin 190 cmd.Stdout = os.Stdout 191 cmd.Stderr = os.Stderr 192 if err := cmd.Run(); err != nil { 193 return nil, err 194 } 195 fs, err := buildImageWithHasher(imageSClient, filter, "/mnt", h) 196 syscall.Unmount("/mnt", 0) 197 return fs, err 198 } 199 200 func loadImageFiles(img *image.Image, objectClient *objectclient.ObjectClient, 201 filterFilename, triggersFilename string) error { 202 var err error 203 if filterFilename != "" { 204 img.Filter, err = filter.Load(filterFilename) 205 if err != nil { 206 return err 207 } 208 } 209 if triggersFilename != "" { 210 img.Triggers, err = triggers.Load(triggersFilename) 211 if err != nil { 212 return err 213 } 214 } 215 img.BuildLog, err = getAnnotation(objectClient, *buildLog) 216 if err != nil { 217 return err 218 } 219 img.ReleaseNotes, err = getAnnotation(objectClient, *releaseNotes) 220 if err != nil { 221 return err 222 } 223 return nil 224 } 225 226 func getAnnotation(objectClient *objectclient.ObjectClient, name string) ( 227 *image.Annotation, error) { 228 if name == "" { 229 return nil, nil 230 } 231 file, err := os.Open(name) 232 if err != nil { 233 return &image.Annotation{URL: name}, nil 234 } 235 defer file.Close() 236 fi, err := file.Stat() 237 if err != nil { 238 return nil, err 239 } 240 reader := bufio.NewReader(file) 241 hash, _, err := objectClient.AddObject(reader, uint64(fi.Size()), nil) 242 return &image.Annotation{Object: &hash}, err 243 }