2016-06-28 22:35:41 -04:00
package profitbricks
import (
2018-01-22 18:32:33 -05:00
"context"
2016-09-14 09:05:45 -04:00
"encoding/json"
2016-06-28 22:35:41 -04:00
"errors"
"fmt"
2016-08-01 07:09:07 -04:00
"strconv"
2016-06-28 22:35:41 -04:00
"strings"
"time"
2017-03-28 20:45:01 -04:00
2020-12-17 16:29:25 -05:00
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
2017-03-28 20:45:01 -04:00
"github.com/profitbricks/profitbricks-sdk-go"
2016-06-28 22:35:41 -04:00
)
type stepCreateServer struct { }
2019-03-29 11:50:02 -04:00
func ( s * stepCreateServer ) Run ( ctx context . Context , state multistep . StateBag ) multistep . StepAction {
2020-11-19 14:54:31 -05:00
ui := state . Get ( "ui" ) . ( packersdk . Ui )
2016-06-28 22:35:41 -04:00
c := state . Get ( "config" ) . ( * Config )
profitbricks . SetAuth ( c . PBUsername , c . PBPassword )
2016-07-08 13:19:19 -04:00
profitbricks . SetDepth ( "5" )
2021-02-09 10:56:06 -05:00
if c . Comm . SSHPublicKey != nil {
c . SSHKey = string ( c . Comm . SSHPublicKey )
} else {
ui . Error ( "No ssh private key set; ssh authentication won't be possible. Please specify your private key in the ssh_private_key_file configuration key." )
return multistep . ActionHalt
2016-11-15 18:17:30 -05:00
}
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 )
2017-08-10 09:15:53 -04:00
alias := ""
if img == "" {
alias = s . getImageAlias ( c . Image , c . Region , ui )
}
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
} ,
2017-08-10 09:15:53 -04:00
}
server := profitbricks . Server {
Properties : profitbricks . ServerProperties {
Name : c . SnapshotName ,
Ram : c . Ram ,
Cores : c . Cores ,
} ,
Entities : & profitbricks . ServerEntities {
Volumes : & profitbricks . Volumes {
Items : [ ] profitbricks . Volume {
2016-08-01 07:09:07 -04:00
{
2017-08-10 09:15:53 -04:00
Properties : profitbricks . VolumeProperties {
Type : c . DiskType ,
Size : c . DiskSize ,
Name : c . SnapshotName ,
ImageAlias : alias ,
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 != "" {
2017-08-10 09:15:53 -04:00
server . Entities . Volumes . Items [ 0 ] . Properties . SshKeys = [ ] string { c . SSHKey }
2016-11-15 18:17:30 -05:00
}
if c . Comm . SSHPassword != "" {
2017-08-10 09:15:53 -04:00
server . Entities . Volumes . Items [ 0 ] . Properties . ImagePassword = c . Comm . SSHPassword
2016-11-15 18:17:30 -05:00
}
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
2017-03-29 16:38:31 -04:00
err := json . Unmarshal ( [ ] byte ( datacenter . Response ) , & restError )
if err != nil {
ui . Error ( fmt . Sprintf ( "Error decoding json response: %s" , err . Error ( ) ) )
return multistep . ActionHalt
}
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 {
2017-03-28 20:45:01 -04:00
ui . Error ( fmt . Sprintf ( "Error occurred while creating a datacenter %s" , err . Error ( ) ) )
2016-11-15 18:17:30 -05:00
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
2017-08-10 09:15:53 -04:00
server = profitbricks . CreateServer ( datacenter . Id , server )
if server . StatusCode > 299 {
ui . Error ( fmt . Sprintf ( "Error occurred %s" , parseErrorMessage ( server . Response ) ) )
return multistep . ActionHalt
}
err = s . waitTillProvisioned ( server . Headers . Get ( "Location" ) , * c )
if err != nil {
ui . Error ( fmt . Sprintf ( "Error occurred while creating a server %s" , err . Error ( ) ) )
return multistep . ActionHalt
}
lan := profitbricks . CreateLan ( datacenter . Id , profitbricks . CreateLanRequest {
Properties : profitbricks . CreateLanProperties {
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 {
2017-03-28 20:45:01 -04:00
ui . Error ( fmt . Sprintf ( "Error occurred %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 {
2017-03-28 20:45:01 -04:00
ui . Error ( fmt . Sprintf ( "Error occurred while creating a LAN %s" , err . Error ( ) ) )
2016-11-15 18:17:30 -05:00
return multistep . ActionHalt
}
2016-06-28 22:35:41 -04:00
2016-08-01 07:09:07 -04:00
lanId , _ := strconv . Atoi ( lan . Id )
2017-08-10 09:15:53 -04:00
nic := profitbricks . CreateNic ( datacenter . Id , server . 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 {
2017-03-28 20:45:01 -04:00
ui . Error ( fmt . Sprintf ( "Error occurred %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 {
2017-03-28 20:45:01 -04:00
ui . Error ( fmt . Sprintf ( "Error occurred while creating a NIC %s" , err . Error ( ) ) )
2016-11-15 18:17:30 -05:00
return multistep . ActionHalt
}
2016-06-28 22:35:41 -04:00
2017-08-10 09:15:53 -04:00
state . Put ( "volume_id" , server . Entities . Volumes . Items [ 0 ] . Id )
2016-06-28 22:35:41 -04:00
2017-08-10 09:15:53 -04:00
server = profitbricks . GetServer ( datacenter . Id , server . Id )
2019-12-13 14:57:01 -05:00
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state . Put ( "instance_id" , server . 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 )
2020-11-19 14:54:31 -05:00
ui := state . Get ( "ui" ) . ( packersdk . Ui )
2016-06-28 22:35:41 -04:00
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 ) )
2017-03-29 16:38:31 -04:00
if err := s . checkForErrors ( resp ) ; err != nil {
ui . Error ( fmt . Sprintf (
"Error deleting Virtual Data Center. Please destroy it manually: %s" , err ) )
}
if err := s . waitTillProvisioned ( resp . Headers . Get ( "Location" ) , * c ) ; err != nil {
2016-10-12 18:41:04 -04:00
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 {
2021-02-09 10:56:06 -05:00
return fmt . Errorf ( "Error occurred %s" , string ( instance . Body ) )
2016-06-28 22:35:41 -04:00
}
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
2020-11-19 14:54:31 -05:00
func ( d * stepCreateServer ) getImageAlias ( imageAlias string , location string , ui packersdk . Ui ) string {
2017-08-10 09:15:53 -04:00
if imageAlias == "" {
return ""
}
locations := profitbricks . GetLocation ( location )
if len ( locations . Properties . ImageAliases ) > 0 {
for _ , i := range locations . Properties . ImageAliases {
alias := ""
if i != "" {
alias = i
}
2020-08-31 09:44:42 -04:00
if alias != "" && strings . EqualFold ( alias , imageAlias ) {
2017-08-10 09:15:53 -04:00
return alias
}
}
}
return ""
}
2016-09-14 09:05:45 -04:00
func parseErrorMessage ( raw string ) ( toreturn string ) {
var tmp map [ string ] interface { }
2021-02-09 10:56:06 -05:00
if json . Unmarshal ( [ ] byte ( raw ) , & tmp ) != nil {
return ""
}
2016-09-14 09:05:45 -04:00
for _ , v := range tmp [ "messages" ] . ( [ ] interface { } ) {
for index , i := range v . ( map [ string ] interface { } ) {
if index == "message" {
toreturn = toreturn + i . ( string ) + "\n"
}
}
}
return toreturn
}