mirror of https://github.com/containers/podman.git
Configure HealthCheck with `podman update`
New flags in a `podman update` can change the configuration of HealthCheck when the container is started, without having to restart or recreate the container. This can help determine why a given container suddenly started failing HealthCheck without interfering with the services it provides. For example, reconfigure HealthCheck to keep logs longer than the usual last X results, store logs to other destinations, etc. Fixes: https://issues.redhat.com/browse/RHEL-60561 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
This commit is contained in:
parent
77e67e7a54
commit
a1249425bd
|
@ -168,78 +168,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthCmdFlagName := "health-cmd"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthCmd,
|
||||
healthCmdFlagName, "",
|
||||
"set a healthcheck command for the container ('none' disables the existing healthcheck)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthIntervalFlagName := "health-interval"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthInterval,
|
||||
healthIntervalFlagName, define.DefaultHealthCheckInterval,
|
||||
"set an interval for the healthcheck (a value of disable results in no automatic timer setup)",
|
||||
)
|
||||
_ = 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"
|
||||
createFlags.UintVar(
|
||||
&cf.HealthRetries,
|
||||
healthRetriesFlagName, define.DefaultHealthCheckRetries,
|
||||
"the number of retries allowed before a healthcheck is considered to be unhealthy",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthStartPeriodFlagName := "health-start-period"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthStartPeriod,
|
||||
healthStartPeriodFlagName, define.DefaultHealthCheckStartPeriod,
|
||||
"the initialization time needed for a container to bootstrap",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthTimeoutFlagName := "health-timeout"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthTimeout,
|
||||
healthTimeoutFlagName, define.DefaultHealthCheckTimeout,
|
||||
"the maximum time allowed to complete the healthcheck before an interval is considered failed",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthOnFailureFlagName := "health-on-failure"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthOnFailure,
|
||||
healthOnFailureFlagName, "none",
|
||||
"action to take once the container turns unhealthy",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthOnFailureFlagName, AutocompleteHealthOnFailure)
|
||||
|
||||
createFlags.BoolVar(
|
||||
&cf.HTTPProxy,
|
||||
"http-proxy", podmanConfig.ContainersConfDefaultsRO.Containers.HTTPProxy,
|
||||
|
@ -311,11 +239,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt)
|
||||
|
||||
createFlags.BoolVar(
|
||||
&cf.NoHealthCheck,
|
||||
"no-healthcheck", false,
|
||||
"Disable healthchecks on container",
|
||||
)
|
||||
createFlags.BoolVar(
|
||||
&cf.OOMKillDisable,
|
||||
"oom-kill-disable", false,
|
||||
|
@ -452,46 +375,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets)
|
||||
|
||||
startupHCCmdFlagName := "health-startup-cmd"
|
||||
createFlags.StringVar(
|
||||
&cf.StartupHCCmd,
|
||||
startupHCCmdFlagName, "",
|
||||
"Set a startup healthcheck command for the container",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCCmdFlagName, completion.AutocompleteNone)
|
||||
|
||||
startupHCIntervalFlagName := "health-startup-interval"
|
||||
createFlags.StringVar(
|
||||
&cf.StartupHCInterval,
|
||||
startupHCIntervalFlagName, define.DefaultHealthCheckInterval,
|
||||
"Set an interval for the startup healthcheck",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCIntervalFlagName, completion.AutocompleteNone)
|
||||
|
||||
startupHCRetriesFlagName := "health-startup-retries"
|
||||
createFlags.UintVar(
|
||||
&cf.StartupHCRetries,
|
||||
startupHCRetriesFlagName, 0,
|
||||
"Set the maximum number of retries before the startup healthcheck will restart the container",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCRetriesFlagName, completion.AutocompleteNone)
|
||||
|
||||
startupHCSuccessesFlagName := "health-startup-success"
|
||||
createFlags.UintVar(
|
||||
&cf.StartupHCSuccesses,
|
||||
startupHCSuccessesFlagName, 0,
|
||||
"Set the number of consecutive successes before the startup healthcheck is marked as successful and the normal healthcheck begins (0 indicates any success will start the regular healthcheck)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCSuccessesFlagName, completion.AutocompleteNone)
|
||||
|
||||
startupHCTimeoutFlagName := "health-startup-timeout"
|
||||
createFlags.StringVar(
|
||||
&cf.StartupHCTimeout,
|
||||
startupHCTimeoutFlagName, define.DefaultHealthCheckTimeout,
|
||||
"Set the maximum amount of time that the startup healthcheck may take before it is considered failed",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCTimeoutFlagName, completion.AutocompleteNone)
|
||||
|
||||
stopSignalFlagName := "stop-signal"
|
||||
createFlags.StringVar(
|
||||
&cf.StopSignal,
|
||||
|
@ -665,6 +548,140 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
|
|||
`If a container with the same name exists, replace it`,
|
||||
)
|
||||
}
|
||||
if mode == entities.CreateMode || mode == entities.UpdateMode {
|
||||
createFlags.BoolVar(
|
||||
&cf.NoHealthCheck,
|
||||
"no-healthcheck", false,
|
||||
"Disable healthchecks on container",
|
||||
)
|
||||
|
||||
healthCmdFlagName := "health-cmd"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthCmd,
|
||||
healthCmdFlagName, "",
|
||||
"set a healthcheck command for the container ('none' disables the existing healthcheck)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone)
|
||||
|
||||
info := ""
|
||||
if mode == entities.UpdateMode {
|
||||
info = "Changing this setting resets timer."
|
||||
}
|
||||
healthIntervalFlagName := "health-interval"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthInterval,
|
||||
healthIntervalFlagName, define.DefaultHealthCheckInterval,
|
||||
"set an interval for the healthcheck. (a value of disable results in no automatic timer setup) "+info,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone)
|
||||
|
||||
warning := ""
|
||||
if mode == entities.UpdateMode {
|
||||
warning = "Warning: Changing this setting may cause the loss of previous logs!"
|
||||
}
|
||||
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) "+warning,
|
||||
)
|
||||
_ = 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"
|
||||
createFlags.UintVar(
|
||||
&cf.HealthRetries,
|
||||
healthRetriesFlagName, define.DefaultHealthCheckRetries,
|
||||
"the number of retries allowed before a healthcheck is considered to be unhealthy",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthStartPeriodFlagName := "health-start-period"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthStartPeriod,
|
||||
healthStartPeriodFlagName, define.DefaultHealthCheckStartPeriod,
|
||||
"the initialization time needed for a container to bootstrap",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthTimeoutFlagName := "health-timeout"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthTimeout,
|
||||
healthTimeoutFlagName, define.DefaultHealthCheckTimeout,
|
||||
"the maximum time allowed to complete the healthcheck before an interval is considered failed",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone)
|
||||
|
||||
healthOnFailureFlagName := "health-on-failure"
|
||||
createFlags.StringVar(
|
||||
&cf.HealthOnFailure,
|
||||
healthOnFailureFlagName, "none",
|
||||
"action to take once the container turns unhealthy",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(healthOnFailureFlagName, AutocompleteHealthOnFailure)
|
||||
|
||||
// Startup HealthCheck
|
||||
|
||||
startupHCCmdFlagName := "health-startup-cmd"
|
||||
createFlags.StringVar(
|
||||
&cf.StartupHCCmd,
|
||||
startupHCCmdFlagName, "",
|
||||
"Set a startup healthcheck command for the container",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCCmdFlagName, completion.AutocompleteNone)
|
||||
|
||||
info = ""
|
||||
if mode == entities.UpdateMode {
|
||||
info = "Changing this setting resets the timer, depending on the state of the container."
|
||||
}
|
||||
startupHCIntervalFlagName := "health-startup-interval"
|
||||
createFlags.StringVar(
|
||||
&cf.StartupHCInterval,
|
||||
startupHCIntervalFlagName, define.DefaultHealthCheckInterval,
|
||||
"Set an interval for the startup healthcheck. "+info,
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCIntervalFlagName, completion.AutocompleteNone)
|
||||
|
||||
startupHCRetriesFlagName := "health-startup-retries"
|
||||
createFlags.UintVar(
|
||||
&cf.StartupHCRetries,
|
||||
startupHCRetriesFlagName, 0,
|
||||
"Set the maximum number of retries before the startup healthcheck will restart the container",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCRetriesFlagName, completion.AutocompleteNone)
|
||||
|
||||
startupHCSuccessesFlagName := "health-startup-success"
|
||||
createFlags.UintVar(
|
||||
&cf.StartupHCSuccesses,
|
||||
startupHCSuccessesFlagName, 0,
|
||||
"Set the number of consecutive successes before the startup healthcheck is marked as successful and the normal healthcheck begins (0 indicates any success will start the regular healthcheck)",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCSuccessesFlagName, completion.AutocompleteNone)
|
||||
|
||||
startupHCTimeoutFlagName := "health-startup-timeout"
|
||||
createFlags.StringVar(
|
||||
&cf.StartupHCTimeout,
|
||||
startupHCTimeoutFlagName, define.DefaultHealthCheckTimeout,
|
||||
"Set the maximum amount of time that the startup healthcheck may take before it is considered failed",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(startupHCTimeoutFlagName, completion.AutocompleteNone)
|
||||
}
|
||||
|
||||
// Restart is allowed for created, updated, and infra ctr
|
||||
if mode == entities.InfraMode || mode == entities.CreateMode || mode == entities.UpdateMode {
|
||||
restartFlagName := "restart"
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
updateDescription = `Updates the cgroup configuration of a given container`
|
||||
updateDescription = `Updates the configuration of an existing container, allowing changes to resource limits and healthchecks`
|
||||
|
||||
updateCommand = &cobra.Command{
|
||||
Use: "update [options] CONTAINER",
|
||||
|
@ -61,6 +61,58 @@ func init() {
|
|||
updateFlags(containerUpdateCommand)
|
||||
}
|
||||
|
||||
func GetChangedHealthCheckConfiguration(cmd *cobra.Command, vals *entities.ContainerCreateOptions) define.UpdateHealthCheckConfig {
|
||||
updateHealthCheckConfig := define.UpdateHealthCheckConfig{}
|
||||
|
||||
if cmd.Flags().Changed("health-log-destination") {
|
||||
updateHealthCheckConfig.HealthLogDestination = &vals.HealthLogDestination
|
||||
}
|
||||
if cmd.Flags().Changed("health-max-log-size") {
|
||||
updateHealthCheckConfig.HealthMaxLogSize = &vals.HealthMaxLogSize
|
||||
}
|
||||
if cmd.Flags().Changed("health-max-log-count") {
|
||||
updateHealthCheckConfig.HealthMaxLogCount = &vals.HealthMaxLogCount
|
||||
}
|
||||
if cmd.Flags().Changed("health-on-failure") {
|
||||
updateHealthCheckConfig.HealthOnFailure = &vals.HealthOnFailure
|
||||
}
|
||||
if cmd.Flags().Changed("no-healthcheck") {
|
||||
updateHealthCheckConfig.NoHealthCheck = &vals.NoHealthCheck
|
||||
}
|
||||
if cmd.Flags().Changed("health-cmd") {
|
||||
updateHealthCheckConfig.HealthCmd = &vals.HealthCmd
|
||||
}
|
||||
if cmd.Flags().Changed("health-interval") {
|
||||
updateHealthCheckConfig.HealthInterval = &vals.HealthInterval
|
||||
}
|
||||
if cmd.Flags().Changed("health-retries") {
|
||||
updateHealthCheckConfig.HealthRetries = &vals.HealthRetries
|
||||
}
|
||||
if cmd.Flags().Changed("health-timeout") {
|
||||
updateHealthCheckConfig.HealthTimeout = &vals.HealthTimeout
|
||||
}
|
||||
if cmd.Flags().Changed("health-start-period") {
|
||||
updateHealthCheckConfig.HealthStartPeriod = &vals.HealthStartPeriod
|
||||
}
|
||||
if cmd.Flags().Changed("health-startup-cmd") {
|
||||
updateHealthCheckConfig.HealthStartupCmd = &vals.StartupHCCmd
|
||||
}
|
||||
if cmd.Flags().Changed("health-startup-interval") {
|
||||
updateHealthCheckConfig.HealthStartupInterval = &vals.StartupHCInterval
|
||||
}
|
||||
if cmd.Flags().Changed("health-startup-retries") {
|
||||
updateHealthCheckConfig.HealthStartupRetries = &vals.StartupHCRetries
|
||||
}
|
||||
if cmd.Flags().Changed("health-startup-timeout") {
|
||||
updateHealthCheckConfig.HealthStartupTimeout = &vals.StartupHCTimeout
|
||||
}
|
||||
if cmd.Flags().Changed("health-startup-success") {
|
||||
updateHealthCheckConfig.HealthStartupSuccess = &vals.StartupHCSuccesses
|
||||
}
|
||||
|
||||
return updateHealthCheckConfig
|
||||
}
|
||||
|
||||
func update(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
// use a specgen since this is the easiest way to hold resource info
|
||||
|
@ -89,9 +141,15 @@ func update(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
healthCheckConfig := GetChangedHealthCheckConfiguration(cmd, &updateOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := &entities.ContainerUpdateOptions{
|
||||
NameOrID: strings.TrimPrefix(args[0], "/"),
|
||||
Specgen: s,
|
||||
NameOrID: strings.TrimPrefix(args[0], "/"),
|
||||
Specgen: s,
|
||||
ChangedHealthCheckConfiguration: &healthCheckConfig,
|
||||
}
|
||||
rep, err := registry.ContainerEngine().ContainerUpdate(context.Background(), opts)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-cmd**=*"command"* | *'["command", "arg1", ...]'*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-interval**=*interval*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-log-destination**=*directory_path*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-max-log-count**=*number of stored logs*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-max-log-size**=*size of stored logs*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-on-failure**=*action*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-retries**=*retries*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-start-period**=*period*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-startup-cmd**=*"command"* | *'["command", "arg1", ...]'*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-startup-interval**=*interval*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-startup-retries**=*retries*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-startup-success**=*retries*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-startup-timeout**=*timeout*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--health-timeout**=*timeout*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
####> This option file is used in:
|
||||
####> podman create, run
|
||||
####> podman create, run, update
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--no-healthcheck**
|
||||
|
|
|
@ -10,8 +10,7 @@ podman\-update - Update the configuration of a given container
|
|||
|
||||
## DESCRIPTION
|
||||
|
||||
Updates the configuration of an already existing container, allowing different resource limits to be set.
|
||||
The currently supported options are a subset of the podman create/run resource limit options.
|
||||
Updates the configuration of an existing container, allowing changes to resource limits and healthchecks.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
|
@ -43,6 +42,40 @@ The currently supported options are a subset of the podman create/run resource l
|
|||
|
||||
@@option device-write-iops
|
||||
|
||||
@@option health-cmd
|
||||
|
||||
@@option health-interval
|
||||
|
||||
Changing this setting resets the timer.
|
||||
|
||||
@@option health-log-destination
|
||||
|
||||
Warning: Changing this setting may cause the loss of previous logs.
|
||||
|
||||
@@option health-max-log-count
|
||||
|
||||
@@option health-max-log-size
|
||||
|
||||
@@option health-on-failure
|
||||
|
||||
@@option health-retries
|
||||
|
||||
@@option health-start-period
|
||||
|
||||
@@option health-startup-cmd
|
||||
|
||||
@@option health-startup-interval
|
||||
|
||||
Changing this setting resets the timer, depending on the state of the container.
|
||||
|
||||
@@option health-startup-retries
|
||||
|
||||
@@option health-startup-success
|
||||
|
||||
@@option health-startup-timeout
|
||||
|
||||
@@option health-timeout
|
||||
|
||||
@@option memory
|
||||
|
||||
@@option memory-reservation
|
||||
|
@ -51,6 +84,8 @@ The currently supported options are a subset of the podman create/run resource l
|
|||
|
||||
@@option memory-swappiness
|
||||
|
||||
@@option no-healthcheck
|
||||
|
||||
@@option pids-limit
|
||||
|
||||
@@option restart
|
||||
|
|
|
@ -116,10 +116,11 @@ func (c *Container) Start(ctx context.Context, recursive bool) (finalErr error)
|
|||
}
|
||||
|
||||
// Update updates the given container.
|
||||
// Either resource limits or restart policy can be updated.
|
||||
// Either resources or restartPolicy must not be nil.
|
||||
// Either resource limits, restart policies, or HealthCheck configuration can be updated.
|
||||
// Either resources, restartPolicy or changedHealthCheckConfiguration must not be nil.
|
||||
// If restartRetries is not nil, restartPolicy must be set and must be "on-failure".
|
||||
func (c *Container) Update(resources *spec.LinuxResources, restartPolicy *string, restartRetries *uint) error {
|
||||
// Nil values of changedHealthCheckConfiguration are not updated.
|
||||
func (c *Container) Update(resources *spec.LinuxResources, restartPolicy *string, restartRetries *uint, changedHealthCheckConfiguration *define.UpdateHealthCheckConfig) error {
|
||||
if !c.batched {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
@ -133,6 +134,41 @@ func (c *Container) Update(resources *spec.LinuxResources, restartPolicy *string
|
|||
return fmt.Errorf("container %s is being removed, cannot update: %w", c.ID(), define.ErrCtrStateInvalid)
|
||||
}
|
||||
|
||||
healthCheckConfig, changedHealthCheck, err := GetNewHealthCheckConfig(&HealthCheckConfig{Schema2HealthConfig: c.HealthCheckConfig()}, *changedHealthCheckConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if changedHealthCheck {
|
||||
if err := c.updateHealthCheck(
|
||||
healthCheckConfig,
|
||||
&HealthCheckConfig{Schema2HealthConfig: c.config.HealthCheckConfig},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
startupHealthCheckConfig, changedStartupHealthCheck, err := GetNewHealthCheckConfig(&StartupHealthCheckConfig{StartupHealthCheck: c.Config().StartupHealthCheckConfig}, *changedHealthCheckConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if changedStartupHealthCheck {
|
||||
if err := c.updateHealthCheck(
|
||||
startupHealthCheckConfig,
|
||||
&StartupHealthCheckConfig{StartupHealthCheck: c.config.StartupHealthCheckConfig},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
globalHealthCheckOptions, err := changedHealthCheckConfiguration.GetNewGlobalHealthCheck()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.updateGlobalHealthCheckConfiguration(globalHealthCheckOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer c.newContainerEvent(events.Update)
|
||||
return c.update(resources, restartPolicy, restartRetries)
|
||||
}
|
||||
|
||||
|
|
|
@ -2733,8 +2733,123 @@ func (c *Container) update(resources *spec.LinuxResources, restartPolicy *string
|
|||
}
|
||||
|
||||
logrus.Debugf("updated container %s", c.ID())
|
||||
|
||||
c.newContainerEvent(events.Update)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Container) resetHealthCheckTimers(noHealthCheck bool, changedTimer bool, wasEnabledHealthCheck bool, isStartup bool) error {
|
||||
if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused) {
|
||||
return nil
|
||||
}
|
||||
if noHealthCheck {
|
||||
if err := c.removeTransientFiles(context.Background(),
|
||||
c.config.StartupHealthCheckConfig != nil && !c.state.StartupHCPassed,
|
||||
c.state.HCUnitName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !changedTimer {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !isStartup {
|
||||
if c.state.StartupHCPassed || c.config.StartupHealthCheckConfig == nil {
|
||||
c.recreateHealthCheckTimer(context.Background(), false, false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !c.state.StartupHCPassed {
|
||||
c.state.StartupHCPassed = !wasEnabledHealthCheck
|
||||
c.state.StartupHCSuccessCount = 0
|
||||
c.state.StartupHCFailureCount = 0
|
||||
if err := c.save(); err != nil {
|
||||
return err
|
||||
}
|
||||
if wasEnabledHealthCheck {
|
||||
c.recreateHealthCheckTimer(context.Background(), true, true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Container) updateHealthCheck(newHealthCheckConfig IHealthCheckConfig, currentHealthCheckConfig IHealthCheckConfig) error {
|
||||
oldHealthCheckConfig := currentHealthCheckConfig
|
||||
if !oldHealthCheckConfig.IsNil() {
|
||||
if err := JSONDeepCopy(currentHealthCheckConfig, oldHealthCheckConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newHealthCheckConfig.SetTo(c.config)
|
||||
|
||||
if err := c.runtime.state.SafeRewriteContainerConfig(c, "", "", c.config); err != nil {
|
||||
// Assume DB write failed, revert to old resources block
|
||||
oldHealthCheckConfig.SetTo(c.config)
|
||||
return err
|
||||
}
|
||||
|
||||
oldInterval := time.Duration(0)
|
||||
if !oldHealthCheckConfig.IsNil() {
|
||||
oldInterval = oldHealthCheckConfig.GetInterval()
|
||||
}
|
||||
|
||||
changedTimer := false
|
||||
if !newHealthCheckConfig.IsNil() {
|
||||
changedTimer = newHealthCheckConfig.IsTimeChanged(oldInterval)
|
||||
}
|
||||
|
||||
noHealthCheck := c.config.HealthCheckConfig != nil && slices.Contains(c.config.HealthCheckConfig.Test, "NONE")
|
||||
|
||||
if err := c.resetHealthCheckTimers(noHealthCheck, changedTimer, !oldHealthCheckConfig.IsNil(), newHealthCheckConfig.IsStartup()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
checkType := "HealthCheck"
|
||||
if newHealthCheckConfig.IsStartup() {
|
||||
checkType = "Startup HealthCheck"
|
||||
}
|
||||
logrus.Debugf("%s configuration updated for container %s", checkType, c.ID())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Container) updateGlobalHealthCheckConfiguration(globalOptions define.GlobalHealthCheckOptions) error {
|
||||
oldHealthCheckOnFailureAction := c.config.HealthCheckOnFailureAction
|
||||
oldHealthLogDestination := c.config.HealthLogDestination
|
||||
oldHealthMaxLogCount := c.config.HealthMaxLogCount
|
||||
oldHealthMaxLogSize := c.config.HealthMaxLogSize
|
||||
|
||||
if globalOptions.HealthCheckOnFailureAction != nil {
|
||||
c.config.HealthCheckOnFailureAction = *globalOptions.HealthCheckOnFailureAction
|
||||
}
|
||||
|
||||
if globalOptions.HealthMaxLogCount != nil {
|
||||
c.config.HealthMaxLogCount = *globalOptions.HealthMaxLogCount
|
||||
}
|
||||
|
||||
if globalOptions.HealthMaxLogSize != nil {
|
||||
c.config.HealthMaxLogSize = *globalOptions.HealthMaxLogSize
|
||||
}
|
||||
|
||||
if globalOptions.HealthLogDestination != nil {
|
||||
dest, err := define.GetValidHealthCheckDestination(*globalOptions.HealthLogDestination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.config.HealthLogDestination = dest
|
||||
}
|
||||
|
||||
if err := c.runtime.state.SafeRewriteContainerConfig(c, "", "", c.config); err != nil {
|
||||
// Assume DB write failed, revert to old resources block
|
||||
c.config.HealthCheckOnFailureAction = oldHealthCheckOnFailureAction
|
||||
c.config.HealthLogDestination = oldHealthLogDestination
|
||||
c.config.HealthMaxLogCount = oldHealthMaxLogCount
|
||||
c.config.HealthMaxLogSize = oldHealthMaxLogSize
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debugf("Global HealthCheck configuration updated for container %s", c.ID())
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package define
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
|
@ -155,3 +157,186 @@ type StartupHealthCheck struct {
|
|||
// If set to 0, a single success will mark the HC as passed.
|
||||
Successes int `json:",omitempty"`
|
||||
}
|
||||
|
||||
type UpdateHealthCheckConfig struct {
|
||||
// HealthLogDestination set the destination of the HealthCheck log.
|
||||
// Directory path, local or events_logger (local use container state file)
|
||||
// Warning: Changing this setting may cause the loss of previous logs!
|
||||
HealthLogDestination *string `json:"health_log_destination,omitempty"`
|
||||
// HealthMaxLogSize set maximum length in characters of stored HealthCheck log.
|
||||
// ('0' value means an infinite log length)
|
||||
HealthMaxLogSize *uint `json:"health_max_log_size,omitempty"`
|
||||
// HealthMaxLogCount set maximum number of attempts in the HealthCheck log file.
|
||||
// ('0' value means an infinite number of attempts in the log file)
|
||||
HealthMaxLogCount *uint `json:"health_max_log_count,omitempty"`
|
||||
// HealthOnFailure set the action to take once the container turns unhealthy.
|
||||
HealthOnFailure *string `json:"health_on_failure,omitempty"`
|
||||
// Disable healthchecks on container.
|
||||
NoHealthCheck *bool `json:"no_healthcheck,omitempty"`
|
||||
// HealthCmd set a healthcheck command for the container. ('none' disables the existing healthcheck)
|
||||
HealthCmd *string `json:"health_cmd,omitempty"`
|
||||
// HealthInterval set an interval for the healthcheck.
|
||||
// (a value of disable results in no automatic timer setup) Changing this setting resets timer.
|
||||
HealthInterval *string `json:"health_interval,omitempty"`
|
||||
// HealthRetries set the number of retries allowed before a healthcheck is considered to be unhealthy.
|
||||
HealthRetries *uint `json:"health_retries,omitempty"`
|
||||
// HealthTimeout set the maximum time allowed to complete the healthcheck before an interval is considered failed.
|
||||
HealthTimeout *string `json:"health_timeout,omitempty"`
|
||||
// HealthStartPeriod set the initialization time needed for a container to bootstrap.
|
||||
HealthStartPeriod *string `json:"health_start_period,omitempty"`
|
||||
// HealthStartupCmd set a startup healthcheck command for the container.
|
||||
HealthStartupCmd *string `json:"health_startup_cmd,omitempty"`
|
||||
// HealthStartupInterval set an interval for the startup healthcheck.
|
||||
// Changing this setting resets the timer, depending on the state of the container.
|
||||
HealthStartupInterval *string `json:"health_startup_interval,omitempty"`
|
||||
// HealthStartupRetries set the maximum number of retries before the startup healthcheck will restart the container.
|
||||
HealthStartupRetries *uint `json:"health_startup_retries,omitempty"`
|
||||
// HealthStartupTimeout set the maximum amount of time that the startup healthcheck may take before it is considered failed.
|
||||
HealthStartupTimeout *string `json:"health_startup_timeout,omitempty"`
|
||||
// HealthStartupSuccess set the number of consecutive successes before the startup healthcheck is marked as successful
|
||||
// and the normal healthcheck begins (0 indicates any success will start the regular healthcheck)
|
||||
HealthStartupSuccess *uint `json:"health_startup_success,omitempty"`
|
||||
}
|
||||
|
||||
func (u *UpdateHealthCheckConfig) IsStartupHealthCheckCommandSet(startupHealthCheck *StartupHealthCheck) bool {
|
||||
containsStartupHealthCheckCmd := u.HealthStartupCmd != nil
|
||||
containsFlags := (u.HealthStartupInterval != nil || u.HealthStartupRetries != nil ||
|
||||
u.HealthStartupTimeout != nil || u.HealthStartupSuccess != nil)
|
||||
return startupHealthCheck == nil && !containsStartupHealthCheckCmd && containsFlags
|
||||
}
|
||||
|
||||
func (u *UpdateHealthCheckConfig) IsHealthCheckCommandSet(healthCheck *manifest.Schema2HealthConfig) bool {
|
||||
containsStartupHealthCheckCmd := u.HealthCmd != nil
|
||||
containsFlags := (u.HealthInterval != nil || u.HealthRetries != nil ||
|
||||
u.HealthTimeout != nil || u.HealthStartPeriod != nil)
|
||||
return healthCheck == nil && !containsStartupHealthCheckCmd && containsFlags
|
||||
}
|
||||
|
||||
func (u *UpdateHealthCheckConfig) SetNewStartupHealthCheckConfigTo(healthCheckOptions *HealthCheckOptions) bool {
|
||||
changed := false
|
||||
|
||||
if u.HealthStartupCmd != nil {
|
||||
healthCheckOptions.Cmd = *u.HealthStartupCmd
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthStartupInterval != nil {
|
||||
healthCheckOptions.Interval = *u.HealthStartupInterval
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthStartupRetries != nil {
|
||||
healthCheckOptions.Retries = int(*u.HealthStartupRetries)
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthStartupTimeout != nil {
|
||||
healthCheckOptions.Timeout = *u.HealthStartupTimeout
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthStartupSuccess != nil {
|
||||
healthCheckOptions.Successes = int(*u.HealthStartupSuccess)
|
||||
changed = true
|
||||
}
|
||||
|
||||
healthCheckOptions.StartPeriod = "1s"
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
func (u *UpdateHealthCheckConfig) SetNewHealthCheckConfigTo(healthCheckOptions *HealthCheckOptions) bool {
|
||||
changed := false
|
||||
|
||||
if u.HealthCmd != nil {
|
||||
healthCheckOptions.Cmd = *u.HealthCmd
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthInterval != nil {
|
||||
healthCheckOptions.Interval = *u.HealthInterval
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthRetries != nil {
|
||||
healthCheckOptions.Retries = int(*u.HealthRetries)
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthTimeout != nil {
|
||||
healthCheckOptions.Timeout = *u.HealthTimeout
|
||||
changed = true
|
||||
}
|
||||
|
||||
if u.HealthStartPeriod != nil {
|
||||
healthCheckOptions.StartPeriod = *u.HealthStartPeriod
|
||||
changed = true
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
func GetValidHealthCheckDestination(destination string) (string, error) {
|
||||
if destination == HealthCheckEventsLoggerDestination || destination == DefaultHealthCheckLocalDestination {
|
||||
return destination, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return absPath, nil
|
||||
}
|
||||
|
||||
func (u *UpdateHealthCheckConfig) GetNewGlobalHealthCheck() (GlobalHealthCheckOptions, error) {
|
||||
globalOptions := GlobalHealthCheckOptions{}
|
||||
|
||||
healthLogDestination := u.HealthLogDestination
|
||||
if u.HealthLogDestination != nil {
|
||||
dest, err := GetValidHealthCheckDestination(*u.HealthLogDestination)
|
||||
if err != nil {
|
||||
return GlobalHealthCheckOptions{}, err
|
||||
}
|
||||
healthLogDestination = &dest
|
||||
}
|
||||
globalOptions.HealthLogDestination = healthLogDestination
|
||||
|
||||
globalOptions.HealthMaxLogSize = u.HealthMaxLogSize
|
||||
|
||||
globalOptions.HealthMaxLogCount = u.HealthMaxLogCount
|
||||
|
||||
if u.HealthOnFailure != nil {
|
||||
val, err := ParseHealthCheckOnFailureAction(*u.HealthOnFailure)
|
||||
if err != nil {
|
||||
return globalOptions, err
|
||||
}
|
||||
globalOptions.HealthCheckOnFailureAction = &val
|
||||
}
|
||||
|
||||
return globalOptions, nil
|
||||
}
|
||||
|
||||
type HealthCheckOptions struct {
|
||||
Cmd string
|
||||
Interval string
|
||||
Retries int
|
||||
Timeout string
|
||||
StartPeriod string
|
||||
Successes int
|
||||
}
|
||||
|
||||
type GlobalHealthCheckOptions struct {
|
||||
HealthLogDestination *string
|
||||
HealthMaxLogCount *uint
|
||||
HealthMaxLogSize *uint
|
||||
HealthCheckOnFailureAction *HealthCheckOnFailureAction
|
||||
}
|
||||
|
|
|
@ -258,18 +258,6 @@ func (c *Container) incrementStartupHCSuccessCounter(ctx context.Context) {
|
|||
}
|
||||
|
||||
if recreateTimer {
|
||||
logrus.Infof("Startup healthcheck for container %s passed, recreating timer", c.ID())
|
||||
|
||||
oldUnit := c.state.HCUnitName
|
||||
// Create the new, standard healthcheck timer first.
|
||||
if err := c.createTimer(c.HealthCheckConfig().Interval.String(), false); err != nil {
|
||||
logrus.Errorf("Error recreating container %s healthcheck: %v", c.ID(), err)
|
||||
return
|
||||
}
|
||||
if err := c.startTimer(false); err != nil {
|
||||
logrus.Errorf("Error restarting container %s healthcheck timer: %v", c.ID(), err)
|
||||
}
|
||||
|
||||
// This kills the process the healthcheck is running.
|
||||
// Which happens to be us.
|
||||
// So this has to be last - after this, systemd serves us a
|
||||
|
@ -281,10 +269,31 @@ func (c *Container) incrementStartupHCSuccessCounter(ctx context.Context) {
|
|||
// is the case here as we should not alter the exit code of another process that just
|
||||
// happened to call this.
|
||||
shutdown.SetExitCode(0)
|
||||
if err := c.removeTransientFiles(ctx, true, oldUnit); err != nil {
|
||||
logrus.Errorf("Error removing container %s healthcheck: %v", c.ID(), err)
|
||||
return
|
||||
}
|
||||
c.recreateHealthCheckTimer(ctx, false, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Container) recreateHealthCheckTimer(ctx context.Context, isStartup bool, isStartupRemoved bool) {
|
||||
logrus.Infof("Startup healthcheck for container %s passed, recreating timer", c.ID())
|
||||
|
||||
oldUnit := c.state.HCUnitName
|
||||
// Create the new, standard healthcheck timer first.
|
||||
interval := c.HealthCheckConfig().Interval.String()
|
||||
if isStartup {
|
||||
interval = c.config.StartupHealthCheckConfig.StartInterval.String()
|
||||
}
|
||||
|
||||
if err := c.createTimer(interval, isStartup); err != nil {
|
||||
logrus.Errorf("Error recreating container %s (isStartup: %t) healthcheck: %v", c.ID(), isStartup, err)
|
||||
return
|
||||
}
|
||||
if err := c.startTimer(isStartup); err != nil {
|
||||
logrus.Errorf("Error restarting container %s (isStartup: %t) healthcheck timer: %v", c.ID(), isStartup, err)
|
||||
}
|
||||
|
||||
if err := c.removeTransientFiles(ctx, isStartupRemoved, oldUnit); err != nil {
|
||||
logrus.Errorf("Error removing container %s healthcheck: %v", c.ID(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
//go:build !remote
|
||||
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/podman/v5/libpod/define"
|
||||
"github.com/containers/podman/v5/pkg/specgenutil"
|
||||
)
|
||||
|
||||
type HealthCheckConfig struct {
|
||||
*manifest.Schema2HealthConfig
|
||||
}
|
||||
|
||||
type StartupHealthCheckConfig struct {
|
||||
*define.StartupHealthCheck
|
||||
}
|
||||
|
||||
type IHealthCheckConfig interface {
|
||||
SetTo(config *ContainerConfig)
|
||||
IsStartup() bool
|
||||
IsNil() bool
|
||||
IsTimeChanged(oldInterval time.Duration) bool
|
||||
GetInterval() time.Duration
|
||||
SetCurrentConfigTo(healthCheckOptions *define.HealthCheckOptions)
|
||||
IsHealthCheckCommandSet(updateHealthCheckConfig define.UpdateHealthCheckConfig) bool
|
||||
SetNewHealthCheckOptions(updateHealthCheckConfig define.UpdateHealthCheckConfig, healthCheckOptions *define.HealthCheckOptions) bool
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) SetTo(config *ContainerConfig) {
|
||||
config.HealthCheckConfig = h.Schema2HealthConfig
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) SetTo(config *ContainerConfig) {
|
||||
config.StartupHealthCheckConfig = h.StartupHealthCheck
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) IsNil() bool {
|
||||
return h.Schema2HealthConfig == nil
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) IsNil() bool {
|
||||
return h.StartupHealthCheck == nil
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) IsStartup() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) IsStartup() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) IsTimeChanged(oldInterval time.Duration) bool {
|
||||
return h.Interval != oldInterval
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) IsTimeChanged(oldInterval time.Duration) bool {
|
||||
return h.Interval != oldInterval
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) GetInterval() time.Duration {
|
||||
return h.Interval
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) GetInterval() time.Duration {
|
||||
return h.Interval
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) SetCurrentConfigTo(healthCheckOptions *define.HealthCheckOptions) {
|
||||
healthCheckOptions.Cmd = strings.Join(h.Test, " ")
|
||||
healthCheckOptions.Interval = h.Interval.String()
|
||||
healthCheckOptions.Retries = h.Retries
|
||||
healthCheckOptions.Timeout = h.Timeout.String()
|
||||
healthCheckOptions.StartPeriod = h.StartPeriod.String()
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) SetCurrentConfigTo(healthCheckOptions *define.HealthCheckOptions) {
|
||||
healthCheckOptions.Cmd = strings.Join(h.Test, " ")
|
||||
healthCheckOptions.Interval = h.Interval.String()
|
||||
healthCheckOptions.Retries = h.Retries
|
||||
healthCheckOptions.Timeout = h.Timeout.String()
|
||||
healthCheckOptions.Successes = h.Successes
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) IsHealthCheckCommandSet(updateHealthCheckConfig define.UpdateHealthCheckConfig) bool {
|
||||
return updateHealthCheckConfig.IsHealthCheckCommandSet(h.Schema2HealthConfig)
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) IsHealthCheckCommandSet(updateHealthCheckConfig define.UpdateHealthCheckConfig) bool {
|
||||
return updateHealthCheckConfig.IsStartupHealthCheckCommandSet(h.StartupHealthCheck)
|
||||
}
|
||||
|
||||
func (h *HealthCheckConfig) SetNewHealthCheckOptions(updateHealthCheckConfig define.UpdateHealthCheckConfig, healthCheckOptions *define.HealthCheckOptions) bool {
|
||||
return updateHealthCheckConfig.SetNewHealthCheckConfigTo(healthCheckOptions)
|
||||
}
|
||||
|
||||
func (h *StartupHealthCheckConfig) SetNewHealthCheckOptions(updateHealthCheckConfig define.UpdateHealthCheckConfig, healthCheckOptions *define.HealthCheckOptions) bool {
|
||||
return updateHealthCheckConfig.SetNewStartupHealthCheckConfigTo(healthCheckOptions)
|
||||
}
|
||||
|
||||
func GetNewHealthCheckConfig(originalHealthCheckConfig IHealthCheckConfig, updateHealthCheckConfig define.UpdateHealthCheckConfig) (IHealthCheckConfig, bool, error) {
|
||||
if originalHealthCheckConfig.IsHealthCheckCommandSet(updateHealthCheckConfig) {
|
||||
return nil, false, errors.New("startup healthcheck command is not set")
|
||||
}
|
||||
|
||||
healthCheckOptions := define.HealthCheckOptions{
|
||||
Cmd: "",
|
||||
Interval: define.DefaultHealthCheckInterval,
|
||||
Retries: int(define.DefaultHealthCheckRetries),
|
||||
Timeout: define.DefaultHealthCheckTimeout,
|
||||
StartPeriod: define.DefaultHealthCheckStartPeriod,
|
||||
Successes: 0,
|
||||
}
|
||||
|
||||
if originalHealthCheckConfig.IsStartup() {
|
||||
healthCheckOptions.Retries = 0
|
||||
}
|
||||
|
||||
if !originalHealthCheckConfig.IsNil() {
|
||||
originalHealthCheckConfig.SetCurrentConfigTo(&healthCheckOptions)
|
||||
}
|
||||
|
||||
noHealthCheck := false
|
||||
if updateHealthCheckConfig.NoHealthCheck != nil {
|
||||
noHealthCheck = *updateHealthCheckConfig.NoHealthCheck
|
||||
}
|
||||
|
||||
changed := originalHealthCheckConfig.SetNewHealthCheckOptions(updateHealthCheckConfig, &healthCheckOptions)
|
||||
|
||||
if noHealthCheck && changed {
|
||||
return nil, false, errors.New("cannot specify both --no-healthcheck and other HealthCheck flags")
|
||||
}
|
||||
|
||||
if noHealthCheck {
|
||||
if originalHealthCheckConfig.IsStartup() {
|
||||
return &StartupHealthCheckConfig{StartupHealthCheck: nil}, true, nil
|
||||
}
|
||||
return &HealthCheckConfig{Schema2HealthConfig: &manifest.Schema2HealthConfig{Test: []string{"NONE"}}}, true, nil
|
||||
}
|
||||
|
||||
newHealthCheckConfig, err := specgenutil.MakeHealthCheckFromCli(
|
||||
healthCheckOptions.Cmd,
|
||||
healthCheckOptions.Interval,
|
||||
uint(healthCheckOptions.Retries),
|
||||
healthCheckOptions.Timeout,
|
||||
healthCheckOptions.StartPeriod,
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if originalHealthCheckConfig.IsStartup() {
|
||||
newStartupHealthCheckConfig := new(define.StartupHealthCheck)
|
||||
newStartupHealthCheckConfig.Schema2HealthConfig = *newHealthCheckConfig
|
||||
newStartupHealthCheckConfig.Successes = healthCheckOptions.Successes
|
||||
return &StartupHealthCheckConfig{StartupHealthCheck: newStartupHealthCheckConfig}, changed, nil
|
||||
}
|
||||
return &HealthCheckConfig{Schema2HealthConfig: newHealthCheckConfig}, changed, nil
|
||||
}
|
|
@ -6,8 +6,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -1521,25 +1519,11 @@ func WithHealthCheckLogDestination(destination string) CtrCreateOption {
|
|||
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
|
||||
dest, err := define.GetValidHealthCheckDestination(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctr.config.HealthLogDestination = dest
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -786,7 +786,7 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
restartRetries = &localRetries
|
||||
}
|
||||
|
||||
if err := ctr.Update(resources, restartPolicy, restartRetries); err != nil {
|
||||
if err := ctr.Update(resources, restartPolicy, restartRetries, &define.UpdateHealthCheckConfig{}); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("updating container: %w", err))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"github.com/containers/podman/v5/pkg/domain/infra/abi"
|
||||
"github.com/containers/podman/v5/pkg/util"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -443,12 +442,12 @@ func UpdateContainer(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
options := &handlers.UpdateEntities{Resources: &specs.LinuxResources{}}
|
||||
if err := json.NewDecoder(r.Body).Decode(&options.Resources); err != nil {
|
||||
options := &handlers.UpdateEntities{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&options); err != nil {
|
||||
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err))
|
||||
return
|
||||
}
|
||||
err = ctr.Update(options.Resources, restartPolicy, restartRetries)
|
||||
err = ctr.Update(&options.LinuxResources, restartPolicy, restartRetries, &options.UpdateHealthCheckConfig)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
|
|
|
@ -54,4 +54,6 @@ type networkUpdateRequestLibpod entities.NetworkUpdateOptions
|
|||
|
||||
// Container update
|
||||
// swagger:model
|
||||
type containerUpdateRequest container.UpdateConfig
|
||||
type containerUpdateRequest struct {
|
||||
container.UpdateConfig
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/containers/podman/v5/libpod/define"
|
||||
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||
docker "github.com/docker/docker/api/types"
|
||||
dockerBackend "github.com/docker/docker/api/types/backend"
|
||||
|
@ -73,7 +74,8 @@ type LibpodContainersRmReport struct {
|
|||
// UpdateEntities used to wrap the oci resource spec in a swagger model
|
||||
// swagger:model
|
||||
type UpdateEntities struct {
|
||||
Resources *specs.LinuxResources
|
||||
specs.LinuxResources
|
||||
define.UpdateHealthCheckConfig
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
|
|
|
@ -1778,8 +1778,8 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
|||
// ---
|
||||
// tags:
|
||||
// - containers
|
||||
// summary: Update an existing containers cgroup configuration
|
||||
// description: Update an existing containers cgroup configuration.
|
||||
// summary: Updates the configuration of an existing container, allowing changes to resource limits and healthchecks
|
||||
// description: Updates the configuration of an existing container, allowing changes to resource limits and healthchecks.
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/api/handlers"
|
||||
"github.com/containers/podman/v5/pkg/bindings"
|
||||
"github.com/containers/podman/v5/pkg/domain/entities/types"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
@ -25,12 +26,15 @@ func Update(ctx context.Context, options *types.ContainerUpdateOptions) (string,
|
|||
params.Set("restartRetries", strconv.Itoa(int(*options.Specgen.RestartRetries)))
|
||||
}
|
||||
}
|
||||
|
||||
resources, err := jsoniter.MarshalToString(options.Specgen.ResourceLimits)
|
||||
updateEntities := &handlers.UpdateEntities{
|
||||
LinuxResources: *options.Specgen.ResourceLimits,
|
||||
UpdateHealthCheckConfig: *options.ChangedHealthCheckConfiguration,
|
||||
}
|
||||
requestData, err := jsoniter.MarshalToString(updateEntities)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stringReader := strings.NewReader(resources)
|
||||
stringReader := strings.NewReader(requestData)
|
||||
response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/containers/%s/update", params, nil, options.NameOrID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -36,6 +36,7 @@ type ContainerStatsReport struct {
|
|||
}
|
||||
|
||||
type ContainerUpdateOptions struct {
|
||||
NameOrID string
|
||||
Specgen *specgen.SpecGenerator
|
||||
NameOrID string
|
||||
Specgen *specgen.SpecGenerator
|
||||
ChangedHealthCheckConfiguration *define.UpdateHealthCheckConfig
|
||||
}
|
||||
|
|
|
@ -1806,13 +1806,14 @@ func (ic *ContainerEngine) ContainerUpdate(ctx context.Context, updateOptions *e
|
|||
if len(containers) != 1 {
|
||||
return "", fmt.Errorf("container not found")
|
||||
}
|
||||
container := containers[0].Container
|
||||
|
||||
var restartPolicy *string
|
||||
if updateOptions.Specgen.RestartPolicy != "" {
|
||||
restartPolicy = &updateOptions.Specgen.RestartPolicy
|
||||
}
|
||||
|
||||
if err = containers[0].Update(updateOptions.Specgen.ResourceLimits, restartPolicy, updateOptions.Specgen.RestartRetries); err != nil {
|
||||
if err = container.Update(updateOptions.Specgen.ResourceLimits, restartPolicy, updateOptions.Specgen.RestartRetries, updateOptions.ChangedHealthCheckConfiguration); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return containers[0].ID(), nil
|
||||
|
|
|
@ -354,7 +354,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||
if c.NoHealthCheck {
|
||||
return errors.New("cannot specify both --no-healthcheck and --health-cmd")
|
||||
}
|
||||
s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod, false)
|
||||
s.HealthConfig, err = MakeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||
// The hardcoded "1s" will be discarded, as the startup
|
||||
// healthcheck does not have a period. So just hardcode
|
||||
// something that parses correctly.
|
||||
tmpHcConfig, err := makeHealthCheckFromCli(c.StartupHCCmd, c.StartupHCInterval, c.StartupHCRetries, c.StartupHCTimeout, "1s", true)
|
||||
tmpHcConfig, err := MakeHealthCheckFromCli(c.StartupHCCmd, c.StartupHCInterval, c.StartupHCRetries, c.StartupHCTimeout, "1s", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -948,7 +948,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
|
|||
return nil
|
||||
}
|
||||
|
||||
func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string, isStartup bool) (*manifest.Schema2HealthConfig, error) {
|
||||
func MakeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string, isStartup bool) (*manifest.Schema2HealthConfig, error) {
|
||||
cmdArr := []string{}
|
||||
isArr := true
|
||||
err := json.Unmarshal([]byte(inCmd), &cmdArr) // array unmarshalling
|
||||
|
@ -1017,7 +1017,6 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start
|
|||
return nil, errors.New("healthcheck-start-period must be 0 seconds or greater")
|
||||
}
|
||||
hc.StartPeriod = startPeriodDuration
|
||||
|
||||
return &hc, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -161,4 +161,152 @@ device-write-iops = /dev/zero:4000 | - | -
|
|||
run_podman rm -f -t0 testctr
|
||||
}
|
||||
|
||||
# HealthCheck configuration
|
||||
|
||||
function nrand() {
|
||||
# 1-59 seconds. Don't exceed 59, because podman then shows as "1mXXs"
|
||||
echo $((1 + RANDOM % 58))
|
||||
}
|
||||
|
||||
# bats test_tags=ci:parallel
|
||||
@test "podman update - test all HealthCheck flags" {
|
||||
local ctrname="c-h-$(safename)"
|
||||
local msg="healthmsg-$(random_string)"
|
||||
local TMP_DIR_HEALTHCHECK="$PODMAN_TMPDIR/healthcheck"
|
||||
mkdir $TMP_DIR_HEALTHCHECK
|
||||
|
||||
# flag-name | value | inspect format, .Config.Xxx
|
||||
tests="
|
||||
cmd | echo $msg | Healthcheck.Test
|
||||
interval | $(nrand)s | Healthcheck.Interval
|
||||
log-destination | $TMP_DIR_HEALTHCHECK | HealthLogDestination
|
||||
max-log-count | $(nrand) | HealthMaxLogCount
|
||||
max-log-size | $(nrand) | HealthMaxLogSize
|
||||
on-failure | restart | HealthcheckOnFailureAction
|
||||
retries | $(nrand) | Healthcheck.Retries
|
||||
timeout | $(nrand)s | Healthcheck.Timeout
|
||||
start-period | $(nrand)s | Healthcheck.StartPeriod
|
||||
startup-cmd | echo $msg | StartupHealthCheck.Test
|
||||
startup-interval | $(nrand)s | StartupHealthCheck.Interval
|
||||
startup-retries | $(nrand) | StartupHealthCheck.Retries
|
||||
startup-success | $(nrand) | StartupHealthCheck.Successes
|
||||
startup-timeout | $(nrand)s | StartupHealthCheck.Timeout
|
||||
"
|
||||
|
||||
run_podman run -d --name $ctrname $IMAGE top
|
||||
cid="$output"
|
||||
|
||||
# Pass 1: read the table above, gather up the options, format and expected values
|
||||
local -a opts
|
||||
local -A formats
|
||||
local -A checks
|
||||
while read opt value format ; do
|
||||
fullopt="--health-$opt=$value"
|
||||
opts+=("$fullopt")
|
||||
formats["$fullopt"]="{{.Config.$format}}"
|
||||
expected=$value
|
||||
# Special case for commands
|
||||
if [[ $opt =~ cmd ]]; then
|
||||
expected="[CMD-SHELL $value]"
|
||||
fi
|
||||
checks["$fullopt"]=$expected
|
||||
done < <(parse_table "$tests")
|
||||
|
||||
# Now do the update in one fell swoop
|
||||
run_podman update "${opts[@]}" $ctrname
|
||||
|
||||
# ...and check one by one
|
||||
defer-assertion-failures
|
||||
for opt in "${opts[@]}"; do
|
||||
run_podman inspect $ctrname --format "${formats[$opt]}"
|
||||
assert "$output" == "${checks[$opt]}" "$opt"
|
||||
done
|
||||
immediate-assertion-failures
|
||||
|
||||
# Clean up
|
||||
run_podman rm -f -t0 $cid
|
||||
}
|
||||
|
||||
# bats test_tags=ci:parallel
|
||||
@test "podman update - test HealthCheck flags without HealthCheck commands" {
|
||||
local ctrname="c-h-$(safename)"
|
||||
|
||||
# flag-name=value
|
||||
tests="
|
||||
interval=10s
|
||||
retries=5
|
||||
timeout=10s
|
||||
start-period=10s
|
||||
startup-interval=10s
|
||||
startup-retries=5
|
||||
startup-success=10
|
||||
startup-timeout=10s
|
||||
"
|
||||
|
||||
run_podman run -d --name $ctrname $IMAGE top
|
||||
cid="$output"
|
||||
|
||||
defer-assertion-failures
|
||||
for opt in $tests; do
|
||||
run_podman 125 update "--health-$opt" $ctrname
|
||||
assert "$output" =~ "healthcheck command is not set" "--$opt with no startup"
|
||||
done
|
||||
immediate-assertion-failures
|
||||
|
||||
run_podman rm -f -t0 $cid
|
||||
}
|
||||
|
||||
# bats test_tags=ci:parallel
|
||||
@test "podman update - --no-healthcheck" {
|
||||
local msg="healthmsg-$(random_string)"
|
||||
local ctrname="c-h-$(safename)"
|
||||
|
||||
run_podman run -d --name $ctrname \
|
||||
--health-cmd "echo $msg" \
|
||||
--health-startup-cmd "echo startup$msg" \
|
||||
$IMAGE /home/podman/pause
|
||||
cid="$output"
|
||||
|
||||
run_podman update $ctrname --no-healthcheck
|
||||
|
||||
run_podman inspect $ctrname --format {{.Config.Healthcheck.Test}}
|
||||
assert "$output" == "[NONE]" "HealthCheck command is disabled"
|
||||
|
||||
run_podman inspect $ctrname --format {{.Config.StartupHealthCheck}}
|
||||
assert "$output" == "<nil>" "startup HealthCheck command is disabled"
|
||||
|
||||
run_podman rm -t 0 -f $ctrname
|
||||
}
|
||||
|
||||
# bats test_tags=ci:parallel
|
||||
@test "podman update - check behavior - change cmd and destination healthcheck" {
|
||||
local TMP_DIR_HEALTHCHECK="$PODMAN_TMPDIR/healthcheck"
|
||||
mkdir $TMP_DIR_HEALTHCHECK
|
||||
local ctrname="c-h-$(safename)"
|
||||
local msg="healthmsg-$(random_string)"
|
||||
|
||||
run_podman run -d --name $ctrname \
|
||||
--health-cmd "echo $msg" \
|
||||
$IMAGE /home/podman/pause
|
||||
cid="$output"
|
||||
|
||||
run_podman healthcheck run $ctrname
|
||||
is "$output" "" "output from 'podman healthcheck run'"
|
||||
|
||||
# Run podman update in two separate runs to make sure HealthCheck is overwritten correctly.
|
||||
run_podman update $ctrname --health-cmd "echo healthmsg-new"
|
||||
|
||||
run_podman update $ctrname --health-log-destination $TMP_DIR_HEALTHCHECK
|
||||
|
||||
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 "healthmsg-new" $healthcheck_log_path)
|
||||
assert "$count" -ge 1 "Number of matching health log messages"
|
||||
|
||||
run_podman rm -t 0 -f $ctrname
|
||||
}
|
||||
# vim: filetype=sh
|
||||
|
|
Loading…
Reference in New Issue