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  }