2016-06-28 22:35:41 -04:00
|
|
|
package profitbricks
|
|
|
|
|
|
|
|
import (
|
2016-09-14 09:05:45 -04:00
|
|
|
"encoding/json"
|
2016-06-28 22:35:41 -04:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/mitchellh/multistep"
|
|
|
|
"github.com/mitchellh/packer/packer"
|
|
|
|
"github.com/profitbricks/profitbricks-sdk-go"
|
2016-08-01 07:09:07 -04:00
|
|
|
"strconv"
|
2016-06-28 22:35:41 -04:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type stepCreateServer struct{}
|
|
|
|
|
|
|
|
func (s *stepCreateServer) Run(state multistep.StateBag) multistep.StepAction {
|
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
c := state.Get("config").(*Config)
|
|
|
|
|
|
|
|
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
|
2016-07-08 13:19:19 -04:00
|
|
|
profitbricks.SetDepth("5")
|
2016-11-15 18:17:30 -05:00
|
|
|
if sshkey, ok := state.GetOk("publicKey"); ok {
|
|
|
|
c.SSHKey = sshkey.(string)
|
|
|
|
}
|
2016-08-01 07:09:07 -04:00
|
|
|
ui.Say("Creating Virtual Data Center...")
|
2016-07-08 13:19:19 -04:00
|
|
|
img := s.getImageId(c.Image, c)
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-08-01 07:09:07 -04:00
|
|
|
datacenter := profitbricks.Datacenter{
|
|
|
|
Properties: profitbricks.DatacenterProperties{
|
|
|
|
Name: c.SnapshotName,
|
|
|
|
Location: c.Region,
|
2016-06-28 22:35:41 -04:00
|
|
|
},
|
2016-08-01 07:09:07 -04:00
|
|
|
Entities: profitbricks.DatacenterEntities{
|
|
|
|
Servers: &profitbricks.Servers{
|
|
|
|
Items: []profitbricks.Server{
|
|
|
|
{
|
|
|
|
Properties: profitbricks.ServerProperties{
|
|
|
|
Name: c.SnapshotName,
|
|
|
|
Ram: c.Ram,
|
2016-07-08 13:19:19 -04:00
|
|
|
Cores: c.Cores,
|
|
|
|
},
|
2016-08-01 07:09:07 -04:00
|
|
|
Entities: &profitbricks.ServerEntities{
|
|
|
|
Volumes: &profitbricks.Volumes{
|
|
|
|
Items: []profitbricks.Volume{
|
|
|
|
{
|
|
|
|
Properties: profitbricks.VolumeProperties{
|
2016-11-15 18:17:30 -05:00
|
|
|
Type: c.DiskType,
|
|
|
|
Size: c.DiskSize,
|
|
|
|
Name: c.SnapshotName,
|
|
|
|
Image: img,
|
2016-07-08 13:19:19 -04:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-06-28 22:35:41 -04:00
|
|
|
},
|
|
|
|
}
|
2016-11-15 18:17:30 -05:00
|
|
|
if c.SSHKey != "" {
|
|
|
|
datacenter.Entities.Servers.Items[0].Entities.Volumes.Items[0].Properties.SshKeys = []string{c.SSHKey}
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Comm.SSHPassword != "" {
|
|
|
|
datacenter.Entities.Servers.Items[0].Entities.Volumes.Items[0].Properties.ImagePassword = c.Comm.SSHPassword
|
|
|
|
}
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-07-08 13:19:19 -04:00
|
|
|
datacenter = profitbricks.CompositeCreateDatacenter(datacenter)
|
|
|
|
if datacenter.StatusCode > 299 {
|
2016-10-12 18:41:04 -04:00
|
|
|
if datacenter.StatusCode > 299 {
|
|
|
|
var restError RestError
|
|
|
|
json.Unmarshal([]byte(datacenter.Response), &restError)
|
2016-11-01 17:08:04 -04:00
|
|
|
if len(restError.Messages) > 0 {
|
2016-10-12 18:41:04 -04:00
|
|
|
ui.Error(restError.Messages[0].Message)
|
|
|
|
} else {
|
|
|
|
ui.Error(datacenter.Response)
|
|
|
|
}
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2016-06-28 22:35:41 -04:00
|
|
|
}
|
2016-11-15 18:17:30 -05:00
|
|
|
|
|
|
|
err := s.waitTillProvisioned(datacenter.Headers.Get("Location"), *c)
|
|
|
|
if err != nil {
|
|
|
|
ui.Error(fmt.Sprintf("Error occured while creating a datacenter %s", err.Error()))
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-07-08 13:19:19 -04:00
|
|
|
state.Put("datacenter_id", datacenter.Id)
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-08-01 07:09:07 -04:00
|
|
|
lan := profitbricks.CreateLan(datacenter.Id, profitbricks.Lan{
|
|
|
|
Properties: profitbricks.LanProperties{
|
2016-06-28 22:35:41 -04:00
|
|
|
Public: true,
|
2016-07-07 04:28:46 -04:00
|
|
|
Name: c.SnapshotName,
|
2016-06-28 22:35:41 -04:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2016-08-01 07:09:07 -04:00
|
|
|
if lan.StatusCode > 299 {
|
2016-09-14 09:05:45 -04:00
|
|
|
ui.Error(fmt.Sprintf("Error occured %s", parseErrorMessage(lan.Response)))
|
2016-06-28 22:35:41 -04:00
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2016-11-15 18:17:30 -05:00
|
|
|
err = s.waitTillProvisioned(lan.Headers.Get("Location"), *c)
|
|
|
|
if err != nil {
|
|
|
|
ui.Error(fmt.Sprintf("Error occured while creating a LAN %s", err.Error()))
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-08-01 07:09:07 -04:00
|
|
|
lanId, _ := strconv.Atoi(lan.Id)
|
|
|
|
nic := profitbricks.CreateNic(datacenter.Id, datacenter.Entities.Servers.Items[0].Id, profitbricks.Nic{
|
|
|
|
Properties: profitbricks.NicProperties{
|
2016-07-07 04:28:46 -04:00
|
|
|
Name: c.SnapshotName,
|
2016-08-01 07:09:07 -04:00
|
|
|
Lan: lanId,
|
2016-06-28 22:35:41 -04:00
|
|
|
Dhcp: true,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2016-08-01 07:09:07 -04:00
|
|
|
if lan.StatusCode > 299 {
|
2016-09-14 09:05:45 -04:00
|
|
|
ui.Error(fmt.Sprintf("Error occured %s", parseErrorMessage(nic.Response)))
|
2016-06-28 22:35:41 -04:00
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2016-11-15 18:17:30 -05:00
|
|
|
err = s.waitTillProvisioned(nic.Headers.Get("Location"), *c)
|
|
|
|
if err != nil {
|
|
|
|
ui.Error(fmt.Sprintf("Error occured while creating a NIC %s", err.Error()))
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-07-08 13:19:19 -04:00
|
|
|
state.Put("volume_id", datacenter.Entities.Servers.Items[0].Entities.Volumes.Items[0].Id)
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-07-08 13:19:19 -04:00
|
|
|
server := profitbricks.GetServer(datacenter.Id, datacenter.Entities.Servers.Items[0].Id)
|
2016-06-28 22:35:41 -04:00
|
|
|
|
2016-08-01 07:09:07 -04:00
|
|
|
state.Put("server_ip", server.Entities.Nics.Items[0].Properties.Ips[0])
|
2016-06-28 22:35:41 -04:00
|
|
|
|
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *stepCreateServer) Cleanup(state multistep.StateBag) {
|
|
|
|
c := state.Get("config").(*Config)
|
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
|
|
|
|
ui.Say("Removing Virtual Data Center...")
|
|
|
|
|
|
|
|
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
|
|
|
|
|
2016-10-12 18:41:04 -04:00
|
|
|
if dcId, ok := state.GetOk("datacenter_id"); ok {
|
|
|
|
resp := profitbricks.DeleteDatacenter(dcId.(string))
|
|
|
|
s.checkForErrors(resp)
|
|
|
|
err := s.waitTillProvisioned(resp.Headers.Get("Location"), *c)
|
|
|
|
if err != nil {
|
|
|
|
ui.Error(fmt.Sprintf(
|
|
|
|
"Error deleting Virtual Data Center. Please destroy it manually: %s", err))
|
|
|
|
}
|
2016-06-28 22:35:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *stepCreateServer) waitTillProvisioned(path string, config Config) error {
|
|
|
|
d.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
|
2016-08-31 07:55:11 -04:00
|
|
|
waitCount := 120
|
2016-11-15 18:17:30 -05:00
|
|
|
if config.Retries > 0 {
|
|
|
|
waitCount = config.Retries
|
2016-08-01 07:09:07 -04:00
|
|
|
}
|
2016-06-28 22:35:41 -04:00
|
|
|
for i := 0; i < waitCount; i++ {
|
|
|
|
request := profitbricks.GetRequestStatus(path)
|
2016-08-01 07:09:07 -04:00
|
|
|
if request.Metadata.Status == "DONE" {
|
2016-06-28 22:35:41 -04:00
|
|
|
return nil
|
|
|
|
}
|
2016-08-01 07:09:07 -04:00
|
|
|
if request.Metadata.Status == "FAILED" {
|
|
|
|
return errors.New(request.Metadata.Message)
|
2016-06-28 22:35:41 -04:00
|
|
|
}
|
2016-08-31 07:55:11 -04:00
|
|
|
time.Sleep(1 * time.Second)
|
2016-06-28 22:35:41 -04:00
|
|
|
i++
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *stepCreateServer) setPB(username string, password string, url string) {
|
|
|
|
profitbricks.SetAuth(username, password)
|
|
|
|
profitbricks.SetEndpoint(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error {
|
|
|
|
if instance.StatusCode > 299 {
|
|
|
|
return errors.New(fmt.Sprintf("Error occured %s", string(instance.Body)))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-10-12 18:41:04 -04:00
|
|
|
type RestError struct {
|
2016-11-01 17:08:04 -04:00
|
|
|
HttpStatus int `json:"httpStatus,omitempty"`
|
|
|
|
Messages []Message `json:"messages,omitempty"`
|
2016-10-12 18:41:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type Message struct {
|
|
|
|
ErrorCode string `json:"errorCode,omitempty"`
|
2016-11-01 17:08:04 -04:00
|
|
|
Message string `json:"message,omitempty"`
|
2016-10-12 18:41:04 -04:00
|
|
|
}
|
|
|
|
|
2016-06-28 22:35:41 -04:00
|
|
|
func (d *stepCreateServer) getImageId(imageName string, c *Config) string {
|
|
|
|
d.setPB(c.PBUsername, c.PBPassword, c.PBUrl)
|
|
|
|
|
|
|
|
images := profitbricks.ListImages()
|
|
|
|
|
|
|
|
for i := 0; i < len(images.Items); i++ {
|
|
|
|
imgName := ""
|
2016-08-01 07:09:07 -04:00
|
|
|
if images.Items[i].Properties.Name != "" {
|
|
|
|
imgName = images.Items[i].Properties.Name
|
2016-06-28 22:35:41 -04:00
|
|
|
}
|
|
|
|
diskType := c.DiskType
|
|
|
|
if c.DiskType == "SSD" {
|
|
|
|
diskType = "HDD"
|
|
|
|
}
|
2016-09-13 06:06:45 -04:00
|
|
|
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 {
|
2016-06-28 22:35:41 -04:00
|
|
|
return images.Items[i].Id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2016-09-14 09:05:45 -04:00
|
|
|
|
|
|
|
func parseErrorMessage(raw string) (toreturn string) {
|
|
|
|
var tmp map[string]interface{}
|
|
|
|
json.Unmarshal([]byte(raw), &tmp)
|
|
|
|
|
|
|
|
for _, v := range tmp["messages"].([]interface{}) {
|
|
|
|
for index, i := range v.(map[string]interface{}) {
|
|
|
|
if index == "message" {
|
|
|
|
toreturn = toreturn + i.(string) + "\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return toreturn
|
|
|
|
}
|