func (clst amazonCluster) Stop(machines []Machine) error { var awsIDs []awsID for _, m := range machines { awsIDs = append(awsIDs, awsID{ region: m.Region, spotID: m.ID, }) } for region, ids := range groupByRegion(awsIDs) { session := clst.getSession(region) spotIDs := getSpotIDs(ids) spots, err := session.DescribeSpotInstanceRequests( &ec2.DescribeSpotInstanceRequestsInput{ SpotInstanceRequestIds: aws.StringSlice(spotIDs), }) if err != nil { return err } instIds := []string{} for _, spot := range spots.SpotInstanceRequests { if spot.InstanceId != nil { instIds = append(instIds, *spot.InstanceId) } } if len(instIds) > 0 { _, err = session.TerminateInstances(&ec2.TerminateInstancesInput{ InstanceIds: aws.StringSlice(instIds), }) if err != nil { return err } } _, err = session.CancelSpotInstanceRequests( &ec2.CancelSpotInstanceRequestsInput{ SpotInstanceRequestIds: aws.StringSlice(spotIDs), }) if err != nil { return err } if err := clst.wait(ids, false); err != nil { return err } } return nil }
func (clst amazonCluster) List() ([]Machine, error) { machines := []Machine{} for region := range amis { session := clst.getSession(region) spots, err := session.DescribeSpotInstanceRequests(nil) if err != nil { return nil, err } insts, err := session.DescribeInstances(&ec2.DescribeInstancesInput{ Filters: []*ec2.Filter{ { Name: aws.String("instance.group-name"), Values: []*string{aws.String(clst.namespace)}, }, }, }) if err != nil { return nil, err } instMap := make(map[string]*ec2.Instance) for _, res := range insts.Reservations { for _, inst := range res.Instances { instMap[*inst.InstanceId] = inst } } for _, spot := range spots.SpotInstanceRequests { if *spot.State != ec2.SpotInstanceStateActive && *spot.State != ec2.SpotInstanceStateOpen { continue } var inst *ec2.Instance if spot.InstanceId != nil { inst = instMap[*spot.InstanceId] } // Due to a race condition in the AWS API, it's possible that // spot requests might lose their Tags. If handled naively, // those spot requests would technically be without a namespace, // meaning the instances they create would be live forever as // zombies. // // To mitigate this issue, we rely not only on the spot request // tags, but additionally on the instance security group. If a // spot request has a running instance in the appropriate // security group, it is by definition in our namespace. // Thus, we only check the tags for spot requests without // running instances. if inst == nil { var isOurs bool for _, tag := range spot.Tags { ns := clst.namespace if tag != nil && tag.Key != nil && *tag.Key == ns { isOurs = true break } } if !isOurs { continue } } machine := Machine{ ID: *spot.SpotInstanceRequestId, Region: region, Provider: db.Amazon, } if inst == nil { continue } if *inst.State.Name != ec2.InstanceStateNamePending && *inst.State.Name != ec2.InstanceStateNameRunning { continue } if inst.PublicIpAddress != nil { machine.PublicIP = *inst.PublicIpAddress } if inst.PrivateIpAddress != nil { machine.PrivateIP = *inst.PrivateIpAddress } if inst.InstanceType != nil { machine.Size = *inst.InstanceType } if len(inst.BlockDeviceMappings) != 0 { volumeID := inst.BlockDeviceMappings[0].Ebs.VolumeId filters := []*ec2.Filter{ { Name: aws.String("volume-id"), Values: []*string{ aws.String(*volumeID), }, }, } volumeInfo, err := session.DescribeVolumes( &ec2.DescribeVolumesInput{ Filters: filters, }) if err != nil { return nil, err } if len(volumeInfo.Volumes) == 1 { machine.DiskSize = int( *volumeInfo.Volumes[0].Size) } } machines = append(machines, machine) } } return machines, nil }