github.com/markdia/terraform@v0.5.1-0.20150508012022-f1ae920aa970/communicator/ssh/communicator_test.go (about)

     1  // +build !race
     2  
     3  package ssh
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"net"
     9  	"regexp"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/hashicorp/terraform/communicator/remote"
    14  	"github.com/hashicorp/terraform/terraform"
    15  	"golang.org/x/crypto/ssh"
    16  )
    17  
    18  // private key for mock server
    19  const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
    20  MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU
    21  70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx
    22  9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF
    23  tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z
    24  s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc
    25  qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT
    26  +IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea
    27  riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH
    28  D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh
    29  atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT
    30  b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN
    31  ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M
    32  MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4
    33  KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8
    34  e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1
    35  D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+
    36  3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj
    37  orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw
    38  64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc
    39  XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc
    40  QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g
    41  /SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ
    42  I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk
    43  gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl
    44  NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw==
    45  -----END RSA PRIVATE KEY-----`
    46  
    47  var serverConfig = &ssh.ServerConfig{
    48  	PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
    49  		if c.User() == "user" && string(pass) == "pass" {
    50  			return nil, nil
    51  		}
    52  		return nil, fmt.Errorf("password rejected for %q", c.User())
    53  	},
    54  }
    55  
    56  func init() {
    57  	// Parse and set the private key of the server, required to accept connections
    58  	signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey))
    59  	if err != nil {
    60  		panic("unable to parse private key: " + err.Error())
    61  	}
    62  	serverConfig.AddHostKey(signer)
    63  }
    64  
    65  func newMockLineServer(t *testing.T) string {
    66  	l, err := net.Listen("tcp", "127.0.0.1:0")
    67  	if err != nil {
    68  		t.Fatalf("Unable to listen for connection: %s", err)
    69  	}
    70  
    71  	go func() {
    72  		defer l.Close()
    73  		c, err := l.Accept()
    74  		if err != nil {
    75  			t.Errorf("Unable to accept incoming connection: %s", err)
    76  		}
    77  		defer c.Close()
    78  		conn, chans, _, err := ssh.NewServerConn(c, serverConfig)
    79  		if err != nil {
    80  			t.Logf("Handshaking error: %v", err)
    81  		}
    82  		t.Log("Accepted SSH connection")
    83  
    84  		for newChannel := range chans {
    85  			channel, requests, err := newChannel.Accept()
    86  			if err != nil {
    87  				t.Errorf("Unable to accept channel.")
    88  			}
    89  			t.Log("Accepted channel")
    90  
    91  			go func(in <-chan *ssh.Request) {
    92  				for req := range in {
    93  					if req.WantReply {
    94  						req.Reply(true, nil)
    95  					}
    96  				}
    97  			}(requests)
    98  
    99  			go func(newChannel ssh.NewChannel) {
   100  				conn.OpenChannel(newChannel.ChannelType(), nil)
   101  			}(newChannel)
   102  
   103  			defer channel.Close()
   104  		}
   105  		conn.Close()
   106  	}()
   107  
   108  	return l.Addr().String()
   109  }
   110  
   111  func TestNew_Invalid(t *testing.T) {
   112  	address := newMockLineServer(t)
   113  	parts := strings.Split(address, ":")
   114  
   115  	r := &terraform.InstanceState{
   116  		Ephemeral: terraform.EphemeralState{
   117  			ConnInfo: map[string]string{
   118  				"type":     "ssh",
   119  				"user":     "user",
   120  				"password": "i-am-invalid",
   121  				"host":     parts[0],
   122  				"port":     parts[1],
   123  				"timeout":  "30s",
   124  			},
   125  		},
   126  	}
   127  
   128  	c, err := New(r)
   129  	if err != nil {
   130  		t.Fatalf("error creating communicator: %s", err)
   131  	}
   132  
   133  	err = c.Connect(nil)
   134  	if err == nil {
   135  		t.Fatal("should have had an error connecting")
   136  	}
   137  }
   138  
   139  func TestStart(t *testing.T) {
   140  	address := newMockLineServer(t)
   141  	parts := strings.Split(address, ":")
   142  
   143  	r := &terraform.InstanceState{
   144  		Ephemeral: terraform.EphemeralState{
   145  			ConnInfo: map[string]string{
   146  				"type":     "ssh",
   147  				"user":     "user",
   148  				"password": "pass",
   149  				"host":     parts[0],
   150  				"port":     parts[1],
   151  				"timeout":  "30s",
   152  			},
   153  		},
   154  	}
   155  
   156  	c, err := New(r)
   157  	if err != nil {
   158  		t.Fatalf("error creating communicator: %s", err)
   159  	}
   160  
   161  	var cmd remote.Cmd
   162  	stdout := new(bytes.Buffer)
   163  	cmd.Command = "echo foo"
   164  	cmd.Stdout = stdout
   165  
   166  	err = c.Start(&cmd)
   167  	if err != nil {
   168  		t.Fatalf("error executing remote command: %s", err)
   169  	}
   170  }
   171  
   172  func TestScriptPath(t *testing.T) {
   173  	cases := []struct {
   174  		Input   string
   175  		Pattern string
   176  	}{
   177  		{
   178  			"/tmp/script.sh",
   179  			`^/tmp/script\.sh$`,
   180  		},
   181  		{
   182  			"/tmp/script_%RAND%.sh",
   183  			`^/tmp/script_(\d+)\.sh$`,
   184  		},
   185  	}
   186  
   187  	for _, tc := range cases {
   188  		comm := &Communicator{connInfo: &connectionInfo{ScriptPath: tc.Input}}
   189  		output := comm.ScriptPath()
   190  
   191  		match, err := regexp.Match(tc.Pattern, []byte(output))
   192  		if err != nil {
   193  			t.Fatalf("bad: %s\n\nerr: %s", tc.Input, err)
   194  		}
   195  		if !match {
   196  			t.Fatalf("bad: %s\n\n%s", tc.Input, output)
   197  		}
   198  	}
   199  }