// blockDeviceCosts returns the total price of a slice of BlockDevices over the given duration // by using the EC2 API. func blockDeviceCosts(handle *ec2.EC2, devices []ec2.BlockDevice, dur time.Duration) (float64, error) { cost := 0.0 if len(devices) > 0 { volumeIds := []string{} for _, bd := range devices { volumeIds = append(volumeIds, bd.EBS.VolumeId) } vols, err := handle.Volumes(volumeIds, nil) if err != nil { return 0, err } for _, v := range vols.Volumes { // an amazon region is just the availability zone minus the final letter region := azToRegion(v.AvailZone) size, err := strconv.Atoi(v.Size) if err != nil { return 0, fmt.Errorf("reading volume size: %v", err) } p, err := ebsCost(&pkgEBSFetcher, region, size, dur) if err != nil { return 0, fmt.Errorf("EBS volume %v: %v", v.VolumeId, err) } cost += p } } return cost, nil }
func terminateInstances(c *gocheck.C, e *ec2.EC2, insts []*ec2.Instance) { var ids []string for _, inst := range insts { if inst != nil { ids = append(ids, inst.InstanceId) } } _, err := e.TerminateInstances(ids) c.Check(err, gocheck.IsNil, gocheck.Commentf("%d INSTANCES LEFT RUNNING!!!", len(ids))) }
//attachTags makes a call to EC2 to attach the given map of tags to a resource. func attachTags(ec2Handle *ec2.EC2, tags map[string]string, instance string) error { tagSlice := []ec2.Tag{} for tag, value := range tags { tagSlice = append(tagSlice, ec2.Tag{tag, value}) } _, err := ec2Handle.CreateTags([]string{instance}, tagSlice) return err }
//getInstanceInfo returns the full ec2 instance info for the given instance ID. //Note that this is the *instance* id, not the spot request ID, which is different. func getInstanceInfo(ec2Handle *ec2.EC2, instanceId string) (*ec2.Instance, error) { resp, err := ec2Handle.DescribeInstances([]string{instanceId}, nil) if err != nil { return nil, err } reservation := resp.Reservations if len(reservation) < 1 { return nil, evergreen.Logger.Errorf(slogger.ERROR, "No reservation found for "+ "instance id: %v", instanceId) } instances := reservation[0].Instances if len(instances) < 1 { return nil, evergreen.Logger.Errorf(slogger.ERROR, "'%v' was not found in "+ "reservation '%v'", instanceId, resp.Reservations[0].ReservationId) } return &instances[0], nil }
func startEC2Instance(ec2Handle *ec2.EC2, options *ec2.RunInstancesOptions, intentHost *host.Host) (*host.Host, *ec2.RunInstancesResp, error) { // start the instance resp, err := ec2Handle.RunInstances(options) if err != nil { // remove the intent host document rmErr := intentHost.Remove() if rmErr != nil { evergreen.Logger.Errorf(slogger.ERROR, "Could not remove intent host "+ "“%v”: %v", intentHost.Id, rmErr) } return nil, nil, evergreen.Logger.Errorf(slogger.ERROR, "EC2 RunInstances API call returned error: %v", err) } evergreen.Logger.Logf(slogger.DEBUG, "Spawned %v instance", len(resp.Instances)) // the instance should have been successfully spawned instance := resp.Instances[0] evergreen.Logger.Logf(slogger.DEBUG, "Started %v", instance.InstanceId) evergreen.Logger.Logf(slogger.DEBUG, "Key name: %v", string(options.KeyName)) // find old intent host host, err := host.FindOne(host.ById(intentHost.Id)) if host == nil { return nil, nil, evergreen.Logger.Errorf(slogger.ERROR, "Can't locate "+ "record inserted for intended host “%v”", intentHost.Id) } if err != nil { return nil, nil, evergreen.Logger.Errorf(slogger.ERROR, "Can't locate "+ "record inserted for intended host “%v” due to error: %v", intentHost.Id, err) } // we found the old document now we can insert the new one host.Id = instance.InstanceId err = host.Insert() if err != nil { return nil, nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not insert "+ "updated host information for “%v” with “%v”: %v", intentHost.Id, host.Id, err) } // remove the intent host document err = intentHost.Remove() if err != nil { return nil, nil, evergreen.Logger.Errorf(slogger.ERROR, "Could not remove "+ "insert host “%v” (replaced by “%v”): %v", intentHost.Id, host.Id, err) } var infoResp *ec2.DescribeInstancesResp instanceInfoRetryCount := 0 instanceInfoMaxRetries := 5 for { infoResp, err = ec2Handle.DescribeInstances([]string{instance.InstanceId}, nil) if err != nil { instanceInfoRetryCount++ if instanceInfoRetryCount == instanceInfoMaxRetries { evergreen.Logger.Errorf(slogger.ERROR, "There was an error querying for the "+ "instance's information and retries are exhausted. The insance may "+ "be up.") return nil, resp, err } evergreen.Logger.Errorf(slogger.DEBUG, "There was an error querying for the "+ "instance's information. Retrying in 30 seconds. Error: %v", err) time.Sleep(30 * time.Second) continue } break } reservations := infoResp.Reservations if len(reservations) < 1 { return nil, resp, fmt.Errorf("Reservation was returned as nil, you " + "may have to check manually") } instancesInfo := reservations[0].Instances if len(instancesInfo) < 1 { return nil, resp, fmt.Errorf("Reservation appears to have no " + "associated instances") } return host, resp, nil }