github.phpd.cn/hashicorp/packer@v1.3.2/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/openstack/blockstorage/extensions/volumeactions"
    10  
    11  	"github.com/gophercloud/gophercloud"
    12  	"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
    13  	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
    14  	"github.com/hashicorp/packer/helper/multistep"
    15  	"github.com/hashicorp/packer/packer"
    16  )
    17  
    18  type stepCreateImage struct {
    19  	UseBlockStorageVolume bool
    20  }
    21  
    22  func (s *stepCreateImage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    23  	config := state.Get("config").(*Config)
    24  	server := state.Get("server").(*servers.Server)
    25  	ui := state.Get("ui").(packer.Ui)
    26  
    27  	// We need the v2 compute client
    28  	client, err := config.computeV2Client()
    29  	if err != nil {
    30  		err = fmt.Errorf("Error initializing compute client: %s", err)
    31  		state.Put("error", err)
    32  		return multistep.ActionHalt
    33  	}
    34  
    35  	// Create the image.
    36  	// Image source depends on the type of the Compute instance. It can be
    37  	// Block Storage service volume or regular Compute service local volume.
    38  	ui.Say(fmt.Sprintf("Creating the image: %s", config.ImageName))
    39  	var imageId string
    40  	if s.UseBlockStorageVolume {
    41  		// We need the v3 block storage client.
    42  		blockStorageClient, err := config.blockStorageV3Client()
    43  		if err != nil {
    44  			err = fmt.Errorf("Error initializing block storage client: %s", err)
    45  			state.Put("error", err)
    46  			return multistep.ActionHalt
    47  		}
    48  		volume := state.Get("volume_id").(string)
    49  		image, err := volumeactions.UploadImage(blockStorageClient, volume, volumeactions.UploadImageOpts{
    50  			DiskFormat: config.ImageDiskFormat,
    51  			ImageName:  config.ImageName,
    52  		}).Extract()
    53  		if err != nil {
    54  			err := fmt.Errorf("Error creating image: %s", err)
    55  			state.Put("error", err)
    56  			ui.Error(err.Error())
    57  			return multistep.ActionHalt
    58  		}
    59  		imageId = image.ImageID
    60  	} else {
    61  		imageId, err = servers.CreateImage(client, server.ID, servers.CreateImageOpts{
    62  			Name:     config.ImageName,
    63  			Metadata: config.ImageMetadata,
    64  		}).ExtractImageID()
    65  		if err != nil {
    66  			err := fmt.Errorf("Error creating image: %s", err)
    67  			state.Put("error", err)
    68  			ui.Error(err.Error())
    69  			return multistep.ActionHalt
    70  		}
    71  	}
    72  
    73  	// Set the Image ID in the state
    74  	ui.Message(fmt.Sprintf("Image: %s", imageId))
    75  	state.Put("image", imageId)
    76  
    77  	// Wait for the image to become ready
    78  	ui.Say(fmt.Sprintf("Waiting for image %s (image id: %s) to become ready...", config.ImageName, imageId))
    79  	if err := WaitForImage(client, imageId); err != nil {
    80  		err := fmt.Errorf("Error waiting for image: %s", err)
    81  		state.Put("error", err)
    82  		ui.Error(err.Error())
    83  		return multistep.ActionHalt
    84  	}
    85  
    86  	return multistep.ActionContinue
    87  }
    88  
    89  func (s *stepCreateImage) Cleanup(multistep.StateBag) {
    90  	// No cleanup...
    91  }
    92  
    93  // WaitForImage waits for the given Image ID to become ready.
    94  func WaitForImage(client *gophercloud.ServiceClient, imageId string) error {
    95  	maxNumErrors := 10
    96  	numErrors := 0
    97  
    98  	for {
    99  		image, err := images.Get(client, imageId).Extract()
   100  		if err != nil {
   101  			errCode, ok := err.(*gophercloud.ErrUnexpectedResponseCode)
   102  			if ok && (errCode.Actual == 500 || errCode.Actual == 404) {
   103  				numErrors++
   104  				if numErrors >= maxNumErrors {
   105  					log.Printf("[ERROR] Maximum number of errors (%d) reached; failing with: %s", numErrors, err)
   106  					return err
   107  				}
   108  				log.Printf("[ERROR] %d error received, will ignore and retry: %s", errCode.Actual, err)
   109  				time.Sleep(2 * time.Second)
   110  				continue
   111  			}
   112  
   113  			return err
   114  		}
   115  
   116  		if image.Status == "ACTIVE" {
   117  			return nil
   118  		}
   119  
   120  		log.Printf("Waiting for image creation status: %s (%d%%)", image.Status, image.Progress)
   121  		time.Sleep(2 * time.Second)
   122  	}
   123  }