fix(host): container exec probing (#21979)

This commit is contained in:
Zexi Li
2025-01-16 10:42:00 +08:00
committed by GitHub
parent 324cfe02f6
commit e41a91ff60
8 changed files with 59 additions and 26 deletions
+7
View File
@@ -437,6 +437,13 @@ func (c *SContainer) ValidateUpdateData(ctx context.Context, userCred mcclient.T
return input, nil
}
func (c *SContainer) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
c.SVirtualResourceBase.PostUpdate(ctx, userCred, query, data)
if err := c.GetPod().StartSyncTaskWithoutSyncstatus(ctx, userCred, false, ""); err != nil {
log.Errorf("container %s StartSyncTaskWithoutSyncstatus error: %v", c.GetName(), err)
}
}
func (c *SContainer) GetPod() *SGuest {
return GuestManager.FetchGuestById(c.GuestId)
}
+1 -10
View File
@@ -17,8 +17,6 @@ package lifecycle
import (
"context"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
@@ -42,17 +40,10 @@ func (e execDriver) GetType() apis.ContainerLifecyleHandlerType {
func (e execDriver) Run(ctx context.Context, input *apis.ContainerLifecyleHandler, cri pod.CRI, id string) error {
cfg := input.Exec
cli := cri.GetRuntimeClient()
resp, err := cli.ExecSync(ctx, &runtimeapi.ExecSyncRequest{
ContainerId: id,
Cmd: cfg.Command,
})
resp, err := cri.ExecSync(ctx, id, cfg.Command, 0)
if err != nil {
return errors.Wrapf(err, "exec sync")
}
if resp.GetExitCode() != 0 {
return errors.Wrapf(err, "stdout: %s, stderr: %s, exited: %d", resp.GetStdout(), resp.GetStderr(), resp.GetExitCode())
}
log.Infof("run command %v: stdout: %s, stderr: %s", cfg.Command, resp.Stdout, resp.Stderr)
return nil
}
+8 -1
View File
@@ -33,6 +33,7 @@ package prober
import (
"fmt"
"io"
"strings"
"time"
"yunion.io/x/log"
@@ -127,7 +128,7 @@ func (pb *prober) runProbe(probeType apis.ContainerProbeType, p *apis.ContainerP
timeout := time.Duration(p.TimeoutSeconds) * time.Second
if p.Exec != nil {
log.Debugf("Exec-Probe Pod: %v, Container: %v, Command: %v", pod.GetDesc().Name, container.Name, p.Exec.Command)
return pb.exec.Probe(pb.newExecInContainer(pod, container, p.Exec.Command, timeout))
return pb.exec.Probe(pb.newExecInContainer(pod, container, p.Exec.Command, timeout), strings.Join(p.Exec.Command, " "))
}
if p.TCPSocket != nil {
port := p.TCPSocket.Port
@@ -156,16 +157,22 @@ type execInContainer struct {
// error is returned if one occurred.
run func() ([]byte, error)
writer io.Writer
cmd []string
}
func (pb *prober) newExecInContainer(pod IPod, container *hostapi.ContainerDesc, cmd []string, timeout time.Duration) exec.Cmd {
return &execInContainer{
cmd: cmd,
run: func() ([]byte, error) {
return pb.runner.RunInContainer(pod.GetId(), container.Id, cmd, timeout)
},
}
}
func (eic *execInContainer) Command() []string {
return eic.cmd
}
func (eic *execInContainer) Run() error {
return nil
}
@@ -287,7 +287,7 @@ func (d disk) Unmount(pod volume_mount.IPodInfo, ctrId string, vm *hostapi.Conta
}
if len(vm.Disk.PostOverlay) != 0 {
if err := d.UnmountPostOverlays(pod, ctrId, vm, vm.Disk.PostOverlay, false, false); err != nil {
return errors.Wrap(err, "mount post overlay dirs")
return errors.Wrap(err, "umount post overlay dirs")
}
}
if vm.Disk.Overlay != nil {
+1 -6
View File
@@ -99,12 +99,7 @@ func (cr *containerRunner) RunInContainer(podId string, containerId string, cmd
if err != nil {
return nil, errors.Wrap(err, "get container cri id")
}
cli := s.getCRI().GetRuntimeClient()
resp, err := cli.ExecSync(context.Background(), &runtimeapi.ExecSyncRequest{
ContainerId: ctrCriId,
Cmd: cmd,
Timeout: int64(timeout),
})
resp, err := s.getCRI().ExecSync(context.Background(), ctrCriId, cmd, int64(timeout.Seconds()))
if err != nil {
return nil, errors.Wrapf(err, "exec sync %#v to %s", cmd, ctrCriId)
}
+31
View File
@@ -30,6 +30,7 @@ import (
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/exec"
"yunion.io/x/onecloud/pkg/util/procutils"
)
@@ -49,6 +50,7 @@ type CRI interface {
ListImages(ctx context.Context, filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error)
PullImage(ctx context.Context, req *runtimeapi.PullImageRequest) (*runtimeapi.PullImageResponse, error)
ImageStatus(ctx context.Context, req *runtimeapi.ImageStatusRequest) (*runtimeapi.ImageStatusResponse, error)
ExecSync(ctx context.Context, ctrId string, command []string, timeout int64) (*ExecSyncResponse, error)
// lower layer client
GetImageClient() runtimeapi.ImageServiceClient
@@ -509,3 +511,32 @@ func (c crictl) PullImage(ctx context.Context, req *runtimeapi.PullImageRequest)
func (c crictl) ImageStatus(ctx context.Context, req *runtimeapi.ImageStatusRequest) (*runtimeapi.ImageStatusResponse, error) {
return c.GetImageClient().ImageStatus(ctx, req)
}
type ExecSyncResponse struct {
Stdout []byte
Stderr []byte
ExitCode int32
}
func (c crictl) ExecSync(ctx context.Context, ctrId string, command []string, timeout int64) (*ExecSyncResponse, error) {
cli := c.GetRuntimeClient()
resp, err := cli.ExecSync(ctx, &runtimeapi.ExecSyncRequest{
ContainerId: ctrId,
Cmd: command,
Timeout: timeout,
})
if err != nil {
return nil, errors.Wrapf(err, "exec sync container %s with command: %v", ctrId, command)
}
if resp.GetExitCode() != 0 {
return nil, exec.CodeExitError{
Err: errors.Errorf("stdout: %s, stderr: %s, exited: %d", resp.GetStdout(), resp.GetStderr(), resp.GetExitCode()),
Code: int(resp.ExitCode),
}
}
return &ExecSyncResponse{
Stdout: resp.Stdout,
Stderr: resp.Stderr,
ExitCode: resp.ExitCode,
}, nil
}
+7 -5
View File
@@ -32,8 +32,10 @@ package exec
import (
"bytes"
"fmt"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/util/exec"
"yunion.io/x/onecloud/pkg/util/ioutils"
@@ -46,7 +48,7 @@ const (
// Prober is an interface defining the Probe object for container readiness/liveness checks.
type Prober interface {
Probe(e exec.Cmd) (probe.Result, string, error)
Probe(e exec.Cmd, info string) (probe.Result, string, error)
}
// New creates a Prober.
@@ -59,7 +61,7 @@ type execProber struct{}
// Probe executes a command to check the liveness/readiness of container
// from executing a command. Returns the Result status, command output, and
// errors if any.
func (pr execProber) Probe(e exec.Cmd) (probe.Result, string, error) {
func (pr execProber) Probe(e exec.Cmd, info string) (probe.Result, string, error) {
var dataBuffer bytes.Buffer
writer := ioutils.LimitWriter(&dataBuffer, maxReadLength)
@@ -71,14 +73,14 @@ func (pr execProber) Probe(e exec.Cmd) (probe.Result, string, error) {
}
data := dataBuffer.Bytes()
log.Infof("Exec probe response: %q", string(data))
log.Debugf("Exec probe response: %q, error: %v", string(data), err)
if err != nil {
exit, ok := err.(exec.ExitError)
exit, ok := errors.Cause(err).(exec.ExitError)
if ok {
if exit.ExitStatus() == 0 {
return probe.Success, string(data), nil
}
return probe.Failure, string(data), nil
return probe.Failure, fmt.Sprintf("%s, %s: %s", info, exit, data), nil
}
timeoutErr, ok := err.(*TimeoutError)
+3 -3
View File
@@ -142,7 +142,7 @@ func TestExec(t *testing.T) {
out: []byte(test.output),
err: test.err,
}
status, output, err := prober.Probe(&fake)
status, output, err := prober.Probe(&fake, "")
if status != test.expectedStatus {
t.Errorf("[%d] expected %v, got %v", i, test.expectedStatus, status)
}
@@ -152,8 +152,8 @@ func TestExec(t *testing.T) {
if err == nil && test.expectError == true {
t.Errorf("[%d] unexpected non-error", i)
}
if test.output != output {
t.Errorf("[%d] expected %s, got %s", i, test.output, output)
if status == probe.Success && test.output != output {
t.Errorf("[%d] expected %q, got %q", i, test.output, output)
}
}
}