Skip to content

Commit 2c6951a

Browse files
committed
daemonize limactl start
Close issue 26 Signed-off-by: Akihiro Suda <[email protected]>
1 parent 4ccd409 commit 2c6951a

File tree

24 files changed

+1006
-160
lines changed

24 files changed

+1006
-160
lines changed

.github/workflows/release.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,11 @@ jobs:
3636
(Changes to be documented)
3737
3838
## Usage
39-
Terminal 1:
4039
\`\`\`console
4140
[macOS]$ limactl start
4241
...
4342
INFO[0029] READY. Run `lima` to open the shell.
44-
\`\`\`
4543
46-
Terminal 2:
47-
\`\`\`console
4844
[macOS]$ lima uname
4945
Linux
5046
\`\`\`

.github/workflows/test.yml

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ jobs:
3737
version: v1.35
3838
args: --verbose
3939

40-
tests:
41-
name: Tests
40+
unit:
41+
name: Unit tests
4242
runs-on: ${{ matrix.os }}
4343
strategy:
4444
matrix:
@@ -50,12 +50,45 @@ jobs:
5050
go-version: 1.16.x
5151
- uses: actions/checkout@v2
5252
with:
53-
fetch-depth: 25
53+
fetch-depth: 1
5454
- name: Make
5555
run: make
5656
- name: Unit tests
5757
run: go test -v ./...
5858

59+
integration:
60+
name: Integration tests
61+
runs-on: macos-10.15
62+
timeout-minutes: 40
63+
steps:
64+
- uses: actions/setup-go@v2
65+
with:
66+
go-version: 1.16.x
67+
- uses: actions/checkout@v2
68+
with:
69+
fetch-depth: 1
70+
- name: Make
71+
run: make
72+
- name: Install
73+
run: make install
74+
- name: Install QEMU
75+
run: brew install qemu
76+
- name: Prepare ssh
77+
run: |
78+
if [ -e ~/.ssh/id_rsa ]; then
79+
echo "~/.ssh/id_rsa already exists"
80+
else
81+
echo "Generating ~/.ssh/id_rsa"
82+
ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ""
83+
fi
84+
- name: Smoke tests
85+
run: |
86+
limactl start --tty=false default
87+
lima uname -a
88+
echo "this file was created on macOS" > ~/foo
89+
lima cat ~/foo
90+
limactl stop default
91+
5992
artifacts:
6093
name: Artifacts
6194
# the release pipeline uses Linux, so we Linux here as well
@@ -67,6 +100,6 @@ jobs:
67100
go-version: 1.16.x
68101
- uses: actions/checkout@v2
69102
with:
70-
fetch-depth: 25
103+
fetch-depth: 1
71104
- name: Make artifacts
72105
run: make artifacts

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,11 @@ To install from the source, run `make && make install`.
9797

9898
### Usage
9999

100-
Terminal 1:
101100
```console
102101
[macOS]$ limactl start
103102
...
104103
INFO[0029] READY. Run `lima` to open the shell.
105-
```
106104

107-
Terminal 2:
108-
```console
109105
[macOS]$ lima uname
110106
Linux
111107
```
@@ -118,12 +114,14 @@ Detailed usage:
118114
Wait until "READY" to be printed on the host terminal.
119115

120116
- Run `limactl shell <INSTANCE> <COMMAND>` to launch `<COMMAND>` on Linux.
121-
For the "default" instance, this command can be shortened as just `lima <COMMAND>`.
117+
For the "default" instance, this command can be shortened as `lima <COMMAND>`.
122118
The `lima` command also accepts the instance name as the environment variable `$LIMA_INSTANCE`.
123119

124-
- Run `limactl ls` to show the instances.
120+
- Run `limactl list [--json]` to show the instances.
121+
122+
- Run `limactl stop [--force] <INSTANCE>` to stop the instance.
125123

126-
- Run `limactl delete <INSTANCE>` to delete the instance.
124+
- Run `limactl delete [--force] <INSTANCE>` to delete the instance.
127125

128126
- To enable bash completion, add `source <(limactl completion bash)` to `~/.bash_profile`.
129127

cmd/limactl/delete.go

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ import (
1010
)
1111

1212
var deleteCommand = &cli.Command{
13-
Name: "delete",
14-
Usage: "Delete an instance of Lima.",
15-
ArgsUsage: "INSTANCE [INSTANCE, ...]",
13+
Name: "delete",
14+
Aliases: []string{"remove", "rm"},
15+
Usage: "Delete an instance of Lima.",
16+
ArgsUsage: "INSTANCE [INSTANCE, ...]",
17+
Flags: []cli.Flag{
18+
&cli.BoolFlag{
19+
Name: "force",
20+
Aliases: []string{"f"},
21+
Usage: "forcibly kill the processes",
22+
},
23+
},
1624
Action: deleteAction,
1725
BashComplete: deleteBashComplete,
1826
}
@@ -21,19 +29,33 @@ func deleteAction(clicontext *cli.Context) error {
2129
if clicontext.NArg() == 0 {
2230
return errors.Errorf("requires at least 1 argument")
2331
}
32+
force := clicontext.Bool("force")
2433
for _, instName := range clicontext.Args().Slice() {
25-
instDir, err := store.InstanceDir(instName)
34+
inst, err := store.Inspect(instName)
2635
if err != nil {
36+
if errors.Is(err, os.ErrNotExist) {
37+
logrus.Warnf("Ignoring non-existent instance %q", instName)
38+
continue
39+
}
2740
return err
2841
}
29-
if _, err := os.Stat(instDir); errors.Is(err, os.ErrNotExist) {
30-
logrus.Warnf("Ignoring non-existent instance %q (%q)", instName, instDir)
31-
return nil
42+
if err := deleteInstance(inst, force); err != nil {
43+
return errors.Wrapf(err, "failed to delete instance %q", instName)
3244
}
33-
if err := os.RemoveAll(instDir); err != nil {
34-
return errors.Wrapf(err, "failed to remove %q", instDir)
35-
}
36-
logrus.Infof("Deleted %q (%q)", instName, instDir)
45+
logrus.Infof("Deleted %q (%q)", instName, inst.Dir)
46+
}
47+
return nil
48+
}
49+
50+
func deleteInstance(inst *store.Instance, force bool) error {
51+
if !force && inst.Status != store.StatusStopped {
52+
return errors.Errorf("expected status %q, got %q", store.StatusStopped, inst.Status)
53+
}
54+
55+
stopInstanceForcibly(inst)
56+
57+
if err := os.RemoveAll(inst.Dir); err != nil {
58+
return errors.Wrapf(err, "failed to remove %q", inst.Dir)
3759
}
3860
return nil
3961
}

cmd/limactl/hostagent.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"os/signal"
6+
"strconv"
7+
8+
"github.com/AkihiroSuda/lima/pkg/hostagent"
9+
"github.com/pkg/errors"
10+
"github.com/urfave/cli/v2"
11+
)
12+
13+
var hostagentCommand = &cli.Command{
14+
Name: "hostagent",
15+
Usage: "DO NOT EXECUTE MANUALLY",
16+
ArgsUsage: "NAME",
17+
Hidden: true,
18+
Flags: []cli.Flag{
19+
&cli.StringFlag{
20+
Name: "pidfile",
21+
Usage: "PID file",
22+
},
23+
},
24+
25+
Action: hostagentAction,
26+
}
27+
28+
func hostagentAction(clicontext *cli.Context) error {
29+
if pidfile := clicontext.String("pidfile"); pidfile != "" {
30+
if _, err := os.Stat(pidfile); !errors.Is(err, os.ErrNotExist) {
31+
return errors.Errorf("pidfile %q already exists", pidfile)
32+
}
33+
if err := os.WriteFile(pidfile, []byte(strconv.Itoa(os.Getpid())+"\n"), 0644); err != nil {
34+
return err
35+
}
36+
defer os.RemoveAll(pidfile)
37+
}
38+
39+
if clicontext.NArg() != 1 {
40+
return errors.Errorf("requires exactly 1 argument")
41+
}
42+
43+
instName := clicontext.Args().First()
44+
45+
sigintCh := make(chan os.Signal, 1)
46+
signal.Notify(sigintCh, os.Interrupt)
47+
48+
stdout := clicontext.App.Writer
49+
stderr := clicontext.App.ErrWriter
50+
51+
ha, err := hostagent.New(instName, stdout, stderr, sigintCh)
52+
if err != nil {
53+
return err
54+
}
55+
return ha.Run(clicontext.Context)
56+
}

cmd/limactl/list.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"text/tabwriter"
7+
8+
"github.com/AkihiroSuda/lima/pkg/store"
9+
"github.com/pkg/errors"
10+
"github.com/sirupsen/logrus"
11+
"github.com/urfave/cli/v2"
12+
)
13+
14+
var listCommand = &cli.Command{
15+
Name: "list",
16+
Aliases: []string{"ls"},
17+
Usage: "List instances of Lima.",
18+
Flags: []cli.Flag{
19+
&cli.BoolFlag{
20+
Name: "json",
21+
Usage: "JSONify output",
22+
},
23+
&cli.BoolFlag{
24+
Name: "quiet",
25+
Aliases: []string{"q"},
26+
Usage: "Only show names",
27+
},
28+
},
29+
30+
Action: listAction,
31+
}
32+
33+
func listAction(clicontext *cli.Context) error {
34+
if clicontext.NArg() > 0 {
35+
return errors.New("too many arguments")
36+
}
37+
38+
if clicontext.Bool("quiet") && clicontext.Bool("json") {
39+
return errors.New("option --quiet conflicts with --json")
40+
}
41+
42+
instances, err := store.Instances()
43+
if err != nil {
44+
return err
45+
}
46+
47+
if clicontext.Bool("quiet") {
48+
for _, instName := range instances {
49+
fmt.Fprintln(clicontext.App.Writer, instName)
50+
}
51+
return nil
52+
}
53+
54+
if clicontext.Bool("json") {
55+
for _, instName := range instances {
56+
inst, err := store.Inspect(instName)
57+
if err != nil {
58+
logrus.WithError(err).Errorf("instance %q does not exist?", instName)
59+
continue
60+
}
61+
b, err := json.Marshal(inst)
62+
if err != nil {
63+
return err
64+
}
65+
fmt.Fprintln(clicontext.App.Writer, string(b))
66+
}
67+
return nil
68+
}
69+
70+
w := tabwriter.NewWriter(clicontext.App.Writer, 4, 8, 4, ' ', 0)
71+
fmt.Fprintln(w, "NAME\tSTATUS\tSSH\tARCH\tDIR")
72+
73+
if len(instances) == 0 {
74+
logrus.Warn("No instance found. Run `limactl start` to create an instance.")
75+
}
76+
77+
for _, instName := range instances {
78+
inst, err := store.Inspect(instName)
79+
if err != nil {
80+
logrus.WithError(err).Errorf("instance %q does not exist?", instName)
81+
continue
82+
}
83+
if len(inst.Errors) > 0 {
84+
logrus.WithField("errors", inst.Errors).Warnf("instance %q has errors", instName)
85+
}
86+
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
87+
inst.Name,
88+
inst.Status,
89+
fmt.Sprintf("127.0.0.1:%d", inst.SSHLocalPort),
90+
inst.Arch,
91+
inst.Dir,
92+
)
93+
}
94+
95+
return w.Flush()
96+
}

cmd/limactl/ls.go

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)