Example #1
0
// 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
}
Example #3
0
// 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
}
Example #4
0
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)
}
Example #5
0
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)
	}
}