diff --git a/builder/osc/common/access_config.go b/builder/osc/common/access_config.go new file mode 100644 index 000000000..3b76ea7bb --- /dev/null +++ b/builder/osc/common/access_config.go @@ -0,0 +1,84 @@ +package common + +import ( + "crypto/tls" + "fmt" + "log" + "net/http" + + "github.com/hashicorp/packer/template/interpolate" + "github.com/outscale/osc-go/oapi" +) + +// AccessConfig is for common configuration related to AWS access +type AccessConfig struct { + AccessKey string `mapstructure:"access_key"` + CustomEndpoint string `mapstructure:"custom_endpoint"` + DecodeAuthZMessages bool `mapstructure:"decode_authorization_messages"` + InsecureSkipTLSVerify bool `mapstructure:"insecure_skip_tls_verify"` + MFACode string `mapstructure:"mfa_code"` + ProfileName string `mapstructure:"profile"` + RawRegion string `mapstructure:"region"` + SecretKey string `mapstructure:"secret_key"` + SkipValidation bool `mapstructure:"skip_region_validation"` + SkipMetadataApiCheck bool `mapstructure:"skip_metadata_api_check"` + Token string `mapstructure:"token"` + clientConfig *oapi.Config + + getOAPIConnection func() oapi.OAPIClient +} + +// Config returns a valid aws.Config object for access to AWS services, or +// an error if the authentication and region couldn't be resolved +func (c *AccessConfig) Config() (*oapi.Config, error) { + if c.clientConfig != nil { + return c.clientConfig, nil + } + + config := &oapi.Config{ + AccessKey: c.AccessKey, + SecretKey: c.SecretKey, + Region: c.RawRegion, + URL: c.CustomEndpoint, + Service: "api", + } + + return config, nil + +} + +func (c *AccessConfig) NewOAPIConnection() (oapi.OAPIClient, error) { + if c.getOAPIConnection != nil { + return c.getOAPIConnection(), nil + } + oapicfg, err := c.Config() + if err != nil { + return nil, err + } + + skipClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + + oapiClient := oapi.NewClient(oapicfg, skipClient) + + return oapiClient, nil +} + +func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { + var errs []error + + if c.SkipMetadataApiCheck { + log.Println("(WARN) skip_metadata_api_check ignored.") + } + // Either both access and secret key must be set or neither of them should + // be. + if (len(c.AccessKey) > 0) != (len(c.SecretKey) > 0) { + errs = append(errs, + fmt.Errorf("`access_key` and `secret_key` must both be either set or not set.")) + } + + return errs +} diff --git a/builder/osc/common/access_config_test.go b/builder/osc/common/access_config_test.go new file mode 100644 index 000000000..71de1c446 --- /dev/null +++ b/builder/osc/common/access_config_test.go @@ -0,0 +1,66 @@ +package common + +import ( + "testing" + + "github.com/outscale/osc-go/oapi" +) + +type mockOAPIClient struct { + oapi.OAPIClient +} + +func testAccessConfig() *AccessConfig { + return &AccessConfig{ + getOAPIConnection: func() oapi.OAPIClient { + return &mockOAPIClient{} + }, + } +} + +func (m *mockOAPIClient) POST_ReadRegions(oapi.ReadRegionsRequest) (*oapi.POST_ReadRegionsResponses, error) { + return &oapi.POST_ReadRegionsResponses{ + OK: &oapi.ReadRegionsResponse{ + Regions: []oapi.Region{ + {RegionEndpoint: "us-west1", RegionName: "us-west1"}, + {RegionEndpoint: "us-east-1", RegionName: "us-east-1"}, + }, + }, + }, nil +} + +func TestAccessConfigPrepare_Region(t *testing.T) { + c := testAccessConfig() + + c.RawRegion = "us-east-12" + err := c.ValidateRegion(c.RawRegion) + if err == nil { + t.Fatalf("should have region validation err: %s", c.RawRegion) + } + + c.RawRegion = "us-east-1" + err = c.ValidateRegion(c.RawRegion) + if err != nil { + t.Fatalf("shouldn't have region validation err: %s", c.RawRegion) + } + + c.RawRegion = "custom" + err = c.ValidateRegion(c.RawRegion) + if err == nil { + t.Fatalf("should have region validation err: %s", c.RawRegion) + } + + c.RawRegion = "custom" + c.SkipValidation = true + // testing whole prepare func here; this is checking that validation is + // skipped, so we don't need a mock connection + if err := c.Prepare(nil); err != nil { + t.Fatalf("shouldn't have err: %s", err) + } + + c.SkipValidation = false + c.RawRegion = "" + if err := c.Prepare(nil); err != nil { + t.Fatalf("shouldn't have err: %s", err) + } +} diff --git a/builder/osc/common/regions.go b/builder/osc/common/regions.go new file mode 100644 index 000000000..f52a53c3a --- /dev/null +++ b/builder/osc/common/regions.go @@ -0,0 +1,56 @@ +package common + +import ( + "fmt" + + "github.com/outscale/osc-go/oapi" +) + +func listOAPIRegions(oapiconn oapi.OAPIClient) ([]string, error) { + var regions []string + resp, err := oapiconn.POST_ReadRegions(oapi.ReadRegionsRequest{}) + if resp.OK == nil || err != nil { + return []string{}, err + } + + resultRegions := resp.OK + + for _, region := range resultRegions.Regions { + regions = append(regions, region.RegionName) + } + + return regions, nil +} + +// ValidateRegion returns true if the supplied region is a valid AWS +// region and false if it's not. +func (c *AccessConfig) ValidateRegion(regions ...string) error { + oapiconn, err := c.NewOAPIConnection() + if err != nil { + return err + } + + validRegions, err := listOAPIRegions(oapiconn) + if err != nil { + return err + } + + var invalidRegions []string + for _, region := range regions { + found := false + for _, validRegion := range validRegions { + if region == validRegion { + found = true + break + } + } + if !found { + invalidRegions = append(invalidRegions, region) + } + } + + if len(invalidRegions) > 0 { + return fmt.Errorf("Invalid region(s): %v", invalidRegions) + } + return nil +}