@ -3,6 +3,9 @@ package profitbricks
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"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 )
volumeId := state . Get ( "volume_id" ) . ( string )
serverId := state . Get ( "instance_id" ) . ( string )
snapshot := profitbricks . CreateSnapshot ( dcId , volumeId , c . SnapshotName , "" )
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 , "" )
state . Put ( "snapshotname" , c . SnapshotName )
if snapshot . StatusCode > 299 {
@ -42,31 +68,123 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
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
}
func ( s * stepTakeSnapshot ) Cleanup ( state multistep . StateBag ) {
func ( s * stepTakeSnapshot ) Cleanup ( _ multistep . StateBag ) {
}
func ( d * stepTakeSnapshot ) waitTillProvisioned ( path string , config Config ) {
d . setPB ( config . PBUsername , config . PBPassword , config . PBUrl )
func ( s * stepTakeSnapshot ) waitForRequest ( path string , config Config , ui packersdk . Ui ) error {
ui . Say ( fmt . Sprintf ( "Watching request %s" , path ) )
s . setPB ( config . PBUsername , config . PBPassword , config . PBUrl )
waitCount := 50
var waitInterval = 10 * time . Second
if config . Retries > 0 {
waitCount = config . Retries
}
done := false
for i := 0 ; i < waitCount ; i ++ {
request := profitbricks . GetRequestStatus ( path )
ui . Say ( fmt . Sprintf ( "request status = %s" , request . Metadata . Status ) )
if request . Metadata . Status == "DONE" {
done = true
break
}
time . Sleep ( 10 * time . Second )
if request . Metadata . Status == "FAILED" {
return fmt . Errorf ( "Request failed: %s" , request . Response )
}
time . Sleep ( waitInterval )
i ++
}
if done == false {
return fmt . Errorf ( "request not fulfilled after waiting %d seconds" ,
int64 ( waitCount ) * int64 ( waitInterval ) / int64 ( time . Second ) )
}
return nil
}
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 ( d * stepTakeSnapshot ) setPB ( username string , password string , url string ) {
func ( s * stepTakeSnapshot ) setPB ( username string , password string , url string ) {
profitbricks . SetAuth ( username , password )
profitbricks . SetEndpoint ( url )
}