Skip to content

Commit 872704f

Browse files
committed
feat(restarter): add k8s distro-specific restarters
Signed-off-by: Vaughn Dice <[email protected]>
1 parent 53b7f9b commit 872704f

File tree

2 files changed

+135
-15
lines changed

2 files changed

+135
-15
lines changed

internal/containerd/restart_unix.go

Lines changed: 123 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,146 @@ package containerd
2222
import (
2323
"fmt"
2424
"log/slog"
25+
"os"
26+
"os/exec"
27+
"regexp"
2528
"syscall"
2629

2730
"github.com/mitchellh/go-ps"
2831
)
2932

3033
var psProcesses = ps.Processes
3134

32-
type restarter struct{}
35+
type defaultRestarter struct{}
3336

34-
func NewRestarter() Restarter {
35-
return restarter{}
37+
func NewDefaultRestarter() Restarter {
38+
return defaultRestarter{}
3639
}
3740

38-
func (c restarter) Restart() error {
39-
pid, err := getPid()
41+
func (c defaultRestarter) Restart() error {
42+
// If systemctl exists, use that, otherwise go pid
43+
if UsesSystemd() {
44+
out, err := nsenterCmd("systemctl", "restart", "containerd").CombinedOutput()
45+
slog.Debug(string(out))
46+
if err != nil {
47+
return fmt.Errorf("unable to restart containerd: %w", err)
48+
}
49+
} else {
50+
pid, err := getPid("containerd")
51+
if err != nil {
52+
return err
53+
}
54+
slog.Debug("found containerd process", "pid", pid)
55+
56+
err = syscall.Kill(pid, syscall.SIGHUP)
57+
if err != nil {
58+
return fmt.Errorf("failed to send SIGHUP to containerd: %w", err)
59+
}
60+
}
61+
62+
return nil
63+
}
64+
65+
type K0sRestarter struct{}
66+
67+
func (c K0sRestarter) Restart() error {
68+
// First, collect systemd units to determine which mode k0s is running in, eg
69+
// k0sworker or k0scontroller
70+
units, err := nsenterCmd("systemctl", "list-units").CombinedOutput()
71+
if err != nil {
72+
return fmt.Errorf("unable to list systemd units: %w", err)
73+
}
74+
service := regexp.MustCompile("k0sworker|k0scontroller").FindString(string(units))
75+
76+
out, err := nsenterCmd("systemctl", "restart", service).CombinedOutput()
77+
slog.Debug(string(out))
78+
if err != nil {
79+
return fmt.Errorf("unable to restart %s: %w", service, err)
80+
}
81+
82+
return nil
83+
}
84+
85+
type K3sRestarter struct{}
86+
87+
func (c K3sRestarter) Restart() error {
88+
// This restarter will be used both for stock K3s distros
89+
// using systemd as well as K3d, which does not.
90+
if UsesSystemd() {
91+
out, err := nsenterCmd("systemctl", "restart", "k3s").CombinedOutput()
92+
slog.Debug(string(out))
93+
if err != nil {
94+
return fmt.Errorf("unable to restart k3s: %w", err)
95+
}
96+
} else {
97+
// TODO: this approach still leads to the behavior mentioned in https://github.com/spinframework/runtime-class-manager/issues/140:
98+
// The first pod's provisioner container exits with code 255, leading to pod status Unknown,
99+
// followed by the subsequent pod's provisioner container no-op-ing and finishing with status Completed.
100+
pid, err := getPid("k3s")
101+
if err != nil {
102+
return err
103+
}
104+
slog.Debug("found k3s process", "pid", pid)
105+
106+
err = syscall.Kill(pid, syscall.SIGHUP)
107+
if err != nil {
108+
return fmt.Errorf("failed to send SIGHUP to k3s: %w", err)
109+
}
110+
}
111+
112+
return nil
113+
}
114+
115+
type MicroK8sRestarter struct{}
116+
117+
func (c MicroK8sRestarter) Restart() error {
118+
out, err := nsenterCmd("systemctl", "restart", "snap.microk8s.daemon-containerd").CombinedOutput()
119+
slog.Debug(string(out))
40120
if err != nil {
41-
return err
121+
return fmt.Errorf("unable to restart snap.microk8s.daemon-containerd: %w", err)
42122
}
43-
slog.Debug("found containerd process", "pid", pid)
44123

45-
err = syscall.Kill(pid, syscall.SIGHUP)
124+
return nil
125+
}
46126

127+
type RKE2Restarter struct{}
128+
129+
func (c RKE2Restarter) Restart() error {
130+
// First, collect systemd units to determine which mode rke2 is running in, eg
131+
// rke2-agent or rke2-server
132+
units, err := nsenterCmd("systemctl", "list-units").CombinedOutput()
47133
if err != nil {
48-
return fmt.Errorf("failed to send SIGHUP to containerd: %w", err)
134+
return fmt.Errorf("unable to list systemd units: %w", err)
49135
}
136+
service := regexp.MustCompile("rke2-agent|rke2-server").FindString(string(units))
137+
138+
out, err := nsenterCmd("systemctl", "restart", service).CombinedOutput()
139+
slog.Debug(string(out))
140+
if err != nil {
141+
return fmt.Errorf("unable to restart %s: %w", service, err)
142+
}
143+
50144
return nil
51145
}
52146

53-
func getPid() (int, error) {
147+
// TODO: lifted and amended from https://github.com/spinframework/runtime-class-manager/pull/387
148+
//
149+
// UsesSystemd checks if the system is using systemd
150+
func UsesSystemd() bool {
151+
cmd := nsenterCmd("systemctl", "list-units", "|", "grep", "-q", "containerd.service")
152+
if err := cmd.Run(); err != nil {
153+
slog.Debug("Error with systemctl: \n", "error", err)
154+
return false
155+
}
156+
return true
157+
}
158+
159+
func nsenterCmd(cmd ...string) *exec.Cmd {
160+
return exec.Command("nsenter",
161+
append([]string{fmt.Sprintf("-m/%s/proc/1/ns/mnt", os.Getenv("HOST_ROOT")), "--"}, cmd...)...) // #nosec G204
162+
}
163+
164+
func getPid(executable string) (int, error) {
54165
processes, err := psProcesses()
55166
if err != nil {
56167
return 0, fmt.Errorf("could not get processes: %w", err)
@@ -59,13 +170,13 @@ func getPid() (int, error) {
59170
var containerdProcesses = []ps.Process{}
60171

61172
for _, process := range processes {
62-
if process.Executable() == "containerd" {
173+
if process.Executable() == executable {
63174
containerdProcesses = append(containerdProcesses, process)
64175
}
65176
}
66177

67178
if len(containerdProcesses) != 1 {
68-
return 0, fmt.Errorf("need exactly one containerd process, found: %d", len(containerdProcesses))
179+
return 0, fmt.Errorf("need exactly one %s process, found: %d", executable, len(containerdProcesses))
69180
}
70181

71182
return containerdProcesses[0].Pid(), nil

internal/preset/preset.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type Env struct {
2424
var Default = Settings{
2525
ConfigPath: "/etc/containerd/config.toml",
2626
Setup: func(_ Env) error { return nil },
27-
Restarter: containerd.NewRestarter(),
27+
Restarter: containerd.NewDefaultRestarter(),
2828
}
2929

3030
func (s Settings) WithConfigPath(path string) Settings {
@@ -37,9 +37,16 @@ func (s Settings) WithSetup(setup func(env Env) error) Settings {
3737
return s
3838
}
3939

40-
var MicroK8s = Default.WithConfigPath("/var/snap/microk8s/current/args/containerd-template.toml")
40+
func (s Settings) WithRestarter(restarter containerd.Restarter) Settings {
41+
s.Restarter = restarter
42+
return s
43+
}
44+
45+
var MicroK8s = Default.WithConfigPath("/var/snap/microk8s/current/args/containerd-template.toml").
46+
WithRestarter(containerd.MicroK8sRestarter{})
4147

4248
var RKE2 = Default.WithConfigPath("/var/lib/rancher/rke2/agent/etc/containerd/config.toml.tmpl").
49+
WithRestarter(containerd.RKE2Restarter{}).
4350
WithSetup(func(env Env) error {
4451
_, err := env.HostFs.Stat(env.ConfigPath)
4552
if err == nil {
@@ -75,9 +82,11 @@ var RKE2 = Default.WithConfigPath("/var/lib/rancher/rke2/agent/etc/containerd/co
7582
return err
7683
})
7784

78-
var K3s = RKE2.WithConfigPath("/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl")
85+
var K3s = RKE2.WithConfigPath("/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl").
86+
WithRestarter(containerd.K3sRestarter{})
7987

8088
var K0s = Default.WithConfigPath("/etc/k0s/containerd.d/config.toml").
89+
WithRestarter(containerd.K0sRestarter{}).
8190
WithSetup(func(env Env) error {
8291
_, err := env.HostFs.Stat(env.ConfigPath)
8392
if err == nil {

0 commit comments

Comments
 (0)