moved adapter to common package

This commit is contained in:
xinau 2019-02-12 07:10:57 +00:00
parent 8526244285
commit 36c1e8d838
7 changed files with 20 additions and 419 deletions

View File

@ -1,4 +1,4 @@
package ansible
package adapter
import (
"bytes"
@ -17,7 +17,7 @@ import (
// An adapter satisfies SSH requests (from an Ansible client) by delegating SSH
// exec and subsystem commands to a packer.Communicator.
type adapter struct {
type Adapter struct {
done <-chan struct{}
l net.Listener
config *ssh.ServerConfig
@ -26,8 +26,8 @@ type adapter struct {
comm packer.Communicator
}
func newAdapter(done <-chan struct{}, l net.Listener, config *ssh.ServerConfig, sftpCmd string, ui packer.Ui, comm packer.Communicator) *adapter {
return &adapter{
func NewAdapter(done <-chan struct{}, l net.Listener, config *ssh.ServerConfig, sftpCmd string, ui packer.Ui, comm packer.Communicator) *Adapter {
return &Adapter{
done: done,
l: l,
config: config,
@ -37,7 +37,7 @@ func newAdapter(done <-chan struct{}, l net.Listener, config *ssh.ServerConfig,
}
}
func (c *adapter) Serve() {
func (c *Adapter) Serve() {
log.Printf("SSH proxy: serving on %s", c.l.Addr())
for {
@ -62,7 +62,7 @@ func (c *adapter) Serve() {
}
}
func (c *adapter) Handle(conn net.Conn, ui packer.Ui) error {
func (c *Adapter) Handle(conn net.Conn, ui packer.Ui) error {
log.Print("SSH proxy: accepted connection")
_, chans, reqs, err := ssh.NewServerConn(conn, c.config)
if err != nil {
@ -89,7 +89,7 @@ func (c *adapter) Handle(conn net.Conn, ui packer.Ui) error {
return nil
}
func (c *adapter) handleSession(newChannel ssh.NewChannel) error {
func (c *Adapter) handleSession(newChannel ssh.NewChannel) error {
channel, requests, err := newChannel.Accept()
if err != nil {
return err
@ -182,11 +182,11 @@ func (c *adapter) handleSession(newChannel ssh.NewChannel) error {
return nil
}
func (c *adapter) Shutdown() {
func (c *Adapter) Shutdown() {
c.l.Close()
}
func (c *adapter) exec(command string, in io.Reader, out io.Writer, err io.Writer) int {
func (c *Adapter) exec(command string, in io.Reader, out io.Writer, err io.Writer) int {
var exitStatus int
switch {
case strings.HasPrefix(command, "scp ") && serveSCP(command[4:]):
@ -206,7 +206,7 @@ func serveSCP(args string) bool {
return bytes.IndexAny(opts, "tf") >= 0
}
func (c *adapter) scpExec(args string, in io.Reader, out io.Writer) error {
func (c *Adapter) scpExec(args string, in io.Reader, out io.Writer) error {
opts, rest := scpOptions(args)
// remove the quoting that ansible added to rest for shell safety.
@ -226,7 +226,7 @@ func (c *adapter) scpExec(args string, in io.Reader, out io.Writer) error {
return errors.New("no scp mode specified")
}
func (c *adapter) remoteExec(command string, in io.Reader, out io.Writer, err io.Writer) int {
func (c *Adapter) remoteExec(command string, in io.Reader, out io.Writer, err io.Writer) int {
cmd := &packer.RemoteCmd{
Stdin: in,
Stdout: out,

View File

@ -1,4 +1,4 @@
package inspec
package adapter
import (
"errors"
@ -26,7 +26,7 @@ func TestAdapter_Serve(t *testing.T) {
ui := new(packer.NoopUi)
sut := newAdapter(done, &l, config, newUi(ui), communicator{})
sut := NewAdapter(done, &l, config, "", ui, communicator{})
go func() {
i := 0
for range acceptC {

View File

@ -1,4 +1,4 @@
package ansible
package adapter
import (
"bufio"

View File

@ -1,116 +0,0 @@
package ansible
import (
"errors"
"io"
"log"
"net"
"os"
"testing"
"time"
"github.com/hashicorp/packer/packer"
"golang.org/x/crypto/ssh"
)
func TestAdapter_Serve(t *testing.T) {
// done signals the adapter that the provisioner is done
done := make(chan struct{})
acceptC := make(chan struct{})
l := listener{done: make(chan struct{}), acceptC: acceptC}
config := &ssh.ServerConfig{}
ui := new(packer.NoopUi)
sut := newAdapter(done, &l, config, "", newUi(ui), communicator{})
go func() {
i := 0
for range acceptC {
i++
if i == 4 {
close(done)
l.Close()
}
}
}()
sut.Serve()
}
type listener struct {
done chan struct{}
acceptC chan<- struct{}
i int
}
func (l *listener) Accept() (net.Conn, error) {
log.Println("Accept() called")
l.acceptC <- struct{}{}
select {
case <-l.done:
log.Println("done, serving an error")
return nil, errors.New("listener is closed")
case <-time.After(10 * time.Millisecond):
l.i++
if l.i%2 == 0 {
c1, c2 := net.Pipe()
go func(c net.Conn) {
<-time.After(100 * time.Millisecond)
log.Println("closing c")
c.Close()
}(c1)
return c2, nil
}
}
return nil, errors.New("accept error")
}
func (l *listener) Close() error {
close(l.done)
return nil
}
func (l *listener) Addr() net.Addr {
return addr{}
}
type addr struct{}
func (a addr) Network() string {
return a.String()
}
func (a addr) String() string {
return "test"
}
type communicator struct{}
func (c communicator) Start(*packer.RemoteCmd) error {
return errors.New("communicator not supported")
}
func (c communicator) Upload(string, io.Reader, *os.FileInfo) error {
return errors.New("communicator not supported")
}
func (c communicator) UploadDir(dst string, src string, exclude []string) error {
return errors.New("communicator not supported")
}
func (c communicator) Download(string, io.Writer) error {
return errors.New("communicator not supported")
}
func (c communicator) DownloadDir(src string, dst string, exclude []string) error {
return errors.New("communicator not supported")
}

View File

@ -26,6 +26,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/adapter"
commonhelper "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
@ -63,7 +64,7 @@ type Config struct {
type Provisioner struct {
config Config
adapter *adapter
adapter *adapter.Adapter
done chan struct{}
ansibleVersion string
ansibleMajVersion uint
@ -286,7 +287,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
}
ui = newUi(ui)
p.adapter = newAdapter(p.done, localListener, config, p.config.SFTPCmd, ui, comm)
p.adapter = adapter.NewAdapter(p.done, localListener, config, p.config.SFTPCmd, ui, comm)
defer func() {
log.Print("shutting down the SSH proxy")

View File

@ -1,285 +0,0 @@
package inspec
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"net"
"github.com/hashicorp/packer/packer"
"golang.org/x/crypto/ssh"
)
// An adapter satisfies SSH requests (from an Inspec client) by delegating SSH
// exec and subsystem commands to a packer.Communicator.
type adapter struct {
done <-chan struct{}
l net.Listener
config *ssh.ServerConfig
ui packer.Ui
comm packer.Communicator
}
func newAdapter(done <-chan struct{}, l net.Listener, config *ssh.ServerConfig, ui packer.Ui, comm packer.Communicator) *adapter {
return &adapter{
done: done,
l: l,
config: config,
ui: ui,
comm: comm,
}
}
func (c *adapter) Serve() {
log.Printf("SSH proxy: serving on %s", c.l.Addr())
for {
// Accept will return if either the underlying connection is closed or if a connection is made.
// after returning, check to see if c.done can be received. If so, then Accept() returned because
// the connection has been closed.
conn, err := c.l.Accept()
select {
case <-c.done:
return
default:
if err != nil {
c.ui.Error(fmt.Sprintf("listen.Accept failed: %v", err))
continue
}
go func(conn net.Conn) {
if err := c.Handle(conn, c.ui); err != nil {
c.ui.Error(err.Error())
}
}(conn)
}
}
}
func (c *adapter) Handle(conn net.Conn, ui packer.Ui) error {
log.Print("SSH proxy: accepted connection")
_, chans, reqs, err := ssh.NewServerConn(conn, c.config)
if err != nil {
return errors.New("failed to handshake")
}
// discard all global requests
go ssh.DiscardRequests(reqs)
// Service the incoming NewChannels
for newChannel := range chans {
if newChannel.ChannelType() != "session" {
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
continue
}
go func(ch ssh.NewChannel) {
if err := c.handleSession(ch); err != nil {
c.ui.Error(err.Error())
}
}(newChannel)
}
return nil
}
func (c *adapter) handleSession(newChannel ssh.NewChannel) error {
channel, requests, err := newChannel.Accept()
if err != nil {
return err
}
defer channel.Close()
done := make(chan struct{})
// Sessions have requests such as "pty-req", "shell", "env", and "exec".
// see RFC 4254, section 6
go func(in <-chan *ssh.Request) {
env := make([]envRequestPayload, 4)
for req := range in {
switch req.Type {
case "pty-req":
log.Println("inspec provisioner pty-req request")
// accept pty-req requests, but don't actually do anything. Necessary for OpenSSH and sudo.
req.Reply(true, nil)
case "env":
req, err := newEnvRequest(req)
if err != nil {
c.ui.Error(err.Error())
req.Reply(false, nil)
continue
}
env = append(env, req.Payload)
log.Printf("new env request: %s", req.Payload)
req.Reply(true, nil)
case "exec":
req, err := newExecRequest(req)
if err != nil {
c.ui.Error(err.Error())
req.Reply(false, nil)
close(done)
continue
}
log.Printf("new exec request: %s", req.Payload)
if len(req.Payload) == 0 {
req.Reply(false, nil)
close(done)
return
}
go func(channel ssh.Channel) {
exit := c.exec(string(req.Payload), channel, channel, channel.Stderr())
exitStatus := make([]byte, 4)
binary.BigEndian.PutUint32(exitStatus, uint32(exit))
channel.SendRequest("exit-status", false, exitStatus)
close(done)
}(channel)
req.Reply(true, nil)
case "subsystem":
req, err := newSubsystemRequest(req)
if err != nil {
c.ui.Error(err.Error())
req.Reply(false, nil)
continue
}
log.Printf("new subsystem request: %s", req.Payload)
c.ui.Error(fmt.Sprintf("unsupported subsystem requested: %s", req.Payload))
req.Reply(false, nil)
default:
log.Printf("rejecting %s request", req.Type)
req.Reply(false, nil)
}
}
}(requests)
<-done
return nil
}
func (c *adapter) Shutdown() {
c.l.Close()
}
func (c *adapter) exec(command string, in io.Reader, out io.Writer, err io.Writer) int {
var exitStatus int
exitStatus = c.remoteExec(command, in, out, err)
return exitStatus
}
func (c *adapter) remoteExec(command string, in io.Reader, out io.Writer, err io.Writer) int {
cmd := &packer.RemoteCmd{
Stdin: in,
Stdout: out,
Stderr: err,
Command: command,
}
if err := c.comm.Start(cmd); err != nil {
c.ui.Error(err.Error())
return cmd.ExitStatus
}
cmd.Wait()
return cmd.ExitStatus
}
type envRequest struct {
*ssh.Request
Payload envRequestPayload
}
type envRequestPayload struct {
Name string
Value string
}
func (p envRequestPayload) String() string {
return fmt.Sprintf("%s=%s", p.Name, p.Value)
}
func newEnvRequest(raw *ssh.Request) (*envRequest, error) {
r := new(envRequest)
r.Request = raw
if err := ssh.Unmarshal(raw.Payload, &r.Payload); err != nil {
return nil, err
}
return r, nil
}
func sshString(buf io.Reader) (string, error) {
var size uint32
err := binary.Read(buf, binary.BigEndian, &size)
if err != nil {
return "", err
}
b := make([]byte, size)
err = binary.Read(buf, binary.BigEndian, b)
if err != nil {
return "", err
}
return string(b), nil
}
type execRequest struct {
*ssh.Request
Payload execRequestPayload
}
type execRequestPayload string
func (p execRequestPayload) String() string {
return string(p)
}
func newExecRequest(raw *ssh.Request) (*execRequest, error) {
r := new(execRequest)
r.Request = raw
buf := bytes.NewReader(r.Request.Payload)
var err error
var payload string
if payload, err = sshString(buf); err != nil {
return nil, err
}
r.Payload = execRequestPayload(payload)
return r, nil
}
type subsystemRequest struct {
*ssh.Request
Payload subsystemRequestPayload
}
type subsystemRequestPayload string
func (p subsystemRequestPayload) String() string {
return string(p)
}
func newSubsystemRequest(raw *ssh.Request) (*subsystemRequest, error) {
r := new(subsystemRequest)
r.Request = raw
buf := bytes.NewReader(r.Request.Payload)
var err error
var payload string
if payload, err = sshString(buf); err != nil {
return nil, err
}
r.Payload = subsystemRequestPayload(payload)
return r, nil
}

View File

@ -25,6 +25,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/adapter"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -58,7 +59,7 @@ type Config struct {
type Provisioner struct {
config Config
adapter *adapter
adapter *adapter.Adapter
done chan struct{}
inspecVersion string
inspecMajVersion uint
@ -275,7 +276,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
}
ui = newUi(ui)
p.adapter = newAdapter(p.done, localListener, config, ui, comm)
p.adapter = adapter.NewAdapter(p.done, localListener, config, "", ui, comm)
defer func() {
log.Print("shutting down the SSH proxy")