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