Skip to content

Commit c684da0

Browse files
committed
Run HealthCheck without creating and removing the ExecSession in the database
Fixes: https://issues.redhat.com/browse/RHEL-69970 Signed-off-by: Jan Rodák <[email protected]>
1 parent e943a2b commit c684da0

File tree

3 files changed

+137
-26
lines changed

3 files changed

+137
-26
lines changed

Diff for: libpod/container_exec.go

+105-25
Original file line numberDiff line numberDiff line change
@@ -158,34 +158,20 @@ type legacyExecSession struct {
158158
PID int `json:"pid"`
159159
}
160160

161-
// ExecCreate creates a new exec session for the container.
162-
// The session is not started. The ID of the new exec session will be returned.
163-
func (c *Container) ExecCreate(config *ExecConfig) (string, error) {
164-
if !c.batched {
165-
c.lock.Lock()
166-
defer c.lock.Unlock()
167-
168-
if err := c.syncContainer(); err != nil {
169-
return "", err
170-
}
171-
}
172-
173-
// Verify our config
161+
func (c *Container) verifyExecConfig(config *ExecConfig) error {
174162
if config == nil {
175-
return "", fmt.Errorf("must provide a configuration to ExecCreate: %w", define.ErrInvalidArg)
163+
return fmt.Errorf("must provide a configuration to ExecCreate: %w", define.ErrInvalidArg)
176164
}
177165
if len(config.Command) == 0 {
178-
return "", fmt.Errorf("must provide a non-empty command to start an exec session: %w", define.ErrInvalidArg)
166+
return fmt.Errorf("must provide a non-empty command to start an exec session: %w", define.ErrInvalidArg)
179167
}
180168
if config.ExitCommandDelay > 0 && len(config.ExitCommand) == 0 {
181-
return "", fmt.Errorf("must provide a non-empty exit command if giving an exit command delay: %w", define.ErrInvalidArg)
182-
}
183-
184-
// Verify that we are in a good state to continue
185-
if !c.ensureState(define.ContainerStateRunning) {
186-
return "", fmt.Errorf("can only create exec sessions on running containers: %w", define.ErrCtrStateInvalid)
169+
return fmt.Errorf("must provide a non-empty exit command if giving an exit command delay: %w", define.ErrInvalidArg)
187170
}
171+
return nil
172+
}
188173

174+
func (c *Container) getUniqueExecSessionID() string {
189175
// Generate an ID for our new exec session
190176
sessionID := stringid.GenerateRandomID()
191177
found := true
@@ -202,20 +188,52 @@ func (c *Container) ExecCreate(config *ExecConfig) (string, error) {
202188
sessionID = stringid.GenerateRandomID()
203189
}
204190
}
191+
return sessionID
192+
}
205193

206-
// Make our new exec session
194+
func (c *Container) createExecSession(config *ExecConfig) (*ExecSession, error) {
207195
session := new(ExecSession)
208-
session.Id = sessionID
196+
session.Id = c.getUniqueExecSessionID()
209197
session.ContainerId = c.ID()
210198
session.State = define.ExecStateCreated
211199
session.Config = new(ExecConfig)
212200
if err := JSONDeepCopy(config, session.Config); err != nil {
213-
return "", fmt.Errorf("copying exec configuration into exec session: %w", err)
201+
return nil, fmt.Errorf("copying exec configuration into exec session: %w", err)
214202
}
215203

216204
if len(session.Config.ExitCommand) > 0 {
217205
session.Config.ExitCommand = append(session.Config.ExitCommand, []string{session.ID(), c.ID()}...)
218206
}
207+
return session, nil
208+
}
209+
210+
// ExecCreate creates a new exec session for the container.
211+
// The session is not started. The ID of the new exec session will be returned.
212+
func (c *Container) ExecCreate(config *ExecConfig) (string, error) {
213+
if !c.batched {
214+
c.lock.Lock()
215+
defer c.lock.Unlock()
216+
217+
if err := c.syncContainer(); err != nil {
218+
return "", err
219+
}
220+
}
221+
222+
// Verify our config
223+
if err := c.verifyExecConfig(config); err != nil {
224+
return "", err
225+
}
226+
227+
// Verify that we are in a good state to continue
228+
if !c.ensureState(define.ContainerStateRunning) {
229+
return "", fmt.Errorf("can only create exec sessions on running containers: %w", define.ErrCtrStateInvalid)
230+
}
231+
232+
// Make our new exec session
233+
session, err := c.createExecSession(config)
234+
if err != nil {
235+
return "", err
236+
}
219237

220238
if c.state.ExecSessions == nil {
221239
c.state.ExecSessions = make(map[string]*ExecSession)
@@ -232,7 +250,7 @@ func (c *Container) ExecCreate(config *ExecConfig) (string, error) {
232250

233251
logrus.Infof("Created exec session %s in container %s", session.ID(), c.ID())
234252

235-
return sessionID, nil
253+
return session.Id, nil
236254
}
237255

238256
// ExecStart starts an exec session in the container, but does not attach to it.
@@ -775,6 +793,68 @@ func (c *Container) ExecResize(sessionID string, newSize resize.TerminalSize) er
775793
return c.ociRuntime.ExecAttachResize(c, sessionID, newSize)
776794
}
777795

796+
func (c *Container) healthCheckExec(config *ExecConfig, streams *define.AttachStreams) (int, error) {
797+
unlock := true
798+
if !c.batched {
799+
c.lock.Lock()
800+
defer func() {
801+
if unlock {
802+
c.lock.Unlock()
803+
}
804+
}()
805+
806+
if err := c.syncContainer(); err != nil {
807+
return -1, err
808+
}
809+
}
810+
811+
if err := c.verifyExecConfig(config); err != nil {
812+
return -1, err
813+
}
814+
815+
if !c.ensureState(define.ContainerStateRunning) {
816+
return -1, fmt.Errorf("can only create exec sessions on running containers: %w", define.ErrCtrStateInvalid)
817+
}
818+
819+
session, err := c.createExecSession(config)
820+
if err != nil {
821+
return -1, err
822+
}
823+
824+
if c.state.ExecSessions == nil {
825+
c.state.ExecSessions = make(map[string]*ExecSession)
826+
}
827+
c.state.ExecSessions[session.ID()] = session
828+
defer delete(c.state.ExecSessions, session.ID())
829+
830+
opts, err := prepareForExec(c, session)
831+
if err != nil {
832+
return -1, err
833+
}
834+
defer func() {
835+
if err := c.cleanupExecBundle(session.ID()); err != nil {
836+
logrus.Errorf("Container %s light exec session cleanup error: %v", c.ID(), err)
837+
}
838+
}()
839+
840+
pid, attachErrChan, err := c.ociRuntime.ExecContainer(c, session.ID(), opts, streams, nil)
841+
if err != nil {
842+
return -1, err
843+
}
844+
845+
if !c.batched {
846+
c.lock.Unlock()
847+
unlock = false
848+
}
849+
850+
err = <-attachErrChan
851+
if err != nil {
852+
return -1, fmt.Errorf("container %s light exec session with pid: %d error: %v", c.ID(), pid, err)
853+
}
854+
855+
return c.readExecExitCode(session.ID())
856+
}
857+
778858
func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan resize.TerminalSize) (int, error) {
779859
return c.exec(config, streams, resize, false)
780860
}

Diff for: libpod/healthcheck.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func (c *Container) runHealthCheck(ctx context.Context, isStartup bool) (define.
9797
hcResult := define.HealthCheckSuccess
9898
config := new(ExecConfig)
9999
config.Command = newCommand
100-
exitCode, hcErr := c.exec(config, streams, nil, true)
100+
exitCode, hcErr := c.healthCheckExec(config, streams)
101101
if hcErr != nil {
102102
hcResult = define.HealthCheckFailure
103103
if errors.Is(hcErr, define.ErrOCIRuntimeNotFound) ||

Diff for: test/system/220-healthcheck.bats

+31
Original file line numberDiff line numberDiff line change
@@ -466,4 +466,35 @@ function _check_health_log {
466466
run_podman rm -t 0 -f $ctrname
467467
}
468468

469+
@test "podman healthcheck - stop container when healthcheck runs" {
470+
ctr="c-h-$(safename)"
471+
msg="hc-msg-$(random_string)"
472+
473+
run_podman run -d --name $ctr \
474+
--health-cmd "sleep 20; echo $msg" \
475+
$IMAGE /home/podman/pause
476+
477+
timeout --foreground -v --kill=10 60 \
478+
$PODMAN healthcheck run $ctr &
479+
hc_pid=$!
480+
481+
run_podman inspect $ctr --format "{{.State.Status}}"
482+
assert "$output" == "running" "Container is running"
483+
484+
run_podman stop $ctr
485+
486+
# Wait for background healthcheck to finish and make sure the exit status is 1
487+
rc=0
488+
wait -n $hc_pid || rc=$?
489+
assert $rc -eq 1 "exit status check of healthcheck command"
490+
491+
run_podman inspect $ctr --format "{{.State.Status}}"
492+
assert "$output" == "exited" "Container is stopped"
493+
494+
run_podman inspect $ctr --format "{{.State.Health.Log}}"
495+
assert "$output" !~ "$msg" "Health log message not found"
496+
497+
run_podman rm -f -t0 $ctr
498+
}
499+
469500
# vim: filetype=sh

0 commit comments

Comments
 (0)