github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/openstack/step_create_image.go (about)

     1  package openstack
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/gophercloud/gophercloud"
    10  	"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
    11  	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
    12  	"github.com/hashicorp/packer/helper/multistep"
    13  	"github.com/hashicorp/packer/packer"
    14  )
    15  
    16  type stepCreateImage struct{}
    17  
    18  func (s *stepCreateImage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    19  	config := state.Get("config").(Config)
    20  	server := state.Get("server").(*servers.Server)
    21  	ui := state.Get("ui").(packer.Ui)
    22  
    23  	// We need the v2 compute client
    24  	client, err := config.computeV2Client()
    25  	if err != nil {
    26  		err = fmt.Errorf("Error initializing compute client: %s", err)
    27  		state.Put("error", err)
    28  		return multistep.ActionHalt
    29  	}
    30  
    31  	// Create the image
    32  	ui.Say(fmt.Sprintf("Creating the image: %s", config.ImageName))
    33  	imageId, err := servers.CreateImage(client, server.ID, servers.CreateImageOpts{
    34  		Name:     config.ImageName,
    35  		Metadata: config.ImageMetadata,
    36  	}).ExtractImageID()
    37  	if err != nil {
    38  		err := fmt.Errorf("Error creating image: %s", err)
    39  		state.Put("error", err)
    40  		ui.Error(err.Error())
    41  		return multistep.ActionHalt
    42  	}
    43  
    44  	// Set the Image ID in the state
    45  	ui.Message(fmt.Sprintf("Image: %s", imageId))
    46  	state.Put("image", imageId)
    47  
    48  	// Wait for the image to become ready
    49  	ui.Say(fmt.Sprintf("Waiting for image %s (image id: %s) to become ready...", config.ImageName, imageId))
    50  	if err := WaitForImage(client, imageId); err != nil {
    51  		err := fmt.Errorf("Error waiting for image: %s", err)
    52  		state.Put("error", err)
    53  		ui.Error(err.Error())
    54  		return multistep.ActionHalt
    55  	}
    56  
    57  	return multistep.ActionContinue
    58  }
    59  
    60  func (s *stepCreateImage) Cleanup(multistep.StateBag) {
    61  	// No cleanup...
    62  }
    63  
    64  // WaitForImage waits for the given Image ID to become ready.
    65  func WaitForImage(client *gophercloud.ServiceClient, imageId string) error {
    66  	maxNumErrors := 10
    67  	numErrors := 0
    68  
    69  	for {
    70  		image, err := images.Get(client, imageId).Extract()
    71  		if err != nil {
    72  			errCode, ok := err.(*gophercloud.ErrUnexpectedResponseCode)
    73  			if ok && (errCode.Actual == 500 || errCode.Actual == 404) {
    74  				numErrors++
    75  				if numErrors >= maxNumErrors {
    76  					log.Printf("[ERROR] Maximum number of errors (%d) reached; failing with: %s", numErrors, err)
    77  					return err
    78  				}
    79  				log.Printf("[ERROR] %d error received, will ignore and retry: %s", errCode.Actual, err)
    80  				time.Sleep(2 * time.Second)
    81  				continue
    82  			}
    83  
    84  			return err
    85  		}
    86  
    87  		if image.Status == "ACTIVE" {
    88  			return nil
    89  		}
    90  
    91  		log.Printf("Waiting for image creation status: %s (%d%%)", image.Status, image.Progress)
    92  		time.Sleep(2 * time.Second)
    93  	}
    94  }