Unverified Commit 3c794462 authored by Sylvia Moss's avatar Sylvia Moss Committed by GitHub
Browse files

(3) Add amazon-ami data source (#10467)

parent 291121dd
Showing with 407 additions and 87 deletions
+407 -87
......@@ -285,9 +285,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
var warns []string
errs = packersdk.MultiErrorAppend(errs, b.config.RootVolumeTag.CopyOn(&b.config.RootVolumeTags)...)
errs = packersdk.MultiErrorAppend(errs, b.config.SourceAmiFilter.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs,
b.config.AMIConfig.Prepare(&b.config.AccessConfig, &b.config.ctx)...)
......
......@@ -17,7 +17,6 @@ import (
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
awsbase "github.com/hashicorp/aws-sdk-go-base"
cleanhttp "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
vaultapi "github.com/hashicorp/vault/api"
)
......@@ -370,7 +369,7 @@ func (c *AccessConfig) GetCredsFromVault() error {
return nil
}
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
func (c *AccessConfig) Prepare() []error {
var errs []error
if c.SkipMetadataApiCheck {
......
......@@ -47,7 +47,7 @@ func TestAccessConfigPrepare_RegionRestricted(t *testing.T) {
Region: aws.String("us-gov-west-1"),
}))
if err := c.Prepare(nil); err != nil {
if err := c.Prepare(); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
......
//go:generate struct-markdown
package common
import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/service/ec2"
)
type AmiFilterOptions struct {
// Filters used to select an AMI. Any filter described in the docs for
// [DescribeImages](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html)
// is valid.
Filters map[string]string `mapstructure:"filters"`
// Filters the images by their owner. You
// may specify one or more AWS account IDs, "self" (which will use the
// account whose credentials you are using to run Packer), or an AWS owner
// alias: for example, `amazon`, `aws-marketplace`, or `microsoft`. This
// option is required for security reasons.
Owners []string `mapstructure:"owners"`
// Selects the newest created image when true.
// This is most useful for selecting a daily distro build.
MostRecent bool `mapstructure:"most_recent"`
}
func (d *AmiFilterOptions) GetOwners() []*string {
res := make([]*string, 0, len(d.Owners))
for _, owner := range d.Owners {
i := owner
res = append(res, &i)
}
return res
}
func (d *AmiFilterOptions) Empty() bool {
return len(d.Owners) == 0 && len(d.Filters) == 0
}
func (d *AmiFilterOptions) NoOwner() bool {
return len(d.Owners) == 0
}
func (d *AmiFilterOptions) GetFilteredImage(params *ec2.DescribeImagesInput, ec2conn *ec2.EC2) (*ec2.Image, error) {
// We have filters to apply
if len(d.Filters) > 0 {
params.Filters = buildEc2Filters(d.Filters)
}
if len(d.Owners) > 0 {
params.Owners = d.GetOwners()
}
log.Printf("Using AMI Filters %v", params)
imageResp, err := ec2conn.DescribeImages(params)
if err != nil {
err := fmt.Errorf("Error querying AMI: %s", err)
return nil, err
}
if len(imageResp.Images) == 0 {
err := fmt.Errorf("No AMI was found matching filters: %v", params)
return nil, err
}
if len(imageResp.Images) > 1 && !d.MostRecent {
err := fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.")
return nil, err
}
var image *ec2.Image
if d.MostRecent {
image = mostRecentAmi(imageResp.Images)
} else {
image = imageResp.Images[0]
}
return image, nil
}
......@@ -19,29 +19,6 @@ import (
var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$")
type AmiFilterOptions struct {
config.KeyValueFilter `mapstructure:",squash"`
Owners []string
MostRecent bool `mapstructure:"most_recent"`
}
func (d *AmiFilterOptions) GetOwners() []*string {
res := make([]*string, 0, len(d.Owners))
for _, owner := range d.Owners {
i := owner
res = append(res, &i)
}
return res
}
func (d *AmiFilterOptions) Empty() bool {
return len(d.Owners) == 0 && d.KeyValueFilter.Empty()
}
func (d *AmiFilterOptions) NoOwner() bool {
return len(d.Owners) == 0
}
type SubnetFilterOptions struct {
config.NameValueFilter `mapstructure:",squash"`
MostFree bool `mapstructure:"most_free"`
......@@ -515,7 +492,6 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
errs = append(errs, c.SpotTag.CopyOn(&c.SpotTags)...)
for _, preparer := range []interface{ Prepare() []error }{
&c.SourceAmiFilter,
&c.SecurityGroupFilter,
&c.SubnetFilter,
&c.VpcFilter,
......
......@@ -11,10 +11,9 @@ import (
// FlatAmiFilterOptions is an auto-generated flat version of AmiFilterOptions.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatAmiFilterOptions struct {
Filters map[string]string `cty:"filters" hcl:"filters"`
Filter []config.FlatKeyValue `cty:"filter" hcl:"filter"`
Owners []string `cty:"owners" hcl:"owners"`
MostRecent *bool `mapstructure:"most_recent" cty:"most_recent" hcl:"most_recent"`
Filters map[string]string `mapstructure:"filters" cty:"filters" hcl:"filters"`
Owners []string `mapstructure:"owners" cty:"owners" hcl:"owners"`
MostRecent *bool `mapstructure:"most_recent" cty:"most_recent" hcl:"most_recent"`
}
// FlatMapstructure returns a new FlatAmiFilterOptions.
......@@ -30,7 +29,6 @@ func (*AmiFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcl
func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"filters": &hcldec.AttrSpec{Name: "filters", Type: cty.Map(cty.String), Required: false},
"filter": &hcldec.BlockListSpec{TypeName: "filter", Nested: hcldec.ObjectSpec((*config.FlatKeyValue)(nil).HCL2Spec())},
"owners": &hcldec.AttrSpec{Name: "owners", Type: cty.List(cty.String), Required: false},
"most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false},
}
......
......@@ -7,7 +7,6 @@ import (
"testing"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/template/config"
)
func init() {
......@@ -86,10 +85,8 @@ func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) {
filter_key := "name"
filter_value := "foo"
goodFilter := AmiFilterOptions{
Owners: []string{owner},
KeyValueFilter: config.KeyValueFilter{
Filters: map[string]string{filter_key: filter_value},
},
Owners: []string{owner},
Filters: map[string]string{filter_key: filter_value},
}
c.SourceAmiFilter = goodFilter
if err := c.Prepare(nil); len(err) != 0 {
......
......@@ -3,7 +3,6 @@ package common
import (
"context"
"fmt"
"log"
"sort"
"time"
......@@ -53,44 +52,13 @@ func (s *StepSourceAMIInfo) Run(ctx context.Context, state multistep.StateBag) m
params.ImageIds = []*string{&s.SourceAmi}
}
// We have filters to apply
if len(s.AmiFilters.Filters) > 0 {
params.Filters = buildEc2Filters(s.AmiFilters.Filters)
}
if len(s.AmiFilters.Owners) > 0 {
params.Owners = s.AmiFilters.GetOwners()
}
log.Printf("Using AMI Filters %v", params)
imageResp, err := ec2conn.DescribeImages(params)
image, err := s.AmiFilters.GetFilteredImage(params, ec2conn)
if err != nil {
err := fmt.Errorf("Error querying AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(imageResp.Images) == 0 {
err := fmt.Errorf("No AMI was found matching filters: %v", params)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(imageResp.Images) > 1 && !s.AmiFilters.MostRecent {
err := fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
var image *ec2.Image
if s.AmiFilters.MostRecent {
image = mostRecentAmi(imageResp.Images)
} else {
image = imageResp.Images[0]
}
ui.Message(fmt.Sprintf("Found Image ID: %s", *image.ImageId))
// Enhanced Networking can only be enabled on HVM AMIs.
......
......@@ -34,11 +34,13 @@ func (a *AWSHelper) CleanUpAmi() error {
return fmt.Errorf("AWSAMICleanUp: Unable to find Image %s: %s", a.AMIName, err.Error())
}
_, err = regionconn.DeregisterImage(&ec2.DeregisterImageInput{
ImageId: resp.Images[0].ImageId,
})
if err != nil {
return fmt.Errorf("AWSAMICleanUp: Unable to Deregister Image %s", err.Error())
if resp != nil && len(resp.Images) > 0 {
_, err = regionconn.DeregisterImage(&ec2.DeregisterImageInput{
ImageId: resp.Images[0].ImageId,
})
if err != nil {
return fmt.Errorf("AWSAMICleanUp: Unable to Deregister Image %s", err.Error())
}
}
return nil
}
......@@ -118,7 +118,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
errs = packersdk.MultiErrorAppend(errs, b.config.VolumeRunTag.CopyOn(&b.config.VolumeRunTags)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs,
b.config.AMIConfig.Prepare(&b.config.AccessConfig, &b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AMIMappings.Prepare(&b.config.ctx)...)
......
......@@ -116,7 +116,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
var warns []string
errs = packersdk.MultiErrorAppend(errs, b.config.VolumeRunTag.CopyOn(&b.config.VolumeRunTags)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs,
b.config.AMIConfig.Prepare(&b.config.AccessConfig, &b.config.ctx)...)
......
......@@ -124,7 +124,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
var errs *packersdk.MultiError
var warns []string
errs = packersdk.MultiErrorAppend(errs, b.config.VolumeRunTag.CopyOn(&b.config.VolumeRunTags)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.launchBlockDevices.Prepare(&b.config.ctx)...)
......
......@@ -178,7 +178,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
// Accumulate any errors
var errs *packersdk.MultiError
var warns []string
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, b.config.AMIMappings.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.LaunchMappings.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs,
......
......@@ -9,6 +9,7 @@ import (
"github.com/hashicorp/packer/builder/amazon/ebssurrogate"
"github.com/hashicorp/packer/builder/amazon/ebsvolume"
"github.com/hashicorp/packer/builder/osc/chroot"
amazonami "github.com/hashicorp/packer/datasource/amazon/ami"
amazonimport "github.com/hashicorp/packer/post-processor/amazon-import"
)
......@@ -19,6 +20,7 @@ func main() {
pps.RegisterBuilder("ebssurrogate", new(ebssurrogate.Builder))
pps.RegisterBuilder("ebsvolume", new(ebsvolume.Builder))
pps.RegisterPostProcessor("import", new(amazonimport.PostProcessor))
pps.RegisterDatasource("ami", new(amazonami.Datasource))
err := pps.Run()
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
......
......@@ -15,7 +15,7 @@ import (
"github.com/hashicorp/packer/packer/plugin"
)
const packerPluginCheck = "packer-plugin-check"
const packerPluginsCheck = "packer-plugins-check"
var (
docs = flag.Bool("docs", false, "flag to indicate that documentation files should be checked.")
......@@ -24,15 +24,15 @@ var (
// Usage is a replacement usage function for the flags package.
func Usage() {
fmt.Fprintf(os.Stderr, "Usage of "+packerPluginCheck+":\n")
fmt.Fprintf(os.Stderr, "\t"+packerPluginCheck+" [flags]\n")
fmt.Fprintf(os.Stderr, "Usage of "+packerPluginsCheck+":\n")
fmt.Fprintf(os.Stderr, "\t"+packerPluginsCheck+" [flags]\n")
fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults()
}
func main() {
log.SetFlags(0)
log.SetPrefix(packerPluginCheck + ": ")
log.SetPrefix(packerPluginsCheck + ": ")
flag.Usage = Usage
flag.Parse()
......
......@@ -65,6 +65,7 @@ import (
vsphereclonebuilder "github.com/hashicorp/packer/builder/vsphere/clone"
vsphereisobuilder "github.com/hashicorp/packer/builder/vsphere/iso"
yandexbuilder "github.com/hashicorp/packer/builder/yandex"
amazonamidatasource "github.com/hashicorp/packer/datasource/amazon/ami"
alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import"
amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import"
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
......@@ -212,7 +213,9 @@ var PostProcessors = map[string]packersdk.PostProcessor{
"yandex-import": new(yandeximportpostprocessor.PostProcessor),
}
var Datasources = map[string]packersdk.Datasource{}
var Datasources = map[string]packersdk.Datasource{
"amazon-ami": new(amazonamidatasource.Datasource),
}
var pluginRegexp = regexp.MustCompile("packer-(builder|post-processor|provisioner|datasource)-(.+)")
......
//go:generate mapstructure-to-hcl2 -type DatasourceOutput,Config
package ami
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/hcl2helper"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/zclconf/go-cty/cty"
)
type Datasource struct {
config Config
}
type Config struct {
awscommon.AccessConfig `mapstructure:",squash"`
awscommon.AmiFilterOptions `mapstructure:",squash"`
}
func (d *Datasource) ConfigSpec() hcldec.ObjectSpec {
return d.config.FlatMapstructure().HCL2Spec()
}
func (d *Datasource) Configure(raws ...interface{}) error {
err := config.Decode(&d.config, nil, raws...)
if err != nil {
return err
}
var errs *packersdk.MultiError
errs = packersdk.MultiErrorAppend(errs, d.config.AccessConfig.Prepare()...)
if d.config.Empty() {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("The `filters` must be specified"))
}
if d.config.NoOwner() {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("For security reasons, you must declare an owner."))
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
type DatasourceOutput struct {
ID string `mapstructure:"id"`
Name string `mapstructure:"name"`
CreationDate string `mapstructure:"creation_date"`
Owner string `mapstructure:"owner"`
OwnerName string `mapstructure:"owner_name"`
Tags map[string]string `mapstructure:"tags"`
}
func (d *Datasource) OutputSpec() hcldec.ObjectSpec {
return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec()
}
func (d *Datasource) Execute() (cty.Value, error) {
session, err := d.config.Session()
if err != nil {
return cty.NullVal(cty.EmptyObject), err
}
image, err := d.config.AmiFilterOptions.GetFilteredImage(&ec2.DescribeImagesInput{}, ec2.New(session))
if err != nil {
return cty.NullVal(cty.EmptyObject), err
}
imageTags := make(map[string]string, len(image.Tags))
for _, tag := range image.Tags {
imageTags[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value)
}
output := DatasourceOutput{
ID: aws.StringValue(image.ImageId),
Name: aws.StringValue(image.Name),
CreationDate: aws.StringValue(image.CreationDate),
Owner: aws.StringValue(image.OwnerId),
OwnerName: aws.StringValue(image.ImageOwnerAlias),
Tags: imageTags,
}
return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil
}
// Code generated by "mapstructure-to-hcl2 -type DatasourceOutput,Config"; DO NOT EDIT.
package ami
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer/builder/amazon/common"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"`
AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"`
CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"`
CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"`
DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"`
InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"`
MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code" hcl:"mfa_code"`
ProfileName *string `mapstructure:"profile" required:"false" cty:"profile" hcl:"profile"`
RawRegion *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"`
SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"`
SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"`
SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"`
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"`
PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"`
Filters map[string]string `mapstructure:"filters" cty:"filters" hcl:"filters"`
Owners []string `mapstructure:"owners" cty:"owners" hcl:"owners"`
MostRecent *bool `mapstructure:"most_recent" cty:"most_recent" hcl:"most_recent"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"assume_role": &hcldec.BlockSpec{TypeName: "assume_role", Nested: hcldec.ObjectSpec((*common.FlatAssumeRoleConfig)(nil).HCL2Spec())},
"custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false},
"shared_credentials_file": &hcldec.AttrSpec{Name: "shared_credentials_file", Type: cty.String, Required: false},
"decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false},
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"max_retries": &hcldec.AttrSpec{Name: "max_retries", Type: cty.Number, Required: false},
"mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false},
"profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false},
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false},
"skip_credential_validation": &hcldec.AttrSpec{Name: "skip_credential_validation", Type: cty.Bool, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())},
"aws_polling": &hcldec.BlockSpec{TypeName: "aws_polling", Nested: hcldec.ObjectSpec((*common.FlatAWSPollingConfig)(nil).HCL2Spec())},
"filters": &hcldec.AttrSpec{Name: "filters", Type: cty.Map(cty.String), Required: false},
"owners": &hcldec.AttrSpec{Name: "owners", Type: cty.List(cty.String), Required: false},
"most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false},
}
return s
}
// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatDatasourceOutput struct {
ID *string `mapstructure:"id" cty:"id" hcl:"id"`
Name *string `mapstructure:"name" cty:"name" hcl:"name"`
CreationDate *string `mapstructure:"creation_date" cty:"creation_date" hcl:"creation_date"`
Owner *string `mapstructure:"owner" cty:"owner" hcl:"owner"`
OwnerName *string `mapstructure:"owner_name" cty:"owner_name" hcl:"owner_name"`
Tags map[string]string `mapstructure:"tags" cty:"tags" hcl:"tags"`
}
// FlatMapstructure returns a new FlatDatasourceOutput.
// FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*DatasourceOutput) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatDatasourceOutput)
}
// HCL2Spec returns the hcl spec of a DatasourceOutput.
// This spec is used by HCL to read the fields of DatasourceOutput.
// The decoded values from this spec will then be applied to a FlatDatasourceOutput.
func (*FlatDatasourceOutput) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"id": &hcldec.AttrSpec{Name: "id", Type: cty.String, Required: false},
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
"creation_date": &hcldec.AttrSpec{Name: "creation_date", Type: cty.String, Required: false},
"owner": &hcldec.AttrSpec{Name: "owner", Type: cty.String, Required: false},
"owner_name": &hcldec.AttrSpec{Name: "owner_name", Type: cty.String, Required: false},
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.Map(cty.String), Required: false},
}
return s
}
package ami
import (
"fmt"
"os/exec"
"testing"
"github.com/hashicorp/packer-plugin-sdk/acctest"
amazonacc "github.com/hashicorp/packer/builder/amazon/ebs/acceptance"
)
func TestAmazonAmi(t *testing.T) {
testCase := &acctest.DatasourceTestCase{
Name: "amazon_ami_datasource_basic_test",
Teardown: func() error {
helper := amazonacc.AWSHelper{
Region: "us-west-2",
AMIName: "packer-amazon-ami-test",
}
return helper.CleanUpAmi()
},
Template: testDatasourceBasic,
Type: "amazon-ami",
Check: func(buildCommand *exec.Cmd, logfile string) error {
if buildCommand.ProcessState != nil {
if buildCommand.ProcessState.ExitCode() != 0 {
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
}
}
return nil
},
}
acctest.TestDatasource(t, testCase)
}
const testDatasourceBasic = `
data "amazon-ami" "test" {
filters = {
virtualization-type = "hvm"
name = "Windows_Server-2016-English-Full-Base-*"
root-device-type = "ebs"
}
most_recent = true
owners = ["801119661308"]
}
source "amazon-ebs" "basic-example" {
user_data_file = "./test-fixtures/configure-source-ssh.ps1"
region = "us-west-2"
source_ami = data.amazon-ami.test.id
instance_type = "t2.small"
ssh_agent_auth = false
ami_name = "packer-amazon-ami-test"
communicator = "ssh"
ssh_timeout = "10m"
ssh_username = "Administrator"
}
build {
sources = [
"source.amazon-ebs.basic-example"
]
}
`
package ami
import (
"testing"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
)
func TestDatasourceConfigure_FilterBlank(t *testing.T) {
datasource := Datasource{
config: Config{
AmiFilterOptions: awscommon.AmiFilterOptions{},
},
}
if err := datasource.Configure(nil); err == nil {
t.Fatalf("Should error if filters map is empty or not specified")
}
}
func TestRunConfigPrepare_SourceAmiFilterOwnersBlank(t *testing.T) {
datasource := Datasource{
config: Config{
AmiFilterOptions: awscommon.AmiFilterOptions{
Filters: map[string]string{"foo": "bar"},
},
},
}
if err := datasource.Configure(nil); err == nil {
t.Fatalf("Should error if Owners is not specified)")
}
}
func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) {
datasource := Datasource{
config: Config{
AmiFilterOptions: awscommon.AmiFilterOptions{
Owners: []string{"1234567"},
Filters: map[string]string{"foo": "bar"},
},
},
}
if err := datasource.Configure(nil); err != nil {
t.Fatalf("err: %s", err)
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment