github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/imagetool/patchDirectory.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "syscall" 11 "time" 12 13 domlib "github.com/Cloud-Foundations/Dominator/dom/lib" 14 "github.com/Cloud-Foundations/Dominator/lib/filesystem/scanner" 15 "github.com/Cloud-Foundations/Dominator/lib/format" 16 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 17 "github.com/Cloud-Foundations/Dominator/lib/hash" 18 "github.com/Cloud-Foundations/Dominator/lib/image" 19 "github.com/Cloud-Foundations/Dominator/lib/log" 20 "github.com/Cloud-Foundations/Dominator/lib/log/nulllogger" 21 "github.com/Cloud-Foundations/Dominator/lib/objectcache" 22 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 23 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 24 subproto "github.com/Cloud-Foundations/Dominator/proto/sub" 25 sublib "github.com/Cloud-Foundations/Dominator/sub/lib" 26 ) 27 28 func patchDirectorySubcommand(args []string, logger log.DebugLogger) error { 29 if err := patchDirectory(args[0], args[1], logger); err != nil { 30 return fmt.Errorf("error patching directory: %s", err) 31 } 32 return nil 33 } 34 35 func patchDirectory(imageName, dirName string, logger log.DebugLogger) error { 36 _, objectClient := getClients() 37 logger.Debugf(0, "getting image: %s\n", imageName) 38 img, imageName, err := getTypedImageAndName(imageName) 39 if err != nil { 40 return err 41 } 42 if err := img.FileSystem.RebuildInodePointers(); err != nil { 43 return err 44 } 45 img.FileSystem.BuildEntryMap() 46 rootDir, err := ioutil.TempDir("", "") 47 if err != nil { 48 return err 49 } 50 defer os.Remove(rootDir) 51 errorChannel := make(chan error) 52 go func() { 53 errorChannel <- patchRoot(img, objectClient, imageName, dirName, 54 rootDir, logger) 55 }() 56 return <-errorChannel 57 } 58 59 func patchRoot(img *image.Image, objectsGetter objectserver.ObjectsGetter, 60 imageName, dirName, rootDir string, logger log.DebugLogger) error { 61 if err := wsyscall.UnshareMountNamespace(); err != nil { 62 return fmt.Errorf("unable to unshare mount namesace: %s", err) 63 } 64 syscall.Unmount(rootDir, 0) 65 err := wsyscall.Mount(dirName, rootDir, "", wsyscall.MS_BIND, "") 66 if err != nil { 67 return fmt.Errorf("unable to bind mount %s to %s: %s", 68 dirName, rootDir, err) 69 } 70 logger.Debugf(0, "scanning directory: %s\n", dirName) 71 sfs, err := scanner.ScanFileSystem(rootDir, nil, img.Filter, nil, nil, nil) 72 if err != nil { 73 return err 74 } 75 fs := &sfs.FileSystem 76 if err := fs.RebuildInodePointers(); err != nil { 77 return err 78 } 79 fs.BuildEntryMap() 80 subObj := domlib.Sub{FileSystem: fs} 81 fetchMap, _ := domlib.BuildMissingLists(subObj, img, false, true, 82 logger) 83 objectsToFetch := objectcache.ObjectMapToCache(fetchMap) 84 subdDir := filepath.Join(rootDir, ".subd") 85 objectsDir := filepath.Join(subdDir, "objects") 86 defer os.RemoveAll(subdDir) 87 startTime := time.Now() 88 objectsReader, err := objectsGetter.GetObjects(objectsToFetch) 89 if err != nil { 90 return err 91 } 92 defer objectsReader.Close() 93 logger.Debugf(0, "fetching %d objects", len(objectsToFetch)) 94 for _, hashVal := range objectsToFetch { 95 length, reader, err := objectsReader.NextObject() 96 if err != nil { 97 return err 98 } 99 err = readOne(objectsDir, hashVal, length, reader) 100 reader.Close() 101 if err != nil { 102 return err 103 } 104 } 105 logger.Debugf(0, "fetched %d objects in %s", 106 len(objectsToFetch), format.Duration(time.Since(startTime))) 107 subObj.ObjectCache = append(subObj.ObjectCache, objectsToFetch...) 108 var subRequest subproto.UpdateRequest 109 if domlib.BuildUpdateRequest(subObj, img, &subRequest, false, true, 110 nulllogger.New()) { 111 return errors.New("failed building update: missing computed files") 112 } 113 subRequest.ImageName = imageName 114 subRequest.Triggers = nil 115 logger.Debugln(0, "starting update") 116 _, _, err = sublib.Update(subRequest, rootDir, objectsDir, nil, nil, nil, 117 logger) 118 if err != nil { 119 return err 120 } 121 return nil 122 } 123 124 func readOne(objectsDir string, hashVal hash.Hash, length uint64, 125 reader io.Reader) error { 126 filename := filepath.Join(objectsDir, objectcache.HashToFilename(hashVal)) 127 dirname := filepath.Dir(filename) 128 if err := os.MkdirAll(dirname, fsutil.DirPerms); err != nil { 129 return err 130 } 131 return fsutil.CopyToFile(filename, fsutil.PrivateFilePerms, reader, length) 132 }