feature: bsusurrogate, add StepUpdateBSUBackedVm and StepSnapshotVolumes step

This commit is contained in:
Marin Salinas 2019-02-04 13:47:27 -06:00 committed by Megan Marsh
parent 09b899bb84
commit 871df8be85
4 changed files with 211 additions and 1 deletions

View File

@ -131,7 +131,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
//VMStep
//omiDevices := b.config.BuildOMIDevices()
//launchDevices := b.config.BuildLaunchDevices()
launchDevices := b.config.BuildLaunchDevices()
steps := []multistep.Step{
&osccommon.StepPreValidate{
@ -208,6 +208,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Skip: false,
DisableStopVm: b.config.DisableStopVm,
},
&osccommon.StepUpdateBSUBackedVm{
EnableAMISriovNetSupport: b.config.OMISriovNetSupport,
EnableAMIENASupport: b.config.OMIENASupport,
},
&StepSnapshotVolumes{
LaunchDevices: launchDevices,
},
}
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)

View File

@ -0,0 +1,107 @@
package bsusurrogate
import (
"context"
"fmt"
"sync"
"time"
multierror "github.com/hashicorp/go-multierror"
osccommon "github.com/hashicorp/packer/builder/osc/common"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/outscale/osc-go/oapi"
)
// StepSnapshotVolumes creates snapshots of the created volumes.
//
// Produces:
// snapshot_ids map[string]string - IDs of the created snapshots
type StepSnapshotVolumes struct {
LaunchDevices []oapi.BlockDeviceMappingVmCreation
snapshotIds map[string]string
}
func (s *StepSnapshotVolumes) snapshotVolume(ctx context.Context, deviceName string, state multistep.StateBag) error {
oapiconn := state.Get("oapi").(*oapi.Client)
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*oapi.Vm)
var volumeId string
for _, volume := range vm.BlockDeviceMappings {
if volume.DeviceName == deviceName {
volumeId = volume.Bsu.VolumeId
}
}
if volumeId == "" {
return fmt.Errorf("Volume ID for device %s not found", deviceName)
}
ui.Say(fmt.Sprintf("Creating snapshot of EBS Volume %s...", volumeId))
description := fmt.Sprintf("Packer: %s", time.Now().String())
createSnapResp, err := oapiconn.POST_CreateSnapshot(oapi.CreateSnapshotRequest{
VolumeId: volumeId,
Description: description,
})
if err != nil {
return err
}
// Set the snapshot ID so we can delete it later
s.snapshotIds[deviceName] = createSnapResp.OK.Snapshot.SnapshotId
// Wait for snapshot to be created
err = osccommon.WaitUntilSnapshotCompleted(oapiconn, createSnapResp.OK.Snapshot.SnapshotId)
return err
}
func (s *StepSnapshotVolumes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
s.snapshotIds = map[string]string{}
var wg sync.WaitGroup
var errs *multierror.Error
for _, device := range s.LaunchDevices {
wg.Add(1)
go func(device oapi.BlockDeviceMappingVmCreation) {
defer wg.Done()
if err := s.snapshotVolume(ctx, device.DeviceName, state); err != nil {
errs = multierror.Append(errs, err)
}
}(device)
}
wg.Wait()
if errs != nil {
state.Put("error", errs)
ui.Error(errs.Error())
return multistep.ActionHalt
}
state.Put("snapshot_ids", s.snapshotIds)
return multistep.ActionContinue
}
func (s *StepSnapshotVolumes) Cleanup(state multistep.StateBag) {
if len(s.snapshotIds) == 0 {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
oapiconn := state.Get("oapi").(*oapi.Client)
ui := state.Get("ui").(packer.Ui)
ui.Say("Removing snapshots since we cancelled or halted...")
for _, snapshotId := range s.snapshotIds {
_, err := oapiconn.POST_DeleteSnapshot(oapi.DeleteSnapshotRequest{SnapshotId: snapshotId})
if err != nil {
ui.Error(fmt.Sprintf("Error: %s", err))
}
}
}
}

View File

@ -36,6 +36,12 @@ func waitUntilVmStopped(conn *oapi.Client, vmID string) error {
return <-errCh
}
func WaitUntilSnapshotCompleted(conn *oapi.Client, vmID string) error {
errCh := make(chan error, 1)
go waitForState(errCh, "completed", waitUntilSnapshotStateFunc(conn, vmID))
return <-errCh
}
func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) error {
err := common.Retry(2, 2, 0, func(_ uint) (bool, error) {
state, err := refresh()
@ -77,6 +83,33 @@ func waitUntilVmStateFunc(conn *oapi.Client, id string) stateRefreshFunc {
}
}
func waitUntilSnapshotStateFunc(conn *oapi.Client, id string) stateRefreshFunc {
return func() (string, error) {
log.Printf("[Debug] Check if Snapshot with id %s exists", id)
resp, err := conn.POST_ReadSnapshots(oapi.ReadSnapshotsRequest{
Filters: oapi.FiltersSnapshot{
SnapshotIds: []string{id},
},
})
log.Printf("[Debug] Read Response %+v", resp.OK)
if err != nil {
return "", err
}
if resp.OK == nil {
return "", fmt.Errorf("Vm with ID %s. Not Found", id)
}
if len(resp.OK.Snapshots) == 0 {
return "pending", nil
}
return resp.OK.Snapshots[0].State, nil
}
}
func securityGroupWaitFunc(conn *oapi.Client, id string) stateRefreshFunc {
return func() (string, error) {
log.Printf("[Debug] Check if SG with id %s exists", id)

View File

@ -0,0 +1,63 @@
package common
import (
"context"
"github.com/hashicorp/packer/helper/multistep"
)
type StepUpdateBSUBackedVm struct {
EnableAMIENASupport *bool
EnableAMISriovNetSupport bool
}
func (s *StepUpdateBSUBackedVm) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
// oapiconn := state.Get("oapi").(*oapi.Client)
// vm := state.Get("vm").(*oapi.Vm)
// ui := state.Get("ui").(packer.Ui)
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
// if s.EnableAMISriovNetSupport {
// ui.Say("Enabling Enhanced Networking (SR-IOV)...")
// simple := "simple"
// _, err := oapiconn.POST_UpdateVm(oapi.UpdateVmRequest{
// VmId: vm.VmId,
// SriovNetSupport: &oapi.AttributeValue{Value: &simple},
// })
// if err != nil {
// err := fmt.Errorf("Error enabling Enhanced Networking (SR-IOV) on %s: %s", *vm.VmId, err)
// state.Put("error", err)
// ui.Error(err.Error())
// return multistep.ActionHalt
// }
// }
// Handle EnaSupport flag.
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
// if s.EnableAMIENASupport != nil {
// var prefix string
// if *s.EnableAMIENASupport {
// prefix = "En"
// } else {
// prefix = "Dis"
// }
// ui.Say(fmt.Sprintf("%sabling Enhanced Networking (ENA)...", prefix))
// _, err := oapiconn.UpdateVmAttribute(&oapi.UpdateVmAttributeInput{
// VmId: vm.VmId,
// EnaSupport: &oapi.AttributeBooleanValue{Value: aws.Bool(*s.EnableAMIENASupport)},
// })
// if err != nil {
// err := fmt.Errorf("Error %sabling Enhanced Networking (ENA) on %s: %s", strings.ToLower(prefix), *vm.VmId, err)
// state.Put("error", err)
// ui.Error(err.Error())
// return multistep.ActionHalt
// }
// }
return multistep.ActionContinue
}
func (s *StepUpdateBSUBackedVm) Cleanup(state multistep.StateBag) {
// No cleanup...
}