github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/pkg/providers/aws/stage.go (about)

     1  package aws
     2  
     3  import (
     4  	"github.com/sirupsen/logrus"
     5  	"github.com/aws/aws-sdk-go/aws"
     6  	"github.com/aws/aws-sdk-go/service/ec2"
     7  	"github.com/emc-advanced-dev/pkg/errors"
     8  	"github.com/solo-io/unik/pkg/providers/common"
     9  	"github.com/solo-io/unik/pkg/types"
    10  	"io/ioutil"
    11  	"os"
    12  	"time"
    13  )
    14  
    15  var kernelIdMap = map[string]string{
    16  	"ap-northeast-1": "aki-176bf516",
    17  	"ap-southeast-1": "aki-503e7402",
    18  	"ap-southeast-2": "aki-c362fff9",
    19  	"eu-central-1":   "aki-184c7a05",
    20  	"eu-west-1":      "aki-52a34525",
    21  	"sa-east-1":      "aki-5553f448",
    22  	"us-east-1":      "aki-919dcaf8",
    23  	"us-gov-west-1":  "aki-1de98d3e",
    24  	"us-west-1":      "aki-880531cd",
    25  	"us-west-2":      "aki-fc8f11cc",
    26  }
    27  
    28  func (p *AwsProvider) Stage(params types.StageImageParams) (_ *types.Image, err error) {
    29  	images, err := p.ListImages()
    30  	if err != nil {
    31  		return nil, errors.New("retrieving image list for existing image", err)
    32  	}
    33  
    34  	for _, image := range images {
    35  		if image.Name == params.Name {
    36  			if !params.Force {
    37  				return nil, errors.New("an image already exists with name '"+params.Name+"', try again with --force", nil)
    38  			} else {
    39  				logrus.WithField("image", image).Warnf("force: deleting previous image with name " + params.Name)
    40  				err = p.DeleteImage(image.Id, true)
    41  				if err != nil {
    42  					return nil, errors.New("removing previously existing image", err)
    43  				}
    44  			}
    45  		}
    46  	}
    47  
    48  	var snapshotId, volumeId string
    49  	s3svc := p.newS3()
    50  	ec2svc := p.newEC2()
    51  	defer func() {
    52  		if err != nil {
    53  			logrus.WithError(err).Errorf("aws staging encountered an error")
    54  			if snapshotId != "" {
    55  				logrus.Warnf("cleaning up snapshot %s", snapshotId)
    56  				deleteSnapshot(ec2svc, snapshotId)
    57  			}
    58  			if volumeId != "" {
    59  				logrus.Warnf("cleaning up volume %s", volumeId)
    60  				deleteVolume(ec2svc, volumeId)
    61  			}
    62  		}
    63  	}()
    64  
    65  	logrus.WithField("raw-image", params.RawImage).WithField("az", p.config.Zone).Infof("creating boot volume from raw image")
    66  
    67  	rawImageFile, err := os.Stat(params.RawImage.LocalImagePath)
    68  	if err != nil {
    69  		return nil, errors.New("statting raw image file", err)
    70  	}
    71  
    72  	imageSize := rawImageFile.Size()
    73  
    74  	switch params.RawImage.StageSpec.ImageFormat {
    75  	case types.ImageFormat_QCOW2:
    76  		rawImage, err := ioutil.TempFile("", "converted.raw.img.")
    77  		if err != nil {
    78  			return nil, errors.New("creating tmp file for qemu img convert", err)
    79  		}
    80  		defer os.Remove(rawImage.Name())
    81  		//vpc indicates VHD image type to qemu-img
    82  		if err := common.ConvertRawImage(types.ImageFormat_QCOW2, types.ImageFormat_VHD, params.RawImage.LocalImagePath, rawImage.Name()); err != nil {
    83  			return nil, errors.New("converting qcow2 to vhd image", err)
    84  		}
    85  		os.Remove(params.RawImage.LocalImagePath)
    86  		//point at the new image
    87  		params.RawImage.LocalImagePath = rawImage.Name()
    88  		params.RawImage.StageSpec.ImageFormat = types.ImageFormat_VHD
    89  		imageSize, err = common.GetVirtualImageSize(params.RawImage.LocalImagePath, params.RawImage.StageSpec.ImageFormat)
    90  		if err != nil {
    91  			return nil, errors.New("getting virtual image size", err)
    92  		}
    93  	}
    94  
    95  	volumeId, err = createDataVolumeFromRawImage(s3svc, ec2svc, params.RawImage.LocalImagePath, imageSize, params.RawImage.StageSpec.ImageFormat, p.config.Zone)
    96  	if err != nil {
    97  		return nil, errors.New("creating aws boot volume", err)
    98  	}
    99  
   100  	logrus.WithField("volume-id", volumeId).Infof("creating snapshot from boot volume")
   101  	createSnasphotInput := &ec2.CreateSnapshotInput{
   102  		Description: aws.String("snapshot for unikernel image " + params.Name),
   103  		VolumeId:    aws.String(volumeId),
   104  	}
   105  	createSnapshotOutput, err := ec2svc.CreateSnapshot(createSnasphotInput)
   106  	if err != nil {
   107  		return nil, errors.New("creating aws snapshot", err)
   108  	}
   109  	snapshotId = *createSnapshotOutput.SnapshotId
   110  
   111  	snapDesc := &ec2.DescribeSnapshotsInput{
   112  		SnapshotIds: []*string{aws.String(snapshotId)},
   113  	}
   114  	err = ec2svc.WaitUntilSnapshotCompleted(snapDesc)
   115  	if err != nil {
   116  		return nil, errors.New("waiting for snapshot to complete", err)
   117  	}
   118  
   119  	blockDeviceMappings := []*ec2.BlockDeviceMapping{}
   120  	rootDeviceName := ""
   121  	for _, deviceMapping := range params.RawImage.RunSpec.DeviceMappings {
   122  		if deviceMapping.MountPoint == "/" {
   123  			blockDeviceMappings = append(blockDeviceMappings, &ec2.BlockDeviceMapping{
   124  				DeviceName: aws.String(deviceMapping.DeviceName),
   125  				Ebs: &ec2.EbsBlockDevice{
   126  					SnapshotId: aws.String(snapshotId),
   127  				},
   128  			})
   129  			rootDeviceName = deviceMapping.DeviceName
   130  			break
   131  		}
   132  	}
   133  	if len(blockDeviceMappings) < 1 {
   134  		return nil, errors.New("did not find root device mapping for image", nil)
   135  	}
   136  
   137  	architecture := "x86_64"
   138  	kernelId := aws.String(kernelIdMap[p.config.Region])
   139  	switch params.RawImage.StageSpec.XenVirtualizationType {
   140  	case types.XenVirtualizationType_HVM:
   141  		kernelId = nil //no kernel id for HVM
   142  	}
   143  
   144  	logrus.WithFields(logrus.Fields{
   145  		"name":                  params.Name,
   146  		"architecture":          architecture,
   147  		"virtualization-type":   params.RawImage.StageSpec.XenVirtualizationType,
   148  		"kernel-id":             kernelId,
   149  		"block-device-mappings": blockDeviceMappings,
   150  		"root-device-name":      rootDeviceName,
   151  	}).Infof("creating AMI for unikernel image")
   152  
   153  	registerImageInput := &ec2.RegisterImageInput{
   154  		Name:                aws.String(params.Name),
   155  		Architecture:        aws.String(architecture),
   156  		BlockDeviceMappings: blockDeviceMappings,
   157  		RootDeviceName:      aws.String(rootDeviceName),
   158  		VirtualizationType:  aws.String(string(params.RawImage.StageSpec.XenVirtualizationType)),
   159  		KernelId:            kernelId,
   160  	}
   161  
   162  	registerImageOutput, err := ec2svc.RegisterImage(registerImageInput)
   163  	if err != nil {
   164  		return nil, errors.New("registering snapshot as image", err)
   165  	}
   166  
   167  	imageId := *registerImageOutput.ImageId
   168  
   169  	logrus.WithField("volume-id", volumeId).Infof("tagging image, snapshot, and volume with unikernel id")
   170  	tagObjects := &ec2.CreateTagsInput{
   171  		Resources: []*string{
   172  			aws.String(imageId),
   173  			aws.String(snapshotId),
   174  			aws.String(volumeId),
   175  		},
   176  		Tags: []*ec2.Tag{
   177  			&ec2.Tag{
   178  				Key:   aws.String(UNIK_IMAGE_ID),
   179  				Value: aws.String(imageId),
   180  			},
   181  			&ec2.Tag{
   182  				Key:   aws.String("Name"),
   183  				Value: aws.String(params.Name),
   184  			},
   185  		},
   186  	}
   187  	_, err = ec2svc.CreateTags(tagObjects)
   188  	if err != nil {
   189  		return nil, errors.New("tagging snapshot, image, and volume", err)
   190  	}
   191  
   192  	sizeMb := imageSize >> 20
   193  
   194  	image := &types.Image{
   195  		Id:             imageId,
   196  		Name:           params.Name,
   197  		RunSpec:        params.RawImage.RunSpec,
   198  		StageSpec:      params.RawImage.StageSpec,
   199  		SizeMb:         sizeMb,
   200  		Infrastructure: types.Infrastructure_AWS,
   201  		Created:        time.Now(),
   202  	}
   203  	if err := p.state.ModifyImages(func(images map[string]*types.Image) error {
   204  		images[imageId] = image
   205  		return nil
   206  	}); err != nil {
   207  		return nil, errors.New("modifying image map in state", err)
   208  	}
   209  
   210  	logrus.WithFields(logrus.Fields{"image": image}).Infof("image created succesfully")
   211  	return image, nil
   212  }