feat: add harness runner (scenario executor, alert receiver, action executor, judge, revert)
This commit is contained in:
1 parent
8f5378b6ea
commit
5daf13b669
16 files changed
+2550
No files matched your search
@@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// sshRun executes one command on a lab VM as the harness user (key from Secret Manager, design 6.11).
|
||||
// Host resolution: IP as-is, "legacy:N" or pet VM short name via the compute API.
|
||||
func (a *App) sshRun(ctx context.Context, host, command string) (string, error) {
|
||||
ip := host
|
||||
if net.ParseIP(host) == nil {
|
||||
in, err := a.resolveInstance(ctx, host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip = in.IP
|
||||
}
|
||||
key, err := os.ReadFile(a.cfg.SSHKeyFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ssh key: %w", err)
|
||||
}
|
||||
signer, err := ssh.ParsePrivateKey(key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ssh key parse: %w", err)
|
||||
}
|
||||
cfg := &ssh.ClientConfig{
|
||||
User: a.cfg.SSHUser,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // lab VMs are recreated by autohealing; host keys are not pinned
|
||||
Timeout: 15 * time.Second,
|
||||
}
|
||||
d := net.Dialer{Timeout: 15 * time.Second}
|
||||
conn, err := d.DialContext(ctx, "tcp", ip+":22")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("dial %s: %w", ip, err)
|
||||
}
|
||||
c, chans, reqs, err := ssh.NewClientConn(conn, ip+":22", cfg)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return "", fmt.Errorf("ssh %s: %w", ip, err)
|
||||
}
|
||||
client := ssh.NewClient(c, chans, reqs)
|
||||
defer client.Close()
|
||||
sess, err := client.NewSession()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer sess.Close()
|
||||
var out bytes.Buffer
|
||||
sess.Stdout, sess.Stderr = &out, &out
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- sess.Run(command) }()
|
||||
select {
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
return out.String(), fmt.Errorf("%s: %w: %s", command, err, out.String())
|
||||
}
|
||||
return out.String(), nil
|
||||
case <-ctx.Done():
|
||||
return out.String(), ctx.Err()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user