diff --git a/builder/osc/bsusurrogate/builder.go b/builder/osc/bsusurrogate/builder.go index a0e16f00e..80f05c3f0 100644 --- a/builder/osc/bsusurrogate/builder.go +++ b/builder/osc/bsusurrogate/builder.go @@ -227,6 +227,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe OMIDevices: omiDevices, LaunchDevices: launchDevices, }, + &osccommon.StepUpdateOMIAttributes{ + AccountIds: b.config.OMIAccountIDs, + SnapshotAccountIds: b.config.SnapshotAccountIDs, + Ctx: b.config.ctx, + }, } b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) diff --git a/builder/osc/common/omi_config.go b/builder/osc/common/omi_config.go index eb09c7425..684f6fca4 100644 --- a/builder/osc/common/omi_config.go +++ b/builder/osc/common/omi_config.go @@ -10,25 +10,22 @@ import ( // OMIConfig is for common configuration related to creating OMIs. type OMIConfig struct { - 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"` - OMISriovNetSupport bool `mapstructure:"sriov_support"` - OMIForceDeregister bool `mapstructure:"force_deregister"` - OMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"` - OMIEncryptBootVolume bool `mapstructure:"encrypt_boot"` - OMIKmsKeyId string `mapstructure:"kms_key_id"` - OMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids"` - SnapshotTags TagMap `mapstructure:"snapshot_tags"` - SnapshotUsers []string `mapstructure:"snapshot_users"` - SnapshotGroups []string `mapstructure:"snapshot_groups"` + OMIName string `mapstructure:"omi_name"` + OMIDescription string `mapstructure:"omi_description"` + OMIVirtType string `mapstructure:"omi_virtualization_type"` + OMIAccountIDs []string `mapstructure:"omi_account_ids"` + 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"` + OMISriovNetSupport bool `mapstructure:"sriov_support"` + OMIForceDeregister bool `mapstructure:"force_deregister"` + OMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"` + SnapshotTags TagMap `mapstructure:"snapshot_tags"` + SnapshotAccountIDs []string `mapstructure:"snapshot_account_ids"` + SnapshotGroups []string `mapstructure:"snapshot_groups"` } func stringInSlice(s []string, searchstr string) bool { @@ -47,52 +44,8 @@ func (c *OMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context 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 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 omi_regions", kmsKeyRegion)) - } - } - } - errs = append(errs, c.prepareRegions(accessConfig)...) - if len(c.OMIUsers) > 0 && c.OMIEncryptBootVolume { - errs = append(errs, fmt.Errorf("Cannot share OMI with encrypted boot volume")) - } - - var kmsKeys []string - if len(c.OMIKmsKeyId) > 0 { - kmsKeys = append(kmsKeys, c.OMIKmsKeyId) - } - if len(c.OMIRegionKMSKeyIDs) > 0 { - for _, kmsKey := range c.OMIRegionKMSKeyIDs { - if len(kmsKey) == 0 { - kmsKeys = append(kmsKeys, c.OMIKmsKeyId) - } - } - } - for _, kmsKey := range kmsKeys { - if !validateKmsKey(kmsKey) { - errs = append(errs, fmt.Errorf("%s is not a valid KMS Key Id.", kmsKey)) - } - } - - if len(c.SnapshotUsers) > 0 { - if len(c.OMIKmsKeyId) == 0 && c.OMIEncryptBootVolume { - errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key")) - } - if len(c.OMIRegionKMSKeyIDs) > 0 { - for _, kmsKey := range c.OMIRegionKMSKeyIDs { - if len(kmsKey) == 0 { - errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key")) - } - } - } - } - if len(c.OMIName) < 3 || len(c.OMIName) > 128 { errs = append(errs, fmt.Errorf("omi_name must be between 3 and 128 characters long")) } @@ -126,13 +79,6 @@ func (c *OMIConfig) prepareRegions(accessConfig *AccessConfig) (errs []error) { // Mark that we saw the region regionSet[region] = struct{}{} - // Make sure that if we have region_kms_key_ids defined, - // 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 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. diff --git a/builder/osc/common/omi_config_test.go b/builder/osc/common/omi_config_test.go index d52be2aae..512a93972 100644 --- a/builder/osc/common/omi_config_test.go +++ b/builder/osc/common/omi_config_test.go @@ -68,64 +68,35 @@ func TestOMIConfigPrepare_regions(t *testing.T) { } c.OMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"} - c.OMIRegionKMSKeyIDs = map[string]string{ - "us-east-1": "123-456-7890", - "us-west-1": "789-012-3456", - "us-east-2": "456-789-0123", - } + if errs = c.prepareRegions(accessConf); len(errs) > 0 { t.Fatal(fmt.Sprintf("shouldn't have error: %s", errs[0])) } c.OMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"} - c.OMIRegionKMSKeyIDs = map[string]string{ - "us-east-1": "123-456-7890", - "us-west-1": "789-012-3456", - "us-east-2": "", - } + if errs = c.prepareRegions(accessConf); len(errs) > 0 { t.Fatal("should have passed; we are able to use default KMS key if not sharing") } - c.SnapshotUsers = []string{"user-foo", "user-bar"} + c.SnapshotAccountIDs = []string{"user-foo", "user-bar"} c.OMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"} - c.OMIRegionKMSKeyIDs = map[string]string{ - "us-east-1": "123-456-7890", - "us-west-1": "789-012-3456", - "us-east-2": "", - } + if errs = c.prepareRegions(accessConf); len(errs) > 0 { t.Fatal("should have an error b/c can't use default KMS key if sharing") } c.OMIRegions = []string{"us-east-1", "us-west-1"} - c.OMIRegionKMSKeyIDs = map[string]string{ - "us-east-1": "123-456-7890", - "us-west-1": "789-012-3456", - "us-east-2": "456-789-0123", - } + if errs = c.prepareRegions(accessConf); len(errs) > 0 { t.Fatal("should have error b/c theres a region in the key map that isn't in omi_regions") } c.OMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"} - c.OMIRegionKMSKeyIDs = map[string]string{ - "us-east-1": "123-456-7890", - "us-west-1": "789-012-3456", - } - if err := c.Prepare(accessConf, nil); err == nil { - t.Fatal("should have error b/c theres a region in in omi_regions that isn't in the key map") - } - - c.SnapshotUsers = []string{"foo", "bar"} - c.OMIKmsKeyId = "123-abc-456" - c.OMIEncryptBootVolume = true + c.SnapshotAccountIDs = []string{"foo", "bar"} c.OMIRegions = []string{"us-east-1", "us-west-1"} - c.OMIRegionKMSKeyIDs = map[string]string{ - "us-east-1": "123-456-7890", - "us-west-1": "", - } + if errs = c.prepareRegions(accessConf); len(errs) > 0 { t.Fatal("should have error b/c theres a region in in omi_regions that isn't in the key map") } @@ -133,66 +104,13 @@ func TestOMIConfigPrepare_regions(t *testing.T) { // allow rawregion to exist in omi_regions list. accessConf = getFakeAccessConfig("us-east-1") c.OMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"} - c.OMIRegionKMSKeyIDs = nil + if errs = c.prepareRegions(accessConf); len(errs) > 0 { t.Fatal("should allow user to have the raw region in omi_regions") } } -func TestOMIConfigPrepare_Share_EncryptedBoot(t *testing.T) { - c := testOMIConfig() - c.OMIUsers = []string{"testAccountID"} - c.OMIEncryptBootVolume = true - - accessConf := testAccessConfig() - - c.OMIKmsKeyId = "" - if err := c.Prepare(accessConf, nil); err == nil { - t.Fatal("shouldn't be able to share omi with encrypted boot volume") - } - - c.OMIKmsKeyId = "89c3fb9a-de87-4f2a-aedc-fddc5138193c" - if err := c.Prepare(accessConf, nil); err == nil { - t.Fatal("shouldn't be able to share omi with encrypted boot volume") - } -} - -func TestOMIConfigPrepare_ValidateKmsKey(t *testing.T) { - c := testOMIConfig() - c.OMIEncryptBootVolume = true - - accessConf := testAccessConfig() - - validCases := []string{ - "abcd1234-e567-890f-a12b-a123b4cd56ef", - "alias/foo/bar", - "arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef", - "arn:aws:kms:us-east-1:012345678910:alias/foo/bar", - } - for _, validCase := range validCases { - c.OMIKmsKeyId = validCase - if err := c.Prepare(accessConf, nil); err != nil { - t.Fatalf("%s should not have failed KMS key validation", validCase) - } - } - - invalidCases := []string{ - "ABCD1234-e567-890f-a12b-a123b4cd56ef", - "ghij1234-e567-890f-a12b-a123b4cd56ef", - "ghij1234+e567_890f-a12b-a123b4cd56ef", - "foo/bar", - "arn:aws:kms:us-east-1:012345678910:foo/bar", - } - for _, invalidCase := range invalidCases { - c.OMIKmsKeyId = invalidCase - if err := c.Prepare(accessConf, nil); err == nil { - t.Fatalf("%s should have failed KMS key validation", invalidCase) - } - } - -} - func TestOMINameValidation(t *testing.T) { c := testOMIConfig() diff --git a/builder/osc/common/step_update_omi.go b/builder/osc/common/step_update_omi.go new file mode 100644 index 000000000..52aeca335 --- /dev/null +++ b/builder/osc/common/step_update_omi.go @@ -0,0 +1,125 @@ +package common + +import ( + "context" + "crypto/tls" + "fmt" + "net/http" + + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/template/interpolate" + "github.com/outscale/osc-go/oapi" +) + +type StepUpdateOMIAttributes struct { + AccountIds []string + SnapshotAccountIds []string + Ctx interpolate.Context +} + +func (s *StepUpdateOMIAttributes) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + oapiconn := state.Get("oapi").(*oapi.Client) + config := state.Get("config").(*oapi.Config) + ui := state.Get("ui").(packer.Ui) + omis := state.Get("omis").(map[string]string) + snapshots := state.Get("snapshots").(map[string][]string) + + // Determine if there is any work to do. + valid := false + valid = valid || (s.AccountIds != nil && len(s.AccountIds) > 0) + valid = valid || (s.SnapshotAccountIds != nil && len(s.SnapshotAccountIds) > 0) + + if !valid { + return multistep.ActionContinue + } + + s.Ctx.Data = extractBuildInfo(oapiconn.GetConfig().Region, state) + + updateSnapshoptRequest := oapi.UpdateSnapshotRequest{ + PermissionsToCreateVolume: oapi.PermissionsOnResourceCreation{ + Additions: oapi.PermissionsOnResource{ + AccountIds: s.AccountIds, + GlobalPermission: true, + }, + }, + } + + updateImageRequest := oapi.UpdateImageRequest{ + PermissionsToLaunch: oapi.PermissionsOnResourceCreation{ + Additions: oapi.PermissionsOnResource{ + AccountIds: s.AccountIds, + GlobalPermission: true, + }, + }, + } + + // Updating image attributes + for region, omi := range omis { + ui.Say(fmt.Sprintf("Updating attributes on OMI (%s)...", omi)) + newConfig := &oapi.Config{ + UserAgent: config.UserAgent, + SecretKey: config.SecretKey, + Service: config.Service, + Region: region, //New region + URL: config.URL, + } + + skipClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + + regionconn := oapi.NewClient(newConfig, skipClient) + + ui.Message(fmt.Sprintf("Updating: %s", omi)) + updateImageRequest.ImageId = omi + _, err := regionconn.POST_UpdateImage(updateImageRequest) + if err != nil { + err := fmt.Errorf("Error updating OMI: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + // Updating snapshot attributes + for region, region_snapshots := range snapshots { + for _, snapshot := range region_snapshots { + ui.Say(fmt.Sprintf("Updating attributes on snapshot (%s)...", snapshot)) + newConfig := &oapi.Config{ + UserAgent: config.UserAgent, + SecretKey: config.SecretKey, + Service: config.Service, + Region: region, //New region + URL: config.URL, + } + + skipClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + + regionconn := oapi.NewClient(newConfig, skipClient) + + ui.Message(fmt.Sprintf("Updating: %s", snapshot)) + updateSnapshoptRequest.SnapshotId = snapshot + _, err := regionconn.POST_UpdateSnapshot(updateSnapshoptRequest) + if err != nil { + err := fmt.Errorf("Error updating snapshot: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + } + } + + return multistep.ActionContinue +} + +func (s *StepUpdateOMIAttributes) Cleanup(state multistep.StateBag) { + // No cleanup... +}