github.com/Hashicorp/packer@v1.3.2/builder/profitbricks/step_create_server.go (about) 1 package profitbricks 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "strconv" 9 "strings" 10 "time" 11 12 "github.com/hashicorp/packer/helper/multistep" 13 "github.com/hashicorp/packer/packer" 14 "github.com/profitbricks/profitbricks-sdk-go" 15 ) 16 17 type stepCreateServer struct{} 18 19 func (s *stepCreateServer) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { 20 ui := state.Get("ui").(packer.Ui) 21 c := state.Get("config").(*Config) 22 23 profitbricks.SetAuth(c.PBUsername, c.PBPassword) 24 profitbricks.SetDepth("5") 25 if sshkey, ok := state.GetOk("publicKey"); ok { 26 c.SSHKey = sshkey.(string) 27 } 28 ui.Say("Creating Virtual Data Center...") 29 img := s.getImageId(c.Image, c) 30 alias := "" 31 if img == "" { 32 alias = s.getImageAlias(c.Image, c.Region, ui) 33 } 34 35 datacenter := profitbricks.Datacenter{ 36 Properties: profitbricks.DatacenterProperties{ 37 Name: c.SnapshotName, 38 Location: c.Region, 39 }, 40 } 41 server := profitbricks.Server{ 42 Properties: profitbricks.ServerProperties{ 43 Name: c.SnapshotName, 44 Ram: c.Ram, 45 Cores: c.Cores, 46 }, 47 Entities: &profitbricks.ServerEntities{ 48 Volumes: &profitbricks.Volumes{ 49 Items: []profitbricks.Volume{ 50 { 51 Properties: profitbricks.VolumeProperties{ 52 Type: c.DiskType, 53 Size: c.DiskSize, 54 Name: c.SnapshotName, 55 ImageAlias: alias, 56 Image: img, 57 }, 58 }, 59 }, 60 }, 61 }, 62 } 63 if c.SSHKey != "" { 64 server.Entities.Volumes.Items[0].Properties.SshKeys = []string{c.SSHKey} 65 } 66 67 if c.Comm.SSHPassword != "" { 68 server.Entities.Volumes.Items[0].Properties.ImagePassword = c.Comm.SSHPassword 69 } 70 71 datacenter = profitbricks.CompositeCreateDatacenter(datacenter) 72 if datacenter.StatusCode > 299 { 73 if datacenter.StatusCode > 299 { 74 var restError RestError 75 err := json.Unmarshal([]byte(datacenter.Response), &restError) 76 if err != nil { 77 ui.Error(fmt.Sprintf("Error decoding json response: %s", err.Error())) 78 return multistep.ActionHalt 79 } 80 if len(restError.Messages) > 0 { 81 ui.Error(restError.Messages[0].Message) 82 } else { 83 ui.Error(datacenter.Response) 84 } 85 return multistep.ActionHalt 86 } 87 } 88 89 err := s.waitTillProvisioned(datacenter.Headers.Get("Location"), *c) 90 if err != nil { 91 ui.Error(fmt.Sprintf("Error occurred while creating a datacenter %s", err.Error())) 92 return multistep.ActionHalt 93 } 94 95 state.Put("datacenter_id", datacenter.Id) 96 97 server = profitbricks.CreateServer(datacenter.Id, server) 98 if server.StatusCode > 299 { 99 ui.Error(fmt.Sprintf("Error occurred %s", parseErrorMessage(server.Response))) 100 return multistep.ActionHalt 101 } 102 103 err = s.waitTillProvisioned(server.Headers.Get("Location"), *c) 104 if err != nil { 105 ui.Error(fmt.Sprintf("Error occurred while creating a server %s", err.Error())) 106 return multistep.ActionHalt 107 } 108 109 lan := profitbricks.CreateLan(datacenter.Id, profitbricks.CreateLanRequest{ 110 Properties: profitbricks.CreateLanProperties{ 111 Public: true, 112 Name: c.SnapshotName, 113 }, 114 }) 115 116 if lan.StatusCode > 299 { 117 ui.Error(fmt.Sprintf("Error occurred %s", parseErrorMessage(lan.Response))) 118 return multistep.ActionHalt 119 } 120 121 err = s.waitTillProvisioned(lan.Headers.Get("Location"), *c) 122 if err != nil { 123 ui.Error(fmt.Sprintf("Error occurred while creating a LAN %s", err.Error())) 124 return multistep.ActionHalt 125 } 126 127 lanId, _ := strconv.Atoi(lan.Id) 128 nic := profitbricks.CreateNic(datacenter.Id, server.Id, profitbricks.Nic{ 129 Properties: &profitbricks.NicProperties{ 130 Name: c.SnapshotName, 131 Lan: lanId, 132 Dhcp: true, 133 }, 134 }) 135 136 if lan.StatusCode > 299 { 137 ui.Error(fmt.Sprintf("Error occurred %s", parseErrorMessage(nic.Response))) 138 return multistep.ActionHalt 139 } 140 141 err = s.waitTillProvisioned(nic.Headers.Get("Location"), *c) 142 if err != nil { 143 ui.Error(fmt.Sprintf("Error occurred while creating a NIC %s", err.Error())) 144 return multistep.ActionHalt 145 } 146 147 state.Put("volume_id", server.Entities.Volumes.Items[0].Id) 148 149 server = profitbricks.GetServer(datacenter.Id, server.Id) 150 151 state.Put("server_ip", server.Entities.Nics.Items[0].Properties.Ips[0]) 152 153 return multistep.ActionContinue 154 } 155 156 func (s *stepCreateServer) Cleanup(state multistep.StateBag) { 157 c := state.Get("config").(*Config) 158 ui := state.Get("ui").(packer.Ui) 159 160 ui.Say("Removing Virtual Data Center...") 161 162 profitbricks.SetAuth(c.PBUsername, c.PBPassword) 163 164 if dcId, ok := state.GetOk("datacenter_id"); ok { 165 resp := profitbricks.DeleteDatacenter(dcId.(string)) 166 if err := s.checkForErrors(resp); err != nil { 167 ui.Error(fmt.Sprintf( 168 "Error deleting Virtual Data Center. Please destroy it manually: %s", err)) 169 } 170 if err := s.waitTillProvisioned(resp.Headers.Get("Location"), *c); err != nil { 171 ui.Error(fmt.Sprintf( 172 "Error deleting Virtual Data Center. Please destroy it manually: %s", err)) 173 } 174 } 175 } 176 177 func (d *stepCreateServer) waitTillProvisioned(path string, config Config) error { 178 d.setPB(config.PBUsername, config.PBPassword, config.PBUrl) 179 waitCount := 120 180 if config.Retries > 0 { 181 waitCount = config.Retries 182 } 183 for i := 0; i < waitCount; i++ { 184 request := profitbricks.GetRequestStatus(path) 185 if request.Metadata.Status == "DONE" { 186 return nil 187 } 188 if request.Metadata.Status == "FAILED" { 189 return errors.New(request.Metadata.Message) 190 } 191 time.Sleep(1 * time.Second) 192 i++ 193 } 194 return nil 195 } 196 197 func (d *stepCreateServer) setPB(username string, password string, url string) { 198 profitbricks.SetAuth(username, password) 199 profitbricks.SetEndpoint(url) 200 } 201 202 func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error { 203 if instance.StatusCode > 299 { 204 return errors.New(fmt.Sprintf("Error occurred %s", string(instance.Body))) 205 } 206 return nil 207 } 208 209 type RestError struct { 210 HttpStatus int `json:"httpStatus,omitempty"` 211 Messages []Message `json:"messages,omitempty"` 212 } 213 214 type Message struct { 215 ErrorCode string `json:"errorCode,omitempty"` 216 Message string `json:"message,omitempty"` 217 } 218 219 func (d *stepCreateServer) getImageId(imageName string, c *Config) string { 220 d.setPB(c.PBUsername, c.PBPassword, c.PBUrl) 221 222 images := profitbricks.ListImages() 223 224 for i := 0; i < len(images.Items); i++ { 225 imgName := "" 226 if images.Items[i].Properties.Name != "" { 227 imgName = images.Items[i].Properties.Name 228 } 229 diskType := c.DiskType 230 if c.DiskType == "SSD" { 231 diskType = "HDD" 232 } 233 if imgName != "" && strings.Contains(strings.ToLower(imgName), strings.ToLower(imageName)) && images.Items[i].Properties.ImageType == diskType && images.Items[i].Properties.Location == c.Region && images.Items[i].Properties.Public == true { 234 return images.Items[i].Id 235 } 236 } 237 return "" 238 } 239 240 func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui packer.Ui) string { 241 if imageAlias == "" { 242 return "" 243 } 244 locations := profitbricks.GetLocation(location) 245 if len(locations.Properties.ImageAliases) > 0 { 246 for _, i := range locations.Properties.ImageAliases { 247 alias := "" 248 if i != "" { 249 alias = i 250 } 251 if alias != "" && strings.ToLower(alias) == strings.ToLower(imageAlias) { 252 return alias 253 } 254 } 255 } 256 return "" 257 } 258 259 func parseErrorMessage(raw string) (toreturn string) { 260 var tmp map[string]interface{} 261 json.Unmarshal([]byte(raw), &tmp) 262 263 for _, v := range tmp["messages"].([]interface{}) { 264 for index, i := range v.(map[string]interface{}) { 265 if index == "message" { 266 toreturn = toreturn + i.(string) + "\n" 267 } 268 } 269 } 270 return toreturn 271 }