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  }