podman

Форк
0
/
healthcheck_linux.go 
161 строка · 5.0 Кб
1
//go:build !remote && systemd
2

3
package libpod
4

5
import (
6
	"context"
7
	"fmt"
8
	"os"
9
	"os/exec"
10
	"strings"
11

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"
17
)
18

19
// createTimer systemd timers for healthchecks of a container
20
func (c *Container) createTimer(interval string, isStartup bool) error {
21
	if c.disableHealthCheckSystemd(isStartup) {
22
		return nil
23
	}
24
	podman, err := os.Executable()
25
	if err != nil {
26
		return fmt.Errorf("failed to get path for podman for a health check timer: %w", err)
27
	}
28

29
	var cmd = []string{"--property", "LogLevelMax=notice"}
30
	if rootless.IsRootless() {
31
		cmd = append(cmd, "--user")
32
	}
33
	path := os.Getenv("PATH")
34
	if path != "" {
35
		cmd = append(cmd, "--setenv=PATH="+path)
36
	}
37

38
	cmd = append(cmd, "--unit", c.hcUnitName(isStartup), fmt.Sprintf("--on-unit-inactive=%s", interval), "--timer-property=AccuracySec=1s", podman)
39

40
	if logrus.IsLevelEnabled(logrus.DebugLevel) {
41
		cmd = append(cmd, "--log-level=debug", "--syslog")
42
	}
43

44
	cmd = append(cmd, "healthcheck", "run", c.ID())
45

46
	conn, err := systemd.ConnectToDBUS()
47
	if err != nil {
48
		return fmt.Errorf("unable to get systemd connection to add healthchecks: %w", err)
49
	}
50
	conn.Close()
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)
55
	}
56
	return nil
57
}
58

59
// Wait for a message on the channel.  Throw an error if the message is not "done".
60
func systemdOpSuccessful(c chan string) error {
61
	msg := <-c
62
	switch msg {
63
	case "done":
64
		return nil
65
	default:
66
		return fmt.Errorf("expected %q but received %q", "done", msg)
67
	}
68
}
69

70
// startTimer starts a systemd timer for the healthchecks
71
func (c *Container) startTimer(isStartup bool) error {
72
	if c.disableHealthCheckSystemd(isStartup) {
73
		return nil
74
	}
75
	conn, err := systemd.ConnectToDBUS()
76
	if err != nil {
77
		return fmt.Errorf("unable to get systemd connection to start healthchecks: %w", err)
78
	}
79
	defer conn.Close()
80

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 {
84
		return err
85
	}
86
	if err := systemdOpSuccessful(startChan); err != nil {
87
		return fmt.Errorf("starting systemd health-check timer %q: %w", startFile, err)
88
	}
89

90
	return nil
91
}
92

93
// removeTransientFiles removes the systemd timer and unit files
94
// for the container
95
func (c *Container) removeTransientFiles(ctx context.Context, isStartup bool) error {
96
	if c.disableHealthCheckSystemd(isStartup) {
97
		return nil
98
	}
99
	conn, err := systemd.ConnectToDBUS()
100
	if err != nil {
101
		return fmt.Errorf("unable to get systemd connection to remove healthchecks: %w", err)
102
	}
103
	defer conn.Close()
104

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{}
108

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))
116
		}
117
	} else if err := systemdOpSuccessful(timerChan); err != nil {
118
		stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check timer %q: %w", timerFile, err))
119
	}
120

121
	// Reset the service before stopping it to make sure it's being removed
122
	// on stop.
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)
127
	}
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))
131
		}
132
	} else if err := systemdOpSuccessful(serviceChan); err != nil {
133
		stopErrors = append(stopErrors, fmt.Errorf("stopping systemd health-check service %q: %w", serviceFile, err))
134
	}
135

136
	return errorhandling.JoinErrors(stopErrors)
137
}
138

139
func (c *Container) disableHealthCheckSystemd(isStartup bool) bool {
140
	if !systemdCommon.RunsOnSystemd() || os.Getenv("DISABLE_HC_SYSTEMD") == "true" {
141
		return true
142
	}
143
	if isStartup {
144
		if c.config.StartupHealthCheckConfig.Interval == 0 {
145
			return true
146
		}
147
	}
148
	if c.config.HealthCheckConfig.Interval == 0 {
149
		return true
150
	}
151
	return false
152
}
153

154
// Systemd unit name for the healthcheck systemd unit
155
func (c *Container) hcUnitName(isStartup bool) string {
156
	unitName := c.ID()
157
	if isStartup {
158
		unitName += "-startup"
159
	}
160
	return unitName
161
}
162

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.