Skip to content

Commit bef550f

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

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

pkg/hostagent/hostagent.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,11 @@ sudo chown -R "${USER}" /run/host-services`
490490
return errors.Join(unlockErrs...)
491491
})
492492
}
493+
if *a.instConfig.VMType == limayaml.EXT {
494+
if err := a.runProvisionScripts(); err != nil {
495+
return err
496+
}
497+
}
493498
if !*a.instConfig.Plain {
494499
go a.watchGuestAgentEvents(ctx)
495500
}

pkg/hostagent/provision.go

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

pkg/hostagent/sudo.go

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

0 commit comments

Comments
 (0)