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 }