v0.1.0 cbb8ad6

host_cmd.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package main
4
5import (
6	"context"
7	"errors"
8	"flag"
9	"fmt"
10	"os"
11
12	"github.com/bnema/zerowrap"
13
14	"bnema.dev/ermite-api/gen/ermite/ermiteconnect"
15
16	"bnema.dev/ermite-runner/internal/host"
17	"bnema.dev/ermite-runner/internal/sandbox"
18	"bnema.dev/ermite-runner/internal/sandbox/drivers"
19)
20
21// loadHostConfig reads the sandbox settings and the host settings together, so
22// that the state directory and the capacity are the same for both.
23func loadHostConfig() (sandbox.Config, host.Config, error) {
24	scfg, err := sandbox.LoadConfig(os.Getenv)
25	if err != nil {
26		return scfg, host.Config{}, err
27	}
28	name, _ := os.Hostname()
29	hcfg, err := host.LoadConfig(os.Getenv, name, scfg.StateDir, scfg.Capacity, version)
30	return scfg, hcfg, err
31}
32
33func buildDriver(ctx context.Context, scfg sandbox.Config) (sandbox.Driver, error) {
34	return sandbox.Select(ctx, scfg, drivers.Factories(), sandbox.Prober{})
35}
36
37// registerCmd is `ermite-runner register [--token-stdin] [--token T] [--replace]`:
38// it chooses the isolation mode like `run` will, registers the runner with it,
39// and stores the runner token (mode 0600) in the state directory. The register
40// token is read from standard input (--token-stdin) or from
41// ERMITE_RUNNER_REGISTER_TOKEN; --token is accepted but the arguments of a
42// process are visible to every user of the host.
43func registerCmd(ctx context.Context, args []string) int {
44	fs := flag.NewFlagSet("register", flag.ContinueOnError)
45	token := fs.String("token", "", "token with the runner:register scope (visible in the process list: prefer $"+host.EnvRegisterToken+" or --token-stdin)")
46	fromStdin := fs.Bool("token-stdin", false, "read the token from standard input")
47	replace := fs.Bool("replace", false, "replace a runner of the same name")
48	if err := fs.Parse(args); err != nil {
49		return 2
50	}
51	tok, err := host.ResolveRegisterToken(*token, *fromStdin, os.Stdin, os.Getenv)
52	if err != nil {
53		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
54		return 2
55	}
56	*token = tok
57	scfg, hcfg, err := loadHostConfig()
58	if err != nil {
59		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
60		return 2
61	}
62	if hcfg.URL == "" {
63		fmt.Fprintf(os.Stderr, "ermite-runner: %s is not set\n", host.EnvURL)
64		return 2
65	}
66	drv, err := buildDriver(ctx, scfg)
67	if err != nil {
68		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
69		return 1
70	}
71	iso := drv.Isolation()
72	_ = drv.Close()
73
74	client := hcfg.Client(hcfg.URL, *token)
75	st, err := host.Register(ctx, hcfg, client, iso, *replace)
76	if err != nil {
77		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
78		return 1
79	}
80	fmt.Printf("registered runner %q (%s) with isolation %s; token stored in %s\n", st.Name, st.RunnerID, iso, host.StateFile(hcfg.StateDir))
81	return 0
82}
83
84// runCmd is `ermite-runner run`: poll ermite for jobs and run each one in its
85// own sandbox until SIGINT or SIGTERM, then cancel the running jobs with
86// runner_shutdown and report them.
87func runCmd(ctx context.Context) int {
88	log := zerowrap.FromCtx(ctx)
89	scfg, hcfg, err := loadHostConfig()
90	if err != nil {
91		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
92		return 2
93	}
94	st, err := host.LoadState(hcfg.StateDir)
95	if err != nil {
96		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
97		return 2
98	}
99	url := hcfg.URL
100	if url == "" {
101		url = st.URL
102	}
103	if url == "" {
104		fmt.Fprintf(os.Stderr, "ermite-runner: %s is not set\n", host.EnvURL)
105		return 2
106	}
107	drv, err := buildDriver(ctx, scfg)
108	if err != nil {
109		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
110		return 1
111	}
112	defer func() { _ = drv.Close() }()
113	log.Info().Str("runner", st.Name).Str("isolation", string(drv.Isolation())).Int("capacity", scfg.Capacity).Msg("runner started")
114
115	r := &host.Runner{
116		Cfg:    hcfg,
117		Driver: drv,
118		Poll:   hcfg.Client(url, st.RunnerToken),
119		Job: func(tok string) ermiteconnect.RunnerServiceClient {
120			return hcfg.Client(url, tok)
121		},
122	}
123	r.Cfg.URL = url
124	if err := r.Run(ctx); err != nil {
125		if errors.Is(err, host.ErrRemoved) {
126			fmt.Fprintln(os.Stderr, "ermite-runner:", err)
127			return 3
128		}
129		fmt.Fprintln(os.Stderr, "ermite-runner:", err)
130		return 1
131	}
132	log.Info().Msg("runner stopped")
133	return 0
134}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close