Move ssm code to its own ssm package and make it singlethreaded
This commit is contained in:
parent
646b973bd3
commit
8e355d0fe7
|
@ -0,0 +1,111 @@
|
|||
package ssm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"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/hashicorp/packer/helper/builder/localexec"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
SvcClient ssmiface.SSMAPI
|
||||
Region string
|
||||
Input ssm.StartSessionInput
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// getCommand return a valid ordered set of arguments to pass to the driver command.
|
||||
func (s Session) getCommand(ctx context.Context) ([]string, string, error) {
|
||||
var session *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) {
|
||||
session, err = s.SvcClient.StartSessionWithContext(ctx, &s.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(s.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 %q", *s.Input.Target)
|
||||
args, sessionID, err := s.getCommand(ctx)
|
||||
if sessionID != "" {
|
||||
defer func() {
|
||||
_, err := s.SvcClient.TerminateSession(&ssm.TerminateSessionInput{SessionId: aws.String(sessionID)})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("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
|
||||
}
|
|
@ -2,17 +2,9 @@ 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 (
|
||||
|
@ -34,212 +26,9 @@ type SSMDriver struct {
|
|||
session *ssm.StartSessionOutput
|
||||
sessionParams ssm.StartSessionInput
|
||||
pluginCmdFunc func(context.Context) error
|
||||
|
||||
retryAfterTermination chan bool
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
output, err := d.StartSessionWithContext(ctx, input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encountered in starting session for instance %q: %s", aws.StringValue(input.Target), err)
|
||||
}
|
||||
|
||||
d.retryAfterTermination = make(chan bool, 1)
|
||||
// Starts go routine that will keep listening to channels and retry the session creation/connection when needed.
|
||||
// The log polling process will add data to the channels whenever a retryable error happens to the session or if it's terminated.
|
||||
go func(ctx context.Context, driver *SSMDriver, input ssm.StartSessionInput) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case r, ok := <-driver.retryAfterTermination:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if r {
|
||||
log.Printf("[DEBUG] Attempting to restablishing SSM connection")
|
||||
_, _ = driver.StartSession(ctx, input)
|
||||
// End this routine. Another routine will start.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}(ctx, d, input)
|
||||
|
||||
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) StartSessionWithContext(ctx context.Context, input ssm.StartSessionInput) (*ssm.StartSessionOutput, error) {
|
||||
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
|
||||
})
|
||||
|
||||
return output, err
|
||||
}
|
||||
|
||||
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 errorOut, ok := <-stderrCh:
|
||||
if !ok {
|
||||
stderrCh = nil
|
||||
break
|
||||
}
|
||||
|
||||
if errorOut == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("[ERROR] %s: %s", prefix, errorOut)
|
||||
case output, ok := <-stdoutCh:
|
||||
if !ok {
|
||||
stdoutCh = nil
|
||||
break
|
||||
}
|
||||
|
||||
if output == "" {
|
||||
continue
|
||||
}
|
||||
log.Printf("[DEBUG] %s: %s", prefix, output)
|
||||
}
|
||||
|
||||
if stdoutCh == nil && stderrCh == nil {
|
||||
// It's possible that a remote SSM session was terminated prematurely due to a system reboot, or a termination
|
||||
// on the AWS console, or that the SSMAgent service was stopped on the instance. This will try to reconnect
|
||||
// until the build eventually fails or hits the ssh_timeout threshold.
|
||||
log.Printf("[DEBUG] %s: %s", prefix, "active session has been terminated; stopping all log polling processes.")
|
||||
|
||||
// A call to StopSession will nullify any active sessions so only retry if StopSession has not be called,
|
||||
// since StopSession will also close the retryAfterTermination channel
|
||||
if d.session != nil {
|
||||
d.retryAfterTermination <- true
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
// Store session id before we nullify any active sessions
|
||||
sid := aws.StringValue(d.session.SessionId)
|
||||
|
||||
// Stop retry polling process to avoid unwanted retries at this point
|
||||
d.session = nil
|
||||
close(d.retryAfterTermination)
|
||||
_, err := d.SvcClient.TerminateSession(&ssm.TerminateSessionInput{SessionId: aws.String(sid)})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error terminating SSM Session %q. Please terminate the session manually: %s", sid, 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
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ func (svc *MockSSMSvc) StartSessionWithContext(ctx aws.Context, input *ssm.Start
|
|||
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
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"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/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
@ -24,6 +25,7 @@ type StepCreateSSMTunnel struct {
|
|||
instanceId string
|
||||
PauseBeforeSSM time.Duration
|
||||
driver *SSMDriver
|
||||
stopSSMCommand func()
|
||||
}
|
||||
|
||||
// Run executes the Packer build step that creates a session tunnel.
|
||||
|
@ -73,30 +75,38 @@ func (s *StepCreateSSMTunnel) Run(ctx context.Context, state multistep.StateBag)
|
|||
driver := SSMDriver{SSMDriverConfig: cfg}
|
||||
s.driver = &driver
|
||||
}
|
||||
state.Put("sessionPort", s.LocalPortNumber)
|
||||
|
||||
input := s.BuildTunnelInputForInstance(s.instanceId)
|
||||
_, err := s.driver.StartSession(ctx, input)
|
||||
|
||||
ssmCtx, ssmCancel := context.WithCancel(ctx)
|
||||
s.stopSSMCommand = ssmCancel
|
||||
|
||||
go func() {
|
||||
err := pssm.Session{
|
||||
SvcClient: s.driver.SvcClient,
|
||||
Input: input,
|
||||
Region: s.driver.Region,
|
||||
}.Start(ssmCtx, ui)
|
||||
|
||||
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)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
if !s.SSMAgentEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.driver.StopSession(); err != nil {
|
||||
ui.Error(err.Error())
|
||||
if s.stopSSMCommand != nil {
|
||||
s.stopSSMCommand()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package error
|
||||
|
||||
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
|
||||
}
|
2
go.mod
2
go.mod
|
@ -93,7 +93,6 @@ require (
|
|||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/go-testing-interface v1.0.3 // indirect
|
||||
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/mapstructure v1.2.3
|
||||
github.com/mitchellh/panicwrap v1.0.0
|
||||
|
@ -104,7 +103,6 @@ require (
|
|||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
||||
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/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a
|
||||
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/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.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.30.8 h1:4BHbh8K3qKmcnAgToZ2LShldRF9inoqIBccpCLNCy3I=
|
||||
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.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
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/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
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/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
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/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
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/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/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/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
|
||||
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/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/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/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/go.mod h1:Zn/U9YKH0w8n83ezLps5eB6Jftc4gSoZWxVR8hgXgoY=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
|
Loading…
Reference in New Issue