github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/builder/profitbricks/step_create_server.go (about)

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