2020-03-10 16:39:19 -04:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2020-04-29 14:58:36 -04:00
|
|
|
"log"
|
2020-03-12 22:26:38 -04:00
|
|
|
"strconv"
|
|
|
|
"time"
|
2020-03-10 16:39:19 -04:00
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
2020-03-12 22:26:38 -04:00
|
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
2020-03-10 16:39:19 -04:00
|
|
|
"github.com/aws/aws-sdk-go/service/ssm"
|
2020-03-30 07:47:31 -04:00
|
|
|
"github.com/hashicorp/packer/common/net"
|
2020-03-12 22:26:38 -04:00
|
|
|
"github.com/hashicorp/packer/common/retry"
|
2020-03-10 16:39:19 -04:00
|
|
|
"github.com/hashicorp/packer/helper/multistep"
|
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
)
|
|
|
|
|
|
|
|
type StepCreateSSMTunnel struct {
|
2020-04-29 14:58:36 -04:00
|
|
|
AWSSession *session.Session
|
|
|
|
Region string
|
|
|
|
LocalPortNumber int
|
|
|
|
RemotePortNumber int
|
|
|
|
SSMAgentEnabled bool
|
|
|
|
instanceId string
|
|
|
|
session *ssm.StartSessionOutput
|
2020-03-10 16:39:19 -04:00
|
|
|
}
|
|
|
|
|
2020-04-29 14:58:36 -04:00
|
|
|
// Run executes the Packer build step that creates a session tunnel.
|
2020-03-10 16:39:19 -04:00
|
|
|
func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
2020-04-01 17:33:44 -04:00
|
|
|
if !s.SSMAgentEnabled {
|
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
2020-03-10 16:39:19 -04:00
|
|
|
ui := state.Get("ui").(packer.Ui)
|
2020-04-29 14:58:36 -04:00
|
|
|
if err := s.ConfigureLocalHostPort(ctx); err != nil {
|
2020-03-30 07:47:31 -04:00
|
|
|
err := fmt.Errorf("error finding an available port to initiate a session tunnel: %s", err)
|
|
|
|
state.Put("error", err)
|
|
|
|
ui.Error(err.Error())
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2020-03-12 22:26:38 -04:00
|
|
|
instance, ok := state.Get("instance").(*ec2.Instance)
|
|
|
|
if !ok {
|
2020-04-29 14:58:36 -04:00
|
|
|
err := fmt.Errorf("error encountered in obtaining target instance id for session tunnel")
|
2020-03-12 22:26:38 -04:00
|
|
|
ui.Error(err.Error())
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2020-04-01 17:33:44 -04:00
|
|
|
s.instanceId = aws.StringValue(instance.InstanceId)
|
2020-03-30 07:47:31 -04:00
|
|
|
|
2020-05-06 15:21:08 -04:00
|
|
|
log.Printf("Starting PortForwarding session to instance %q on local port %d to remote port %d", s.instanceId, s.LocalPortNumber, s.RemotePortNumber)
|
2020-04-29 14:58:36 -04:00
|
|
|
input := s.BuildTunnelInputForInstance(s.instanceId)
|
|
|
|
ssmconn := ssm.New(s.AWSSession)
|
|
|
|
err := retry.Config{
|
2020-03-12 22:26:38 -04:00
|
|
|
ShouldRetry: func(err error) bool { return isAWSErr(err, "TargetNotConnected", "") },
|
2020-04-01 17:33:44 -04:00
|
|
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 60 * time.Second, Multiplier: 2}).Linear,
|
2020-04-29 14:58:36 -04:00
|
|
|
}.Run(ctx, func(ctx context.Context) (err error) {
|
2020-05-06 15:21:08 -04:00
|
|
|
s.session, err = ssmconn.StartSessionWithContext(ctx, &input)
|
2020-03-12 22:26:38 -04:00
|
|
|
return err
|
|
|
|
})
|
2020-03-10 16:39:19 -04:00
|
|
|
|
|
|
|
if err != nil {
|
2020-04-01 17:33:44 -04:00
|
|
|
err = fmt.Errorf("error encountered in starting session for instance %q: %s", s.instanceId, err)
|
2020-03-10 16:39:19 -04:00
|
|
|
ui.Error(err.Error())
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2020-03-12 22:26:38 -04:00
|
|
|
|
2020-04-29 14:58:36 -04:00
|
|
|
driver := SSMDriver{
|
|
|
|
Region: s.Region,
|
2020-05-06 15:21:08 -04:00
|
|
|
Session: s.session,
|
2020-04-29 14:58:36 -04:00
|
|
|
SessionParams: input,
|
|
|
|
SessionEndpoint: ssmconn.Endpoint,
|
2020-03-12 22:26:38 -04:00
|
|
|
}
|
|
|
|
|
2020-04-29 14:58:36 -04:00
|
|
|
if err := driver.StartSession(ctx); err != nil {
|
|
|
|
err = fmt.Errorf("error encountered in establishing a tunnel %s", err)
|
2020-03-12 22:26:38 -04:00
|
|
|
ui.Error(err.Error())
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2020-05-06 15:21:08 -04:00
|
|
|
ui.Message(fmt.Sprintf("PortForwarding session %q to instance %q has been started", aws.StringValue(s.session.SessionId), s.instanceId))
|
2020-04-29 14:58:36 -04:00
|
|
|
state.Put("sessionPort", s.LocalPortNumber)
|
2020-03-10 16:39:19 -04:00
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
2020-04-29 14:58:36 -04:00
|
|
|
// Cleanup terminates an active session on AWS, which in turn terminates the associated tunnel process running on the local machine.
|
2020-03-10 16:39:19 -04:00
|
|
|
func (s *StepCreateSSMTunnel) Cleanup(state multistep.StateBag) {
|
builder/amazon Fix invalid pointer issue for non SSMAgengtEnabled builds
Tests before change
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1392ca2]
goroutine 299 [running]:
github.com/hashicorp/packer/builder/amazon/common.(*StepCreateSSMTunnel).Cleanup(0xc0003dc460, 0x4d1a4c0, 0xc0006e9800)
/home/wilken/Development/packer/builder/amazon/common/step_create_ssm_tunnel.go:95 +0xf2
github.com/hashicorp/packer/helper/multistep.(*BasicRunner).Run(0xc0006e98f0, 0x4d408c0, 0xc00065fcc0, 0x4d1a4c0, 0xc0006e9800)
/home/wilken/Development/packer/helper/multistep/basic_runner.go:79 +0x2c6
github.com/hashicorp/packer/builder/amazon/ebs.(*Builder).Run(0xc000726800, 0x4d408c0, 0xc00065fcc0, 0x4d5e300, 0xc0006e8d80, 0x4cc7220, 0xc000434120, 0x0, 0x0, 0x0, ...)
/home/wilken/Development/packer/builder/amazon/ebs/builder.go:330 +0x17e2
github.com/hashicorp/packer/packer.(*CoreBuild).Run(0xc000720500, 0x4d408c0, 0xc00065fcc0, 0x4d5e180, 0xc0006fe510, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/wilken/Development/packer/packer/build.go:287 +0x7ef
github.com/hashicorp/packer/command.(*BuildCommand).RunContext.func1(0xc0004d14d0, 0xc0003dc3c0, 0xc000441500, 0xa, 0x4d5e1e0, 0xc000720500, 0x4d408c0, 0xc00065fcc0, 0x4d5e180, 0xc0006fe510, ...)
/home/wilken/Development/packer/command/build.go:290 +0x189
created by github.com/hashicorp/packer/command.(*BuildCommand).RunContext
/home/wilken/Development/packer/command/build.go:284 +0xd5a
FAIL github.com/hashicorp/packer/provisioner/shell 188.335s
FAIL
```
Test After change
```
--- PASS: TestShellProvisioner (212.39s)
--- PASS: TestShellProvisioner/testing_amazon-ebs_builder_against_shell_provisioner (212.39s)
PASS
```
2020-05-11 11:11:55 -04:00
|
|
|
if !s.SSMAgentEnabled {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-10 16:39:19 -04:00
|
|
|
ui := state.Get("ui").(packer.Ui)
|
2020-05-06 15:21:08 -04:00
|
|
|
|
builder/amazon Fix invalid pointer issue for non SSMAgengtEnabled builds
Tests before change
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1392ca2]
goroutine 299 [running]:
github.com/hashicorp/packer/builder/amazon/common.(*StepCreateSSMTunnel).Cleanup(0xc0003dc460, 0x4d1a4c0, 0xc0006e9800)
/home/wilken/Development/packer/builder/amazon/common/step_create_ssm_tunnel.go:95 +0xf2
github.com/hashicorp/packer/helper/multistep.(*BasicRunner).Run(0xc0006e98f0, 0x4d408c0, 0xc00065fcc0, 0x4d1a4c0, 0xc0006e9800)
/home/wilken/Development/packer/helper/multistep/basic_runner.go:79 +0x2c6
github.com/hashicorp/packer/builder/amazon/ebs.(*Builder).Run(0xc000726800, 0x4d408c0, 0xc00065fcc0, 0x4d5e300, 0xc0006e8d80, 0x4cc7220, 0xc000434120, 0x0, 0x0, 0x0, ...)
/home/wilken/Development/packer/builder/amazon/ebs/builder.go:330 +0x17e2
github.com/hashicorp/packer/packer.(*CoreBuild).Run(0xc000720500, 0x4d408c0, 0xc00065fcc0, 0x4d5e180, 0xc0006fe510, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/wilken/Development/packer/packer/build.go:287 +0x7ef
github.com/hashicorp/packer/command.(*BuildCommand).RunContext.func1(0xc0004d14d0, 0xc0003dc3c0, 0xc000441500, 0xa, 0x4d5e1e0, 0xc000720500, 0x4d408c0, 0xc00065fcc0, 0x4d5e180, 0xc0006fe510, ...)
/home/wilken/Development/packer/command/build.go:290 +0x189
created by github.com/hashicorp/packer/command.(*BuildCommand).RunContext
/home/wilken/Development/packer/command/build.go:284 +0xd5a
FAIL github.com/hashicorp/packer/provisioner/shell 188.335s
FAIL
```
Test After change
```
--- PASS: TestShellProvisioner (212.39s)
--- PASS: TestShellProvisioner/testing_amazon-ebs_builder_against_shell_provisioner (212.39s)
PASS
```
2020-05-11 11:11:55 -04:00
|
|
|
if s.session == nil || s.session.SessionId == nil {
|
|
|
|
msg := fmt.Sprintf("Unable to find a valid session to instance %q; skipping the termination step", s.instanceId)
|
|
|
|
ui.Error(msg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-03-10 16:39:19 -04:00
|
|
|
ssmconn := ssm.New(s.AWSSession)
|
2020-04-29 14:58:36 -04:00
|
|
|
_, err := ssmconn.TerminateSession(&ssm.TerminateSessionInput{SessionId: s.session.SessionId})
|
2020-03-10 16:39:19 -04:00
|
|
|
if err != nil {
|
2020-05-06 15:21:08 -04:00
|
|
|
msg := fmt.Sprintf("Error terminating SSM Session %q. Please terminate the session manually: %s", aws.StringValue(s.session.SessionId), err)
|
2020-03-10 16:39:19 -04:00
|
|
|
ui.Error(msg)
|
|
|
|
}
|
|
|
|
}
|
2020-04-29 14:58:36 -04:00
|
|
|
|
|
|
|
// ConfigureLocalHostPort finds an available port on the localhost that can be used for the remote tunnel.
|
|
|
|
// Defaults to using s.LocalPortNumber if it is set.
|
|
|
|
func (s *StepCreateSSMTunnel) ConfigureLocalHostPort(ctx context.Context) error {
|
2020-04-30 06:20:48 -04:00
|
|
|
minPortNumber, maxPortNumber := 8000, 9000
|
|
|
|
|
2020-04-29 14:58:36 -04:00
|
|
|
if s.LocalPortNumber != 0 {
|
2020-04-30 06:20:48 -04:00
|
|
|
minPortNumber = s.LocalPortNumber
|
|
|
|
maxPortNumber = minPortNumber
|
2020-04-29 14:58:36 -04:00
|
|
|
}
|
2020-04-30 06:20:48 -04:00
|
|
|
|
2020-04-29 14:58:36 -04:00
|
|
|
// Find an available TCP port for our HTTP server
|
|
|
|
l, err := net.ListenRangeConfig{
|
2020-04-30 06:20:48 -04:00
|
|
|
Min: minPortNumber,
|
|
|
|
Max: maxPortNumber,
|
2020-04-29 14:58:36 -04:00
|
|
|
Addr: "0.0.0.0",
|
|
|
|
Network: "tcp",
|
|
|
|
}.Listen(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.LocalPortNumber = l.Port
|
|
|
|
// Stop listening on selected port so that the AWS session-manager-plugin can use it.
|
|
|
|
// The port is closed right before we start the session to avoid two Packer builds from getting the same port - fingers-crossed
|
|
|
|
l.Close()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StepCreateSSMTunnel) BuildTunnelInputForInstance(instance string) ssm.StartSessionInput {
|
|
|
|
dst, src := strconv.Itoa(s.RemotePortNumber), strconv.Itoa(s.LocalPortNumber)
|
|
|
|
params := map[string][]*string{
|
|
|
|
"portNumber": []*string{aws.String(dst)},
|
|
|
|
"localPortNumber": []*string{aws.String(src)},
|
|
|
|
}
|
|
|
|
|
|
|
|
input := ssm.StartSessionInput{
|
|
|
|
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
|
|
|
Parameters: params,
|
|
|
|
Target: aws.String(instance),
|
|
|
|
}
|
|
|
|
|
|
|
|
return input
|
|
|
|
}
|