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