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 }