Profitbricks builder fixes (#10549)

This commit is contained in:
mflorin 2021-02-09 17:56:06 +02:00 committed by GitHub
parent f5006d0842
commit 9afaa5a21f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 135 additions and 11 deletions

View File

@ -22,8 +22,11 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
profitbricks.SetAuth(c.PBUsername, c.PBPassword) profitbricks.SetAuth(c.PBUsername, c.PBPassword)
profitbricks.SetDepth("5") profitbricks.SetDepth("5")
if sshkey, ok := state.GetOk("publicKey"); ok { if c.Comm.SSHPublicKey != nil {
c.SSHKey = sshkey.(string) 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
} }
ui.Say("Creating Virtual Data Center...") ui.Say("Creating Virtual Data Center...")
img := s.getImageId(c.Image, c) img := s.getImageId(c.Image, c)
@ -204,7 +207,7 @@ func (d *stepCreateServer) setPB(username string, password string, url string) {
func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error { func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error {
if instance.StatusCode > 299 { if instance.StatusCode > 299 {
return errors.New(fmt.Sprintf("Error occurred %s", string(instance.Body))) return fmt.Errorf("Error occurred %s", string(instance.Body))
} }
return nil return nil
} }
@ -261,7 +264,9 @@ func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui
func parseErrorMessage(raw string) (toreturn string) { func parseErrorMessage(raw string) (toreturn string) {
var tmp map[string]interface{} var tmp map[string]interface{}
json.Unmarshal([]byte(raw), &tmp) if json.Unmarshal([]byte(raw), &tmp) != nil {
return ""
}
for _, v := range tmp["messages"].([]interface{}) { for _, v := range tmp["messages"].([]interface{}) {
for index, i := range v.(map[string]interface{}) { for index, i := range v.(map[string]interface{}) {

View File

@ -3,6 +3,9 @@ package profitbricks
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt"
"strings"
"time" "time"
"github.com/hashicorp/packer-plugin-sdk/multistep" "github.com/hashicorp/packer-plugin-sdk/multistep"
@ -22,9 +25,32 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
dcId := state.Get("datacenter_id").(string) dcId := state.Get("datacenter_id").(string)
volumeId := state.Get("volume_id").(string) volumeId := state.Get("volume_id").(string)
serverId := state.Get("instance_id").(string)
comm, _ := state.Get("communicator").(packersdk.Communicator)
if comm == nil {
ui.Error("no communicator found")
return multistep.ActionHalt
}
/* sync fs changes from the provisioning step */
os, err := s.getOs(dcId, serverId)
if err != nil {
ui.Error(fmt.Sprintf("an error occurred while getting the server os: %s", err.Error()))
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Server OS is %s", os))
switch strings.ToLower(os) {
case "linux":
ui.Say("syncing file system changes")
if err := s.syncFs(ctx, comm); err != nil {
ui.Error(fmt.Sprintf("error syncing fs changes: %s", err.Error()))
return multistep.ActionHalt
}
}
snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "") snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "")
state.Put("snapshotname", c.SnapshotName) state.Put("snapshotname", c.SnapshotName)
if snapshot.StatusCode > 299 { if snapshot.StatusCode > 299 {
@ -42,31 +68,123 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
return multistep.ActionHalt return multistep.ActionHalt
} }
s.waitTillProvisioned(snapshot.Headers.Get("Location"), *c) ui.Say(fmt.Sprintf("Creating a snapshot for %s/volumes/%s", dcId, volumeId))
err = s.waitForRequest(snapshot.Headers.Get("Location"), *c, ui)
if err != nil {
ui.Error(fmt.Sprintf("An error occurred while waiting for the request to be done: %s", err.Error()))
return multistep.ActionHalt
}
err = s.waitTillSnapshotAvailable(snapshot.Id, *c, ui)
if err != nil {
ui.Error(fmt.Sprintf("An error occurred while waiting for the snapshot to be created: %s", err.Error()))
return multistep.ActionHalt
}
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *stepTakeSnapshot) Cleanup(state multistep.StateBag) { func (s *stepTakeSnapshot) Cleanup(_ multistep.StateBag) {
} }
func (d *stepTakeSnapshot) waitTillProvisioned(path string, config Config) { func (s *stepTakeSnapshot) waitForRequest(path string, config Config, ui packersdk.Ui) error {
d.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
ui.Say(fmt.Sprintf("Watching request %s", path))
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
waitCount := 50 waitCount := 50
var waitInterval = 10 * time.Second
if config.Retries > 0 { if config.Retries > 0 {
waitCount = config.Retries waitCount = config.Retries
} }
done := false
for i := 0; i < waitCount; i++ { for i := 0; i < waitCount; i++ {
request := profitbricks.GetRequestStatus(path) request := profitbricks.GetRequestStatus(path)
ui.Say(fmt.Sprintf("request status = %s", request.Metadata.Status))
if request.Metadata.Status == "DONE" { if request.Metadata.Status == "DONE" {
done = true
break break
} }
time.Sleep(10 * time.Second) if request.Metadata.Status == "FAILED" {
return fmt.Errorf("Request failed: %s", request.Response)
}
time.Sleep(waitInterval)
i++ i++
} }
if done == false {
return fmt.Errorf("request not fulfilled after waiting %d seconds",
int64(waitCount)*int64(waitInterval)/int64(time.Second))
}
return nil
} }
func (d *stepTakeSnapshot) setPB(username string, password string, url string) { func (s *stepTakeSnapshot) waitTillSnapshotAvailable(id string, config Config, ui packersdk.Ui) error {
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
waitCount := 50
var waitInterval = 10 * time.Second
if config.Retries > 0 {
waitCount = config.Retries
}
done := false
ui.Say(fmt.Sprintf("waiting for snapshot %s to become available", id))
for i := 0; i < waitCount; i++ {
snap := profitbricks.GetSnapshot(id)
ui.Say(fmt.Sprintf("snapshot status = %s", snap.Metadata.State))
if snap.StatusCode != 200 {
return fmt.Errorf("%s", snap.Response)
}
if snap.Metadata.State == "AVAILABLE" {
done = true
break
}
time.Sleep(waitInterval)
i++
ui.Say(fmt.Sprintf("... still waiting, %d seconds have passed", int64(waitInterval)*int64(i)))
}
if done == false {
return fmt.Errorf("snapshot not created after waiting %d seconds",
int64(waitCount)*int64(waitInterval)/int64(time.Second))
}
ui.Say("snapshot created")
return nil
}
func (s *stepTakeSnapshot) syncFs(ctx context.Context, comm packersdk.Communicator) error {
cmd := &packersdk.RemoteCmd{
Command: "sync",
}
if err := comm.Start(ctx, cmd); err != nil {
return err
}
if cmd.Wait() != 0 {
return fmt.Errorf("sync command exited with code %d", cmd.ExitStatus())
}
return nil
}
func (s *stepTakeSnapshot) getOs(dcId string, serverId string) (string, error) {
server := profitbricks.GetServer(dcId, serverId)
if server.StatusCode != 200 {
return "", errors.New(server.Response)
}
if server.Properties.BootVolume == nil {
return "", errors.New("no boot volume found on server")
}
volumeId := server.Properties.BootVolume.Id
volume := profitbricks.GetVolume(dcId, volumeId)
if volume.StatusCode != 200 {
return "", errors.New(volume.Response)
}
return volume.Properties.LicenceType, nil
}
func (s *stepTakeSnapshot) setPB(username string, password string, url string) {
profitbricks.SetAuth(username, password) profitbricks.SetAuth(username, password)
profitbricks.SetEndpoint(url) profitbricks.SetEndpoint(url)
} }

1
go.sum
View File

@ -530,6 +530,7 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=