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  }