From 1281f28f6d4dcf8ea33dad68e5f02e299f934a14 Mon Sep 17 00:00:00 2001 From: Marin Salinas Date: Wed, 30 Jan 2019 12:48:51 -0600 Subject: [PATCH] add prevalidate step --- builder/osc/bsusurrogate/builder.go | 34 +++++++++++-- builder/osc/bsusurrogate/builder_acc_test.go | 52 ++++++++++++++++++++ builder/osc/common/access_config.go | 19 +++++++ builder/osc/common/omi_config.go | 32 ++++++------ builder/osc/common/step_pre_validate.go | 51 +++++++++++++++++++ command/plugin.go | 2 + 6 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 builder/osc/bsusurrogate/builder_acc_test.go create mode 100644 builder/osc/common/step_pre_validate.go diff --git a/builder/osc/bsusurrogate/builder.go b/builder/osc/bsusurrogate/builder.go index 1aeec4e11..d4f52ac40 100644 --- a/builder/osc/bsusurrogate/builder.go +++ b/builder/osc/bsusurrogate/builder.go @@ -27,7 +27,7 @@ type Config struct { osccommon.BlockDevices `mapstructure:",squash"` osccommon.OMIConfig `mapstructure:",squash"` - RootDevice RootBlockDevice `mapstructure:"ami_root_device"` + RootDevice RootBlockDevice `mapstructure:"omi_root_device"` VolumeRunTags osccommon.TagMap `mapstructure:"run_volume_tags"` ctx interpolate.Context @@ -39,6 +39,7 @@ type Builder struct { } func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + b.config.ctx.Funcs = osccommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ @@ -73,7 +74,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.RootDevice.Prepare(&b.config.ctx)...) if b.config.OMIVirtType == "" { - errs = packer.MultiErrorAppend(errs, errors.New("ami_virtualization_type is required.")) + errs = packer.MultiErrorAppend(errs, errors.New("omi_virtualization_type is required.")) } foundRootVolume := false @@ -126,11 +127,38 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("hook", hook) state.Put("ui", ui) - steps := []multistep.Step{} + //VMStep + + //omiDevices := b.config.BuildOMIDevices() + //launchDevices := b.config.BuildLaunchDevices() + + steps := []multistep.Step{ + &osccommon.StepPreValidate{ + DestOmiName: b.config.OMIName, + ForceDeregister: b.config.OMIForceDeregister, + }, + } b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) b.runner.Run(state) + // If there was an error, return that + if rawErr, ok := state.GetOk("error"); ok { + return nil, rawErr.(error) + } + + //Build the artifact + // if omis, ok := state.GetOk("omis"); ok { + // // Build the artifact and return it + // artifact := &awscommon.Artifact{ + // Amis: omis.(map[string]string), + // BuilderIdValue: BuilderId, + // Session: session, + // } + + // return artifact, nil + // } + return nil, nil } diff --git a/builder/osc/bsusurrogate/builder_acc_test.go b/builder/osc/bsusurrogate/builder_acc_test.go new file mode 100644 index 000000000..5fccd79ef --- /dev/null +++ b/builder/osc/bsusurrogate/builder_acc_test.go @@ -0,0 +1,52 @@ +/* +Deregister the test image with +aws ec2 deregister-image --image-id $(aws ec2 describe-images --output text --filters "Name=name,Values=packer-test-packer-test-dereg" --query 'Images[*].{ID:ImageId}') +*/ +package bsusurrogate + +import ( + "testing" + + builderT "github.com/hashicorp/packer/helper/builder/testing" +) + +func TestBuilderAcc_basic(t *testing.T) { + builderT.Test(t, builderT.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Builder: &Builder{}, + Template: testBuilderAccBasic, + }) +} + +func testAccPreCheck(t *testing.T) { +} + +const testBuilderAccBasic = ` +{ + "builders": [{ + "type": "test", + "region": "eu-west-2", + "vm_type": "m3.medium", + "source_omi": "ami-76b2a71e", + "ssh_username": "ubuntu", + "omi_name": "packer-test {{timestamp}}", + "omi_virtualization_type": "hvm", + "launch_block_device_mappings" : [ + { + "volume_type" : "gp2", + "device_name" : "/dev/sda1", + "delete_on_vm_deletion" : false, + "volume_size" : 10 + } + ], + "omi_root_device":{ + "source_device_name": "/dev/sda1", + "device_name": "/dev/sda2", + "delete_on_vm_deletion": true, + "volume_size": 16, + "volume_type": "gp2" + } + + }] +} +` diff --git a/builder/osc/common/access_config.go b/builder/osc/common/access_config.go index 3b76ea7bb..485811beb 100644 --- a/builder/osc/common/access_config.go +++ b/builder/osc/common/access_config.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net/http" + "os" "github.com/hashicorp/packer/template/interpolate" "github.com/outscale/osc-go/oapi" @@ -35,6 +36,24 @@ func (c *AccessConfig) Config() (*oapi.Config, error) { return c.clientConfig, nil } + //Check env variables if access configuration is not set. + + if c.AccessKey == "" { + c.AccessKey = os.Getenv("OUTSCALE_ACCESSKEYID") + } + + if c.SecretKey == "" { + c.SecretKey = os.Getenv("OUTSCALE_SECRETKEYID") + } + + if c.RawRegion == "" { + c.RawRegion = os.Getenv("OUTSCALE_REGION") + } + + if c.CustomEndpoint == "" { + c.CustomEndpoint = os.Getenv("OUTSCALE_OAPI_URL") + } + config := &oapi.Config{ AccessKey: c.AccessKey, SecretKey: c.SecretKey, diff --git a/builder/osc/common/omi_config.go b/builder/osc/common/omi_config.go index fbd1bc889..6b79afafc 100644 --- a/builder/osc/common/omi_config.go +++ b/builder/osc/common/omi_config.go @@ -10,13 +10,13 @@ import ( // OMIConfig is for common configuration related to creating OMIs. type OMIConfig struct { - OMIName string `mapstructure:"ami_name"` - OMIDescription string `mapstructure:"ami_description"` - OMIVirtType string `mapstructure:"ami_virtualization_type"` - OMIUsers []string `mapstructure:"ami_users"` - OMIGroups []string `mapstructure:"ami_groups"` - OMIProductCodes []string `mapstructure:"ami_product_codes"` - OMIRegions []string `mapstructure:"ami_regions"` + OMIName string `mapstructure:"omi_name"` + OMIDescription string `mapstructure:"omi_description"` + OMIVirtType string `mapstructure:"omi_virtualization_type"` + OMIUsers []string `mapstructure:"omi_users"` + OMIGroups []string `mapstructure:"omi_groups"` + OMIProductCodes []string `mapstructure:"omi_product_codes"` + OMIRegions []string `mapstructure:"omi_regions"` OMISkipRegionValidation bool `mapstructure:"skip_region_validation"` OMITags TagMap `mapstructure:"tags"` OMIENASupport *bool `mapstructure:"ena_support"` @@ -44,15 +44,15 @@ func (c *OMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context var errs []error if c.OMIName == "" { - errs = append(errs, fmt.Errorf("ami_name must be specified")) + errs = append(errs, fmt.Errorf("omi_name must be specified")) } // Make sure that if we have region_kms_key_ids defined, - // the regions in region_kms_key_ids are also in ami_regions + // the regions in region_kms_key_ids are also in omi_regions if len(c.OMIRegionKMSKeyIDs) > 0 { for kmsKeyRegion := range c.OMIRegionKMSKeyIDs { if !stringInSlice(c.OMIRegions, kmsKeyRegion) { - errs = append(errs, fmt.Errorf("Region %s is in region_kms_key_ids but not in ami_regions", kmsKeyRegion)) + errs = append(errs, fmt.Errorf("Region %s is in region_kms_key_ids but not in omi_regions", kmsKeyRegion)) } } } @@ -94,15 +94,15 @@ func (c *OMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context } if len(c.OMIName) < 3 || len(c.OMIName) > 128 { - errs = append(errs, fmt.Errorf("ami_name must be between 3 and 128 characters long")) + errs = append(errs, fmt.Errorf("omi_name must be between 3 and 128 characters long")) } if c.OMIName != templateCleanOMIName(c.OMIName) { errs = append(errs, fmt.Errorf("OMIName should only contain "+ "alphanumeric characters, parentheses (()), square brackets ([]), spaces "+ "( ), periods (.), slashes (/), dashes (-), single quotes ('), at-signs "+ - "(@), or underscores(_). You can use the `clean_ami_name` template "+ - "filter to automatically clean your ami name.")) + "(@), or underscores(_). You can use the `clean_omi_name` template "+ + "filter to automatically clean your omi name.")) } if len(errs) > 0 { @@ -127,16 +127,16 @@ func (c *OMIConfig) prepareRegions(accessConfig *AccessConfig) (errs []error) { regionSet[region] = struct{}{} // Make sure that if we have region_kms_key_ids defined, - // the regions in ami_regions are also in region_kms_key_ids + // the regions in omi_regions are also in region_kms_key_ids if len(c.OMIRegionKMSKeyIDs) > 0 { if _, ok := c.OMIRegionKMSKeyIDs[region]; !ok { - errs = append(errs, fmt.Errorf("Region %s is in ami_regions but not in region_kms_key_ids", region)) + errs = append(errs, fmt.Errorf("Region %s is in omi_regions but not in region_kms_key_ids", region)) } } if (accessConfig != nil) && (region == accessConfig.RawRegion) { // make sure we don't try to copy to the region we originally // create the OMI in. - log.Printf("Cannot copy OMI to AWS session region '%s', deleting it from `ami_regions`.", region) + log.Printf("Cannot copy OMI to AWS session region '%s', deleting it from `omi_regions`.", region) continue } regions = append(regions, region) diff --git a/builder/osc/common/step_pre_validate.go b/builder/osc/common/step_pre_validate.go new file mode 100644 index 000000000..2d74d9e7c --- /dev/null +++ b/builder/osc/common/step_pre_validate.go @@ -0,0 +1,51 @@ +package common + +import ( + "context" + "fmt" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" + "github.com/outscale/osc-go/oapi" +) + +// StepPreValidate provides an opportunity to pre-validate any configuration for +// the build before actually doing any time consuming work +// +type StepPreValidate struct { + DestOmiName string + ForceDeregister bool +} + +func (s *StepPreValidate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + if s.ForceDeregister { + ui.Say("Force Deregister flag found, skipping prevalidating OMI Name") + return multistep.ActionContinue + } + + oapiconn := state.Get("oapi").(*oapi.Client) + + ui.Say(fmt.Sprintf("Prevalidating OMI Name: %s", s.DestOmiName)) + resp, err := oapiconn.POST_ReadImages(oapi.ReadImagesRequest{ + Filters: oapi.FiltersImage{ImageNames: []string{s.DestOmiName}}, + }) + + if err != nil { + err := fmt.Errorf("Error querying OMI: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + if len(resp.OK.Images) > 0 { + err := fmt.Errorf("Error: name conflicts with an existing OMI: %s", resp.OK.Images[0].ImageId) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *StepPreValidate) Cleanup(multistep.StateBag) {} diff --git a/command/plugin.go b/command/plugin.go index f07f5f861..627c45375 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -38,6 +38,7 @@ import ( openstackbuilder "github.com/hashicorp/packer/builder/openstack" oracleclassicbuilder "github.com/hashicorp/packer/builder/oracle/classic" oracleocibuilder "github.com/hashicorp/packer/builder/oracle/oci" + oscbsusurrogatebuilder "github.com/hashicorp/packer/builder/osc/bsusurrogate" parallelsisobuilder "github.com/hashicorp/packer/builder/parallels/iso" parallelspvmbuilder "github.com/hashicorp/packer/builder/parallels/pvm" profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks" @@ -121,6 +122,7 @@ var Builders = map[string]packer.Builder{ "openstack": new(openstackbuilder.Builder), "oracle-classic": new(oracleclassicbuilder.Builder), "oracle-oci": new(oracleocibuilder.Builder), + "osc-bsusurrogate": new(oscbsusurrogatebuilder.Builder), "parallels-iso": new(parallelsisobuilder.Builder), "parallels-pvm": new(parallelspvmbuilder.Builder), "profitbricks": new(profitbricksbuilder.Builder),