feature: bsusurrogate, add StepUpdateOMIAttributes step

This commit is contained in:
Marin Salinas 2019-02-05 13:18:44 -06:00 committed by Megan Marsh
parent b46636a39b
commit 2c4b2b8657
4 changed files with 154 additions and 160 deletions

View File

@ -227,6 +227,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
OMIDevices: omiDevices, OMIDevices: omiDevices,
LaunchDevices: launchDevices, 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) b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)

View File

@ -10,25 +10,22 @@ import (
// OMIConfig is for common configuration related to creating OMIs. // OMIConfig is for common configuration related to creating OMIs.
type OMIConfig struct { type OMIConfig struct {
OMIName string `mapstructure:"omi_name"` OMIName string `mapstructure:"omi_name"`
OMIDescription string `mapstructure:"omi_description"` OMIDescription string `mapstructure:"omi_description"`
OMIVirtType string `mapstructure:"omi_virtualization_type"` OMIVirtType string `mapstructure:"omi_virtualization_type"`
OMIUsers []string `mapstructure:"omi_users"` OMIAccountIDs []string `mapstructure:"omi_account_ids"`
OMIGroups []string `mapstructure:"omi_groups"` OMIGroups []string `mapstructure:"omi_groups"`
OMIProductCodes []string `mapstructure:"omi_product_codes"` OMIProductCodes []string `mapstructure:"omi_product_codes"`
OMIRegions []string `mapstructure:"omi_regions"` OMIRegions []string `mapstructure:"omi_regions"`
OMISkipRegionValidation bool `mapstructure:"skip_region_validation"` OMISkipRegionValidation bool `mapstructure:"skip_region_validation"`
OMITags TagMap `mapstructure:"tags"` OMITags TagMap `mapstructure:"tags"`
OMIENASupport *bool `mapstructure:"ena_support"` OMIENASupport *bool `mapstructure:"ena_support"`
OMISriovNetSupport bool `mapstructure:"sriov_support"` OMISriovNetSupport bool `mapstructure:"sriov_support"`
OMIForceDeregister bool `mapstructure:"force_deregister"` OMIForceDeregister bool `mapstructure:"force_deregister"`
OMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"` OMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
OMIEncryptBootVolume bool `mapstructure:"encrypt_boot"` SnapshotTags TagMap `mapstructure:"snapshot_tags"`
OMIKmsKeyId string `mapstructure:"kms_key_id"` SnapshotAccountIDs []string `mapstructure:"snapshot_account_ids"`
OMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids"` SnapshotGroups []string `mapstructure:"snapshot_groups"`
SnapshotTags TagMap `mapstructure:"snapshot_tags"`
SnapshotUsers []string `mapstructure:"snapshot_users"`
SnapshotGroups []string `mapstructure:"snapshot_groups"`
} }
func stringInSlice(s []string, searchstr string) bool { 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")) 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)...) 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 { if len(c.OMIName) < 3 || len(c.OMIName) > 128 {
errs = append(errs, fmt.Errorf("omi_name must be between 3 and 128 characters long")) 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 // Mark that we saw the region
regionSet[region] = struct{}{} 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) { if (accessConfig != nil) && (region == accessConfig.RawRegion) {
// make sure we don't try to copy to the region we originally // make sure we don't try to copy to the region we originally
// create the OMI in. // create the OMI in.

View File

@ -68,64 +68,35 @@ func TestOMIConfigPrepare_regions(t *testing.T) {
} }
c.OMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"} 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 { if errs = c.prepareRegions(accessConf); len(errs) > 0 {
t.Fatal(fmt.Sprintf("shouldn't have error: %s", 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.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 { if errs = c.prepareRegions(accessConf); len(errs) > 0 {
t.Fatal("should have passed; we are able to use default KMS key if not sharing") 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.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 { if errs = c.prepareRegions(accessConf); len(errs) > 0 {
t.Fatal("should have an error b/c can't use default KMS key if sharing") 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.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 { 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") 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.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 { c.SnapshotAccountIDs = []string{"foo", "bar"}
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.OMIRegions = []string{"us-east-1", "us-west-1"} 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 { 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") 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. // allow rawregion to exist in omi_regions list.
accessConf = getFakeAccessConfig("us-east-1") accessConf = getFakeAccessConfig("us-east-1")
c.OMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"} c.OMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"}
c.OMIRegionKMSKeyIDs = nil
if errs = c.prepareRegions(accessConf); len(errs) > 0 { if errs = c.prepareRegions(accessConf); len(errs) > 0 {
t.Fatal("should allow user to have the raw region in omi_regions") 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) { func TestOMINameValidation(t *testing.T) {
c := testOMIConfig() c := testOMIConfig()

View File

@ -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...
}