github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/common/step_security_group.go (about)

     1  package common
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/aws/request"
    12  	"github.com/aws/aws-sdk-go/service/ec2"
    13  	"github.com/hashicorp/packer/common/uuid"
    14  	"github.com/hashicorp/packer/helper/communicator"
    15  	"github.com/hashicorp/packer/helper/multistep"
    16  	"github.com/hashicorp/packer/packer"
    17  )
    18  
    19  type StepSecurityGroup struct {
    20  	CommConfig            *communicator.Config
    21  	SecurityGroupFilter   SecurityGroupFilterOptions
    22  	SecurityGroupIds      []string
    23  	TemporarySGSourceCidr string
    24  
    25  	createdGroupId string
    26  }
    27  
    28  func (s *StepSecurityGroup) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    29  	ec2conn := state.Get("ec2").(*ec2.EC2)
    30  	ui := state.Get("ui").(packer.Ui)
    31  	vpcId := state.Get("vpc_id").(string)
    32  
    33  	if len(s.SecurityGroupIds) > 0 {
    34  		_, err := ec2conn.DescribeSecurityGroups(
    35  			&ec2.DescribeSecurityGroupsInput{
    36  				GroupIds: aws.StringSlice(s.SecurityGroupIds),
    37  			},
    38  		)
    39  		if err != nil {
    40  			err := fmt.Errorf("Couldn't find specified security group: %s", err)
    41  			log.Printf("[DEBUG] %s", err.Error())
    42  			state.Put("error", err)
    43  			return multistep.ActionHalt
    44  		}
    45  		log.Printf("Using specified security groups: %v", s.SecurityGroupIds)
    46  		state.Put("securityGroupIds", s.SecurityGroupIds)
    47  		return multistep.ActionContinue
    48  	}
    49  
    50  	if !s.SecurityGroupFilter.Empty() {
    51  
    52  		params := &ec2.DescribeSecurityGroupsInput{}
    53  		if vpcId != "" {
    54  			s.SecurityGroupFilter.Filters[aws.String("vpc-id")] = &vpcId
    55  		}
    56  		params.Filters = buildEc2Filters(s.SecurityGroupFilter.Filters)
    57  
    58  		log.Printf("Using SecurityGroup Filters %v", params)
    59  
    60  		sgResp, err := ec2conn.DescribeSecurityGroups(params)
    61  		if err != nil {
    62  			err := fmt.Errorf("Couldn't find security groups for filter: %s", err)
    63  			log.Printf("[DEBUG] %s", err.Error())
    64  			state.Put("error", err)
    65  			return multistep.ActionHalt
    66  		}
    67  
    68  		securityGroupIds := []string{}
    69  		for _, sg := range sgResp.SecurityGroups {
    70  			securityGroupIds = append(securityGroupIds, *sg.GroupId)
    71  		}
    72  
    73  		ui.Message(fmt.Sprintf("Found Security Group(s): %s", strings.Join(securityGroupIds, ", ")))
    74  		state.Put("securityGroupIds", securityGroupIds)
    75  
    76  		return multistep.ActionContinue
    77  	}
    78  
    79  	port := s.CommConfig.Port()
    80  	if port == 0 {
    81  		if s.CommConfig.Type != "none" {
    82  			panic("port must be set to a non-zero value.")
    83  		}
    84  	}
    85  
    86  	// Create the group
    87  	groupName := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
    88  	ui.Say(fmt.Sprintf("Creating temporary security group for this instance: %s", groupName))
    89  	group := &ec2.CreateSecurityGroupInput{
    90  		GroupName:   &groupName,
    91  		Description: aws.String("Temporary group for Packer"),
    92  	}
    93  
    94  	group.VpcId = &vpcId
    95  
    96  	groupResp, err := ec2conn.CreateSecurityGroup(group)
    97  	if err != nil {
    98  		ui.Error(err.Error())
    99  		state.Put("error", err)
   100  		return multistep.ActionHalt
   101  	}
   102  
   103  	// Set the group ID so we can delete it later
   104  	s.createdGroupId = *groupResp.GroupId
   105  
   106  	// Wait for the security group become available for authorizing
   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  	// Authorize the SSH access for the security group
   123  	groupRules := &ec2.AuthorizeSecurityGroupIngressInput{
   124  		GroupId: groupResp.GroupId,
   125  		IpPermissions: []*ec2.IpPermission{
   126  			{
   127  				FromPort: aws.Int64(int64(port)),
   128  				ToPort:   aws.Int64(int64(port)),
   129  				IpRanges: []*ec2.IpRange{
   130  					{
   131  						CidrIp: aws.String(s.TemporarySGSourceCidr),
   132  					},
   133  				},
   134  				IpProtocol: aws.String("tcp"),
   135  			},
   136  		},
   137  	}
   138  
   139  	ui.Say(fmt.Sprintf(
   140  		"Authorizing access to port %d from %s in the temporary security group...",
   141  		port, s.TemporarySGSourceCidr))
   142  	_, err = ec2conn.AuthorizeSecurityGroupIngress(groupRules)
   143  	if err != nil {
   144  		err := fmt.Errorf("Error authorizing temporary security group: %s", err)
   145  		state.Put("error", err)
   146  		ui.Error(err.Error())
   147  		return multistep.ActionHalt
   148  	}
   149  
   150  	// Set some state data for use in future steps
   151  	state.Put("securityGroupIds", []string{s.createdGroupId})
   152  
   153  	return multistep.ActionContinue
   154  }
   155  
   156  func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
   157  	if s.createdGroupId == "" {
   158  		return
   159  	}
   160  
   161  	ec2conn := state.Get("ec2").(*ec2.EC2)
   162  	ui := state.Get("ui").(packer.Ui)
   163  
   164  	ui.Say("Deleting temporary security group...")
   165  
   166  	var err error
   167  	for i := 0; i < 5; i++ {
   168  		_, err = ec2conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{GroupId: &s.createdGroupId})
   169  		if err == nil {
   170  			break
   171  		}
   172  
   173  		log.Printf("Error deleting security group: %s", err)
   174  		time.Sleep(5 * time.Second)
   175  	}
   176  
   177  	if err != nil {
   178  		ui.Error(fmt.Sprintf(
   179  			"Error cleaning up security group. Please delete the group manually: %s", s.createdGroupId))
   180  	}
   181  }
   182  
   183  func waitUntilSecurityGroupExists(c *ec2.EC2, input *ec2.DescribeSecurityGroupsInput) error {
   184  	ctx := aws.BackgroundContext()
   185  	w := request.Waiter{
   186  		Name:        "DescribeSecurityGroups",
   187  		MaxAttempts: 40,
   188  		Delay:       request.ConstantWaiterDelay(5 * time.Second),
   189  		Acceptors: []request.WaiterAcceptor{
   190  			{
   191  				State:    request.SuccessWaiterState,
   192  				Matcher:  request.PathWaiterMatch,
   193  				Argument: "length(SecurityGroups[]) > `0`",
   194  				Expected: true,
   195  			},
   196  			{
   197  				State:    request.RetryWaiterState,
   198  				Matcher:  request.ErrorWaiterMatch,
   199  				Argument: "",
   200  				Expected: "InvalidGroup.NotFound",
   201  			},
   202  			{
   203  				State:    request.RetryWaiterState,
   204  				Matcher:  request.ErrorWaiterMatch,
   205  				Argument: "",
   206  				Expected: "InvalidSecurityGroupID.NotFound",
   207  			},
   208  		},
   209  		Logger: c.Config.Logger,
   210  		NewRequest: func(opts []request.Option) (*request.Request, error) {
   211  			var inCpy *ec2.DescribeSecurityGroupsInput
   212  			if input != nil {
   213  				tmp := *input
   214  				inCpy = &tmp
   215  			}
   216  			req, _ := c.DescribeSecurityGroupsRequest(inCpy)
   217  			req.SetContext(ctx)
   218  			req.ApplyOptions(opts...)
   219  			return req, nil
   220  		},
   221  	}
   222  	return w.WaitWithContext(ctx)
   223  }