mirror of
https://github.com/containers/podman.git
synced 2025-05-20 00:27:03 +08:00
Merge pull request #24216 from Honny1/dev/jrodak/healthcheck-log
[v5.2-rhel] Add --health-max-log-count, --health-max-log-size, --health-log-destination flags
This commit is contained in:
@ -184,6 +184,30 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||||||
)
|
)
|
||||||
_ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone)
|
_ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
|
healthLogDestinationFlagName := "health-log-destination"
|
||||||
|
createFlags.StringVar(
|
||||||
|
&cf.HealthLogDestination,
|
||||||
|
healthLogDestinationFlagName, define.DefaultHealthCheckLocalDestination,
|
||||||
|
"set the destination of the HealthCheck log. Directory path, local or events_logger (local use container state file)",
|
||||||
|
)
|
||||||
|
_ = cmd.RegisterFlagCompletionFunc(healthLogDestinationFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
|
healthMaxLogCountFlagName := "health-max-log-count"
|
||||||
|
createFlags.UintVar(
|
||||||
|
&cf.HealthMaxLogCount,
|
||||||
|
healthMaxLogCountFlagName, define.DefaultHealthMaxLogCount,
|
||||||
|
"set maximum number of attempts in the HealthCheck log file. ('0' value means an infinite number of attempts in the log file)",
|
||||||
|
)
|
||||||
|
_ = cmd.RegisterFlagCompletionFunc(healthMaxLogCountFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
|
healthMaxLogSizeFlagName := "health-max-log-size"
|
||||||
|
createFlags.UintVar(
|
||||||
|
&cf.HealthMaxLogSize,
|
||||||
|
healthMaxLogSizeFlagName, define.DefaultHealthMaxLogSize,
|
||||||
|
"set maximum length in characters of stored HealthCheck log. ('0' value means an infinite log length)",
|
||||||
|
)
|
||||||
|
_ = cmd.RegisterFlagCompletionFunc(healthMaxLogSizeFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
healthRetriesFlagName := "health-retries"
|
healthRetriesFlagName := "health-retries"
|
||||||
createFlags.UintVar(
|
createFlags.UintVar(
|
||||||
&cf.HealthRetries,
|
&cf.HealthRetries,
|
||||||
|
@ -85,4 +85,7 @@ func DefineCreateDefaults(opts *entities.ContainerCreateOptions) {
|
|||||||
opts.Ulimit = ulimits()
|
opts.Ulimit = ulimits()
|
||||||
opts.SeccompPolicy = "default"
|
opts.SeccompPolicy = "default"
|
||||||
opts.Volume = volumes()
|
opts.Volume = volumes()
|
||||||
|
opts.HealthLogDestination = define.DefaultHealthCheckLocalDestination
|
||||||
|
opts.HealthMaxLogCount = define.DefaultHealthMaxLogCount
|
||||||
|
opts.HealthMaxLogSize = define.DefaultHealthMaxLogSize
|
||||||
}
|
}
|
||||||
|
11
docs/source/markdown/options/health-log-destination.md
Normal file
11
docs/source/markdown/options/health-log-destination.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
####> This option file is used in:
|
||||||
|
####> podman create, run
|
||||||
|
####> If file is edited, make sure the changes
|
||||||
|
####> are applicable to all of those.
|
||||||
|
#### **--health-log-destination**=*directory_path*
|
||||||
|
|
||||||
|
Set the destination of the HealthCheck log. Directory path, local or events_logger (local use container state file) (Default: local)
|
||||||
|
|
||||||
|
* `local`: (default) HealthCheck logs are stored in overlay containers. (For example: `$runroot/healthcheck.log`)
|
||||||
|
* `directory`: creates a log file named `<container-ID>-healthcheck.log` with HealthCheck logs in the specified directory.
|
||||||
|
* `events_logger`: The log will be written with logging mechanism set by events_logger. It also saves the log to a default directory, for performance on a system with a large number of logs.
|
7
docs/source/markdown/options/health-max-log-count.md
Normal file
7
docs/source/markdown/options/health-max-log-count.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
####> This option file is used in:
|
||||||
|
####> podman create, run
|
||||||
|
####> If file is edited, make sure the changes
|
||||||
|
####> are applicable to all of those.
|
||||||
|
#### **--health-max-log-count**=*number of stored logs*
|
||||||
|
|
||||||
|
Set maximum number of attempts in the HealthCheck log file. ('0' value means an infinite number of attempts in the log file) (Default: 5 attempts)
|
7
docs/source/markdown/options/health-max-log-size.md
Normal file
7
docs/source/markdown/options/health-max-log-size.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
####> This option file is used in:
|
||||||
|
####> podman create, run
|
||||||
|
####> If file is edited, make sure the changes
|
||||||
|
####> are applicable to all of those.
|
||||||
|
#### **--health-max-log-size**=*size of stored logs*
|
||||||
|
|
||||||
|
Set maximum length in characters of stored HealthCheck log. ("0" value means an infinite log length) (Default: 500 characters)
|
@ -169,6 +169,12 @@ See [**Environment**](#environment) note below for precedence and examples.
|
|||||||
|
|
||||||
@@option health-interval
|
@@option health-interval
|
||||||
|
|
||||||
|
@@option health-log-destination
|
||||||
|
|
||||||
|
@@option health-max-log-count
|
||||||
|
|
||||||
|
@@option health-max-log-size
|
||||||
|
|
||||||
@@option health-on-failure
|
@@option health-on-failure
|
||||||
|
|
||||||
@@option health-retries
|
@@option health-retries
|
||||||
|
@ -203,6 +203,12 @@ See [**Environment**](#environment) note below for precedence and examples.
|
|||||||
|
|
||||||
@@option health-interval
|
@@option health-interval
|
||||||
|
|
||||||
|
@@option health-log-destination
|
||||||
|
|
||||||
|
@@option health-max-log-count
|
||||||
|
|
||||||
|
@@option health-max-log-size
|
||||||
|
|
||||||
@@option health-on-failure
|
@@option health-on-failure
|
||||||
|
|
||||||
@@option health-retries
|
@@option health-retries
|
||||||
|
@ -267,6 +267,9 @@ Valid options for `[Container]` are listed below:
|
|||||||
| GroupAdd=keep-groups | --group-add=keep-groups |
|
| GroupAdd=keep-groups | --group-add=keep-groups |
|
||||||
| HealthCmd=/usr/bin/command | --health-cmd=/usr/bin/command |
|
| HealthCmd=/usr/bin/command | --health-cmd=/usr/bin/command |
|
||||||
| HealthInterval=2m | --health-interval=2m |
|
| HealthInterval=2m | --health-interval=2m |
|
||||||
|
| HealthLogDestination=/foo/log | --health-log-destination=/foo/log |
|
||||||
|
| HealthMaxLogCount=5 | --health-max-log-count=5 |
|
||||||
|
| HealthMaxLogSize=500 | --health-max-log-size=500 |
|
||||||
| HealthOnFailure=kill | --health-on-failure=kill |
|
| HealthOnFailure=kill | --health-on-failure=kill |
|
||||||
| HealthRetries=5 | --health-retries=5 |
|
| HealthRetries=5 | --health-retries=5 |
|
||||||
| HealthStartPeriod=1m | --health-start-period=period=1m |
|
| HealthStartPeriod=1m | --health-start-period=period=1m |
|
||||||
@ -475,6 +478,28 @@ Equivalent to the Podman `--health-cmd` option.
|
|||||||
Set an interval for the healthchecks. An interval of disable results in no automatic timer setup.
|
Set an interval for the healthchecks. An interval of disable results in no automatic timer setup.
|
||||||
Equivalent to the Podman `--health-interval` option.
|
Equivalent to the Podman `--health-interval` option.
|
||||||
|
|
||||||
|
### `HealthLogDestination=`
|
||||||
|
|
||||||
|
Set the destination of the HealthCheck log. Directory path, local or events_logger (local use container state file)
|
||||||
|
(Default: local)
|
||||||
|
Equivalent to the Podman `--health-log-destination` option.
|
||||||
|
|
||||||
|
* `local`: (default) HealthCheck logs are stored in overlay containers. (For example: `$runroot/healthcheck.log`)
|
||||||
|
* `directory`: creates a log file named `<container-ID>-healthcheck.log` with HealthCheck logs in the specified directory.
|
||||||
|
* `events_logger`: The log will be written with logging mechanism set by events_logger. It also saves the log to a default directory, for performance on a system with a large number of logs.
|
||||||
|
|
||||||
|
### `HealthMaxLogCount=`
|
||||||
|
|
||||||
|
Set maximum number of attempts in the HealthCheck log file. ('0' value means an infinite number of attempts in the log file)
|
||||||
|
(Default: 5 attempts)
|
||||||
|
Equivalent to the Podman `--Health-max-log-count` option.
|
||||||
|
|
||||||
|
### `HealthMaxLogSize=`
|
||||||
|
|
||||||
|
Set maximum length in characters of stored HealthCheck log. ("0" value means an infinite log length)
|
||||||
|
(Default: 500 characters)
|
||||||
|
Equivalent to the Podman `--Health-max-log-size` option.
|
||||||
|
|
||||||
### `HealthOnFailure=`
|
### `HealthOnFailure=`
|
||||||
|
|
||||||
Action to take once the container transitions to an unhealthy state.
|
Action to take once the container transitions to an unhealthy state.
|
||||||
|
@ -413,6 +413,14 @@ type ContainerMiscConfig struct {
|
|||||||
HealthCheckConfig *manifest.Schema2HealthConfig `json:"healthcheck"`
|
HealthCheckConfig *manifest.Schema2HealthConfig `json:"healthcheck"`
|
||||||
// HealthCheckOnFailureAction defines an action to take once the container turns unhealthy.
|
// HealthCheckOnFailureAction defines an action to take once the container turns unhealthy.
|
||||||
HealthCheckOnFailureAction define.HealthCheckOnFailureAction `json:"healthcheck_on_failure_action"`
|
HealthCheckOnFailureAction define.HealthCheckOnFailureAction `json:"healthcheck_on_failure_action"`
|
||||||
|
// HealthLogDestination defines the destination where the log is stored
|
||||||
|
HealthLogDestination string `json:"healthLogDestination,omitempty"`
|
||||||
|
// HealthMaxLogCount is maximum number of attempts in the HealthCheck log file.
|
||||||
|
// ('0' value means an infinite number of attempts in the log file)
|
||||||
|
HealthMaxLogCount uint `json:"healthMaxLogCount,omitempty"`
|
||||||
|
// HealthMaxLogSize is the maximum length in characters of stored HealthCheck log
|
||||||
|
// ("0" value means an infinite log length)
|
||||||
|
HealthMaxLogSize uint `json:"healthMaxLogSize,omitempty"`
|
||||||
// StartupHealthCheckConfig is the configuration of the startup
|
// StartupHealthCheckConfig is the configuration of the startup
|
||||||
// healthcheck for the container. This will run before the regular HC
|
// healthcheck for the container. This will run before the regular HC
|
||||||
// runs, and when it passes the regular HC will be activated.
|
// runs, and when it passes the regular HC will be activated.
|
||||||
|
@ -195,7 +195,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
|
|||||||
// inspect status should be set to nil.
|
// inspect status should be set to nil.
|
||||||
if c.config.HealthCheckConfig != nil && !(len(c.config.HealthCheckConfig.Test) == 1 && c.config.HealthCheckConfig.Test[0] == "NONE") {
|
if c.config.HealthCheckConfig != nil && !(len(c.config.HealthCheckConfig.Test) == 1 && c.config.HealthCheckConfig.Test[0] == "NONE") {
|
||||||
// This container has a healthcheck defined in it; we need to add its state
|
// This container has a healthcheck defined in it; we need to add its state
|
||||||
healthCheckState, err := c.getHealthCheckLog()
|
healthCheckState, err := c.readHealthCheckLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// An error here is not considered fatal; no health state will be displayed
|
// An error here is not considered fatal; no health state will be displayed
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
@ -417,6 +417,12 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
|
|||||||
|
|
||||||
ctrConfig.HealthcheckOnFailureAction = c.config.HealthCheckOnFailureAction.String()
|
ctrConfig.HealthcheckOnFailureAction = c.config.HealthCheckOnFailureAction.String()
|
||||||
|
|
||||||
|
ctrConfig.HealthLogDestination = c.config.HealthLogDestination
|
||||||
|
|
||||||
|
ctrConfig.HealthMaxLogCount = c.config.HealthMaxLogCount
|
||||||
|
|
||||||
|
ctrConfig.HealthMaxLogSize = c.config.HealthMaxLogSize
|
||||||
|
|
||||||
ctrConfig.CreateCommand = c.config.CreateCommand
|
ctrConfig.CreateCommand = c.config.CreateCommand
|
||||||
|
|
||||||
ctrConfig.Timezone = c.config.Timezone
|
ctrConfig.Timezone = c.config.Timezone
|
||||||
|
@ -1123,10 +1123,9 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
|
|||||||
// bugzilla.redhat.com/show_bug.cgi?id=2144754:
|
// bugzilla.redhat.com/show_bug.cgi?id=2144754:
|
||||||
// In case of a restart, make sure to remove the healthcheck log to
|
// In case of a restart, make sure to remove the healthcheck log to
|
||||||
// have a clean state.
|
// have a clean state.
|
||||||
if path := c.healthCheckLogPath(); path != "" {
|
err = c.writeHealthCheckLog(define.HealthCheckResults{Status: define.HealthCheckReset})
|
||||||
if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
if err != nil {
|
||||||
logrus.Error(err)
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.save(); err != nil {
|
if err := c.save(); err != nil {
|
||||||
|
@ -61,6 +61,14 @@ type InspectContainerConfig struct {
|
|||||||
Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
|
Healthcheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"`
|
||||||
// HealthcheckOnFailureAction defines an action to take once the container turns unhealthy.
|
// HealthcheckOnFailureAction defines an action to take once the container turns unhealthy.
|
||||||
HealthcheckOnFailureAction string `json:"HealthcheckOnFailureAction,omitempty"`
|
HealthcheckOnFailureAction string `json:"HealthcheckOnFailureAction,omitempty"`
|
||||||
|
// HealthLogDestination defines the destination where the log is stored
|
||||||
|
HealthLogDestination string `json:"HealthLogDestination,omitempty"`
|
||||||
|
// HealthMaxLogCount is maximum number of attempts in the HealthCheck log file.
|
||||||
|
// ('0' value means an infinite number of attempts in the log file)
|
||||||
|
HealthMaxLogCount uint `json:"HealthcheckMaxLogCount,omitempty"`
|
||||||
|
// HealthMaxLogSize is the maximum length in characters of stored HealthCheck log
|
||||||
|
// ("0" value means an infinite log length)
|
||||||
|
HealthMaxLogSize uint `json:"HealthcheckMaxLogSize,omitempty"`
|
||||||
// CreateCommand is the full command plus arguments of the process the
|
// CreateCommand is the full command plus arguments of the process the
|
||||||
// container has been created with.
|
// container has been created with.
|
||||||
CreateCommand []string `json:"CreateCommand,omitempty"`
|
CreateCommand []string `json:"CreateCommand,omitempty"`
|
||||||
|
@ -16,6 +16,8 @@ const (
|
|||||||
// and the start-period (time allowed for the container to start and application
|
// and the start-period (time allowed for the container to start and application
|
||||||
// to be running) expires.
|
// to be running) expires.
|
||||||
HealthCheckStarting string = "starting"
|
HealthCheckStarting string = "starting"
|
||||||
|
// HealthCheckReset describes reset of HealthCheck logs
|
||||||
|
HealthCheckReset string = "reset"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HealthCheckStatus represents the current state of a container
|
// HealthCheckStatus represents the current state of a container
|
||||||
@ -56,8 +58,16 @@ const (
|
|||||||
DefaultHealthCheckStartPeriod = "0s"
|
DefaultHealthCheckStartPeriod = "0s"
|
||||||
// DefaultHealthCheckTimeout default value
|
// DefaultHealthCheckTimeout default value
|
||||||
DefaultHealthCheckTimeout = "30s"
|
DefaultHealthCheckTimeout = "30s"
|
||||||
|
// DefaultHealthMaxLogCount default value
|
||||||
|
DefaultHealthMaxLogCount uint = 5
|
||||||
|
// DefaultHealthMaxLogSize default value
|
||||||
|
DefaultHealthMaxLogSize uint = 500
|
||||||
|
// DefaultHealthCheckLocalDestination default value
|
||||||
|
DefaultHealthCheckLocalDestination string = "local"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const HealthCheckEventsLoggerDestination string = "events_logger"
|
||||||
|
|
||||||
// HealthConfig.Test options
|
// HealthConfig.Test options
|
||||||
const (
|
const (
|
||||||
// HealthConfigTestNone disables healthcheck
|
// HealthConfigTestNone disables healthcheck
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/libpod/define"
|
||||||
"github.com/containers/podman/v5/libpod/events"
|
"github.com/containers/podman/v5/libpod/events"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -28,27 +29,37 @@ func (r *Runtime) newEventer() (events.Eventer, error) {
|
|||||||
|
|
||||||
// newContainerEvent creates a new event based on a container
|
// newContainerEvent creates a new event based on a container
|
||||||
func (c *Container) newContainerEvent(status events.Status) {
|
func (c *Container) newContainerEvent(status events.Status) {
|
||||||
if err := c.newContainerEventWithInspectData(status, "", false); err != nil {
|
if err := c.newContainerEventWithInspectData(status, define.HealthCheckResults{}, false); err != nil {
|
||||||
logrus.Errorf("Unable to write container event: %v", err)
|
logrus.Errorf("Unable to write container event: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newContainerHealthCheckEvent creates a new healthcheck event with the given status
|
// newContainerHealthCheckEvent creates a new healthcheck event with the given status
|
||||||
func (c *Container) newContainerHealthCheckEvent(healthStatus string) {
|
func (c *Container) newContainerHealthCheckEvent(healthCheckResult define.HealthCheckResults) {
|
||||||
if err := c.newContainerEventWithInspectData(events.HealthStatus, healthStatus, false); err != nil {
|
if err := c.newContainerEventWithInspectData(events.HealthStatus, healthCheckResult, false); err != nil {
|
||||||
logrus.Errorf("Unable to write container event: %v", err)
|
logrus.Errorf("Unable to write container event: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newContainerEventWithInspectData creates a new event and sets the
|
// newContainerEventWithInspectData creates a new event and sets the
|
||||||
// ContainerInspectData field if inspectData is set.
|
// ContainerInspectData field if inspectData is set.
|
||||||
func (c *Container) newContainerEventWithInspectData(status events.Status, healthStatus string, inspectData bool) error {
|
func (c *Container) newContainerEventWithInspectData(status events.Status, healthCheckResult define.HealthCheckResults, inspectData bool) error {
|
||||||
e := events.NewEvent(status)
|
e := events.NewEvent(status)
|
||||||
e.ID = c.ID()
|
e.ID = c.ID()
|
||||||
e.Name = c.Name()
|
e.Name = c.Name()
|
||||||
e.Image = c.config.RootfsImageName
|
e.Image = c.config.RootfsImageName
|
||||||
e.Type = events.Container
|
e.Type = events.Container
|
||||||
e.HealthStatus = healthStatus
|
e.HealthStatus = healthCheckResult.Status
|
||||||
|
if c.config.HealthLogDestination == define.HealthCheckEventsLoggerDestination {
|
||||||
|
if len(healthCheckResult.Log) > 0 {
|
||||||
|
logData, err := json.Marshal(healthCheckResult.Log[len(healthCheckResult.Log)-1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to marshall healthcheck log for writing: %w", err)
|
||||||
|
}
|
||||||
|
e.HealthLog = string(logData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.HealthFailingStreak = healthCheckResult.FailingStreak
|
||||||
|
|
||||||
e.Details = events.Details{
|
e.Details = events.Details{
|
||||||
PodID: c.PodID(),
|
PodID: c.PodID(),
|
||||||
|
@ -41,6 +41,10 @@ type Event struct {
|
|||||||
Type Type
|
Type Type
|
||||||
// Health status of the current container
|
// Health status of the current container
|
||||||
HealthStatus string `json:"health_status,omitempty"`
|
HealthStatus string `json:"health_status,omitempty"`
|
||||||
|
// Healthcheck log of the current container
|
||||||
|
HealthLog string `json:"health_log,omitempty"`
|
||||||
|
// HealthFailingStreak log of the current container
|
||||||
|
HealthFailingStreak int `json:"health_failing_streak,omitempty"`
|
||||||
// Error code for certain events involving errors.
|
// Error code for certain events involving errors.
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
|
|
||||||
|
@ -76,8 +76,10 @@ func (e *Event) ToHumanReadable(truncate bool) string {
|
|||||||
if e.PodID != "" {
|
if e.PodID != "" {
|
||||||
humanFormat += fmt.Sprintf(", pod_id=%s", e.PodID)
|
humanFormat += fmt.Sprintf(", pod_id=%s", e.PodID)
|
||||||
}
|
}
|
||||||
if e.HealthStatus != "" {
|
if e.Status == HealthStatus {
|
||||||
humanFormat += fmt.Sprintf(", health_status=%s", e.HealthStatus)
|
humanFormat += fmt.Sprintf(", health_status=%s", e.HealthStatus)
|
||||||
|
humanFormat += fmt.Sprintf(", health_failing_streak=%d", e.HealthFailingStreak)
|
||||||
|
humanFormat += fmt.Sprintf(", health_log=%s", e.HealthLog)
|
||||||
}
|
}
|
||||||
// check if the container has labels and add it to the output
|
// check if the container has labels and add it to the output
|
||||||
if len(e.Attributes) > 0 {
|
if len(e.Attributes) > 0 {
|
||||||
|
@ -65,8 +65,13 @@ func (e EventJournalD) Write(ee Event) error {
|
|||||||
}
|
}
|
||||||
m["PODMAN_LABELS"] = string(b)
|
m["PODMAN_LABELS"] = string(b)
|
||||||
}
|
}
|
||||||
m["PODMAN_HEALTH_STATUS"] = ee.HealthStatus
|
if ee.Status == HealthStatus {
|
||||||
|
m["PODMAN_HEALTH_STATUS"] = ee.HealthStatus
|
||||||
|
if ee.HealthLog != "" {
|
||||||
|
m["PODMAN_HEALTH_LOG"] = ee.HealthLog
|
||||||
|
}
|
||||||
|
m["PODMAN_HEALTH_FAILING_STREAK"] = strconv.Itoa(ee.HealthFailingStreak)
|
||||||
|
}
|
||||||
if len(ee.Details.ContainerInspectData) > 0 {
|
if len(ee.Details.ContainerInspectData) > 0 {
|
||||||
m["PODMAN_CONTAINER_INSPECT_DATA"] = ee.Details.ContainerInspectData
|
m["PODMAN_CONTAINER_INSPECT_DATA"] = ee.Details.ContainerInspectData
|
||||||
}
|
}
|
||||||
@ -225,6 +230,15 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
newEvent.HealthStatus = entry.Fields["PODMAN_HEALTH_STATUS"]
|
newEvent.HealthStatus = entry.Fields["PODMAN_HEALTH_STATUS"]
|
||||||
|
if log, ok := entry.Fields["PODMAN_HEALTH_LOG"]; ok {
|
||||||
|
newEvent.HealthLog = log
|
||||||
|
}
|
||||||
|
if FailingStreak, ok := entry.Fields["PODMAN_HEALTH_FAILING_STREAK"]; ok {
|
||||||
|
FailingStreakInt, err := strconv.Atoi(FailingStreak)
|
||||||
|
if err == nil {
|
||||||
|
newEvent.HealthFailingStreak = FailingStreakInt
|
||||||
|
}
|
||||||
|
}
|
||||||
newEvent.Details.ContainerInspectData = entry.Fields["PODMAN_CONTAINER_INSPECT_DATA"]
|
newEvent.Details.ContainerInspectData = entry.Fields["PODMAN_CONTAINER_INSPECT_DATA"]
|
||||||
case Network:
|
case Network:
|
||||||
newEvent.ID = entry.Fields["PODMAN_ID"]
|
newEvent.ID = entry.Fields["PODMAN_ID"]
|
||||||
|
@ -19,14 +19,6 @@ import (
|
|||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxHealthCheckNumberLogs is the maximum number of attempts we keep
|
|
||||||
// in the healthcheck history file
|
|
||||||
MaxHealthCheckNumberLogs int = 5
|
|
||||||
// MaxHealthCheckLogLength in characters
|
|
||||||
MaxHealthCheckLogLength = 500
|
|
||||||
)
|
|
||||||
|
|
||||||
// HealthCheck verifies the state and validity of the healthcheck configuration
|
// HealthCheck verifies the state and validity of the healthcheck configuration
|
||||||
// on the container and then executes the healthcheck
|
// on the container and then executes the healthcheck
|
||||||
func (r *Runtime) HealthCheck(ctx context.Context, name string) (define.HealthCheckStatus, error) {
|
func (r *Runtime) HealthCheck(ctx context.Context, name string) (define.HealthCheckStatus, error) {
|
||||||
@ -143,8 +135,8 @@ func (c *Container) runHealthCheck(ctx context.Context, isStartup bool) (define.
|
|||||||
}
|
}
|
||||||
|
|
||||||
eventLog := output.String()
|
eventLog := output.String()
|
||||||
if len(eventLog) > MaxHealthCheckLogLength {
|
if c.config.HealthMaxLogSize != 0 && len(eventLog) > int(c.config.HealthMaxLogSize) {
|
||||||
eventLog = eventLog[:MaxHealthCheckLogLength]
|
eventLog = eventLog[:c.config.HealthMaxLogSize]
|
||||||
}
|
}
|
||||||
|
|
||||||
if timeEnd.Sub(timeStart) > c.HealthCheckConfig().Timeout {
|
if timeEnd.Sub(timeStart) > c.HealthCheckConfig().Timeout {
|
||||||
@ -154,21 +146,22 @@ func (c *Container) runHealthCheck(ctx context.Context, isStartup bool) (define.
|
|||||||
}
|
}
|
||||||
|
|
||||||
hcl := newHealthCheckLog(timeStart, timeEnd, returnCode, eventLog)
|
hcl := newHealthCheckLog(timeStart, timeEnd, returnCode, eventLog)
|
||||||
logStatus, err := c.updateHealthCheckLog(hcl, inStartPeriod, isStartup)
|
|
||||||
|
healthCheckResult, err := c.updateHealthCheckLog(hcl, inStartPeriod, isStartup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hcResult, "", fmt.Errorf("unable to update health check log %s for %s: %w", c.healthCheckLogPath(), c.ID(), err)
|
return hcResult, "", fmt.Errorf("unable to update health check log %s for %s: %w", c.config.HealthLogDestination, c.ID(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write HC event with appropriate status as the last thing before we
|
// Write HC event with appropriate status as the last thing before we
|
||||||
// return.
|
// return.
|
||||||
if hcResult == define.HealthCheckNotDefined || hcResult == define.HealthCheckInternalError {
|
if hcResult == define.HealthCheckNotDefined || hcResult == define.HealthCheckInternalError {
|
||||||
return hcResult, logStatus, hcErr
|
return hcResult, healthCheckResult.Status, hcErr
|
||||||
}
|
}
|
||||||
if c.runtime.config.Engine.HealthcheckEvents {
|
if c.runtime.config.Engine.HealthcheckEvents {
|
||||||
c.newContainerHealthCheckEvent(logStatus)
|
c.newContainerHealthCheckEvent(healthCheckResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
return hcResult, logStatus, hcErr
|
return hcResult, healthCheckResult.Status, hcErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) processHealthCheckStatus(status string) error {
|
func (c *Container) processHealthCheckStatus(status string) error {
|
||||||
@ -340,16 +333,12 @@ func newHealthCheckLog(start, end time.Time, exitCode int, log string) define.He
|
|||||||
// updateHealthStatus updates the health status of the container
|
// updateHealthStatus updates the health status of the container
|
||||||
// in the healthcheck log
|
// in the healthcheck log
|
||||||
func (c *Container) updateHealthStatus(status string) error {
|
func (c *Container) updateHealthStatus(status string) error {
|
||||||
healthCheck, err := c.getHealthCheckLog()
|
healthCheck, err := c.readHealthCheckLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
healthCheck.Status = status
|
healthCheck.Status = status
|
||||||
newResults, err := json.Marshal(healthCheck)
|
return c.writeHealthCheckLog(healthCheck)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to marshall healthchecks for writing status: %w", err)
|
|
||||||
}
|
|
||||||
return os.WriteFile(c.healthCheckLogPath(), newResults, 0700)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isUnhealthy returns true if the current health check status is unhealthy.
|
// isUnhealthy returns true if the current health check status is unhealthy.
|
||||||
@ -357,7 +346,7 @@ func (c *Container) isUnhealthy() (bool, error) {
|
|||||||
if !c.HasHealthCheck() {
|
if !c.HasHealthCheck() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
healthCheck, err := c.getHealthCheckLog()
|
healthCheck, err := c.readHealthCheckLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -365,7 +354,7 @@ func (c *Container) isUnhealthy() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateHealthCheckLog parses the health check results and writes the log
|
// UpdateHealthCheckLog parses the health check results and writes the log
|
||||||
func (c *Container) updateHealthCheckLog(hcl define.HealthCheckLog, inStartPeriod, isStartup bool) (string, error) {
|
func (c *Container) updateHealthCheckLog(hcl define.HealthCheckLog, inStartPeriod, isStartup bool) (define.HealthCheckResults, error) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
@ -373,12 +362,12 @@ func (c *Container) updateHealthCheckLog(hcl define.HealthCheckLog, inStartPerio
|
|||||||
// both failing and succeeding cases to match kube behavior.
|
// both failing and succeeding cases to match kube behavior.
|
||||||
// So don't update the health check log till the start period is over
|
// So don't update the health check log till the start period is over
|
||||||
if _, ok := c.config.Spec.Annotations[define.KubeHealthCheckAnnotation]; ok && inStartPeriod && !isStartup {
|
if _, ok := c.config.Spec.Annotations[define.KubeHealthCheckAnnotation]; ok && inStartPeriod && !isStartup {
|
||||||
return "", nil
|
return define.HealthCheckResults{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
healthCheck, err := c.getHealthCheckLog()
|
healthCheck, err := c.readHealthCheckLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return define.HealthCheckResults{}, err
|
||||||
}
|
}
|
||||||
if hcl.ExitCode == 0 {
|
if hcl.ExitCode == 0 {
|
||||||
// set status to healthy, reset failing state to 0
|
// set status to healthy, reset failing state to 0
|
||||||
@ -398,28 +387,48 @@ func (c *Container) updateHealthCheckLog(hcl define.HealthCheckLog, inStartPerio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
healthCheck.Log = append(healthCheck.Log, hcl)
|
healthCheck.Log = append(healthCheck.Log, hcl)
|
||||||
if len(healthCheck.Log) > MaxHealthCheckNumberLogs {
|
if c.config.HealthMaxLogCount != 0 && len(healthCheck.Log) > int(c.config.HealthMaxLogCount) {
|
||||||
healthCheck.Log = healthCheck.Log[1:]
|
healthCheck.Log = healthCheck.Log[1:]
|
||||||
}
|
}
|
||||||
newResults, err := json.Marshal(healthCheck)
|
return healthCheck, c.writeHealthCheckLog(healthCheck)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) witeToFileHealthCheckResults(path string, result define.HealthCheckResults) error {
|
||||||
|
newResults, err := json.Marshal(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to marshall healthchecks for writing: %w", err)
|
return fmt.Errorf("unable to marshall healthchecks for writing: %w", err)
|
||||||
}
|
}
|
||||||
return healthCheck.Status, os.WriteFile(c.healthCheckLogPath(), newResults, 0700)
|
return os.WriteFile(path, newResults, 0700)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealthCheckLogPath returns the path for where the health check log is
|
func (c *Container) getHealthCheckLogDestination() string {
|
||||||
func (c *Container) healthCheckLogPath() string {
|
var destination string
|
||||||
return filepath.Join(filepath.Dir(c.state.RunDir), "healthcheck.log")
|
switch c.config.HealthLogDestination {
|
||||||
|
case define.DefaultHealthCheckLocalDestination, define.HealthCheckEventsLoggerDestination, "":
|
||||||
|
destination = filepath.Join(filepath.Dir(c.state.RunDir), "healthcheck.log")
|
||||||
|
default:
|
||||||
|
destination = filepath.Join(c.config.HealthLogDestination, c.ID()+"-healthcheck.log")
|
||||||
|
}
|
||||||
|
return destination
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHealthCheckLog returns HealthCheck results by reading the container's
|
func (c *Container) writeHealthCheckLog(result define.HealthCheckResults) error {
|
||||||
|
return c.witeToFileHealthCheckResults(c.getHealthCheckLogDestination(), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readHealthCheckLog read HealthCheck logs from the path or events_logger
|
||||||
|
// The caller should lock the container before this function is called.
|
||||||
|
func (c *Container) readHealthCheckLog() (define.HealthCheckResults, error) {
|
||||||
|
return c.readFromFileHealthCheckLog(c.getHealthCheckLogDestination())
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFromFileHealthCheckLog returns HealthCheck results by reading the container's
|
||||||
// health check log file. If the health check log file does not exist, then
|
// health check log file. If the health check log file does not exist, then
|
||||||
// an empty healthcheck struct is returned
|
// an empty healthcheck struct is returned
|
||||||
// The caller should lock the container before this function is called.
|
// The caller should lock the container before this function is called.
|
||||||
func (c *Container) getHealthCheckLog() (define.HealthCheckResults, error) {
|
func (c *Container) readFromFileHealthCheckLog(path string) (define.HealthCheckResults, error) {
|
||||||
var healthCheck define.HealthCheckResults
|
var healthCheck define.HealthCheckResults
|
||||||
b, err := os.ReadFile(c.healthCheckLogPath())
|
b, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
// If the file does not exists just return empty healthcheck and no error.
|
// If the file does not exists just return empty healthcheck and no error.
|
||||||
@ -428,7 +437,7 @@ func (c *Container) getHealthCheckLog() (define.HealthCheckResults, error) {
|
|||||||
return healthCheck, fmt.Errorf("failed to read health check log file: %w", err)
|
return healthCheck, fmt.Errorf("failed to read health check log file: %w", err)
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(b, &healthCheck); err != nil {
|
if err := json.Unmarshal(b, &healthCheck); err != nil {
|
||||||
return healthCheck, fmt.Errorf("failed to unmarshal existing healthcheck results in %s: %w", c.healthCheckLogPath(), err)
|
return healthCheck, fmt.Errorf("failed to unmarshal existing healthcheck results in %s: %w", path, err)
|
||||||
}
|
}
|
||||||
return healthCheck, nil
|
return healthCheck, nil
|
||||||
}
|
}
|
||||||
@ -454,7 +463,7 @@ func (c *Container) healthCheckStatus() (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := c.getHealthCheckLog()
|
results, err := c.readHealthCheckLog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to get healthcheck log for %s: %w", c.ID(), err)
|
return "", fmt.Errorf("unable to get healthcheck log for %s: %w", c.ID(), err)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@ -1513,6 +1515,57 @@ func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithHealthCheckLogDestination adds the healthLogDestination to the container config
|
||||||
|
func WithHealthCheckLogDestination(destination string) CtrCreateOption {
|
||||||
|
return func(ctr *Container) error {
|
||||||
|
if ctr.valid {
|
||||||
|
return define.ErrCtrFinalized
|
||||||
|
}
|
||||||
|
switch destination {
|
||||||
|
case define.HealthCheckEventsLoggerDestination, define.DefaultHealthCheckLocalDestination:
|
||||||
|
ctr.config.HealthLogDestination = destination
|
||||||
|
default:
|
||||||
|
fileInfo, err := os.Stat(destination)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("HealthCheck Log '%s' destination error: %w", destination, err)
|
||||||
|
}
|
||||||
|
mode := fileInfo.Mode()
|
||||||
|
if !mode.IsDir() {
|
||||||
|
return fmt.Errorf("HealthCheck Log '%s' destination must be directory", destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
absPath, err := filepath.Abs(destination)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctr.config.HealthLogDestination = absPath
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHealthCheckMaxLogCount adds the healthMaxLogCount to the container config
|
||||||
|
func WithHealthCheckMaxLogCount(maxLogCount uint) CtrCreateOption {
|
||||||
|
return func(ctr *Container) error {
|
||||||
|
if ctr.valid {
|
||||||
|
return define.ErrCtrFinalized
|
||||||
|
}
|
||||||
|
ctr.config.HealthMaxLogCount = maxLogCount
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHealthCheckMaxLogSize adds the healthMaxLogSize to the container config
|
||||||
|
func WithHealthCheckMaxLogSize(maxLogSize uint) CtrCreateOption {
|
||||||
|
return func(ctr *Container) error {
|
||||||
|
if ctr.valid {
|
||||||
|
return define.ErrCtrFinalized
|
||||||
|
}
|
||||||
|
ctr.config.HealthMaxLogSize = maxLogSize
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithHealthCheckOnFailureAction adds an on-failure action to health-check config
|
// WithHealthCheckOnFailureAction adds an on-failure action to health-check config
|
||||||
func WithHealthCheckOnFailureAction(action define.HealthCheckOnFailureAction) CtrCreateOption {
|
func WithHealthCheckOnFailureAction(action define.HealthCheckOnFailureAction) CtrCreateOption {
|
||||||
return func(ctr *Container) error {
|
return func(ctr *Container) error {
|
||||||
|
@ -585,7 +585,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ctr.runtime.config.Engine.EventsContainerCreateInspectData {
|
if ctr.runtime.config.Engine.EventsContainerCreateInspectData {
|
||||||
if err := ctr.newContainerEventWithInspectData(events.Create, "", true); err != nil {
|
if err := ctr.newContainerEventWithInspectData(events.Create, define.HealthCheckResults{}, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -423,60 +423,63 @@ func cliOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.C
|
|||||||
CPUSetMems: cc.HostConfig.CpusetMems,
|
CPUSetMems: cc.HostConfig.CpusetMems,
|
||||||
// Detach: false, // don't need
|
// Detach: false, // don't need
|
||||||
// DetachKeys: "", // don't need
|
// DetachKeys: "", // don't need
|
||||||
Devices: devices,
|
Devices: devices,
|
||||||
DeviceCgroupRule: cc.HostConfig.DeviceCgroupRules,
|
DeviceCgroupRule: cc.HostConfig.DeviceCgroupRules,
|
||||||
DeviceReadBPs: readBps,
|
DeviceReadBPs: readBps,
|
||||||
DeviceReadIOPs: readIops,
|
DeviceReadIOPs: readIops,
|
||||||
DeviceWriteBPs: writeBps,
|
DeviceWriteBPs: writeBps,
|
||||||
DeviceWriteIOPs: writeIops,
|
DeviceWriteIOPs: writeIops,
|
||||||
Entrypoint: entrypoint,
|
Entrypoint: entrypoint,
|
||||||
Env: cc.Config.Env,
|
Env: cc.Config.Env,
|
||||||
Expose: expose,
|
Expose: expose,
|
||||||
GroupAdd: cc.HostConfig.GroupAdd,
|
GroupAdd: cc.HostConfig.GroupAdd,
|
||||||
Hostname: cc.Config.Hostname,
|
Hostname: cc.Config.Hostname,
|
||||||
ImageVolume: "anonymous",
|
ImageVolume: "anonymous",
|
||||||
Init: init,
|
Init: init,
|
||||||
Interactive: cc.Config.OpenStdin,
|
Interactive: cc.Config.OpenStdin,
|
||||||
IPC: string(cc.HostConfig.IpcMode),
|
IPC: string(cc.HostConfig.IpcMode),
|
||||||
Label: stringMaptoArray(cc.Config.Labels),
|
Label: stringMaptoArray(cc.Config.Labels),
|
||||||
LogDriver: cc.HostConfig.LogConfig.Type,
|
LogDriver: cc.HostConfig.LogConfig.Type,
|
||||||
LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
|
LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config),
|
||||||
Name: cc.Name,
|
Name: cc.Name,
|
||||||
OOMScoreAdj: &cc.HostConfig.OomScoreAdj,
|
OOMScoreAdj: &cc.HostConfig.OomScoreAdj,
|
||||||
Arch: "",
|
Arch: "",
|
||||||
OS: "",
|
OS: "",
|
||||||
Variant: "",
|
Variant: "",
|
||||||
PID: string(cc.HostConfig.PidMode),
|
PID: string(cc.HostConfig.PidMode),
|
||||||
PIDsLimit: cc.HostConfig.PidsLimit,
|
PIDsLimit: cc.HostConfig.PidsLimit,
|
||||||
Privileged: cc.HostConfig.Privileged,
|
Privileged: cc.HostConfig.Privileged,
|
||||||
PublishAll: cc.HostConfig.PublishAllPorts,
|
PublishAll: cc.HostConfig.PublishAllPorts,
|
||||||
Quiet: false,
|
Quiet: false,
|
||||||
ReadOnly: cc.HostConfig.ReadonlyRootfs,
|
ReadOnly: cc.HostConfig.ReadonlyRootfs,
|
||||||
ReadWriteTmpFS: true, // podman default
|
ReadWriteTmpFS: true, // podman default
|
||||||
Rm: cc.HostConfig.AutoRemove,
|
Rm: cc.HostConfig.AutoRemove,
|
||||||
Annotation: stringMaptoArray(cc.HostConfig.Annotations),
|
Annotation: stringMaptoArray(cc.HostConfig.Annotations),
|
||||||
SecurityOpt: cc.HostConfig.SecurityOpt,
|
SecurityOpt: cc.HostConfig.SecurityOpt,
|
||||||
StopSignal: cc.Config.StopSignal,
|
StopSignal: cc.Config.StopSignal,
|
||||||
StopTimeout: rtc.Engine.StopTimeout, // podman default
|
StopTimeout: rtc.Engine.StopTimeout, // podman default
|
||||||
StorageOpts: stringMaptoArray(cc.HostConfig.StorageOpt),
|
StorageOpts: stringMaptoArray(cc.HostConfig.StorageOpt),
|
||||||
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
|
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
|
||||||
Systemd: "true", // podman default
|
Systemd: "true", // podman default
|
||||||
TmpFS: parsedTmp,
|
TmpFS: parsedTmp,
|
||||||
TTY: cc.Config.Tty,
|
TTY: cc.Config.Tty,
|
||||||
EnvMerge: cc.EnvMerge,
|
EnvMerge: cc.EnvMerge,
|
||||||
UnsetEnv: cc.UnsetEnv,
|
UnsetEnv: cc.UnsetEnv,
|
||||||
UnsetEnvAll: cc.UnsetEnvAll,
|
UnsetEnvAll: cc.UnsetEnvAll,
|
||||||
User: cc.Config.User,
|
User: cc.Config.User,
|
||||||
UserNS: string(cc.HostConfig.UsernsMode),
|
UserNS: string(cc.HostConfig.UsernsMode),
|
||||||
UTS: string(cc.HostConfig.UTSMode),
|
UTS: string(cc.HostConfig.UTSMode),
|
||||||
Mount: mounts,
|
Mount: mounts,
|
||||||
VolumesFrom: cc.HostConfig.VolumesFrom,
|
VolumesFrom: cc.HostConfig.VolumesFrom,
|
||||||
Workdir: cc.Config.WorkingDir,
|
Workdir: cc.Config.WorkingDir,
|
||||||
Net: &netInfo,
|
Net: &netInfo,
|
||||||
HealthInterval: define.DefaultHealthCheckInterval,
|
HealthInterval: define.DefaultHealthCheckInterval,
|
||||||
HealthRetries: define.DefaultHealthCheckRetries,
|
HealthRetries: define.DefaultHealthCheckRetries,
|
||||||
HealthTimeout: define.DefaultHealthCheckTimeout,
|
HealthTimeout: define.DefaultHealthCheckTimeout,
|
||||||
HealthStartPeriod: define.DefaultHealthCheckStartPeriod,
|
HealthStartPeriod: define.DefaultHealthCheckStartPeriod,
|
||||||
|
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
|
||||||
|
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
|
||||||
|
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
|
||||||
}
|
}
|
||||||
if !rootless.IsRootless() {
|
if !rootless.IsRootless() {
|
||||||
var ulimits []string
|
var ulimits []string
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/containers/podman/v5/libpod"
|
"github.com/containers/podman/v5/libpod"
|
||||||
|
"github.com/containers/podman/v5/libpod/define"
|
||||||
"github.com/containers/podman/v5/pkg/api/handlers/utils"
|
"github.com/containers/podman/v5/pkg/api/handlers/utils"
|
||||||
api "github.com/containers/podman/v5/pkg/api/types"
|
api "github.com/containers/podman/v5/pkg/api/types"
|
||||||
"github.com/containers/podman/v5/pkg/domain/entities"
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
@ -42,6 +43,9 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
|
|||||||
Umask: conf.Containers.Umask,
|
Umask: conf.Containers.Umask,
|
||||||
Privileged: &privileged,
|
Privileged: &privileged,
|
||||||
},
|
},
|
||||||
|
ContainerHealthCheckConfig: specgen.ContainerHealthCheckConfig{
|
||||||
|
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&sg); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&sg); err != nil {
|
||||||
|
@ -134,135 +134,138 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ContainerCreateOptions struct {
|
type ContainerCreateOptions struct {
|
||||||
Annotation []string
|
Annotation []string
|
||||||
Attach []string
|
Attach []string
|
||||||
Authfile string
|
Authfile string
|
||||||
BlkIOWeight string
|
BlkIOWeight string
|
||||||
BlkIOWeightDevice []string
|
BlkIOWeightDevice []string
|
||||||
CapAdd []string
|
CapAdd []string
|
||||||
CapDrop []string
|
CapDrop []string
|
||||||
CgroupNS string
|
CgroupNS string
|
||||||
CgroupsMode string
|
CgroupsMode string
|
||||||
CgroupParent string `json:"cgroup_parent,omitempty"`
|
CgroupParent string `json:"cgroup_parent,omitempty"`
|
||||||
CIDFile string
|
CIDFile string
|
||||||
ConmonPIDFile string `json:"container_conmon_pidfile,omitempty"`
|
ConmonPIDFile string `json:"container_conmon_pidfile,omitempty"`
|
||||||
CPUPeriod uint64
|
CPUPeriod uint64
|
||||||
CPUQuota int64
|
CPUQuota int64
|
||||||
CPURTPeriod uint64
|
CPURTPeriod uint64
|
||||||
CPURTRuntime int64
|
CPURTRuntime int64
|
||||||
CPUShares uint64
|
CPUShares uint64
|
||||||
CPUS float64 `json:"cpus,omitempty"`
|
CPUS float64 `json:"cpus,omitempty"`
|
||||||
CPUSetCPUs string `json:"cpuset_cpus,omitempty"`
|
CPUSetCPUs string `json:"cpuset_cpus,omitempty"`
|
||||||
CPUSetMems string
|
CPUSetMems string
|
||||||
Devices []string `json:"devices,omitempty"`
|
Devices []string `json:"devices,omitempty"`
|
||||||
DeviceCgroupRule []string
|
DeviceCgroupRule []string
|
||||||
DeviceReadBPs []string `json:"device_read_bps,omitempty"`
|
DeviceReadBPs []string `json:"device_read_bps,omitempty"`
|
||||||
DeviceReadIOPs []string
|
DeviceReadIOPs []string
|
||||||
DeviceWriteBPs []string
|
DeviceWriteBPs []string
|
||||||
DeviceWriteIOPs []string
|
DeviceWriteIOPs []string
|
||||||
Entrypoint *string `json:"container_command,omitempty"`
|
Entrypoint *string `json:"container_command,omitempty"`
|
||||||
Env []string
|
Env []string
|
||||||
EnvHost bool
|
EnvHost bool
|
||||||
EnvFile []string
|
EnvFile []string
|
||||||
Expose []string
|
Expose []string
|
||||||
GIDMap []string
|
GIDMap []string
|
||||||
GPUs []string
|
GPUs []string
|
||||||
GroupAdd []string
|
GroupAdd []string
|
||||||
HealthCmd string
|
HealthCmd string
|
||||||
HealthInterval string
|
HealthInterval string
|
||||||
HealthRetries uint
|
HealthRetries uint
|
||||||
HealthStartPeriod string
|
HealthLogDestination string
|
||||||
HealthTimeout string
|
HealthMaxLogCount uint
|
||||||
HealthOnFailure string
|
HealthMaxLogSize uint
|
||||||
Hostname string `json:"hostname,omitempty"`
|
HealthStartPeriod string
|
||||||
HTTPProxy bool
|
HealthTimeout string
|
||||||
HostUsers []string
|
HealthOnFailure string
|
||||||
ImageVolume string
|
Hostname string `json:"hostname,omitempty"`
|
||||||
Init bool
|
HTTPProxy bool
|
||||||
InitContainerType string
|
HostUsers []string
|
||||||
InitPath string
|
ImageVolume string
|
||||||
IntelRdtClosID string
|
Init bool
|
||||||
Interactive bool
|
InitContainerType string
|
||||||
IPC string
|
InitPath string
|
||||||
Label []string
|
IntelRdtClosID string
|
||||||
LabelFile []string
|
Interactive bool
|
||||||
LogDriver string
|
IPC string
|
||||||
LogOptions []string
|
Label []string
|
||||||
Memory string
|
LabelFile []string
|
||||||
MemoryReservation string
|
LogDriver string
|
||||||
MemorySwap string
|
LogOptions []string
|
||||||
MemorySwappiness int64
|
Memory string
|
||||||
Name string `json:"container_name"`
|
MemoryReservation string
|
||||||
NoHealthCheck bool
|
MemorySwap string
|
||||||
OOMKillDisable bool
|
MemorySwappiness int64
|
||||||
OOMScoreAdj *int
|
Name string `json:"container_name"`
|
||||||
Arch string
|
NoHealthCheck bool
|
||||||
OS string
|
OOMKillDisable bool
|
||||||
Variant string
|
OOMScoreAdj *int
|
||||||
PID string `json:"pid,omitempty"`
|
Arch string
|
||||||
PIDsLimit *int64
|
OS string
|
||||||
Platform string
|
Variant string
|
||||||
Pod string
|
PID string `json:"pid,omitempty"`
|
||||||
PodIDFile string
|
PIDsLimit *int64
|
||||||
Personality string
|
Platform string
|
||||||
PreserveFDs uint
|
Pod string
|
||||||
PreserveFD []uint
|
PodIDFile string
|
||||||
Privileged bool
|
Personality string
|
||||||
PublishAll bool
|
PreserveFDs uint
|
||||||
Pull string
|
PreserveFD []uint
|
||||||
Quiet bool
|
Privileged bool
|
||||||
ReadOnly bool
|
PublishAll bool
|
||||||
ReadWriteTmpFS bool
|
Pull string
|
||||||
Restart string
|
Quiet bool
|
||||||
Replace bool
|
ReadOnly bool
|
||||||
Requires []string
|
ReadWriteTmpFS bool
|
||||||
Retry *uint `json:"retry,omitempty"`
|
Restart string
|
||||||
RetryDelay string `json:"retry_delay,omitempty"`
|
Replace bool
|
||||||
Rm bool
|
Requires []string
|
||||||
RootFS bool
|
Retry *uint `json:"retry,omitempty"`
|
||||||
Secrets []string
|
RetryDelay string `json:"retry_delay,omitempty"`
|
||||||
SecurityOpt []string `json:"security_opt,omitempty"`
|
Rm bool
|
||||||
SdNotifyMode string
|
RootFS bool
|
||||||
ShmSize string
|
Secrets []string
|
||||||
ShmSizeSystemd string
|
SecurityOpt []string `json:"security_opt,omitempty"`
|
||||||
SignaturePolicy string
|
SdNotifyMode string
|
||||||
StartupHCCmd string
|
ShmSize string
|
||||||
StartupHCInterval string
|
ShmSizeSystemd string
|
||||||
StartupHCRetries uint
|
SignaturePolicy string
|
||||||
StartupHCSuccesses uint
|
StartupHCCmd string
|
||||||
StartupHCTimeout string
|
StartupHCInterval string
|
||||||
StopSignal string
|
StartupHCRetries uint
|
||||||
StopTimeout uint
|
StartupHCSuccesses uint
|
||||||
StorageOpts []string
|
StartupHCTimeout string
|
||||||
SubGIDName string
|
StopSignal string
|
||||||
SubUIDName string
|
StopTimeout uint
|
||||||
Sysctl []string `json:"sysctl,omitempty"`
|
StorageOpts []string
|
||||||
Systemd string
|
SubGIDName string
|
||||||
Timeout uint
|
SubUIDName string
|
||||||
TLSVerify commonFlag.OptionalBool
|
Sysctl []string `json:"sysctl,omitempty"`
|
||||||
TmpFS []string
|
Systemd string
|
||||||
TTY bool
|
Timeout uint
|
||||||
Timezone string
|
TLSVerify commonFlag.OptionalBool
|
||||||
Umask string
|
TmpFS []string
|
||||||
EnvMerge []string
|
TTY bool
|
||||||
UnsetEnv []string
|
Timezone string
|
||||||
UnsetEnvAll bool
|
Umask string
|
||||||
UIDMap []string
|
EnvMerge []string
|
||||||
Ulimit []string
|
UnsetEnv []string
|
||||||
User string
|
UnsetEnvAll bool
|
||||||
UserNS string `json:"-"`
|
UIDMap []string
|
||||||
UTS string
|
Ulimit []string
|
||||||
Mount []string
|
User string
|
||||||
Volume []string `json:"volume,omitempty"`
|
UserNS string `json:"-"`
|
||||||
VolumesFrom []string `json:"volumes_from,omitempty"`
|
UTS string
|
||||||
Workdir string
|
Mount []string
|
||||||
SeccompPolicy string
|
Volume []string `json:"volume,omitempty"`
|
||||||
PidFile string
|
VolumesFrom []string `json:"volumes_from,omitempty"`
|
||||||
ChrootDirs []string
|
Workdir string
|
||||||
IsInfra bool
|
SeccompPolicy string
|
||||||
IsClone bool
|
PidFile string
|
||||||
DecryptionKeys []string
|
ChrootDirs []string
|
||||||
Net *NetOptions `json:"net,omitempty"`
|
IsInfra bool
|
||||||
|
IsClone bool
|
||||||
|
DecryptionKeys []string
|
||||||
|
Net *NetOptions `json:"net,omitempty"`
|
||||||
|
|
||||||
CgroupConf []string
|
CgroupConf []string
|
||||||
|
|
||||||
|
@ -1736,6 +1736,10 @@ func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts enti
|
|||||||
spec.Name = generate.CheckName(ic.Libpod, n, true)
|
spec.Name = generate.CheckName(ic.Libpod, n, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spec.HealthLogDestination = define.DefaultHealthCheckLocalDestination
|
||||||
|
spec.HealthMaxLogCount = define.DefaultHealthMaxLogCount
|
||||||
|
spec.HealthMaxLogSize = define.DefaultHealthMaxLogSize
|
||||||
|
|
||||||
rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), ic.Libpod, spec, true, c)
|
rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), ic.Libpod, spec, true, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -84,8 +84,11 @@ func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name stri
|
|||||||
ReadOnly: true,
|
ReadOnly: true,
|
||||||
ReadWriteTmpFS: false,
|
ReadWriteTmpFS: false,
|
||||||
// No need to spin up slirp etc.
|
// No need to spin up slirp etc.
|
||||||
Net: &entities.NetOptions{Network: specgen.Namespace{NSMode: specgen.NoNetwork}},
|
Net: &entities.NetOptions{Network: specgen.Namespace{NSMode: specgen.NoNetwork}},
|
||||||
StopTimeout: rtc.Engine.StopTimeout,
|
StopTimeout: rtc.Engine.StopTimeout,
|
||||||
|
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
|
||||||
|
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
|
||||||
|
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and fill out the runtime spec.
|
// Create and fill out the runtime spec.
|
||||||
|
@ -444,6 +444,10 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
specg.HealthLogDestination = conf.HealthLogDestination
|
||||||
|
specg.HealthMaxLogCount = conf.HealthMaxLogCount
|
||||||
|
specg.HealthMaxLogSize = conf.HealthMaxLogSize
|
||||||
|
|
||||||
specg.IDMappings = &conf.IDMappings
|
specg.IDMappings = &conf.IDMappings
|
||||||
specg.ContainerCreateCommand = conf.CreateCommand
|
specg.ContainerCreateCommand = conf.CreateCommand
|
||||||
if len(specg.Rootfs) == 0 {
|
if len(specg.Rootfs) == 0 {
|
||||||
|
@ -642,6 +642,10 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l
|
|||||||
options = append(options, libpod.WithHealthCheckOnFailureAction(s.ContainerHealthCheckConfig.HealthCheckOnFailureAction))
|
options = append(options, libpod.WithHealthCheckOnFailureAction(s.ContainerHealthCheckConfig.HealthCheckOnFailureAction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options = append(options, libpod.WithHealthCheckLogDestination(s.ContainerHealthCheckConfig.HealthLogDestination))
|
||||||
|
options = append(options, libpod.WithHealthCheckMaxLogCount(s.ContainerHealthCheckConfig.HealthMaxLogCount))
|
||||||
|
options = append(options, libpod.WithHealthCheckMaxLogSize(s.ContainerHealthCheckConfig.HealthMaxLogSize))
|
||||||
|
|
||||||
if s.SdNotifyMode == define.SdNotifyModeHealthy && !healthCheckSet {
|
if s.SdNotifyMode == define.SdNotifyModeHealthy && !healthCheckSet {
|
||||||
return nil, fmt.Errorf("%w: sdnotify policy %q requires a healthcheck to be set", define.ErrInvalidArg, s.SdNotifyMode)
|
return nil, fmt.Errorf("%w: sdnotify policy %q requires a healthcheck to be set", define.ErrInvalidArg, s.SdNotifyMode)
|
||||||
}
|
}
|
||||||
|
@ -438,6 +438,9 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.Annotations[define.KubeHealthCheckAnnotation] = "true"
|
s.Annotations[define.KubeHealthCheckAnnotation] = "true"
|
||||||
|
s.HealthLogDestination = define.DefaultHealthCheckLocalDestination
|
||||||
|
s.HealthMaxLogCount = define.DefaultHealthMaxLogCount
|
||||||
|
s.HealthMaxLogSize = define.DefaultHealthMaxLogSize
|
||||||
|
|
||||||
// Environment Variables
|
// Environment Variables
|
||||||
envs := map[string]string{}
|
envs := map[string]string{}
|
||||||
|
@ -85,6 +85,12 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (_ *libpod.Pod, finalErr e
|
|||||||
// make sure of that here.
|
// make sure of that here.
|
||||||
p.PodSpecGen.InfraContainerSpec.ResourceLimits = nil
|
p.PodSpecGen.InfraContainerSpec.ResourceLimits = nil
|
||||||
p.PodSpecGen.InfraContainerSpec.WeightDevice = nil
|
p.PodSpecGen.InfraContainerSpec.WeightDevice = nil
|
||||||
|
|
||||||
|
// Set default for HealthCheck
|
||||||
|
p.PodSpecGen.InfraContainerSpec.HealthLogDestination = define.DefaultHealthCheckLocalDestination
|
||||||
|
p.PodSpecGen.InfraContainerSpec.HealthMaxLogCount = define.DefaultHealthMaxLogCount
|
||||||
|
p.PodSpecGen.InfraContainerSpec.HealthMaxLogSize = define.DefaultHealthMaxLogSize
|
||||||
|
|
||||||
rtSpec, spec, opts, err := MakeContainer(context.Background(), rt, p.PodSpecGen.InfraContainerSpec, false, nil)
|
rtSpec, spec, opts, err := MakeContainer(context.Background(), rt, p.PodSpecGen.InfraContainerSpec, false, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -599,6 +599,14 @@ type ContainerHealthCheckConfig struct {
|
|||||||
// Requires that HealthConfig be set.
|
// Requires that HealthConfig be set.
|
||||||
// Optional.
|
// Optional.
|
||||||
StartupHealthConfig *define.StartupHealthCheck `json:"startupHealthConfig,omitempty"`
|
StartupHealthConfig *define.StartupHealthCheck `json:"startupHealthConfig,omitempty"`
|
||||||
|
// HealthLogDestination defines the destination where the log is stored
|
||||||
|
HealthLogDestination string `json:"healthLogDestination,omitempty"`
|
||||||
|
// HealthMaxLogCount is maximum number of attempts in the HealthCheck log file.
|
||||||
|
// ('0' value means an infinite number of attempts in the log file)
|
||||||
|
HealthMaxLogCount uint `json:"healthMaxLogCount,omitempty"`
|
||||||
|
// HealthMaxLogSize is the maximum length in characters of stored HealthCheck log
|
||||||
|
// ("0" value means an infinite log length)
|
||||||
|
HealthMaxLogSize uint `json:"healthMaxLogSize,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpecGenerator creates an OCI spec and Libpod configuration options to create
|
// SpecGenerator creates an OCI spec and Libpod configuration options to create
|
||||||
@ -671,13 +679,25 @@ func NewSpecGenerator(arg string, rootfs bool) *SpecGenerator {
|
|||||||
}
|
}
|
||||||
return &SpecGenerator{
|
return &SpecGenerator{
|
||||||
ContainerStorageConfig: csc,
|
ContainerStorageConfig: csc,
|
||||||
|
ContainerHealthCheckConfig: ContainerHealthCheckConfig{
|
||||||
|
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
|
||||||
|
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
|
||||||
|
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs
|
// NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs
|
||||||
func NewSpecGeneratorWithRootfs(rootfs string) *SpecGenerator {
|
func NewSpecGeneratorWithRootfs(rootfs string) *SpecGenerator {
|
||||||
csc := ContainerStorageConfig{Rootfs: rootfs}
|
csc := ContainerStorageConfig{Rootfs: rootfs}
|
||||||
return &SpecGenerator{ContainerStorageConfig: csc}
|
return &SpecGenerator{
|
||||||
|
ContainerStorageConfig: csc,
|
||||||
|
ContainerHealthCheckConfig: ContainerHealthCheckConfig{
|
||||||
|
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
|
||||||
|
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
|
||||||
|
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StringSlicesEqual(a, b []string) bool {
|
func StringSlicesEqual(a, b []string) bool {
|
||||||
|
@ -370,6 +370,12 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||||||
}
|
}
|
||||||
s.HealthCheckOnFailureAction = onFailureAction
|
s.HealthCheckOnFailureAction = onFailureAction
|
||||||
|
|
||||||
|
s.HealthLogDestination = c.HealthLogDestination
|
||||||
|
|
||||||
|
s.HealthMaxLogCount = c.HealthMaxLogCount
|
||||||
|
|
||||||
|
s.HealthMaxLogSize = c.HealthMaxLogSize
|
||||||
|
|
||||||
if c.StartupHCCmd != "" {
|
if c.StartupHCCmd != "" {
|
||||||
if c.NoHealthCheck {
|
if c.NoHealthCheck {
|
||||||
return errors.New("cannot specify both --no-healthcheck and --health-startup-cmd")
|
return errors.New("cannot specify both --no-healthcheck and --health-startup-cmd")
|
||||||
|
@ -89,6 +89,9 @@ const (
|
|||||||
KeyGroupAdd = "GroupAdd"
|
KeyGroupAdd = "GroupAdd"
|
||||||
KeyHealthCmd = "HealthCmd"
|
KeyHealthCmd = "HealthCmd"
|
||||||
KeyHealthInterval = "HealthInterval"
|
KeyHealthInterval = "HealthInterval"
|
||||||
|
KeyHealthLogDestination = "HealthLogDestination"
|
||||||
|
KeyHealthMaxLogCount = "HealthMaxLogCount"
|
||||||
|
KeyHealthMaxLogSize = "HealthMaxLogSize"
|
||||||
KeyHealthOnFailure = "HealthOnFailure"
|
KeyHealthOnFailure = "HealthOnFailure"
|
||||||
KeyHealthRetries = "HealthRetries"
|
KeyHealthRetries = "HealthRetries"
|
||||||
KeyHealthStartPeriod = "HealthStartPeriod"
|
KeyHealthStartPeriod = "HealthStartPeriod"
|
||||||
@ -201,6 +204,9 @@ var (
|
|||||||
KeyHealthCmd: true,
|
KeyHealthCmd: true,
|
||||||
KeyHealthInterval: true,
|
KeyHealthInterval: true,
|
||||||
KeyHealthOnFailure: true,
|
KeyHealthOnFailure: true,
|
||||||
|
KeyHealthLogDestination: true,
|
||||||
|
KeyHealthMaxLogCount: true,
|
||||||
|
KeyHealthMaxLogSize: true,
|
||||||
KeyHealthRetries: true,
|
KeyHealthRetries: true,
|
||||||
KeyHealthStartPeriod: true,
|
KeyHealthStartPeriod: true,
|
||||||
KeyHealthStartupCmd: true,
|
KeyHealthStartupCmd: true,
|
||||||
@ -1879,6 +1885,9 @@ func handleHealth(unitFile *parser.UnitFile, groupName string, podman *PodmanCmd
|
|||||||
{KeyHealthCmd, "cmd"},
|
{KeyHealthCmd, "cmd"},
|
||||||
{KeyHealthInterval, "interval"},
|
{KeyHealthInterval, "interval"},
|
||||||
{KeyHealthOnFailure, "on-failure"},
|
{KeyHealthOnFailure, "on-failure"},
|
||||||
|
{KeyHealthLogDestination, "log-destination"},
|
||||||
|
{KeyHealthMaxLogCount, "max-log-count"},
|
||||||
|
{KeyHealthMaxLogSize, "max-log-size"},
|
||||||
{KeyHealthRetries, "retries"},
|
{KeyHealthRetries, "retries"},
|
||||||
{KeyHealthStartPeriod, "start-period"},
|
{KeyHealthStartPeriod, "start-period"},
|
||||||
{KeyHealthTimeout, "timeout"},
|
{KeyHealthTimeout, "timeout"},
|
||||||
|
@ -255,4 +255,185 @@ Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\\\n\"
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _create_container_with_health_log_settings {
|
||||||
|
local ctrname="$1"
|
||||||
|
local msg="$2"
|
||||||
|
local format="$3"
|
||||||
|
local flag="$4"
|
||||||
|
local expect="$5"
|
||||||
|
local expect_msg="$6"
|
||||||
|
|
||||||
|
run_podman run -d --name $ctrname \
|
||||||
|
--health-cmd "echo $msg" \
|
||||||
|
$flag \
|
||||||
|
$IMAGE /home/podman/pause
|
||||||
|
cid="$output"
|
||||||
|
|
||||||
|
run_podman inspect $ctrname --format $format
|
||||||
|
is "$output" "$expect" "$expect_msg"
|
||||||
|
|
||||||
|
output=$cid
|
||||||
|
}
|
||||||
|
|
||||||
|
function _check_health_log {
|
||||||
|
local ctrname="$1"
|
||||||
|
local expect_msg="$2"
|
||||||
|
local comparison=$3
|
||||||
|
local expect_count="$4"
|
||||||
|
|
||||||
|
run_podman inspect $ctrname --format "{{.State.Health.Log}}"
|
||||||
|
count=$(grep -co "$expect_msg" <<< "$output")
|
||||||
|
assert "$count" $comparison $expect_count "Number of matching health log messages"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-max-log-count default value (5)" {
|
||||||
|
local msg="healthmsg-$(random_string)"
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthMaxLogCount}}" "" "5" "HealthMaxLogCount is the expected default"
|
||||||
|
|
||||||
|
for i in $(seq 1 10);
|
||||||
|
do
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "unexpected output from podman healthcheck run (pass $i)"
|
||||||
|
done
|
||||||
|
|
||||||
|
_check_health_log $ctrname $msg -eq 5
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-max-log-count infinite value (0)" {
|
||||||
|
local repeat_count=10
|
||||||
|
local msg="healthmsg-$(random_string)"
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthMaxLogCount}}" "--health-max-log-count 0" "0" "HealthMaxLogCount"
|
||||||
|
|
||||||
|
# This is run one more time than repeat_count to check that the cap is working.
|
||||||
|
for i in $(seq 1 $(($repeat_count + 1)));
|
||||||
|
do
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "unexpected output from podman healthcheck run (pass $i)"
|
||||||
|
done
|
||||||
|
|
||||||
|
# The healthcheck is triggered by the podman when the container is started, but its execution depends on systemd.
|
||||||
|
# And since `run_podman healthcheck run` is also run manually, it will result in two runs.
|
||||||
|
_check_health_log $ctrname $msg -ge 11
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-max-log-count 10" {
|
||||||
|
local repeat_count=10
|
||||||
|
local msg="healthmsg-$(random_string)"
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthMaxLogCount}}" "--health-max-log-count $repeat_count" "$repeat_count" "HealthMaxLogCount"
|
||||||
|
|
||||||
|
# This is run one more time than repeat_count to check that the cap is working.
|
||||||
|
for i in $(seq 1 $(($repeat_count + 1)));
|
||||||
|
do
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "unexpected output from podman healthcheck run (pass $i)"
|
||||||
|
done
|
||||||
|
|
||||||
|
_check_health_log $ctrname $msg -eq $repeat_count
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-max-log-size 10" {
|
||||||
|
local msg="healthmsg-$(random_string)"
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthMaxLogSize}}" "--health-max-log-size 10" "10" "HealthMaxLogSize"
|
||||||
|
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "output from 'podman healthcheck run'"
|
||||||
|
|
||||||
|
local substr=${msg:0:10}
|
||||||
|
_check_health_log $ctrname "$substr}]\$" -eq 1
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-max-log-size infinite value (0)" {
|
||||||
|
local s=$(printf "healthmsg-%1000s")
|
||||||
|
local long_msg=${s// /$(random_string)}
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $long_msg "{{.Config.HealthMaxLogSize}}" "--health-max-log-size 0" "0" "HealthMaxLogSize"
|
||||||
|
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "output from 'podman healthcheck run'"
|
||||||
|
|
||||||
|
# The healthcheck is triggered by the podman when the container is started, but its execution depends on systemd.
|
||||||
|
# And since `run_podman healthcheck run` is also run manually, it will result in two runs.
|
||||||
|
_check_health_log $ctrname "$long_msg" -ge 1
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-max-log-size default value (500)" {
|
||||||
|
local s=$(printf "healthmsg-%1000s")
|
||||||
|
local long_msg=${s// /$(random_string)}
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $long_msg "{{.Config.HealthMaxLogSize}}" "" "500" "HealthMaxLogSize is the expected default"
|
||||||
|
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "output from 'podman healthcheck run'"
|
||||||
|
|
||||||
|
local expect_msg="${long_msg:0:500}"
|
||||||
|
_check_health_log $ctrname "$expect_msg}]\$" -eq 1
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-log-destination file" {
|
||||||
|
local TMP_DIR_HEALTHCHECK="$PODMAN_TMPDIR/healthcheck"
|
||||||
|
mkdir $TMP_DIR_HEALTHCHECK
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
local msg="healthmsg-$(random_string)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthLogDestination}}" "--health-log-destination $TMP_DIR_HEALTHCHECK" "$TMP_DIR_HEALTHCHECK" "HealthLogDestination"
|
||||||
|
cid="$output"
|
||||||
|
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "output from 'podman healthcheck run'"
|
||||||
|
|
||||||
|
healthcheck_log_path="${TMP_DIR_HEALTHCHECK}/${cid}-healthcheck.log"
|
||||||
|
# The healthcheck is triggered by the podman when the container is started, but its execution depends on systemd.
|
||||||
|
# And since `run_podman healthcheck run` is also run manually, it will result in two runs.
|
||||||
|
count=$(grep -co "$msg" $healthcheck_log_path)
|
||||||
|
assert "$count" -ge 1 "Number of matching health log messages"
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "podman healthcheck --health-log-destination journal" {
|
||||||
|
skip_if_remote "We cannot read journalctl over remote."
|
||||||
|
|
||||||
|
# We can't use journald on RHEL as rootless, either: rhbz#1895105
|
||||||
|
skip_if_journald_unavailable
|
||||||
|
|
||||||
|
local ctrname="c-h-$(safename)"
|
||||||
|
local msg="healthmsg-$(random_string)"
|
||||||
|
_create_container_with_health_log_settings $ctrname $msg "{{.Config.HealthLogDestination}}" "--health-log-destination events_logger" "events_logger" "HealthLogDestination"
|
||||||
|
cid="$output"
|
||||||
|
|
||||||
|
run_podman healthcheck run $ctrname
|
||||||
|
is "$output" "" "output from 'podman healthcheck run'"
|
||||||
|
|
||||||
|
cmd="journalctl --output cat --output-fields=PODMAN_HEALTH_LOG PODMAN_ID=$cid"
|
||||||
|
echo "$_LOG_PROMPT $cmd"
|
||||||
|
run $cmd
|
||||||
|
echo "$output"
|
||||||
|
assert "$status" -eq 0 "exit status of journalctl"
|
||||||
|
|
||||||
|
# The healthcheck is triggered by the podman when the container is started, but its execution depends on systemd.
|
||||||
|
# And since `run_podman healthcheck run` is also run manually, it will result in two runs.
|
||||||
|
count=$(grep -co "$msg" <<< "$output")
|
||||||
|
assert "$count" -ge 1 "Number of matching health log messages"
|
||||||
|
|
||||||
|
run_podman rm -t 0 -f $ctrname
|
||||||
|
}
|
||||||
|
|
||||||
# vim: filetype=sh
|
# vim: filetype=sh
|
||||||
|
Reference in New Issue
Block a user