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  }