From 871df8be85b88f13901dd1781921b71fe0d1d7a0 Mon Sep 17 00:00:00 2001 From: Marin Salinas Date: Mon, 4 Feb 2019 13:47:27 -0600 Subject: [PATCH] feature: bsusurrogate, add StepUpdateBSUBackedVm and StepSnapshotVolumes step --- builder/osc/bsusurrogate/builder.go | 9 +- .../osc/bsusurrogate/step_snapshop_volumes.go | 107 ++++++++++++++++++ builder/osc/common/state.go | 33 ++++++ builder/osc/common/step_update_bsu_vm.go | 63 +++++++++++ 4 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 builder/osc/bsusurrogate/step_snapshop_volumes.go create mode 100644 builder/osc/common/step_update_bsu_vm.go diff --git a/builder/osc/bsusurrogate/builder.go b/builder/osc/bsusurrogate/builder.go index 49c804a1d..0ab42f9a5 100644 --- a/builder/osc/bsusurrogate/builder.go +++ b/builder/osc/bsusurrogate/builder.go @@ -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) diff --git a/builder/osc/bsusurrogate/step_snapshop_volumes.go b/builder/osc/bsusurrogate/step_snapshop_volumes.go new file mode 100644 index 000000000..22f3c3f45 --- /dev/null +++ b/builder/osc/bsusurrogate/step_snapshop_volumes.go @@ -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)) + } + } + } +} diff --git a/builder/osc/common/state.go b/builder/osc/common/state.go index cf882ad42..0f6c90986 100644 --- a/builder/osc/common/state.go +++ b/builder/osc/common/state.go @@ -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) diff --git a/builder/osc/common/step_update_bsu_vm.go b/builder/osc/common/step_update_bsu_vm.go new file mode 100644 index 000000000..e348916a8 --- /dev/null +++ b/builder/osc/common/step_update_bsu_vm.go @@ -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... +}