github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/imageunpacker/unpacker/exportImage.go (about)

     1  package unpacker
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"os/user"
    10  	"path"
    11  	"strconv"
    12  	"syscall"
    13  	"time"
    14  
    15  	"github.com/Cloud-Foundations/Dominator/lib/format"
    16  	proto "github.com/Cloud-Foundations/Dominator/proto/imageunpacker"
    17  )
    18  
    19  var (
    20  	exportImageTool = flag.String("exportImageTool",
    21  		"/usr/local/etc/export-image", "Name of tool to export image")
    22  	exportImageUsername = flag.String("exportImageUsername",
    23  		"nobody", "Username to run as for export tool")
    24  )
    25  
    26  func (u *Unpacker) exportImage(streamName string,
    27  	exportType string, exportDestination string) error {
    28  	u.rwMutex.Lock()
    29  	u.updateUsageTimeWithLock()
    30  	streamInfo, err := u.setupStream(streamName)
    31  	u.rwMutex.Unlock()
    32  	defer u.updateUsageTime()
    33  	if err != nil {
    34  		return err
    35  	}
    36  	errorChannel := make(chan error)
    37  	request := requestType{
    38  		request:           requestExport,
    39  		exportType:        exportType,
    40  		exportDestination: exportDestination,
    41  		errorChannel:      errorChannel,
    42  	}
    43  	streamInfo.requestChannel <- request
    44  	return <-errorChannel
    45  }
    46  
    47  func (stream *streamManagerState) export(exportType string,
    48  	exportDestination string) error {
    49  	userInfo, err := user.Lookup(*exportImageUsername)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	groupIds, err := userInfo.GroupIds()
    54  	if err != nil {
    55  		return err
    56  	}
    57  	if err := stream.getDevice(); err != nil {
    58  		return err
    59  	}
    60  	stream.unpacker.rwMutex.RLock()
    61  	device := stream.unpacker.pState.Devices[stream.streamInfo.DeviceId]
    62  	stream.unpacker.rwMutex.RUnlock()
    63  	doUnmount := false
    64  	streamInfo := stream.streamInfo
    65  	switch streamInfo.status {
    66  	case proto.StatusStreamNoDevice:
    67  		return errors.New("no device")
    68  	case proto.StatusStreamNotMounted:
    69  		// Nothing to do.
    70  	case proto.StatusStreamMounted:
    71  		doUnmount = true
    72  	case proto.StatusStreamScanning:
    73  		return errors.New("stream scan in progress")
    74  	case proto.StatusStreamScanned:
    75  		doUnmount = true
    76  	case proto.StatusStreamFetching:
    77  		return errors.New("fetch in progress")
    78  	case proto.StatusStreamUpdating:
    79  		return errors.New("update in progress")
    80  	case proto.StatusStreamPreparing:
    81  		return errors.New("preparing to capture")
    82  	case proto.StatusStreamExporting:
    83  		return errors.New("export in progress")
    84  	case proto.StatusStreamNoFileSystem:
    85  		return errors.New("no file-system")
    86  	default:
    87  		panic("invalid status")
    88  	}
    89  	if doUnmount {
    90  		mountPoint := path.Join(stream.unpacker.baseDir, "mnt")
    91  		if err := syscall.Unmount(mountPoint, 0); err != nil {
    92  			return err
    93  		}
    94  		stream.streamInfo.status = proto.StatusStreamNotMounted
    95  	}
    96  	stream.streamInfo.status = proto.StatusStreamExporting
    97  	defer func() {
    98  		stream.streamInfo.status = proto.StatusStreamNotMounted
    99  	}()
   100  	deviceFile, err := os.Open(path.Join("/dev", device.DeviceName))
   101  	if err != nil {
   102  		stream.unpacker.logger.Println("Error exporting: %s", err)
   103  		return fmt.Errorf("error exporting: %s", err)
   104  	}
   105  	defer deviceFile.Close()
   106  	cmd := exec.Command(*exportImageTool, exportType, exportDestination)
   107  	cmd.Stdin = deviceFile
   108  	uid, err := strconv.ParseUint(userInfo.Uid, 10, 32)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	gid, err := strconv.ParseUint(userInfo.Gid, 10, 32)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	gids := make([]uint32, 0, len(groupIds))
   117  	for _, groupId := range groupIds {
   118  		gid, err := strconv.ParseUint(groupId, 10, 32)
   119  		if err != nil {
   120  			return err
   121  		}
   122  		gids = append(gids, uint32(gid))
   123  	}
   124  	creds := &syscall.Credential{
   125  		Uid:    uint32(uid),
   126  		Gid:    uint32(gid),
   127  		Groups: gids,
   128  	}
   129  	cmd.SysProcAttr = &syscall.SysProcAttr{Credential: creds}
   130  	startTime := time.Now()
   131  	output, err := cmd.CombinedOutput()
   132  	if err != nil {
   133  		stream.unpacker.logger.Printf("Error exporting: %s: %s\n",
   134  			err, string(output))
   135  		return fmt.Errorf("error exporting: %s: %s", err, output)
   136  	}
   137  	stream.unpacker.logger.Printf("Exported(%s) type: %s dest: %s in %s\n",
   138  		stream.streamName, exportType, exportDestination,
   139  		format.Duration(time.Since(startTime)))
   140  	return nil
   141  }