diff --git a/internal/cri/server/sandbox_service.go b/internal/cri/server/sandbox_service.go index 8cec187caa..1510aaabed 100644 --- a/internal/cri/server/sandbox_service.go +++ b/internal/cri/server/sandbox_service.go @@ -110,6 +110,14 @@ func (c *criSandboxService) ShutdownSandbox(ctx context.Context, sandboxer strin return ctrl.Shutdown(ctx, sandboxID) } +func (c *criSandboxService) UpdateSandbox(ctx context.Context, sandboxer string, sandboxID string, sandbox sandbox.Sandbox, fields ...string) error { + ctrl, err := c.SandboxController(sandboxer) + if err != nil { + return err + } + return ctrl.Update(ctx, sandboxID, sandbox, fields...) +} + func (c *criSandboxService) StopSandbox(ctx context.Context, sandboxer, sandboxID string, opts ...sandbox.StopOpt) error { ctrl, err := c.SandboxController(sandboxer) if err != nil { diff --git a/internal/cri/server/sandbox_update_resources.go b/internal/cri/server/sandbox_update_resources.go index a38a3030a1..67bf432089 100644 --- a/internal/cri/server/sandbox_update_resources.go +++ b/internal/cri/server/sandbox_update_resources.go @@ -25,6 +25,7 @@ import ( "github.com/containerd/containerd/v2/internal/cri/server/podsandbox" sstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox" "github.com/containerd/containerd/v2/pkg/tracing" + "github.com/containerd/errdefs" "github.com/containerd/log" ) @@ -75,6 +76,14 @@ func (c *criService) UpdatePodSandboxResources(ctx context.Context, r *runtime.U return nil, fmt.Errorf("failed to update sandbox %s in core store: %w", sandbox.ID, err) } + if err := c.sandboxService.UpdateSandbox(ctx, sandboxInfo.Sandboxer, sandboxInfo.ID, sandboxInfo, "extensions"); err != nil { + // Tolerate these errors for older sandbox controllers that may not support this yet. + if !errdefs.IsNotImplemented(err) { + return nil, fmt.Errorf("failed to update sandbox controller: %w", err) + } + log.G(ctx).Tracef("sandbox controller %q does not implement Update endpoint", sandboxInfo.Sandboxer) + } + err = c.nri.PostUpdatePodSandboxResources(ctx, &sandbox) if err != nil { log.G(ctx).WithError(err).Errorf("NRI post-update notification failed") diff --git a/internal/cri/server/sandbox_update_resources_test.go b/internal/cri/server/sandbox_update_resources_test.go index 2860914e31..be6e1d9942 100644 --- a/internal/cri/server/sandbox_update_resources_test.go +++ b/internal/cri/server/sandbox_update_resources_test.go @@ -53,6 +53,17 @@ func (f *fakeSandboxStore) Update(_ context.Context, sb sandbox.Sandbox, _ ...st return sb, nil } +type recordSandboxService struct { + fakeSandboxService + calledID string + returnErr error +} + +func (s *recordSandboxService) UpdateSandbox(_ context.Context, _ string, sandboxID string, _ sandbox.Sandbox, _ ...string) error { + s.calledID = sandboxID + return s.returnErr +} + func TestUpdatePodSandboxResources(t *testing.T) { mySandbox := sstore.Metadata{ ID: "test-sandbox-id", @@ -150,4 +161,63 @@ func TestUpdatePodSandboxResources(t *testing.T) { assert.Equal(t, int64(100), updatedRes.Overhead.MemoryLimitInBytes) assert.Equal(t, int64(200), updatedRes.Resources.CpuQuota) }) + + t.Run("success fallback when sandbox controller does not implement update", func(t *testing.T) { + recordService := &recordSandboxService{returnErr: errdefs.ErrNotImplemented} + c := newTestCRIService(func(service *criService) { + service.sandboxService = recordService + client, _ := containerd.New("", containerd.WithServices(containerd.WithSandboxStore(&fakeSandboxStore{}))) + service.client = client + }) + s := sstore.NewSandbox(mySandbox, sstore.Status{}) + c.sandboxStore.Add(s) + + req := &runtime.UpdatePodSandboxResourcesRequest{ + PodSandboxId: "test-sandbox-id", + } + + _, err := c.UpdatePodSandboxResources(context.Background(), req) + require.NoError(t, err) + + assert.Equal(t, "test-sandbox-id", recordService.calledID) + }) + + t.Run("success when sandbox controller implements update", func(t *testing.T) { + recordService := &recordSandboxService{returnErr: nil} + c := newTestCRIService(func(service *criService) { + service.sandboxService = recordService + client, _ := containerd.New("", containerd.WithServices(containerd.WithSandboxStore(&fakeSandboxStore{}))) + service.client = client + }) + s := sstore.NewSandbox(mySandbox, sstore.Status{}) + c.sandboxStore.Add(s) + + req := &runtime.UpdatePodSandboxResourcesRequest{ + PodSandboxId: "test-sandbox-id", + } + + _, err := c.UpdatePodSandboxResources(context.Background(), req) + require.NoError(t, err) + + assert.Equal(t, "test-sandbox-id", recordService.calledID) + }) + + t.Run("fails when sandbox controller update fails", func(t *testing.T) { + recordService := &recordSandboxService{returnErr: errdefs.ErrInvalidArgument} + c := newTestCRIService(func(service *criService) { + service.sandboxService = recordService + client, _ := containerd.New("", containerd.WithServices(containerd.WithSandboxStore(&fakeSandboxStore{}))) + service.client = client + }) + s := sstore.NewSandbox(mySandbox, sstore.Status{}) + c.sandboxStore.Add(s) + + req := &runtime.UpdatePodSandboxResourcesRequest{ + PodSandboxId: "test-sandbox-id", + } + + _, err := c.UpdatePodSandboxResources(context.Background(), req) + require.Error(t, err) + assert.Contains(t, err.Error(), errdefs.ErrInvalidArgument.Error()) + }) } diff --git a/internal/cri/server/service.go b/internal/cri/server/service.go index c462d46dc0..953e665d28 100644 --- a/internal/cri/server/service.go +++ b/internal/cri/server/service.go @@ -78,6 +78,7 @@ type sandboxService interface { CreateSandbox(ctx context.Context, info sandbox.Sandbox, opts ...sandbox.CreateOpt) error StartSandbox(ctx context.Context, sandboxer string, sandboxID string) (sandbox.ControllerInstance, error) WaitSandbox(ctx context.Context, sandboxer string, sandboxID string) (<-chan containerd.ExitStatus, error) + UpdateSandbox(ctx context.Context, sandboxer string, sandboxID string, sandbox sandbox.Sandbox, fields ...string) error StopSandbox(ctx context.Context, sandboxer, sandboxID string, opts ...sandbox.StopOpt) error ShutdownSandbox(ctx context.Context, sandboxer string, sandboxID string) error SandboxStatus(ctx context.Context, sandboxer string, sandboxID string, verbose bool) (sandbox.ControllerStatus, error) diff --git a/internal/cri/server/service_test.go b/internal/cri/server/service_test.go index 5eaa7abff5..21efe3c7ab 100644 --- a/internal/cri/server/service_test.go +++ b/internal/cri/server/service_test.go @@ -47,6 +47,10 @@ func (f *fakeSandboxService) StartSandbox(ctx context.Context, sandboxer string, return sandbox.ControllerInstance{}, errdefs.ErrNotImplemented } +func (f *fakeSandboxService) UpdateSandbox(ctx context.Context, sandboxer string, sandboxID string, sandbox sandbox.Sandbox, fields ...string) error { + return errdefs.ErrNotImplemented +} + func (f *fakeSandboxService) StopSandbox(ctx context.Context, sandboxer, sandboxID string, opts ...sandbox.StopOpt) error { return errdefs.ErrNotImplemented }