github.com/emc-advanced-dev/unik@v0.0.0-20190717152701-a58d3e8e33b7/pkg/providers/aws/run_instance.go (about) 1 package aws 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "github.com/sirupsen/logrus" 7 "github.com/aws/aws-sdk-go/aws" 8 "github.com/aws/aws-sdk-go/service/ec2" 9 "github.com/emc-advanced-dev/pkg/errors" 10 "github.com/solo-io/unik/pkg/providers/common" 11 "github.com/solo-io/unik/pkg/types" 12 "time" 13 ) 14 15 func (p *AwsProvider) RunInstance(params types.RunInstanceParams) (_ *types.Instance, err error) { 16 logrus.WithFields(logrus.Fields{ 17 "image-id": params.ImageId, 18 "mounts": params.MntPointsToVolumeIds, 19 "env": params.Env, 20 }).Infof("running instance %s", params.Name) 21 22 var instanceId string 23 ec2svc := p.newEC2() 24 25 defer func() { 26 if err != nil { 27 logrus.WithError(err).Errorf("aws running instance encountered an error") 28 if instanceId != "" { 29 if params.NoCleanup { 30 logrus.Warnf("because --no-cleanup flag was provided, not cleaning up failed instance %s0", instanceId) 31 return 32 } 33 logrus.Warnf("cleaning up instance %s", instanceId) 34 terminateInstanceInput := &ec2.TerminateInstancesInput{ 35 InstanceIds: []*string{aws.String(instanceId)}, 36 } 37 ec2svc.TerminateInstances(terminateInstanceInput) 38 if cleanupErr := p.state.ModifyInstances(func(instances map[string]*types.Instance) error { 39 delete(instances, instanceId) 40 return nil 41 }); cleanupErr != nil { 42 logrus.Error(errors.New("modifying instance map in state", cleanupErr)) 43 } 44 } 45 } 46 }() 47 48 image, err := p.GetImage(params.ImageId) 49 if err != nil { 50 return nil, errors.New("getting image", err) 51 } 52 53 if err := common.VerifyMntsInput(p, image, params.MntPointsToVolumeIds); err != nil { 54 return nil, errors.New("invalid mapping for volume", err) 55 } 56 57 envData, err := json.Marshal(params.Env) 58 if err != nil { 59 return nil, errors.New("could not convert instance env to json", err) 60 } 61 encodedData := base64.StdEncoding.EncodeToString(envData) 62 63 //if not set, use default 64 if params.InstanceMemory <= 0 { 65 params.InstanceMemory = image.RunSpec.DefaultInstanceMemory 66 } 67 68 instanceType, err := getInstanceType(image.StageSpec.XenVirtualizationType, params.InstanceMemory) 69 if err != nil { 70 return nil, errors.New("could not find instance type for specified memory", err) 71 } 72 73 logrus.Debugf("determined intstance type %s for memory requirement %v", instanceType, params.InstanceMemory) 74 75 runInstanceInput := &ec2.RunInstancesInput{ 76 ImageId: aws.String(image.Id), 77 MinCount: aws.Int64(1), 78 MaxCount: aws.Int64(1), 79 Placement: &ec2.Placement{ 80 AvailabilityZone: aws.String(p.config.Zone), 81 }, 82 InstanceType: aws.String(instanceType), 83 UserData: aws.String(encodedData), 84 } 85 86 runInstanceOutput, err := ec2svc.RunInstances(runInstanceInput) 87 if err != nil { 88 return nil, errors.New("failed to run instance", err) 89 } 90 if len(runInstanceOutput.Instances) < 1 { 91 logrus.WithFields(logrus.Fields{"output": runInstanceOutput}).Errorf("run instance %s failed, produced %v instances, expected 1", params.Name, len(runInstanceOutput.Instances)) 92 return nil, errors.New("expected 1 instance to be created", nil) 93 } 94 instanceId = *runInstanceOutput.Instances[0].InstanceId 95 96 if len(runInstanceOutput.Instances) > 1 { 97 logrus.WithFields(logrus.Fields{"output": runInstanceOutput}).Errorf("run instance %s failed, produced %v instances, expected 1", params.Name, len(runInstanceOutput.Instances)) 98 return nil, errors.New("expected 1 instance to be created", nil) 99 } 100 101 //must add instance to state before attaching volumes 102 instance := &types.Instance{ 103 Id: instanceId, 104 Name: params.Name, 105 State: types.InstanceState_Pending, 106 Infrastructure: types.Infrastructure_AWS, 107 ImageId: image.Id, 108 Created: time.Now(), 109 } 110 111 if err := p.state.ModifyInstances(func(instances map[string]*types.Instance) error { 112 instances[instance.Id] = instance 113 return nil 114 }); err != nil { 115 return nil, errors.New("modifying instance map in state", err) 116 } 117 118 if len(params.MntPointsToVolumeIds) > 0 { 119 logrus.Debugf("stopping instance for volume attach") 120 waitParam := &ec2.DescribeInstancesInput{ 121 InstanceIds: []*string{aws.String(instanceId)}, 122 } 123 logrus.Debugf("waiting for instance to reach running state") 124 if err := ec2svc.WaitUntilInstanceRunning(waitParam); err != nil { 125 return nil, errors.New("waiting for instance to reach running state", err) 126 } 127 if err := p.StopInstance(instanceId); err != nil { 128 return nil, errors.New("failed to stop instance for attaching volumes", err) 129 } 130 for mountPoint, volumeId := range params.MntPointsToVolumeIds { 131 logrus.WithFields(logrus.Fields{"volume-id": volumeId}).Debugf("attaching volume %s to intance %s", volumeId, instanceId) 132 if err := p.AttachVolume(volumeId, instanceId, mountPoint); err != nil { 133 return nil, errors.New("attaching volume to instance", err) 134 } 135 } 136 if err := p.StartInstance(instanceId); err != nil { 137 return nil, errors.New("starting instance after volume attach", err) 138 } 139 } 140 141 tagObjects := &ec2.CreateTagsInput{ 142 Resources: []*string{ 143 aws.String(instanceId), 144 }, 145 Tags: []*ec2.Tag{ 146 &ec2.Tag{ 147 Key: aws.String("Name"), 148 Value: aws.String(params.Name), 149 }, 150 }, 151 } 152 _, err = ec2svc.CreateTags(tagObjects) 153 if err != nil { 154 return nil, errors.New("tagging snapshot, image, and volume", err) 155 } 156 157 logrus.WithFields(logrus.Fields{"instance": instance}).Infof("instance created succesfully") 158 159 return instance, nil 160 } 161 162 type instanceType struct { 163 memory int 164 name string 165 } 166 167 var hvmInstanceTypes = []instanceType{ 168 instanceType{memory: 512, name: "t2.nano"}, 169 instanceType{memory: 1024, name: "t2.micro"}, 170 instanceType{memory: 2048, name: "t2.small"}, 171 instanceType{memory: 4096, name: "t2.medium"}, 172 instanceType{memory: 8192, name: "t2.large"}, 173 instanceType{memory: 16384, name: "m4.xlarge"}, 174 } 175 176 var pvInstanceTypes = []instanceType{ 177 instanceType{memory: 1741, name: "m1.small"}, 178 instanceType{memory: 3789, name: "m1.medium"}, 179 instanceType{memory: 7680, name: "m1.large"}, 180 instanceType{memory: 15360, name: "m1.xlarge"}, 181 } 182 183 func getInstanceType(virtualizationType types.XenVirtualizationType, memoryRequirement int) (string, error) { 184 switch virtualizationType { 185 case types.XenVirtualizationType_HVM: 186 for _, instanceType := range hvmInstanceTypes { 187 if instanceType.memory >= memoryRequirement { 188 return instanceType.name, nil 189 } 190 } 191 return "", errors.New("memory requirement too large", nil) 192 case types.XenVirtualizationType_Paravirtual: 193 for _, instanceType := range pvInstanceTypes { 194 if instanceType.memory >= memoryRequirement { 195 return instanceType.name, nil 196 } 197 } 198 return "", errors.New("memory requirement too large", nil) 199 } 200 return "", errors.New("unknown virtualization type "+string(virtualizationType), nil) 201 }