Skip to content

Commit 30e4aac

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 1194557 commit 30e4aac

File tree

3 files changed

+124
-26
lines changed

3 files changed

+124
-26
lines changed

Diff for: libpod/container_exec.go

+99-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.
@@ -757,6 +775,62 @@ func (c *Container) ExecResize(sessionID string, newSize resize.TerminalSize) er
757775
return c.ociRuntime.ExecAttachResize(c, sessionID, newSize)
758776
}
759777

778+
func (c *Container) healthCheckExec(config *ExecConfig, streams *define.AttachStreams) (exitCode int, retErr error) {
779+
if !c.batched {
780+
c.lock.Lock()
781+
782+
if err := c.syncContainer(); err != nil {
783+
return -1, err
784+
}
785+
}
786+
787+
if err := c.verifyExecConfig(config); err != nil {
788+
return -1, err
789+
}
790+
791+
if !c.ensureState(define.ContainerStateRunning) {
792+
return -1, fmt.Errorf("can only create exec sessions on running containers: %w", define.ErrCtrStateInvalid)
793+
}
794+
795+
session, err := c.createExecSession(config)
796+
if err != nil {
797+
return -1, err
798+
}
799+
800+
if c.state.ExecSessions == nil {
801+
c.state.ExecSessions = make(map[string]*ExecSession)
802+
}
803+
c.state.ExecSessions[session.ID()] = session
804+
defer delete(c.state.ExecSessions, session.ID())
805+
806+
opts, err := prepareForExec(c, session)
807+
if err != nil {
808+
return -1, err
809+
}
810+
defer func() {
811+
if err := c.cleanupExecBundle(session.ID()); err != nil {
812+
logrus.Errorf("Container %s light exec session cleanup error: %v", c.ID(), err)
813+
}
814+
}()
815+
816+
pid, attachErrChan, err := c.ociRuntime.ExecContainer(c, session.ID(), opts, streams, nil)
817+
if err != nil {
818+
return -1, err
819+
}
820+
821+
if !c.batched {
822+
c.lock.Unlock()
823+
}
824+
825+
err = <-attachErrChan
826+
if err != nil {
827+
logrus.Errorf("Container %s light exec session with pid: %d error: %v", c.ID(), pid, err)
828+
return -1, err
829+
}
830+
831+
return c.readExecExitCode(session.ID())
832+
}
833+
760834
func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resize <-chan resize.TerminalSize) (int, error) {
761835
return c.exec(config, streams, resize, false)
762836
}

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

+24
Original file line numberDiff line numberDiff line change
@@ -466,4 +466,28 @@ 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+
run_podman 1 healthcheck run $ctr &
478+
479+
run_podman inspect $ctr --format "{{.State.Status}}"
480+
assert "$output" == "running" "Container is stopped"
481+
482+
run_podman stop $ctr
483+
484+
run_podman inspect $ctr --format "{{.State.Status}}"
485+
assert "$output" == "exited" "Container is stopped"
486+
487+
run_podman inspect $ctr --format "{{.State.Health.Log}}"
488+
assert "$output" !~ "$msg" "Health log message not found"
489+
490+
run_podman rm -f -t0 $ctr
491+
}
492+
469493
# vim: filetype=sh

0 commit comments

Comments
 (0)