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