Skip to content

Commit e1d9b66

Browse files
committed
Add external support for provision scripts
Signed-off-by: Anders F Björklund <[email protected]>
1 parent 1c7939b commit e1d9b66

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

pkg/hostagent/hostagent.go

+5
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@ sudo chown -R "${USER}" /run/host-services`
517517
return errors.Join(unlockErrs...)
518518
})
519519
}
520+
if *a.y.VMType == limayaml.EXT {
521+
if err := a.runProvisionScripts(); err != nil {
522+
return err
523+
}
524+
}
520525
if !*a.y.Plain {
521526
go a.watchGuestAgentEvents(ctx)
522527
}

pkg/hostagent/provision.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package hostagent
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
"github.com/lima-vm/lima/pkg/limayaml"
8+
"github.com/lima-vm/sshocker/pkg/ssh"
9+
"github.com/sirupsen/logrus"
10+
)
11+
12+
func (a *HostAgent) runProvisionScripts() error {
13+
var errs []error
14+
15+
for i, f := range a.y.Provision {
16+
switch f.Mode {
17+
case limayaml.ProvisionModeSystem, limayaml.ProvisionModeUser:
18+
logrus.Infof("Running %s provision %d of %d", f.Mode, i+1, len(a.y.Provision))
19+
err := a.waitForProvision(
20+
provision{
21+
description: fmt.Sprintf("provision.%s/%08d", f.Mode, i),
22+
sudo: f.Mode == limayaml.ProvisionModeSystem,
23+
script: f.Script,
24+
})
25+
if err != nil {
26+
errs = append(errs, err)
27+
}
28+
case limayaml.ProvisionModeDependency, limayaml.ProvisionModeBoot:
29+
logrus.Infof("Skipping %s provision %d of %d", f.Mode, i+1, len(a.y.Provision))
30+
continue
31+
default:
32+
return fmt.Errorf("unknown provision mode %q", f.Mode)
33+
}
34+
}
35+
return errors.Join(errs...)
36+
}
37+
38+
func (a *HostAgent) waitForProvision(p provision) error {
39+
if p.sudo {
40+
return a.waitForSystemProvision(p)
41+
}
42+
return a.waitForUserProvision(p)
43+
}
44+
45+
func (a *HostAgent) waitForSystemProvision(p provision) error {
46+
logrus.Debugf("executing script %q", p.description)
47+
stdout, stderr, err := sudoExecuteScript(a.instSSHAddress, a.sshLocalPort, a.sshConfig, p.script, p.description)
48+
logrus.Debugf("stdout=%q, stderr=%q, err=%v", stdout, stderr, err)
49+
if err != nil {
50+
return fmt.Errorf("stdout=%q, stderr=%q: %w", stdout, stderr, err)
51+
}
52+
return nil
53+
}
54+
55+
func (a *HostAgent) waitForUserProvision(p provision) error {
56+
logrus.Debugf("executing script %q", p.description)
57+
stdout, stderr, err := ssh.ExecuteScript(a.instSSHAddress, a.sshLocalPort, a.sshConfig, p.script, p.description)
58+
logrus.Debugf("stdout=%q, stderr=%q, err=%v", stdout, stderr, err)
59+
if err != nil {
60+
return fmt.Errorf("stdout=%q, stderr=%q: %w", stdout, stderr, err)
61+
}
62+
return nil
63+
}
64+
65+
type provision struct {
66+
description string
67+
script string
68+
sudo bool
69+
}

pkg/hostagent/sudo.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package hostagent
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
"os/exec"
8+
"strconv"
9+
"strings"
10+
11+
"github.com/lima-vm/sshocker/pkg/ssh"
12+
"github.com/sirupsen/logrus"
13+
)
14+
15+
// sudoExecuteScript executes the given script (as root) on the remote host via stdin.
16+
// Returns stdout and stderr.
17+
//
18+
// scriptName is used only for readability of error strings.
19+
func sudoExecuteScript(host string, port int, c *ssh.SSHConfig, script, scriptName string) (stdout, stderr string, err error) {
20+
if c == nil {
21+
return "", "", errors.New("got nil SSHConfig")
22+
}
23+
interpreter, err := ssh.ParseScriptInterpreter(script)
24+
if err != nil {
25+
return "", "", err
26+
}
27+
sshBinary := c.Binary()
28+
sshArgs := c.Args()
29+
if port != 0 {
30+
sshArgs = append(sshArgs, "-p", strconv.Itoa(port))
31+
}
32+
sshArgs = append(sshArgs, host, "--", "sudo", interpreter)
33+
sshCmd := exec.Command(sshBinary, sshArgs...)
34+
sshCmd.Stdin = strings.NewReader(script)
35+
var buf bytes.Buffer
36+
sshCmd.Stderr = &buf
37+
logrus.Debugf("executing ssh for script %q: %s %v", scriptName, sshCmd.Path, sshCmd.Args)
38+
out, err := sshCmd.Output()
39+
if err != nil {
40+
return string(out), buf.String(), fmt.Errorf("failed to execute script %q: stdout=%q, stderr=%q: %w", scriptName, string(out), buf.String(), err)
41+
}
42+
return string(out), buf.String(), nil
43+
}

0 commit comments

Comments
 (0)