1
//go:build !remote && systemd
12
systemdCommon "github.com/containers/common/pkg/systemd"
13
"github.com/containers/podman/v5/pkg/errorhandling"
14
"github.com/containers/podman/v5/pkg/rootless"
15
"github.com/containers/podman/v5/pkg/systemd"
16
"github.com/sirupsen/logrus"
19
// createTimer systemd timers for healthchecks of a container
20
func (c *Container) createTimer(interval string, isStartup bool) error {
21
if c.disableHealthCheckSystemd(isStartup) {
24
podman, err := os.Executable()
26
return fmt.Errorf("failed to get path for podman for a health check timer: %w", err)
29
var cmd = []string{"--property", "LogLevelMax=notice"}
30
if rootless.IsRootless() {
31
cmd = append(cmd, "--user")
33
path := os.Getenv("PATH")
35
cmd = append(cmd, "--setenv=PATH="+path)
38
cmd = append(cmd, "--unit", c.hcUnitName(isStartup), fmt.Sprintf("--on-unit-inactive=%s", interval), "--timer-property=AccuracySec=1s", podman)
40
if logrus.IsLevelEnabled(logrus.DebugLevel) {
41
cmd = append(cmd, "--log-level=debug", "--syslog")
44
cmd = append(cmd, "healthcheck", "run", c.ID())
46
conn, err := systemd.ConnectToDBUS()
48
return fmt.Errorf("unable to get systemd connection to add healthchecks: %w", err)
51
logrus.Debugf("creating systemd-transient files: %s %s", "systemd-run", cmd)
52
systemdRun := exec.Command("systemd-run", cmd...)
53
if output, err := systemdRun.CombinedOutput(); err != nil {
54
return fmt.Errorf("%s", output)
59
// Wait for a message on the channel. Throw an error if the message is not "done".
60
func systemdOpSuccessful(c chan string) error {
66
return fmt.Errorf("expected %q but received %q", "done", msg)
70
// startTimer starts a systemd timer for the healthchecks
71
func (c *Container) startTimer(isStartup bool) error {
72
if c.disableHealthCheckSystemd(isStartup) {
75
conn, err := systemd.ConnectToDBUS()
77
return fmt.Errorf("unable to get systemd connection to start healthchecks: %w", err)
81
startFile := fmt.Sprintf("%s.service", c.hcUnitName(isStartup))
82
startChan := make(chan string)
83
if _, err := conn.RestartUnitContext(context.Background(), startFile, "fail", startChan); err != nil {
86
if err := systemdOpSuccessful(startChan); err != nil {
87
return fmt.Errorf("starting systemd health-check timer %q: %w", startFile, err)
93
// removeTransientFiles removes the systemd timer and unit files
95
func (c *Container) removeTransientFiles(ctx context.Context, isStartup bool) error {
96
if c.disableHealthCheckSystemd(isStartup) {
99
conn, err := systemd.ConnectToDBUS()
101
return fmt.Errorf("unable to get systemd connection to remove healthchecks: %w", err)
105
// Errors are returned at the very end. Let's make sure to stop and
106
// clean up as much as possible.
107
stopErrors := []error{}
109
// Stop the timer before the service to make sure the timer does not
110
// fire after the service is stopped.
111
timerChan := make(chan string)
112
timerFile := fmt.Sprintf("%s.timer", c.hcUnitName(isStartup))
113
if _, err := conn.StopUnitContext(ctx, timerFile, "ignore-dependencies", timerChan); err != nil {
114
if !strings.HasSuffix(err.Error(), ".timer not loaded.") {
115
stopErrors = append(stopErrors, fmt.Errorf("removing health-check timer %q: %w", timerFile, err))
117
} else if err := systemdOpSuccessful(timerChan); err != nil {
118
stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check timer %q: %w", timerFile, err))
121
// Reset the service before stopping it to make sure it's being removed
123
serviceChan := make(chan string)
124
serviceFile := fmt.Sprintf("%s.service", c.hcUnitName(isStartup))
125
if err := conn.ResetFailedUnitContext(ctx, serviceFile); err != nil {
126
logrus.Debugf("Failed to reset unit file: %q", err)
128
if _, err := conn.StopUnitContext(ctx, serviceFile, "ignore-dependencies", serviceChan); err != nil {
129
if !strings.HasSuffix(err.Error(), ".service not loaded.") {
130
stopErrors = append(stopErrors, fmt.Errorf("removing health-check service %q: %w", serviceFile, err))
132
} else if err := systemdOpSuccessful(serviceChan); err != nil {
133
stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check service %q: %w", serviceFile, err))
136
return errorhandling.JoinErrors(stopErrors)
139
func (c *Container) disableHealthCheckSystemd(isStartup bool) bool {
140
if !systemdCommon.RunsOnSystemd() || os.Getenv("DISABLE_HC_SYSTEMD") == "true" {
144
if c.config.StartupHealthCheckConfig.Interval == 0 {
148
if c.config.HealthCheckConfig.Interval == 0 {
154
// Systemd unit name for the healthcheck systemd unit
155
func (c *Container) hcUnitName(isStartup bool) string {
158
unitName += "-startup"