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) {}