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  }