github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/common/step_network_info.go (about)

     1  package common
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"math/rand"
     8  	"sort"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/service/ec2"
    12  	"github.com/hashicorp/packer/helper/multistep"
    13  	"github.com/hashicorp/packer/packer"
    14  )
    15  
    16  // StepNetworkInfo queries AWS for information about
    17  // VPC's and Subnets that is used throughout the AMI creation process.
    18  //
    19  // Produces (adding them to the state bag):
    20  //   vpc_id string - the VPC ID
    21  //   subnet_id string - the Subnet ID
    22  //   availability_zone string - the AZ name
    23  type StepNetworkInfo struct {
    24  	VpcId               string
    25  	VpcFilter           VpcFilterOptions
    26  	SubnetId            string
    27  	SubnetFilter        SubnetFilterOptions
    28  	AvailabilityZone    string
    29  	SecurityGroupIds    []string
    30  	SecurityGroupFilter SecurityGroupFilterOptions
    31  }
    32  
    33  type subnetsSort []*ec2.Subnet
    34  
    35  func (a subnetsSort) Len() int      { return len(a) }
    36  func (a subnetsSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
    37  func (a subnetsSort) Less(i, j int) bool {
    38  	return *a[i].AvailableIpAddressCount < *a[j].AvailableIpAddressCount
    39  }
    40  
    41  // Returns the most recent AMI out of a slice of images.
    42  func mostFreeSubnet(subnets []*ec2.Subnet) *ec2.Subnet {
    43  	sortedSubnets := subnets
    44  	sort.Sort(subnetsSort(sortedSubnets))
    45  	return sortedSubnets[len(sortedSubnets)-1]
    46  }
    47  
    48  func (s *StepNetworkInfo) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    49  	ec2conn := state.Get("ec2").(*ec2.EC2)
    50  	ui := state.Get("ui").(packer.Ui)
    51  
    52  	// VPC
    53  	if s.VpcId == "" && !s.VpcFilter.Empty() {
    54  		params := &ec2.DescribeVpcsInput{}
    55  		params.Filters = buildEc2Filters(s.VpcFilter.Filters)
    56  		s.VpcFilter.Filters[aws.String("state")] = aws.String("available")
    57  
    58  		log.Printf("Using VPC Filters %v", params)
    59  
    60  		vpcResp, err := ec2conn.DescribeVpcs(params)
    61  		if err != nil {
    62  			err := fmt.Errorf("Error querying VPCs: %s", err)
    63  			state.Put("error", err)
    64  			ui.Error(err.Error())
    65  			return multistep.ActionHalt
    66  		}
    67  
    68  		if len(vpcResp.Vpcs) != 1 {
    69  			err := fmt.Errorf("Exactly one VPC should match the filter, but %d VPC's was found matching filters: %v", len(vpcResp.Vpcs), params)
    70  			state.Put("error", err)
    71  			ui.Error(err.Error())
    72  			return multistep.ActionHalt
    73  		}
    74  
    75  		s.VpcId = *vpcResp.Vpcs[0].VpcId
    76  		ui.Message(fmt.Sprintf("Found VPC ID: %s", s.VpcId))
    77  	}
    78  
    79  	// Subnet
    80  	if s.SubnetId == "" && !s.SubnetFilter.Empty() {
    81  		params := &ec2.DescribeSubnetsInput{}
    82  		s.SubnetFilter.Filters[aws.String("state")] = aws.String("available")
    83  
    84  		if s.VpcId != "" {
    85  			s.SubnetFilter.Filters[aws.String("vpc-id")] = &s.VpcId
    86  		}
    87  		if s.AvailabilityZone != "" {
    88  			s.SubnetFilter.Filters[aws.String("availability-zone")] = &s.AvailabilityZone
    89  		}
    90  		params.Filters = buildEc2Filters(s.SubnetFilter.Filters)
    91  		log.Printf("Using Subnet Filters %v", params)
    92  
    93  		subnetsResp, err := ec2conn.DescribeSubnets(params)
    94  		if err != nil {
    95  			err := fmt.Errorf("Error querying Subnets: %s", err)
    96  			state.Put("error", err)
    97  			ui.Error(err.Error())
    98  			return multistep.ActionHalt
    99  		}
   100  
   101  		if len(subnetsResp.Subnets) == 0 {
   102  			err := fmt.Errorf("No Subnets was found matching filters: %v", params)
   103  			state.Put("error", err)
   104  			ui.Error(err.Error())
   105  			return multistep.ActionHalt
   106  		}
   107  
   108  		if len(subnetsResp.Subnets) > 1 && !s.SubnetFilter.Random && !s.SubnetFilter.MostFree {
   109  			err := fmt.Errorf("Your filter matched %d Subnets. Please try a more specific search, or set random or most_free to true.", len(subnetsResp.Subnets))
   110  			state.Put("error", err)
   111  			ui.Error(err.Error())
   112  			return multistep.ActionHalt
   113  		}
   114  
   115  		var subnet *ec2.Subnet
   116  		switch {
   117  		case s.SubnetFilter.MostFree:
   118  			subnet = mostFreeSubnet(subnetsResp.Subnets)
   119  		case s.SubnetFilter.Random:
   120  			subnet = subnetsResp.Subnets[rand.Intn(len(subnetsResp.Subnets))]
   121  		default:
   122  			subnet = subnetsResp.Subnets[0]
   123  		}
   124  		s.SubnetId = *subnet.SubnetId
   125  		ui.Message(fmt.Sprintf("Found Subnet ID: %s", s.SubnetId))
   126  	}
   127  
   128  	// Try to find AZ and VPC Id from Subnet if they are not yet found/given
   129  	if s.SubnetId != "" && (s.AvailabilityZone == "" || s.VpcId == "") {
   130  		log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", s.SubnetId)
   131  		resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&s.SubnetId}})
   132  		if err != nil {
   133  			err := fmt.Errorf("Describing the subnet: %s returned error: %s.", s.SubnetId, err)
   134  			state.Put("error", err)
   135  			ui.Error(err.Error())
   136  			return multistep.ActionHalt
   137  		}
   138  		if s.AvailabilityZone == "" {
   139  			s.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
   140  			log.Printf("[INFO] AvailabilityZone found: '%s'", s.AvailabilityZone)
   141  		}
   142  		if s.VpcId == "" {
   143  			s.VpcId = *resp.Subnets[0].VpcId
   144  			log.Printf("[INFO] VpcId found: '%s'", s.VpcId)
   145  		}
   146  	}
   147  
   148  	state.Put("vpc_id", s.VpcId)
   149  	state.Put("availability_zone", s.AvailabilityZone)
   150  	state.Put("subnet_id", s.SubnetId)
   151  	return multistep.ActionContinue
   152  }
   153  
   154  func (s *StepNetworkInfo) Cleanup(multistep.StateBag) {}