Merge pull request #10003 from hashicorp/ssm_session_retry
Add retry mechanism to retry SSM session creation
This commit is contained in:
commit
845a10867e
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||||
awsbase "github.com/hashicorp/aws-sdk-go-base"
|
awsbase "github.com/hashicorp/aws-sdk-go-base"
|
||||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
vaultapi "github.com/hashicorp/vault/api"
|
vaultapi "github.com/hashicorp/vault/api"
|
||||||
)
|
)
|
||||||
|
@ -271,7 +272,7 @@ func (c *AccessConfig) Session() (*session.Session, error) {
|
||||||
|
|
||||||
cp, err := c.session.Config.Credentials.Get()
|
cp, err := c.session.Config.Credentials.Get()
|
||||||
|
|
||||||
if IsAWSErr(err, "NoCredentialProviders", "") {
|
if awserrors.Matches(err, "NoCredentialProviders", "") {
|
||||||
return nil, c.NewNoValidCredentialSourcesError(err)
|
return nil, c.NewNoValidCredentialSourcesError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package awserrors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Returns true if the err matches all these conditions:
|
||||||
|
// * err is of type awserr.Error
|
||||||
|
// * Error.Code() matches code
|
||||||
|
// * Error.Message() contains message
|
||||||
|
func Matches(err error, code string, message string) bool {
|
||||||
|
if err, ok := err.(awserr.Error); ok {
|
||||||
|
return err.Code() == code && strings.Contains(err.Message(), message)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -4,12 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
return IsAWSErr(err, "UnauthorizedOperation", "")
|
return awserrors.Matches(err, "UnauthorizedOperation", "")
|
||||||
},
|
},
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
}.Run(ctx, func(ctx context.Context) error {
|
||||||
|
@ -54,7 +53,7 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
return IsAWSErr(err, "UnauthorizedOperation", "")
|
return awserrors.Matches(err, "UnauthorizedOperation", "")
|
||||||
},
|
},
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
||||||
}.Run(ctx, func(ctx context.Context) error {
|
}.Run(ctx, func(ctx context.Context) error {
|
||||||
|
@ -74,14 +73,3 @@ func DestroyAMIs(imageids []*string, ec2conn *ec2.EC2) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the error matches all these conditions:
|
|
||||||
// * err is of type awserr.Error
|
|
||||||
// * Error.Code() matches code
|
|
||||||
// * Error.Message() contains message
|
|
||||||
func IsAWSErr(err error, code string, message string) bool {
|
|
||||||
if err, ok := err.(awserr.Error); ok {
|
|
||||||
return err.Code() == code && strings.Contains(err.Message(), message)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
@ -548,10 +548,6 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
msg := fmt.Errorf(`no iam_instance_profile defined; session_manager connectivity requires a valid instance profile with AmazonSSMManagedInstanceCore permissions. Alternatively a temporary_iam_instance_profile_policy_document can be used.`)
|
msg := fmt.Errorf(`no iam_instance_profile defined; session_manager connectivity requires a valid instance profile with AmazonSSMManagedInstanceCore permissions. Alternatively a temporary_iam_instance_profile_policy_document can be used.`)
|
||||||
errs = append(errs, msg)
|
errs = append(errs, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.PauseBeforeSSM == 0 {
|
|
||||||
c.PauseBeforeSSM = 10 * time.Second
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Comm.SSHKeyPairName != "" {
|
if c.Comm.SSHKeyPairName != "" {
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
package ssm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
|
"github.com/hashicorp/packer/common/retry"
|
||||||
|
"github.com/hashicorp/packer/helper/builder/localexec"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
SvcClient ssmiface.SSMAPI
|
||||||
|
Region string
|
||||||
|
InstanceID string
|
||||||
|
LocalPort, RemotePort int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Session) buildTunnelInput() *ssm.StartSessionInput {
|
||||||
|
portNumber, localPortNumber := strconv.Itoa(s.RemotePort), strconv.Itoa(s.LocalPort)
|
||||||
|
params := map[string][]*string{
|
||||||
|
"portNumber": []*string{aws.String(portNumber)},
|
||||||
|
"localPortNumber": []*string{aws.String(localPortNumber)},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ssm.StartSessionInput{
|
||||||
|
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
||||||
|
Parameters: params,
|
||||||
|
Target: aws.String(s.InstanceID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCommand return a valid ordered set of arguments to pass to the driver command.
|
||||||
|
func (s Session) getCommand(ctx context.Context) ([]string, string, error) {
|
||||||
|
input := s.buildTunnelInput()
|
||||||
|
|
||||||
|
var session *ssm.StartSessionOutput
|
||||||
|
err := retry.Config{
|
||||||
|
ShouldRetry: func(err error) bool { return awserrors.Matches(err, "TargetNotConnected", "") },
|
||||||
|
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 60 * time.Second, Multiplier: 2}).Linear,
|
||||||
|
}.Run(ctx, func(ctx context.Context) (err error) {
|
||||||
|
session, err = s.SvcClient.StartSessionWithContext(ctx, input)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if session == nil {
|
||||||
|
return nil, "", fmt.Errorf("an active Amazon SSM Session is required before trying to open a session tunnel")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWS session-manager-plugin requires a valid session be passed in JSON.
|
||||||
|
sessionDetails, err := json.Marshal(session)
|
||||||
|
if err != nil {
|
||||||
|
return nil, *session.SessionId, fmt.Errorf("error encountered in reading session details %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AWS session-manager-plugin requires the parameters used in the session to be passed in JSON as well.
|
||||||
|
sessionParameters, err := json.Marshal(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("error encountered in reading session parameter details %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Args must be in this order
|
||||||
|
args := []string{
|
||||||
|
string(sessionDetails),
|
||||||
|
s.Region,
|
||||||
|
"StartSession",
|
||||||
|
"", // ProfileName
|
||||||
|
string(sessionParameters),
|
||||||
|
*session.StreamUrl,
|
||||||
|
}
|
||||||
|
return args, *session.SessionId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start an interactive Systems Manager session with a remote instance via the
|
||||||
|
// AWS session-manager-plugin. To terminate the session you must cancell the
|
||||||
|
// context. If you do not wish to terminate the session manually: calling
|
||||||
|
// StopSession on a instance of this driver will terminate the active session
|
||||||
|
// created from calling StartSession.
|
||||||
|
func (s Session) Start(ctx context.Context, ui packer.Ui) error {
|
||||||
|
for ctx.Err() == nil {
|
||||||
|
log.Printf("ssm: Starting PortForwarding session to instance %s", s.InstanceID)
|
||||||
|
args, sessionID, err := s.getCommand(ctx)
|
||||||
|
if sessionID != "" {
|
||||||
|
defer func() {
|
||||||
|
_, err := s.SvcClient.TerminateSession(&ssm.TerminateSessionInput{SessionId: aws.String(sessionID)})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error terminating SSM Session %q. Please terminate the session manually: %s", sessionID, err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, "session-manager-plugin", args...)
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("Starting portForwarding session %q.", sessionID))
|
||||||
|
err = localexec.RunAndStream(cmd, ui, nil)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,197 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os/exec"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ssm"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
|
||||||
"github.com/hashicorp/packer/common/retry"
|
|
||||||
"github.com/mitchellh/iochan"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
sessionManagerPluginName string = "session-manager-plugin"
|
|
||||||
|
|
||||||
//sessionCommand is the AWS-SDK equivalent to the command you would specify to `aws ssm ...`
|
|
||||||
sessionCommand string = "StartSession"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SSMDriverConfig struct {
|
|
||||||
SvcClient ssmiface.SSMAPI
|
|
||||||
Region string
|
|
||||||
ProfileName string
|
|
||||||
SvcEndpoint string
|
|
||||||
}
|
|
||||||
|
|
||||||
type SSMDriver struct {
|
|
||||||
SSMDriverConfig
|
|
||||||
session *ssm.StartSessionOutput
|
|
||||||
sessionParams ssm.StartSessionInput
|
|
||||||
pluginCmdFunc func(context.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSSMDriver(config SSMDriverConfig) *SSMDriver {
|
|
||||||
d := SSMDriver{SSMDriverConfig: config}
|
|
||||||
return &d
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartSession starts an interactive Systems Manager session with a remote instance via the AWS session-manager-plugin
|
|
||||||
// This ssm.StartSessionOutput returned by this function can be used for terminating the session manually. If you do
|
|
||||||
// not wish to manage the session manually calling StopSession on a instance of this driver will terminate the active session
|
|
||||||
// created from calling StartSession.
|
|
||||||
func (d *SSMDriver) StartSession(ctx context.Context, input ssm.StartSessionInput) (*ssm.StartSessionOutput, error) {
|
|
||||||
log.Printf("Starting PortForwarding session to instance %q", aws.StringValue(input.Target))
|
|
||||||
|
|
||||||
var output *ssm.StartSessionOutput
|
|
||||||
err := retry.Config{
|
|
||||||
ShouldRetry: func(err error) bool { return IsAWSErr(err, "TargetNotConnected", "") },
|
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 60 * time.Second, Multiplier: 2}).Linear,
|
|
||||||
}.Run(ctx, func(ctx context.Context) (err error) {
|
|
||||||
output, err = d.SvcClient.StartSessionWithContext(ctx, &input)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error encountered in starting session for instance %q: %s", aws.StringValue(input.Target), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
d.session = output
|
|
||||||
d.sessionParams = input
|
|
||||||
|
|
||||||
if d.pluginCmdFunc == nil {
|
|
||||||
d.pluginCmdFunc = d.openTunnelForSession
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := d.pluginCmdFunc(ctx); err != nil {
|
|
||||||
return nil, fmt.Errorf("error encountered in starting session for instance %q: %s", aws.StringValue(input.Target), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.session, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *SSMDriver) openTunnelForSession(ctx context.Context) error {
|
|
||||||
args, err := d.Args()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error encountered validating session details: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, sessionManagerPluginName, args...)
|
|
||||||
|
|
||||||
// Let's build up our logging
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
stderr, err := cmd.StderrPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the channels we'll use for data
|
|
||||||
stdoutCh := iochan.DelimReader(stdout, '\n')
|
|
||||||
stderrCh := iochan.DelimReader(stderr, '\n')
|
|
||||||
|
|
||||||
/* Loop and get all our output
|
|
||||||
This particular logger will continue to run through an entire Packer run.
|
|
||||||
The decision to continue logging is due to the fact that session-manager-plugin
|
|
||||||
doesn't give a good way of knowing if the command failed or was successful other
|
|
||||||
than looking at the logs. Seeing as the plugin is updated frequently and that the
|
|
||||||
log information is a bit sparse this logger will indefinitely relying on other
|
|
||||||
steps to fail if the tunnel is unable to be created. If successful then the user
|
|
||||||
will get more information on the tunnel connection when running in a debug mode.
|
|
||||||
*/
|
|
||||||
go func(ctx context.Context, prefix string) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case output, ok := <-stderrCh:
|
|
||||||
if !ok {
|
|
||||||
stderrCh = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if output != "" {
|
|
||||||
log.Printf("[ERROR] %s: %s", prefix, output)
|
|
||||||
}
|
|
||||||
case output, ok := <-stdoutCh:
|
|
||||||
if !ok {
|
|
||||||
stdoutCh = nil
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if output != "" {
|
|
||||||
log.Printf("[DEBUG] %s: %s", prefix, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if stdoutCh == nil && stderrCh == nil {
|
|
||||||
log.Printf("[DEBUG] %s: %s", prefix, "active session has been terminated; stopping all log polling processes.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(ctx, sessionManagerPluginName)
|
|
||||||
|
|
||||||
log.Printf("[DEBUG %s] opening session tunnel to instance %q for session %q", sessionManagerPluginName,
|
|
||||||
aws.StringValue(d.sessionParams.Target),
|
|
||||||
aws.StringValue(d.session.SessionId),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
err = fmt.Errorf("error encountered when calling %s: %s\n", sessionManagerPluginName, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StopSession terminates an active Session Manager session
|
|
||||||
func (d *SSMDriver) StopSession() error {
|
|
||||||
|
|
||||||
if d.session == nil || d.session.SessionId == nil {
|
|
||||||
return fmt.Errorf("Unable to find a valid session to instance %q; skipping the termination step",
|
|
||||||
aws.StringValue(d.sessionParams.Target))
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := d.SvcClient.TerminateSession(&ssm.TerminateSessionInput{SessionId: d.session.SessionId})
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Error terminating SSM Session %q. Please terminate the session manually: %s", aws.StringValue(d.session.SessionId), err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args validates the driver inputs before returning an ordered set of arguments to pass to the driver command.
|
|
||||||
func (d *SSMDriver) Args() ([]string, error) {
|
|
||||||
if d.session == nil {
|
|
||||||
return nil, fmt.Errorf("an active Amazon SSM Session is required before trying to open a session tunnel")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AWS session-manager-plugin requires a valid session be passed in JSON.
|
|
||||||
sessionDetails, err := json.Marshal(d.session)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error encountered in reading session details %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AWS session-manager-plugin requires the parameters used in the session to be passed in JSON as well.
|
|
||||||
sessionParameters, err := json.Marshal(d.sessionParams)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error encountered in reading session parameter details %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args must be in this order
|
|
||||||
args := []string{
|
|
||||||
string(sessionDetails),
|
|
||||||
d.Region,
|
|
||||||
sessionCommand,
|
|
||||||
d.ProfileName,
|
|
||||||
string(sessionParameters),
|
|
||||||
d.SvcEndpoint,
|
|
||||||
}
|
|
||||||
|
|
||||||
return args, nil
|
|
||||||
}
|
|
|
@ -1,188 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewSSMDriverWithMockSvc(svc *MockSSMSvc) *SSMDriver {
|
|
||||||
config := SSMDriverConfig{
|
|
||||||
SvcClient: svc,
|
|
||||||
Region: "east",
|
|
||||||
ProfileName: "default",
|
|
||||||
SvcEndpoint: "example.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
driver := SSMDriver{
|
|
||||||
SSMDriverConfig: config,
|
|
||||||
pluginCmdFunc: func(ctx context.Context) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
return &driver
|
|
||||||
}
|
|
||||||
func TestSSMDriver_StartSession(t *testing.T) {
|
|
||||||
mockSvc := MockSSMSvc{}
|
|
||||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
|
||||||
|
|
||||||
if driver.SvcClient == nil {
|
|
||||||
t.Fatalf("SvcClient for driver should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("calling StartSession should not error but got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mockSvc.StartSessionCalled {
|
|
||||||
t.Fatalf("expected test to call ssm mocks but didn't")
|
|
||||||
}
|
|
||||||
|
|
||||||
if session == nil {
|
|
||||||
t.Errorf("expected session to be set after a successful call to StartSession")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(session, MockStartSessionOutput()) {
|
|
||||||
t.Errorf("expected session to be %v but got %v", MockStartSessionOutput(), session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSSMDriver_StartSessionWithError(t *testing.T) {
|
|
||||||
mockSvc := MockSSMSvc{StartSessionError: fmt.Errorf("bogus error")}
|
|
||||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
|
||||||
|
|
||||||
if driver.SvcClient == nil {
|
|
||||||
t.Fatalf("SvcClient for driver should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("StartSession should have thrown an error but didn't")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mockSvc.StartSessionCalled {
|
|
||||||
t.Errorf("expected test to call StartSession mock but didn't")
|
|
||||||
}
|
|
||||||
|
|
||||||
if session != nil {
|
|
||||||
t.Errorf("expected session to be nil after a bad StartSession call, but got %v", session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSSMDriver_StopSession(t *testing.T) {
|
|
||||||
mockSvc := MockSSMSvc{}
|
|
||||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
|
||||||
|
|
||||||
if driver.SvcClient == nil {
|
|
||||||
t.Fatalf("SvcClient for driver should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calling StopSession before StartSession should fail
|
|
||||||
err := driver.StopSession()
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("calling StopSession() on a driver that has no started session should fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
if driver.session != nil {
|
|
||||||
t.Errorf("expected session to be default to nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if mockSvc.TerminateSessionCalled {
|
|
||||||
t.Fatalf("a call to TerminateSession should not occur when there is no valid SSM session")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lets try calling start session, then stopping to see what happens.
|
|
||||||
session, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("calling StartSession should not error but got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mockSvc.StartSessionCalled {
|
|
||||||
t.Fatalf("expected test to call StartSession mock but didn't")
|
|
||||||
}
|
|
||||||
|
|
||||||
if session == nil || driver.session != session {
|
|
||||||
t.Errorf("expected session to be set after a successful call to StartSession")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(session, MockStartSessionOutput()) {
|
|
||||||
t.Errorf("expected session to be %v but got %v", MockStartSessionOutput(), session)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = driver.StopSession()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("calling StopSession() on a driver on a started session should not fail")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mockSvc.TerminateSessionCalled {
|
|
||||||
t.Fatalf("expected test to call StopSession mock but didn't")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSSMDriver_Args(t *testing.T) {
|
|
||||||
tt := []struct {
|
|
||||||
Name string
|
|
||||||
ProfileName string
|
|
||||||
SkipStartSession bool
|
|
||||||
ErrorExpected bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "NilSession",
|
|
||||||
SkipStartSession: true,
|
|
||||||
ErrorExpected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "NonNilSession",
|
|
||||||
ErrorExpected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "SessionWithProfileName",
|
|
||||||
ProfileName: "default",
|
|
||||||
ErrorExpected: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tt {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.Name, func(t *testing.T) {
|
|
||||||
mockSvc := MockSSMSvc{}
|
|
||||||
driver := NewSSMDriverWithMockSvc(&mockSvc)
|
|
||||||
driver.ProfileName = tc.ProfileName
|
|
||||||
|
|
||||||
if driver.SvcClient == nil {
|
|
||||||
t.Fatalf("svcclient for driver should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tc.SkipStartSession {
|
|
||||||
_, err := driver.StartSession(context.TODO(), MockStartSessionInput("fakeinstance"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("got an error when calling StartSession %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args, err := driver.Args()
|
|
||||||
if tc.ErrorExpected && err == nil {
|
|
||||||
t.Fatalf("Driver.Args with a %q should have failed but instead no error was returned", tc.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tc.ErrorExpected {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("got an error when it should've worked %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate launch script
|
|
||||||
expectedArgString := fmt.Sprintf(`{"SessionId":"packerid","StreamUrl":"http://packer.io","TokenValue":"packer-token"} east StartSession %s {"DocumentName":"AWS-StartPortForwardingSession","Parameters":{"localPortNumber":["8001"],"portNumber":["22"]},"Target":"fakeinstance"} example.com`, tc.ProfileName)
|
|
||||||
argString := strings.Join(args, " ")
|
|
||||||
if argString != expectedArgString {
|
|
||||||
t.Errorf("Expected launch script to be %q but got %q", expectedArgString, argString)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ssm"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MockSSMSvc struct {
|
|
||||||
ssmiface.SSMAPI
|
|
||||||
StartSessionError error
|
|
||||||
TerminateSessionError error
|
|
||||||
StartSessionCalled bool
|
|
||||||
TerminateSessionCalled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *MockSSMSvc) StartSessionWithContext(ctx aws.Context, input *ssm.StartSessionInput, options ...request.Option) (*ssm.StartSessionOutput, error) {
|
|
||||||
svc.StartSessionCalled = true
|
|
||||||
return MockStartSessionOutput(), svc.StartSessionError
|
|
||||||
}
|
|
||||||
func (svc *MockSSMSvc) TerminateSession(input *ssm.TerminateSessionInput) (*ssm.TerminateSessionOutput, error) {
|
|
||||||
svc.TerminateSessionCalled = true
|
|
||||||
return new(ssm.TerminateSessionOutput), svc.TerminateSessionError
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockPluginCmdFunc(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockStartSessionOutput() *ssm.StartSessionOutput {
|
|
||||||
id, url, token := "packerid", "http://packer.io", "packer-token"
|
|
||||||
output := ssm.StartSessionOutput{
|
|
||||||
SessionId: &id,
|
|
||||||
StreamUrl: &url,
|
|
||||||
TokenValue: &token,
|
|
||||||
}
|
|
||||||
return &output
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockStartSessionInput(instance string) ssm.StartSessionInput {
|
|
||||||
params := map[string][]*string{
|
|
||||||
"portNumber": []*string{aws.String("22")},
|
|
||||||
"localPortNumber": []*string{aws.String("8001")},
|
|
||||||
}
|
|
||||||
|
|
||||||
input := ssm.StartSessionInput{
|
|
||||||
DocumentName: aws.String("AWS-StartPortForwardingSession"),
|
|
||||||
Parameters: params,
|
|
||||||
Target: aws.String(instance),
|
|
||||||
}
|
|
||||||
|
|
||||||
return input
|
|
||||||
}
|
|
|
@ -3,13 +3,13 @@ package common
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/ssm"
|
"github.com/aws/aws-sdk-go/service/ssm"
|
||||||
|
pssm "github.com/hashicorp/packer/builder/amazon/common/ssm"
|
||||||
"github.com/hashicorp/packer/common/net"
|
"github.com/hashicorp/packer/common/net"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -21,9 +21,8 @@ type StepCreateSSMTunnel struct {
|
||||||
LocalPortNumber int
|
LocalPortNumber int
|
||||||
RemotePortNumber int
|
RemotePortNumber int
|
||||||
SSMAgentEnabled bool
|
SSMAgentEnabled bool
|
||||||
instanceId string
|
|
||||||
PauseBeforeSSM time.Duration
|
PauseBeforeSSM time.Duration
|
||||||
driver *SSMDriver
|
stopSSMCommand func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run executes the Packer build step that creates a session tunnel.
|
// Run executes the Packer build step that creates a session tunnel.
|
||||||
|
@ -36,7 +35,7 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
|
||||||
|
|
||||||
// Wait for the remote port to become available
|
// Wait for the remote port to become available
|
||||||
if s.PauseBeforeSSM > 0 {
|
if s.PauseBeforeSSM > 0 {
|
||||||
ui.Say(fmt.Sprintf("Waiting %s for establishing the SSM session...", s.PauseBeforeSSM))
|
ui.Say(fmt.Sprintf("Waiting %s before establishing the SSM session...", s.PauseBeforeSSM))
|
||||||
select {
|
select {
|
||||||
case <-time.After(s.PauseBeforeSSM):
|
case <-time.After(s.PauseBeforeSSM):
|
||||||
break
|
break
|
||||||
|
@ -61,42 +60,38 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
s.instanceId = aws.StringValue(instance.InstanceId)
|
|
||||||
|
|
||||||
if s.driver == nil {
|
|
||||||
ssmconn := ssm.New(s.AWSSession)
|
|
||||||
cfg := SSMDriverConfig{
|
|
||||||
SvcClient: ssmconn,
|
|
||||||
Region: s.Region,
|
|
||||||
SvcEndpoint: ssmconn.Endpoint,
|
|
||||||
}
|
|
||||||
driver := SSMDriver{SSMDriverConfig: cfg}
|
|
||||||
s.driver = &driver
|
|
||||||
}
|
|
||||||
|
|
||||||
input := s.BuildTunnelInputForInstance(s.instanceId)
|
|
||||||
_, err := s.driver.StartSession(ctx, input)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error encountered in establishing a tunnel %s", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
state.Put("error", err)
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Message(fmt.Sprintf("PortForwarding session %q has been started", s.instanceId))
|
|
||||||
state.Put("sessionPort", s.LocalPortNumber)
|
state.Put("sessionPort", s.LocalPortNumber)
|
||||||
|
|
||||||
|
ssmCtx, ssmCancel := context.WithCancel(ctx)
|
||||||
|
s.stopSSMCommand = ssmCancel
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ssmconn := ssm.New(s.AWSSession)
|
||||||
|
err := pssm.Session{
|
||||||
|
SvcClient: ssmconn,
|
||||||
|
InstanceID: aws.StringValue(instance.InstanceId),
|
||||||
|
RemotePort: s.RemotePortNumber,
|
||||||
|
LocalPort: s.LocalPortNumber,
|
||||||
|
Region: s.Region,
|
||||||
|
}.Start(ssmCtx, ui)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("ssm error: %s", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup terminates an active session on AWS, which in turn terminates the associated tunnel process running on the local machine.
|
// Cleanup terminates an active session on AWS, which in turn terminates the associated tunnel process running on the local machine.
|
||||||
func (s *StepCreateSSMTunnel) Cleanup(state multistep.StateBag) {
|
func (s *StepCreateSSMTunnel) Cleanup(state multistep.StateBag) {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
|
||||||
if !s.SSMAgentEnabled {
|
if !s.SSMAgentEnabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.driver.StopSession(); err != nil {
|
if s.stopSSMCommand != nil {
|
||||||
ui.Error(err.Error())
|
s.stopSSMCommand()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,19 +124,3 @@ func (s *StepCreateSSMTunnel) ConfigureLocalHostPort(ctx context.Context) error
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,139 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepCreateSSMTunnel_Run(t *testing.T) {
|
|
||||||
mockSvc := MockSSMSvc{}
|
|
||||||
config := SSMDriverConfig{
|
|
||||||
SvcClient: &mockSvc,
|
|
||||||
SvcEndpoint: "example.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
mockDriver := NewSSMDriver(config)
|
|
||||||
mockDriver.pluginCmdFunc = MockPluginCmdFunc
|
|
||||||
|
|
||||||
state := testState()
|
|
||||||
state.Put("ui", &packer.NoopUi{})
|
|
||||||
state.Put("instance", &ec2.Instance{InstanceId: aws.String("i-something")})
|
|
||||||
|
|
||||||
step := StepCreateSSMTunnel{
|
|
||||||
driver: mockDriver,
|
|
||||||
}
|
|
||||||
|
|
||||||
step.Run(context.Background(), state)
|
|
||||||
|
|
||||||
err := state.Get("error")
|
|
||||||
if err != nil {
|
|
||||||
err = err.(error)
|
|
||||||
t.Fatalf("the call to Run failed with an error when it should've executed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if mockSvc.StartSessionCalled {
|
|
||||||
t.Errorf("StartSession should not be called when SSMAgentEnabled is false")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run when SSMAgentEnabled is true
|
|
||||||
step.SSMAgentEnabled = true
|
|
||||||
step.Run(context.Background(), state)
|
|
||||||
|
|
||||||
err = state.Get("error")
|
|
||||||
if err != nil {
|
|
||||||
err = err.(error)
|
|
||||||
t.Fatalf("the call to Run failed with an error when it should've executed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mockSvc.StartSessionCalled {
|
|
||||||
t.Errorf("calling run with the correct inputs should call StartSession")
|
|
||||||
}
|
|
||||||
|
|
||||||
step.Cleanup(state)
|
|
||||||
if !mockSvc.TerminateSessionCalled {
|
|
||||||
t.Errorf("calling cleanup on a successful run should call TerminateSession")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateSSMTunnel_Cleanup(t *testing.T) {
|
|
||||||
mockSvc := MockSSMSvc{}
|
|
||||||
config := SSMDriverConfig{
|
|
||||||
SvcClient: &mockSvc,
|
|
||||||
SvcEndpoint: "example.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
mockDriver := NewSSMDriver(config)
|
|
||||||
mockDriver.pluginCmdFunc = MockPluginCmdFunc
|
|
||||||
|
|
||||||
step := StepCreateSSMTunnel{
|
|
||||||
SSMAgentEnabled: true,
|
|
||||||
driver: mockDriver,
|
|
||||||
}
|
|
||||||
|
|
||||||
state := testState()
|
|
||||||
state.Put("ui", &packer.NoopUi{})
|
|
||||||
state.Put("instance", &ec2.Instance{InstanceId: aws.String("i-something")})
|
|
||||||
|
|
||||||
step.Cleanup(state)
|
|
||||||
|
|
||||||
if mockSvc.TerminateSessionCalled {
|
|
||||||
t.Fatalf("calling cleanup on a non started session should not call TerminateSession")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateSSMTunnel_BuildTunnelInputForInstance(t *testing.T) {
|
|
||||||
step := StepCreateSSMTunnel{
|
|
||||||
Region: "region",
|
|
||||||
LocalPortNumber: 8001,
|
|
||||||
RemotePortNumber: 22,
|
|
||||||
SSMAgentEnabled: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
input := step.BuildTunnelInputForInstance("i-something")
|
|
||||||
|
|
||||||
target := aws.StringValue(input.Target)
|
|
||||||
if target != "i-something" {
|
|
||||||
t.Errorf("input should contain instance id as target but it got %q", target)
|
|
||||||
}
|
|
||||||
|
|
||||||
params := map[string][]*string{
|
|
||||||
"portNumber": []*string{aws.String("22")},
|
|
||||||
"localPortNumber": []*string{aws.String("8001")},
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(input.Parameters, params) {
|
|
||||||
t.Errorf("input should contain the expected port parameters but it got %v", input.Parameters)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepCreateSSMTunnel_ConfigureLocalHostPort(t *testing.T) {
|
|
||||||
tt := []struct {
|
|
||||||
Name string
|
|
||||||
Step StepCreateSSMTunnel
|
|
||||||
PortCheck func(int) bool
|
|
||||||
}{
|
|
||||||
{"WithLocalPortNumber", StepCreateSSMTunnel{LocalPortNumber: 9001}, func(port int) bool { return port == 9001 }},
|
|
||||||
{"WithNoLocalPortNumber", StepCreateSSMTunnel{}, func(port int) bool { return port >= 8000 && port <= 9000 }},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tt {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.Name, func(t *testing.T) {
|
|
||||||
step := tc.Step
|
|
||||||
if err := step.ConfigureLocalHostPort(context.TODO()); err != nil {
|
|
||||||
t.Errorf("failed to configure a port on localhost")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tc.PortCheck(step.LocalPortNumber) {
|
|
||||||
t.Errorf("failed to configure a port on localhost")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -91,7 +92,7 @@ func (s *StepCreateTags) Run(ctx context.Context, state multistep.StateBag) mult
|
||||||
|
|
||||||
// Retry creating tags for about 2.5 minutes
|
// Retry creating tags for about 2.5 minutes
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||||
if IsAWSErr(err, "InvalidAMIID.NotFound", "") || IsAWSErr(err, "InvalidSnapshot.NotFound", "") {
|
if awserrors.Matches(err, "InvalidAMIID.NotFound", "") || awserrors.Matches(err, "InvalidSnapshot.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -39,7 +40,7 @@ func (s *StepPreValidate) Run(ctx context.Context, state multistep.StateBag) mul
|
||||||
err := retry.Config{
|
err := retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
if IsAWSErr(err, "AuthFailure", "") {
|
if awserrors.Matches(err, "AuthFailure", "") {
|
||||||
log.Printf("Waiting for Vault-generated AWS credentials" +
|
log.Printf("Waiting for Vault-generated AWS credentials" +
|
||||||
" to pass authentication... trying again.")
|
" to pass authentication... trying again.")
|
||||||
return true
|
return true
|
||||||
|
@ -131,7 +132,7 @@ func (s *StepPreValidate) checkVpc(conn ec2iface.EC2API) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := conn.DescribeVpcs(&ec2.DescribeVpcsInput{VpcIds: []*string{aws.String(s.VpcId)}})
|
res, err := conn.DescribeVpcs(&ec2.DescribeVpcsInput{VpcIds: []*string{aws.String(s.VpcId)}})
|
||||||
if IsAWSErr(err, "InvalidVpcID.NotFound", "") || err != nil {
|
if awserrors.Matches(err, "InvalidVpcID.NotFound", "") || err != nil {
|
||||||
return fmt.Errorf("Error retrieving VPC information for vpc_id %s: %s", s.VpcId, err)
|
return fmt.Errorf("Error retrieving VPC information for vpc_id %s: %s", s.VpcId, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
@ -204,7 +205,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 11,
|
Tries: 11,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
if IsAWSErr(err, "InvalidParameterValue", "iamInstanceProfile") {
|
if awserrors.Matches(err, "InvalidParameterValue", "iamInstanceProfile") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -215,7 +216,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
if IsAWSErr(err, "VPCIdNotSpecified", "No default VPC for this user") && subnetId == "" {
|
if awserrors.Matches(err, "VPCIdNotSpecified", "No default VPC for this user") && subnetId == "" {
|
||||||
err := fmt.Errorf("Error launching source instance: a valid Subnet Id was not specified")
|
err := fmt.Errorf("Error launching source instance: a valid Subnet Id was not specified")
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
|
@ -253,7 +254,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||||
|
|
||||||
var r *ec2.DescribeInstancesOutput
|
var r *ec2.DescribeInstancesOutput
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(err error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(err error) bool {
|
||||||
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -298,7 +299,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||||
ec2Tags.Report(ui)
|
ec2Tags.Report(ui)
|
||||||
// Retry creating tags for about 2.5 minutes
|
// Retry creating tags for about 2.5 minutes
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||||
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/common/random"
|
"github.com/hashicorp/packer/common/random"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
|
@ -396,7 +397,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||||
|
|
||||||
// Retry creating tags for about 2.5 minutes
|
// Retry creating tags for about 2.5 minutes
|
||||||
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
err = retry.Config{Tries: 11, ShouldRetry: func(error) bool {
|
||||||
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -42,7 +43,7 @@ func (s *StepStopEBSBackedInstance) Run(ctx context.Context, state multistep.Sta
|
||||||
|
|
||||||
// Work around this by retrying a few times, up to about 5 minutes.
|
// Work around this by retrying a few times, up to about 5 minutes.
|
||||||
err := retry.Config{Tries: 6, ShouldRetry: func(error) bool {
|
err := retry.Config{Tries: 6, ShouldRetry: func(error) bool {
|
||||||
if IsAWSErr(err, "InvalidInstanceID.NotFound", "") {
|
if awserrors.Matches(err, "InvalidInstanceID.NotFound", "") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -246,6 +246,14 @@ func checkBootEncrypted() builderT.TestCheckFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuilderAcc_SessionManagerInterface(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: testBuilderAccSessionManagerInterface,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testAccPreCheck(t *testing.T) {
|
func testAccPreCheck(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,6 +358,31 @@ const testBuilderAccEncrypted = `
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testBuilderAccSessionManagerInterface = `
|
||||||
|
{
|
||||||
|
"builders": [{
|
||||||
|
"type": "test",
|
||||||
|
"region": "us-east-1",
|
||||||
|
"instance_type": "m3.medium",
|
||||||
|
"source_ami_filter": {
|
||||||
|
"filters": {
|
||||||
|
"virtualization-type": "hvm",
|
||||||
|
"name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
|
||||||
|
"root-device-type": "ebs"
|
||||||
|
},
|
||||||
|
"owners": [
|
||||||
|
"099720109477"
|
||||||
|
],
|
||||||
|
"most_recent": true
|
||||||
|
},
|
||||||
|
"ssh_username": "ubuntu",
|
||||||
|
"ssh_interface": "session_manager",
|
||||||
|
"iam_instance_profile": "SSMInstanceProfile",
|
||||||
|
"ami_name": "packer-ssm-test-{{timestamp}}"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
func buildForceDeregisterConfig(val, name string) string {
|
func buildForceDeregisterConfig(val, name string) string {
|
||||||
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||||
|
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
||||||
"github.com/hashicorp/packer/common/random"
|
"github.com/hashicorp/packer/common/random"
|
||||||
"github.com/hashicorp/packer/common/retry"
|
"github.com/hashicorp/packer/common/retry"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
@ -61,7 +62,7 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||||
err = retry.Config{
|
err = retry.Config{
|
||||||
Tries: 0,
|
Tries: 0,
|
||||||
ShouldRetry: func(err error) bool {
|
ShouldRetry: func(err error) bool {
|
||||||
if awscommon.IsAWSErr(err, "InvalidParameterValue", "Instance is not in state") {
|
if awserrors.Matches(err, "InvalidParameterValue", "Instance is not in state") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -93,7 +93,6 @@ require (
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/go-testing-interface v1.0.3 // indirect
|
github.com/mitchellh/go-testing-interface v1.0.3 // indirect
|
||||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
||||||
github.com/mitchellh/gox v1.0.1 // indirect
|
|
||||||
github.com/mitchellh/iochan v1.0.0
|
github.com/mitchellh/iochan v1.0.0
|
||||||
github.com/mitchellh/mapstructure v1.2.3
|
github.com/mitchellh/mapstructure v1.2.3
|
||||||
github.com/mitchellh/panicwrap v1.0.0
|
github.com/mitchellh/panicwrap v1.0.0
|
||||||
|
@ -104,7 +103,6 @@ require (
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
||||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible
|
github.com/oracle/oci-go-sdk v18.0.0+incompatible
|
||||||
github.com/outscale/osc-go v0.0.1 // indirect
|
|
||||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699
|
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699
|
||||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a
|
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible
|
github.com/pierrec/lz4 v2.0.5+incompatible
|
||||||
|
|
11
go.sum
11
go.sum
|
@ -126,7 +126,6 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||||
github.com/aws/aws-sdk-go v1.16.22/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
|
||||||
github.com/aws/aws-sdk-go v1.26.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.26.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aws/aws-sdk-go v1.30.8 h1:4BHbh8K3qKmcnAgToZ2LShldRF9inoqIBccpCLNCy3I=
|
github.com/aws/aws-sdk-go v1.30.8 h1:4BHbh8K3qKmcnAgToZ2LShldRF9inoqIBccpCLNCy3I=
|
||||||
github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||||
|
@ -377,7 +376,6 @@ github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||||
|
@ -512,8 +510,6 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
|
||||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
|
||||||
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
@ -538,9 +534,6 @@ github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:v
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible h1:FLV4KixsVfF3rwyVTMI6Ryp/Q+OSb9sR5TawbfjFLN4=
|
github.com/oracle/oci-go-sdk v18.0.0+incompatible h1:FLV4KixsVfF3rwyVTMI6Ryp/Q+OSb9sR5TawbfjFLN4=
|
||||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||||
github.com/outscale/osc-go v0.0.1 h1:hvBtORyu7sWSKW1norGlfIP8C7c2aegI2Vkq75SRPCE=
|
|
||||||
github.com/outscale/osc-go v0.0.1/go.mod h1:hJLmXzqU/t07qQYh90I0TqZzu9s85Zs6FMrxk3ukiFM=
|
|
||||||
github.com/outscale/osc-sdk-go v1.2.0 h1:1HKr6OMLLVW4w6KQuiQwYZjhNaVz9mNzy/W3KW+zgnA=
|
|
||||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699 h1:SHe9i7h5cHe+cB77fQ6lsEgIwKg3ckNU90P03CjGMnI=
|
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699 h1:SHe9i7h5cHe+cB77fQ6lsEgIwKg3ckNU90P03CjGMnI=
|
||||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
|
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
|
||||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a h1:A3QMuteviunoaY/8ex+RKFqwhcZJ/Cf3fCW3IwL2wx4=
|
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a h1:A3QMuteviunoaY/8ex+RKFqwhcZJ/Cf3fCW3IwL2wx4=
|
||||||
|
@ -636,12 +629,8 @@ github.com/vmware/govmomi v0.23.1/go.mod h1:Y+Wq4lst78L85Ge/F8+ORXIWiKYqaro1vhAu
|
||||||
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
|
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
|
||||||
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0 h1:NJrcIkdzq0C3I8ypAZwFE9RHtGbfp+mJvqIcoFATZuk=
|
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0 h1:NJrcIkdzq0C3I8ypAZwFE9RHtGbfp+mJvqIcoFATZuk=
|
||||||
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0/go.mod h1:sBh287mCRwCz6zyXHMmw7sSZGPohVpnx+o+OY4M+i3A=
|
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0/go.mod h1:sBh287mCRwCz6zyXHMmw7sSZGPohVpnx+o+OY4M+i3A=
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20200608085315-d6e7ef5ceb97 h1:DoqSUxQkBLislVgA1qkM0u7g04It4VRMidyLBH/O/as=
|
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20200608085315-d6e7ef5ceb97/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd h1:o4pvS7D4OErKOM6y+/q6IfOa65OaentKbEDh1ABirE8=
|
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd h1:o4pvS7D4OErKOM6y+/q6IfOa65OaentKbEDh1ABirE8=
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20200610100221-ae86895efb97 h1:8KwSw9xtQBeyeX1EpOlOjRc0JaHlh8B8GglKA6iXt08=
|
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20200610100221-ae86895efb97/go.mod h1:3p2xVpQrHyPxV4UCKnKozt9n+g1LRENOQ33CH8rqLnY=
|
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c h1:LJrgyICodRAgtBvOO2eCbhDDIoaJgeLa1tGQecqW9ac=
|
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c h1:LJrgyICodRAgtBvOO2eCbhDDIoaJgeLa1tGQecqW9ac=
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c/go.mod h1:Zn/U9YKH0w8n83ezLps5eB6Jftc4gSoZWxVR8hgXgoY=
|
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c/go.mod h1:Zn/U9YKH0w8n83ezLps5eB6Jftc4gSoZWxVR8hgXgoY=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
|
Loading…
Reference in New Issue