github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/amazon/common/step_security_group.go (about) 1 package common 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/request" 10 "github.com/aws/aws-sdk-go/service/ec2" 11 "github.com/hashicorp/packer/common/uuid" 12 "github.com/hashicorp/packer/helper/communicator" 13 "github.com/hashicorp/packer/packer" 14 "github.com/mitchellh/multistep" 15 ) 16 17 type StepSecurityGroup struct { 18 CommConfig *communicator.Config 19 SecurityGroupIds []string 20 VpcId string 21 22 createdGroupId string 23 } 24 25 func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction { 26 ec2conn := state.Get("ec2").(*ec2.EC2) 27 ui := state.Get("ui").(packer.Ui) 28 29 if len(s.SecurityGroupIds) > 0 { 30 _, err := ec2conn.DescribeSecurityGroups( 31 &ec2.DescribeSecurityGroupsInput{ 32 GroupIds: aws.StringSlice(s.SecurityGroupIds), 33 }, 34 ) 35 if err != nil { 36 err := fmt.Errorf("Couldn't find specified security group: %s", err) 37 log.Printf("[DEBUG] %s", err.Error()) 38 state.Put("error", err) 39 return multistep.ActionHalt 40 } 41 log.Printf("Using specified security groups: %v", s.SecurityGroupIds) 42 state.Put("securityGroupIds", s.SecurityGroupIds) 43 return multistep.ActionContinue 44 } 45 46 port := s.CommConfig.Port() 47 if port == 0 { 48 if s.CommConfig.Type != "none" { 49 panic("port must be set to a non-zero value.") 50 } 51 } 52 53 // Create the group 54 groupName := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()) 55 ui.Say(fmt.Sprintf("Creating temporary security group for this instance: %s", groupName)) 56 group := &ec2.CreateSecurityGroupInput{ 57 GroupName: &groupName, 58 Description: aws.String("Temporary group for Packer"), 59 } 60 61 if s.VpcId != "" { 62 group.VpcId = &s.VpcId 63 } 64 65 groupResp, err := ec2conn.CreateSecurityGroup(group) 66 if err != nil { 67 ui.Error(err.Error()) 68 state.Put("error", err) 69 return multistep.ActionHalt 70 } 71 72 // Set the group ID so we can delete it later 73 s.createdGroupId = *groupResp.GroupId 74 75 // Authorize the SSH access for the security group 76 req := &ec2.AuthorizeSecurityGroupIngressInput{ 77 GroupId: groupResp.GroupId, 78 IpProtocol: aws.String("tcp"), 79 FromPort: aws.Int64(int64(port)), 80 ToPort: aws.Int64(int64(port)), 81 CidrIp: aws.String("0.0.0.0/0"), 82 } 83 84 // We loop and retry this a few times because sometimes the security 85 // group isn't available immediately because AWS resources are eventually 86 // consistent. 87 ui.Say(fmt.Sprintf( 88 "Authorizing access to port %d on the temporary security group...", 89 port)) 90 for i := 0; i < 5; i++ { 91 _, err = ec2conn.AuthorizeSecurityGroupIngress(req) 92 if err == nil { 93 break 94 } 95 96 log.Printf("Error authorizing. Will sleep and retry. %s", err) 97 time.Sleep((time.Duration(i) * time.Second) + 1) 98 } 99 100 if err != nil { 101 err := fmt.Errorf("Error creating temporary security group: %s", err) 102 state.Put("error", err) 103 ui.Error(err.Error()) 104 return multistep.ActionHalt 105 } 106 107 log.Printf("[DEBUG] Waiting for temporary security group: %s", s.createdGroupId) 108 err = waitUntilSecurityGroupExists(ec2conn, 109 &ec2.DescribeSecurityGroupsInput{ 110 GroupIds: []*string{aws.String(s.createdGroupId)}, 111 }, 112 ) 113 if err == nil { 114 log.Printf("[DEBUG] Found security group %s", s.createdGroupId) 115 } else { 116 err := fmt.Errorf("Timed out waiting for security group %s: %s", s.createdGroupId, err) 117 log.Printf("[DEBUG] %s", err.Error()) 118 state.Put("error", err) 119 return multistep.ActionHalt 120 } 121 122 // Set some state data for use in future steps 123 state.Put("securityGroupIds", []string{s.createdGroupId}) 124 125 return multistep.ActionContinue 126 } 127 128 func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) { 129 if s.createdGroupId == "" { 130 return 131 } 132 133 ec2conn := state.Get("ec2").(*ec2.EC2) 134 ui := state.Get("ui").(packer.Ui) 135 136 ui.Say("Deleting temporary security group...") 137 138 var err error 139 for i := 0; i < 5; i++ { 140 _, err = ec2conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{GroupId: &s.createdGroupId}) 141 if err == nil { 142 break 143 } 144 145 log.Printf("Error deleting security group: %s", err) 146 time.Sleep(5 * time.Second) 147 } 148 149 if err != nil { 150 ui.Error(fmt.Sprintf( 151 "Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId)) 152 } 153 } 154 155 func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error { 156 ctx := aws.BackgroundContext() 157 w := request.Waiter{ 158 Name: "DescribeSecurityGroups", 159 MaxAttempts: 40, 160 Acceptors: []request.WaiterAcceptor{ 161 { 162 State: request.SuccessWaiterState, 163 Matcher: request.PathWaiterMatch, 164 Argument: "length(SecurityGroups[]) > `0`", 165 Expected: true, 166 }, 167 { 168 State: request.RetryWaiterState, 169 Matcher: request.ErrorWaiterMatch, 170 Argument: "", 171 Expected: "InvalidGroup.NotFound", 172 }, 173 { 174 State: request.RetryWaiterState, 175 Matcher: request.ErrorWaiterMatch, 176 Argument: "", 177 Expected: "InvalidSecurityGroupID.NotFound", 178 }, 179 }, 180 Logger: c.Config.Logger, 181 NewRequest: func(opts []request.Option) (*request.Request, error) { 182 var inCpy *ec2.DescribeSecurityGroupsInput 183 if input != nil { 184 tmp := *input 185 inCpy = &tmp 186 } 187 req, _ := c.DescribeSecurityGroupsRequest(inCpy) 188 req.SetContext(ctx) 189 req.ApplyOptions(opts...) 190 return req, nil 191 }, 192 } 193 return w.WaitWithContext(ctx) 194 }