github.phpd.cn/hashicorp/packer@v1.3.2/builder/googlecompute/step_create_instance.go (about) 1 package googlecompute 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "time" 9 10 "github.com/hashicorp/packer/helper/multistep" 11 "github.com/hashicorp/packer/packer" 12 ) 13 14 // StepCreateInstance represents a Packer build step that creates GCE instances. 15 type StepCreateInstance struct { 16 Debug bool 17 } 18 19 func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string) (map[string]string, error) { 20 instanceMetadata := make(map[string]string) 21 var err error 22 23 // Copy metadata from config. 24 for k, v := range c.Metadata { 25 instanceMetadata[k] = v 26 } 27 28 // Merge any existing ssh keys with our public key, unless there is no 29 // supplied public key. This is possible if a private_key_file was 30 // specified. 31 if sshPublicKey != "" { 32 sshMetaKey := "sshKeys" 33 sshKeys := fmt.Sprintf("%s:%s", c.Comm.SSHUsername, sshPublicKey) 34 if confSshKeys, exists := instanceMetadata[sshMetaKey]; exists { 35 sshKeys = fmt.Sprintf("%s\n%s", sshKeys, confSshKeys) 36 } 37 instanceMetadata[sshMetaKey] = sshKeys 38 } 39 40 // Wrap any startup script with our own startup script. 41 if c.StartupScriptFile != "" { 42 var content []byte 43 content, err = ioutil.ReadFile(c.StartupScriptFile) 44 instanceMetadata[StartupWrappedScriptKey] = string(content) 45 } else if wrappedStartupScript, exists := instanceMetadata[StartupScriptKey]; exists { 46 instanceMetadata[StartupWrappedScriptKey] = wrappedStartupScript 47 } 48 if sourceImage.IsWindows() { 49 // Windows startup script support is not yet implemented. 50 // Mark the startup script as done. 51 instanceMetadata[StartupScriptKey] = StartupScriptWindows 52 instanceMetadata[StartupScriptStatusKey] = StartupScriptStatusDone 53 } else { 54 instanceMetadata[StartupScriptKey] = StartupScriptLinux 55 instanceMetadata[StartupScriptStatusKey] = StartupScriptStatusNotDone 56 } 57 58 return instanceMetadata, err 59 } 60 61 func getImage(c *Config, d Driver) (*Image, error) { 62 name := c.SourceImageFamily 63 fromFamily := true 64 if c.SourceImage != "" { 65 name = c.SourceImage 66 fromFamily = false 67 } 68 if c.SourceImageProjectId == "" { 69 return d.GetImage(name, fromFamily) 70 } else { 71 return d.GetImageFromProject(c.SourceImageProjectId, name, fromFamily) 72 } 73 } 74 75 // Run executes the Packer build step that creates a GCE instance. 76 func (s *StepCreateInstance) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { 77 c := state.Get("config").(*Config) 78 d := state.Get("driver").(Driver) 79 80 ui := state.Get("ui").(packer.Ui) 81 82 sourceImage, err := getImage(c, d) 83 if err != nil { 84 err := fmt.Errorf("Error getting source image for instance creation: %s", err) 85 state.Put("error", err) 86 ui.Error(err.Error()) 87 return multistep.ActionHalt 88 } 89 90 ui.Say(fmt.Sprintf("Using image: %s", sourceImage.Name)) 91 92 if sourceImage.IsWindows() && c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" { 93 state.Put("create_windows_password", true) 94 } 95 96 ui.Say("Creating instance...") 97 name := c.InstanceName 98 99 var errCh <-chan error 100 var metadata map[string]string 101 metadata, err = c.createInstanceMetadata(sourceImage, string(c.Comm.SSHPublicKey)) 102 errCh, err = d.RunInstance(&InstanceConfig{ 103 AcceleratorType: c.AcceleratorType, 104 AcceleratorCount: c.AcceleratorCount, 105 Address: c.Address, 106 Description: "New instance created by Packer", 107 DisableDefaultServiceAccount: c.DisableDefaultServiceAccount, 108 DiskSizeGb: c.DiskSizeGb, 109 DiskType: c.DiskType, 110 Image: sourceImage, 111 Labels: c.Labels, 112 MachineType: c.MachineType, 113 Metadata: metadata, 114 MinCpuPlatform: c.MinCpuPlatform, 115 Name: name, 116 Network: c.Network, 117 NetworkProjectId: c.NetworkProjectId, 118 OmitExternalIP: c.OmitExternalIP, 119 OnHostMaintenance: c.OnHostMaintenance, 120 Preemptible: c.Preemptible, 121 Region: c.Region, 122 ServiceAccountEmail: c.ServiceAccountEmail, 123 Scopes: c.Scopes, 124 Subnetwork: c.Subnetwork, 125 Tags: c.Tags, 126 Zone: c.Zone, 127 }) 128 129 if err == nil { 130 ui.Message("Waiting for creation operation to complete...") 131 select { 132 case err = <-errCh: 133 case <-time.After(c.stateTimeout): 134 err = errors.New("time out while waiting for instance to create") 135 } 136 } 137 138 if err != nil { 139 err := fmt.Errorf("Error creating instance: %s", err) 140 state.Put("error", err) 141 ui.Error(err.Error()) 142 return multistep.ActionHalt 143 } 144 145 ui.Message("Instance has been created!") 146 147 if s.Debug { 148 if name != "" { 149 ui.Message(fmt.Sprintf("Instance: %s started in %s", name, c.Zone)) 150 } 151 } 152 153 // Things succeeded, store the name so we can remove it later 154 state.Put("instance_name", name) 155 156 return multistep.ActionContinue 157 } 158 159 // Cleanup destroys the GCE instance created during the image creation process. 160 func (s *StepCreateInstance) Cleanup(state multistep.StateBag) { 161 nameRaw, ok := state.GetOk("instance_name") 162 if !ok { 163 return 164 } 165 name := nameRaw.(string) 166 if name == "" { 167 return 168 } 169 170 config := state.Get("config").(*Config) 171 driver := state.Get("driver").(Driver) 172 ui := state.Get("ui").(packer.Ui) 173 174 ui.Say("Deleting instance...") 175 errCh, err := driver.DeleteInstance(config.Zone, name) 176 if err == nil { 177 select { 178 case err = <-errCh: 179 case <-time.After(config.stateTimeout): 180 err = errors.New("time out while waiting for instance to delete") 181 } 182 } 183 184 if err != nil { 185 ui.Error(fmt.Sprintf( 186 "Error deleting instance. Please delete it manually.\n\n"+ 187 "Name: %s\n"+ 188 "Error: %s", name, err)) 189 } 190 191 ui.Message("Instance has been deleted!") 192 state.Put("instance_name", "") 193 194 // Deleting the instance does not remove the boot disk. This cleanup removes 195 // the disk. 196 ui.Say("Deleting disk...") 197 errCh, err = driver.DeleteDisk(config.Zone, config.DiskName) 198 if err == nil { 199 select { 200 case err = <-errCh: 201 case <-time.After(config.stateTimeout): 202 err = errors.New("time out while waiting for disk to delete") 203 } 204 } 205 206 if err != nil { 207 ui.Error(fmt.Sprintf( 208 "Error deleting disk. Please delete it manually.\n\n"+ 209 "Name: %s\n"+ 210 "Error: %s", config.InstanceName, err)) 211 } 212 213 ui.Message("Disk has been deleted!") 214 215 return 216 }