// AMIStateRefreshFunc returns a StateRefreshFunc that is used to watch // an AMI for state changes. func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc { return func() (interface{}, string, error) { resp, err := conn.DescribeImages(&ec2.DescribeImagesInput{ ImageIDs: []*string{&imageId}, }) if err != nil { if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidAMIID.NotFound" { // Set this to nil as if we didn't find anything. resp = nil } else if isTransientNetworkError(err) { // Transient network error, treat it as if we didn't find anything resp = nil } else { log.Printf("Error on AMIStateRefresh: %s", err) return nil, "", err } } if resp == nil || len(resp.Images) == 0 { // Sometimes AWS has consistency issues and doesn't see the // AMI. Return an empty state. return nil, "", nil } i := resp.Images[0] return i, *i.State, nil } }
func fetchRootDeviceName(ami string, conn *ec2.EC2) (*string, error) { if ami == "" { return nil, fmt.Errorf("Cannot fetch root device name for blank AMI ID.") } log.Printf("[DEBUG] Describing AMI %q to get root block device name", ami) res, err := conn.DescribeImages(&ec2.DescribeImagesInput{ ImageIds: []*string{aws.String(ami)}, }) if err != nil { return nil, err } // For a bad image, we just return nil so we don't block a refresh if len(res.Images) == 0 { return nil, nil } image := res.Images[0] rootDeviceName := image.RootDeviceName // Instance store backed AMIs do not provide a root device name. if *image.RootDeviceType == ec2.DeviceTypeInstanceStore { return nil, nil } // Some AMIs have a RootDeviceName like "/dev/sda1" that does not appear as a // DeviceName in the BlockDeviceMapping list (which will instead have // something like "/dev/sda") // // While this seems like it breaks an invariant of AMIs, it ends up working // on the AWS side, and AMIs like this are common enough that we need to // special case it so Terraform does the right thing. // // Our heuristic is: if the RootDeviceName does not appear in the // BlockDeviceMapping, assume that the DeviceName of the first // BlockDeviceMapping entry serves as the root device. rootDeviceNameInMapping := false for _, bdm := range image.BlockDeviceMappings { if bdm.DeviceName == image.RootDeviceName { rootDeviceNameInMapping = true } } if !rootDeviceNameInMapping && len(image.BlockDeviceMappings) > 0 { rootDeviceName = image.BlockDeviceMappings[0].DeviceName } if rootDeviceName == nil { return nil, fmt.Errorf("[WARN] Error finding Root Device Name for AMI (%s)", ami) } return rootDeviceName, nil }
// Return true if AMI exists func queryAmi(service *ec2.EC2, ami string) interface{} { input := ec2.DescribeImagesInput{ ImageIds: []*string{&ami}, } output, err := service.DescribeImages(&input) if len(output.Images) > 0 { checkError(err) image := output.Images[0] log.Printf("Found image in account: %s, with name: %s\n", *image.OwnerId, *image.Name) log.Printf("Tags: %v", image.Tags) return image } return nil }
func processAmis(svc *ec2.EC2, pageSize int64, apply func([]*string)) { imagesOut, err := svc.DescribeImages(&ec2.DescribeImagesInput{ Owners: []*string{&self}, }) kingpin.FatalIfError(err, "Could not retrieve EC2 instances") var imageIds []*string for _, image := range imagesOut.Images { imageIds = append(imageIds, image.ImageId) } apply(imageIds) }
func resourceAwsAmiWaitForAvailable(id string, client *ec2.EC2) (*ec2.Image, error) { log.Printf("Waiting for AMI %s to become available...", id) req := &ec2.DescribeImagesInput{ ImageIds: []*string{aws.String(id)}, } pollsWhereNotFound := 0 for { res, err := client.DescribeImages(req) if err != nil { // When using RegisterImage (for aws_ami) the AMI sometimes isn't available at all // right after the API responds, so we need to tolerate a couple Not Found errors // before an available AMI shows up. if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidAMIID.NotFound" { pollsWhereNotFound++ // We arbitrarily stop polling after getting a "not found" error five times, // assuming that the AMI has been deleted by something other than Terraform. if pollsWhereNotFound > 5 { return nil, fmt.Errorf("gave up waiting for AMI to be created: %s", err) } time.Sleep(4 * time.Second) continue } return nil, fmt.Errorf("error reading AMI: %s", err) } if len(res.Images) != 1 { return nil, fmt.Errorf("new AMI vanished while pending") } state := *res.Images[0].State if state == "pending" { // Give it a few seconds before we poll again. time.Sleep(4 * time.Second) continue } if state == "available" { // We're done! return res.Images[0], nil } // If we're not pending or available then we're in one of the invalid/error // states, so stop polling and bail out. stateReason := *res.Images[0].StateReason return nil, fmt.Errorf("new AMI became %s while pending: %s", state, stateReason) } }