-
Lars Seipel authored
After starting up the topology, use LLDP info (through ptmd) to verify that what was built matches the original DOT description.
Lars Seipel authoredAfter starting up the topology, use LLDP info (through ptmd) to verify that what was built matches the original DOT description.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sshutil_test.go 2.90 KiB
package libvirt
// SSH helpers used in tests.
import (
"bytes"
"crypto/ed25519"
"fmt"
"io"
"net"
"strings"
"github.com/pkg/sftp"
"go4.org/writerutil"
"golang.org/x/crypto/ssh"
)
func proxyJump(c *ssh.Client, addr string, config *ssh.ClientConfig) (cc *ssh.Client, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("proxyJump %s: %w", addr, err)
}
}()
conn, err := c.Dial("tcp", net.JoinHostPort(addr, "22"))
if err != nil {
return nil, err
}
sshConn, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
if err != nil {
conn.Close()
return nil, err
}
return ssh.NewClient(sshConn, chans, reqs), nil
}
func runCommand(c *ssh.Client, name string, args ...string) ([]byte, error) {
var b strings.Builder
b.WriteString(shellQuote(name))
for _, a := range args {
b.WriteByte(' ')
b.WriteString(shellQuote(a))
}
cmd := b.String()
sess, err := c.NewSession()
if err != nil {
return nil, err
}
defer sess.Close()
var stdout bytes.Buffer
stderr := &writerutil.PrefixSuffixSaver{N: 1024}
sess.Stdout = &stdout
sess.Stderr = stderr
if err := sess.Run(cmd); err != nil {
if msg := stderr.Bytes(); len(msg) > 0 {
return nil, fmt.Errorf("runCommand: %w | %s |", err, msg)
}
return nil, fmt.Errorf("runCommand: %w", err)
}
return stdout.Bytes(), nil
}
func sftpGet(conn *ssh.Client, path string) (content []byte, err error) {
c, err := sftp.NewClient(conn)
if err != nil {
return nil, err
}
defer c.Close()
fd, err := c.Open(path)
if err != nil {
return nil, err
}
defer fd.Close()
return io.ReadAll(fd)
}
func sftpPutReader(conn *ssh.Client, dstPath string, src io.Reader) (err error) {
c, err := sftp.NewClient(conn)
if err != nil {
return err
}
defer c.Close()
fd, err := c.Create(dstPath)
if err != nil {
return err
}
defer func() {
if cerr := fd.Close(); err == nil {
err = cerr
}
}()
_, err = io.Copy(fd, src)
return err
}
func sftpPut(conn *ssh.Client, dstPath string, content []byte) (err error) {
return sftpPutReader(conn, dstPath, bytes.NewReader(content))
}
func sshKeygen(rand io.Reader) (ssh.Signer, []byte, error) {
_, sk, err := ed25519.GenerateKey(rand)
if err != nil {
return nil, nil, err
}
signer, err := ssh.NewSignerFromSigner(sk)
if err != nil {
return nil, nil, err
}
sshPubKey := ssh.MarshalAuthorizedKey(signer.PublicKey())
return signer, sshPubKey, nil
}
// ShellQuote returns s in a form suitable to pass it to the shell as an
// argument. Obviously, it works for Bourne-like shells only. The way this
// works is that first the whole string is enclosed in single quotes. Now the
// only character that needs special handling is the single quote itself. We
// replace it by '\'' (the outer quotes are part of the replacement) and make
// use of the fact that the shell concatenates adjacent strings.
func shellQuote(s string) string {
t := strings.Replace(s, "'", `'\''`, -1)
return "'" + t + "'"
}