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  }