Skip to content

Commit 9f8b845

Browse files
authored
Merge pull request containerd#9463 from abel-von/sandbox-plugin-1205
sandbox: remove sandboxStore from podsandbox controller
2 parents ddfa334 + 7dadd5f commit 9f8b845

19 files changed

+469
-298
lines changed

integration/main_test.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ import (
3434
"testing"
3535
"time"
3636

37+
"github.com/containerd/log"
38+
"github.com/opencontainers/selinux/go-selinux"
39+
"github.com/stretchr/testify/assert"
40+
"github.com/stretchr/testify/require"
41+
"google.golang.org/grpc"
42+
"google.golang.org/grpc/credentials/insecure"
43+
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
44+
"k8s.io/klog/v2"
45+
3746
containerd "github.com/containerd/containerd/v2/client"
3847
"github.com/containerd/containerd/v2/containers"
3948
cri "github.com/containerd/containerd/v2/integration/cri-api/pkg/apis"
@@ -42,16 +51,8 @@ import (
4251
dialer "github.com/containerd/containerd/v2/integration/remote/util"
4352
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
4453
"github.com/containerd/containerd/v2/pkg/cri/constants"
45-
"github.com/containerd/containerd/v2/pkg/cri/server"
54+
"github.com/containerd/containerd/v2/pkg/cri/server/base"
4655
"github.com/containerd/containerd/v2/pkg/cri/util"
47-
"github.com/containerd/log"
48-
"github.com/opencontainers/selinux/go-selinux"
49-
"github.com/stretchr/testify/assert"
50-
"github.com/stretchr/testify/require"
51-
"google.golang.org/grpc"
52-
"google.golang.org/grpc/credentials/insecure"
53-
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
54-
"k8s.io/klog/v2"
5556
)
5657

5758
const (
@@ -676,7 +677,7 @@ func CRIConfig() (*criconfig.Config, error) {
676677
}
677678

678679
// SandboxInfo gets sandbox info.
679-
func SandboxInfo(id string) (*runtime.PodSandboxStatus, *server.SandboxInfo, error) {
680+
func SandboxInfo(id string) (*runtime.PodSandboxStatus, *base.SandboxInfo, error) {
680681
client, err := RawRuntimeClient()
681682
if err != nil {
682683
return nil, nil, fmt.Errorf("failed to get raw runtime client: %w", err)
@@ -689,7 +690,7 @@ func SandboxInfo(id string) (*runtime.PodSandboxStatus, *server.SandboxInfo, err
689690
return nil, nil, fmt.Errorf("failed to get sandbox status: %w", err)
690691
}
691692
status := resp.GetStatus()
692-
var info server.SandboxInfo
693+
var info base.SandboxInfo
693694
if err := json.Unmarshal([]byte(resp.GetInfo()["info"]), &info); err != nil {
694695
return nil, nil, fmt.Errorf("failed to unmarshal sandbox info: %w", err)
695696
}

integration/sandbox_run_rollback_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
"github.com/stretchr/testify/require"
3636
criapiv1 "k8s.io/cri-api/pkg/apis/runtime/v1"
3737

38-
"github.com/containerd/containerd/v2/pkg/cri/server/podsandbox"
38+
"github.com/containerd/containerd/v2/pkg/cri/server/base"
3939
"github.com/containerd/containerd/v2/pkg/failpoint"
4040
)
4141

@@ -299,7 +299,7 @@ func TestRunPodSandboxAndTeardownCNISlow(t *testing.T) {
299299
}
300300

301301
// sbserverSandboxInfo gets sandbox info.
302-
func sbserverSandboxInfo(id string) (*criapiv1.PodSandboxStatus, *podsandbox.SandboxInfo, error) {
302+
func sbserverSandboxInfo(id string) (*criapiv1.PodSandboxStatus, *base.SandboxInfo, error) {
303303
client, err := RawRuntimeClient()
304304
if err != nil {
305305
return nil, nil, fmt.Errorf("failed to get raw runtime client: %w", err)
@@ -312,7 +312,7 @@ func sbserverSandboxInfo(id string) (*criapiv1.PodSandboxStatus, *podsandbox.San
312312
return nil, nil, fmt.Errorf("failed to get sandbox status: %w", err)
313313
}
314314
status := resp.GetStatus()
315-
var info podsandbox.SandboxInfo
315+
var info base.SandboxInfo
316316
if err := json.Unmarshal([]byte(resp.GetInfo()["info"]), &info); err != nil {
317317
return nil, nil, fmt.Errorf("failed to unmarshal sandbox info: %w", err)
318318
}

pkg/cri/server/base/sandbox_info.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package base
18+
19+
import (
20+
"github.com/containerd/go-cni"
21+
"github.com/opencontainers/runtime-spec/specs-go"
22+
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
23+
24+
"github.com/containerd/containerd/v2/pkg/cri/store/sandbox"
25+
)
26+
27+
// SandboxInfo is extra information for sandbox.
28+
// TODO (mikebrow): discuss predefining constants structures for some or all of these field names in CRI
29+
type SandboxInfo struct {
30+
Pid uint32 `json:"pid"`
31+
Status string `json:"processStatus"`
32+
NetNSClosed bool `json:"netNamespaceClosed"`
33+
Image string `json:"image"`
34+
SnapshotKey string `json:"snapshotKey"`
35+
Snapshotter string `json:"snapshotter"`
36+
// Note: a new field `RuntimeHandler` has been added into the CRI PodSandboxStatus struct, and
37+
// should be set. This `RuntimeHandler` field will be deprecated after containerd 1.3 (tracked
38+
// in https://github.com/containerd/cri/issues/1064).
39+
RuntimeHandler string `json:"runtimeHandler"` // see the Note above
40+
RuntimeType string `json:"runtimeType"`
41+
RuntimeOptions interface{} `json:"runtimeOptions"`
42+
Config *runtime.PodSandboxConfig `json:"config"`
43+
// Note: RuntimeSpec may not be populated if the sandbox has not been fully created.
44+
RuntimeSpec *specs.Spec `json:"runtimeSpec"`
45+
CNIResult *cni.Result `json:"cniResult"`
46+
Metadata *sandbox.Metadata `json:"sandboxMetadata"`
47+
}

pkg/cri/server/events.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ import (
2323
"sync"
2424
"time"
2525

26+
"github.com/containerd/log"
27+
"github.com/containerd/typeurl/v2"
28+
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
29+
"k8s.io/utils/clock"
30+
2631
eventtypes "github.com/containerd/containerd/v2/api/events"
2732
apitasks "github.com/containerd/containerd/v2/api/services/tasks/v1"
2833
containerdio "github.com/containerd/containerd/v2/cio"
@@ -34,10 +39,6 @@ import (
3439
sandboxstore "github.com/containerd/containerd/v2/pkg/cri/store/sandbox"
3540
ctrdutil "github.com/containerd/containerd/v2/pkg/cri/util"
3641
"github.com/containerd/containerd/v2/protobuf"
37-
"github.com/containerd/log"
38-
"github.com/containerd/typeurl/v2"
39-
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
40-
"k8s.io/utils/clock"
4142
)
4243

4344
const (
@@ -108,7 +109,7 @@ func (em *eventMonitor) subscribe(subscriber events.Subscriber) {
108109
}
109110

110111
// startSandboxExitMonitor starts an exit monitor for a given sandbox.
111-
func (em *eventMonitor) startSandboxExitMonitor(ctx context.Context, id string, pid uint32, exitCh <-chan containerd.ExitStatus) <-chan struct{} {
112+
func (em *eventMonitor) startSandboxExitMonitor(ctx context.Context, id string, exitCh <-chan containerd.ExitStatus) <-chan struct{} {
112113
stopCh := make(chan struct{})
113114
go func() {
114115
defer close(stopCh)

pkg/cri/server/podsandbox/controller.go

Lines changed: 25 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ import (
3434
"github.com/containerd/containerd/v2/pkg/cri/constants"
3535
"github.com/containerd/containerd/v2/pkg/cri/server/base"
3636
"github.com/containerd/containerd/v2/pkg/cri/server/images"
37+
"github.com/containerd/containerd/v2/pkg/cri/server/podsandbox/types"
3738
imagestore "github.com/containerd/containerd/v2/pkg/cri/store/image"
38-
sandboxstore "github.com/containerd/containerd/v2/pkg/cri/store/sandbox"
3939
ctrdutil "github.com/containerd/containerd/v2/pkg/cri/util"
4040
osinterface "github.com/containerd/containerd/v2/pkg/os"
4141
"github.com/containerd/containerd/v2/platforms"
@@ -116,8 +116,6 @@ type Controller struct {
116116
client *containerd.Client
117117
// imageService is a dependency to CRI image service.
118118
imageService ImageService
119-
// sandboxStore stores all resources associated with sandboxes.
120-
sandboxStore *sandboxstore.Store
121119
// os is an interface for all required os operations.
122120
os osinterface.OS
123121
// cri is CRI service that provides missing gaps needed by controller.
@@ -129,11 +127,9 @@ type Controller struct {
129127
}
130128

131129
func (c *Controller) Init(
132-
sandboxStore *sandboxstore.Store,
133130
cri CRIService,
134131
) {
135132
c.cri = cri
136-
c.sandboxStore = sandboxStore
137133
}
138134

139135
var _ sandbox.Controller = (*Controller)(nil)
@@ -143,63 +139,46 @@ func (c *Controller) Platform(_ctx context.Context, _sandboxID string) (platform
143139
}
144140

145141
func (c *Controller) Wait(ctx context.Context, sandboxID string) (sandbox.ExitStatus, error) {
146-
status := c.store.Get(sandboxID)
147-
if status == nil {
142+
podSandbox := c.store.Get(sandboxID)
143+
if podSandbox == nil {
148144
return sandbox.ExitStatus{}, fmt.Errorf("failed to get exit channel. %q", sandboxID)
149-
}
150-
151-
exitStatus, exitedAt, err := c.waitSandboxExit(ctx, sandboxID, status.Waiter)
152145

146+
}
147+
exit, err := podSandbox.Wait(ctx)
148+
if err != nil {
149+
return sandbox.ExitStatus{}, fmt.Errorf("failed to wait pod sandbox, %w", err)
150+
}
153151
return sandbox.ExitStatus{
154-
ExitStatus: exitStatus,
155-
ExitedAt: exitedAt,
152+
ExitStatus: exit.ExitCode(),
153+
ExitedAt: exit.ExitTime(),
156154
}, err
155+
157156
}
158157

159-
func (c *Controller) waitSandboxExit(ctx context.Context, id string, exitCh <-chan containerd.ExitStatus) (exitStatus uint32, exitedAt time.Time, err error) {
160-
exitStatus = unknownExitCode
161-
exitedAt = time.Now()
158+
func (c *Controller) waitSandboxExit(ctx context.Context, p *types.PodSandbox, exitCh <-chan containerd.ExitStatus) (exitStatus uint32, exitedAt time.Time, err error) {
162159
select {
163-
case exitRes := <-exitCh:
164-
log.G(ctx).Debugf("received sandbox exit %+v", exitRes)
165-
166-
exitStatus, exitedAt, err = exitRes.Result()
160+
case e := <-exitCh:
161+
exitStatus, exitedAt, err = e.Result()
167162
if err != nil {
168-
log.G(ctx).WithError(err).Errorf("failed to get task exit status for %q", id)
163+
log.G(ctx).WithError(err).Errorf("failed to get task exit status for %q", p.ID)
169164
exitStatus = unknownExitCode
170165
exitedAt = time.Now()
171166
}
172-
173-
err = func() error {
174-
dctx := ctrdutil.NamespacedContext()
175-
dctx, dcancel := context.WithTimeout(dctx, handleEventTimeout)
176-
defer dcancel()
177-
178-
sb, err := c.sandboxStore.Get(id)
179-
if err == nil {
180-
if err := handleSandboxExit(dctx, sb, &eventtypes.TaskExit{ExitStatus: exitStatus, ExitedAt: protobuf.ToTimestamp(exitedAt)}); err != nil {
181-
return err
182-
}
183-
return nil
184-
} else if !errdefs.IsNotFound(err) {
185-
return fmt.Errorf("failed to get sandbox %s: %w", id, err)
186-
}
187-
return nil
188-
}()
189-
if err != nil {
190-
log.G(ctx).WithError(err).Errorf("failed to handle sandbox TaskExit %s", id)
191-
// Don't backoff, the caller is responsible for.
192-
return
167+
dctx := ctrdutil.NamespacedContext()
168+
dctx, dcancel := context.WithTimeout(dctx, handleEventTimeout)
169+
defer dcancel()
170+
event := &eventtypes.TaskExit{ExitStatus: exitStatus, ExitedAt: protobuf.ToTimestamp(exitedAt)}
171+
if cleanErr := handleSandboxTaskExit(dctx, p, event); cleanErr != nil {
172+
c.cri.BackOffEvent(p.ID, e)
193173
}
174+
return
194175
case <-ctx.Done():
195-
return exitStatus, exitedAt, ctx.Err()
176+
return unknownExitCode, time.Now(), ctx.Err()
196177
}
197-
return
198178
}
199179

200-
// handleSandboxExit handles TaskExit event for sandbox.
201-
// TODO https://github.com/containerd/containerd/issues/7548
202-
func handleSandboxExit(ctx context.Context, sb sandboxstore.Sandbox, e *eventtypes.TaskExit) error {
180+
// handleSandboxTaskExit handles TaskExit event for sandbox.
181+
func handleSandboxTaskExit(ctx context.Context, sb *types.PodSandbox, e *eventtypes.TaskExit) error {
203182
// No stream attached to sandbox container.
204183
task, err := sb.Container.Task(ctx, nil)
205184
if err != nil {
@@ -212,17 +191,7 @@ func handleSandboxExit(ctx context.Context, sb sandboxstore.Sandbox, e *eventtyp
212191
if !errdefs.IsNotFound(err) {
213192
return fmt.Errorf("failed to stop sandbox: %w", err)
214193
}
215-
// Move on to make sure container status is updated.
216194
}
217195
}
218-
sb.Status.Update(func(status sandboxstore.Status) (sandboxstore.Status, error) {
219-
status.State = sandboxstore.StateNotReady
220-
status.Pid = 0
221-
status.ExitStatus = e.ExitStatus
222-
status.ExitedAt = e.ExitedAt.AsTime()
223-
return status, nil
224-
})
225-
// Using channel to propagate the information of sandbox stop
226-
sb.Stop()
227196
return nil
228197
}

pkg/cri/server/podsandbox/controller_test.go

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ import (
2121
"testing"
2222
"time"
2323

24+
"github.com/stretchr/testify/assert"
25+
26+
containerd "github.com/containerd/containerd/v2/client"
2427
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
25-
"github.com/containerd/containerd/v2/pkg/cri/store/label"
28+
"github.com/containerd/containerd/v2/pkg/cri/server/podsandbox/types"
2629
sandboxstore "github.com/containerd/containerd/v2/pkg/cri/store/sandbox"
2730
ostesting "github.com/containerd/containerd/v2/pkg/os/testing"
28-
"github.com/stretchr/testify/assert"
2931
)
3032

3133
const (
@@ -48,32 +50,25 @@ var testConfig = criconfig.Config{
4850

4951
// newControllerService creates a fake criService for test.
5052
func newControllerService() *Controller {
51-
labels := label.NewStore()
5253
return &Controller{
53-
config: testConfig,
54-
os: ostesting.NewFakeOS(),
55-
sandboxStore: sandboxstore.NewStore(labels),
54+
config: testConfig,
55+
os: ostesting.NewFakeOS(),
56+
store: NewStore(),
5657
}
5758
}
5859

5960
func Test_Status(t *testing.T) {
6061
sandboxID, pid, exitStatus := "1", uint32(1), uint32(0)
6162
createdAt, exitedAt := time.Now(), time.Now()
6263
controller := newControllerService()
63-
status := sandboxstore.Status{
64-
Pid: pid,
65-
CreatedAt: createdAt,
66-
ExitStatus: exitStatus,
67-
ExitedAt: exitedAt,
68-
State: sandboxstore.StateReady,
69-
}
70-
sb := sandboxstore.Sandbox{
71-
Metadata: sandboxstore.Metadata{
72-
ID: sandboxID,
73-
},
74-
Status: sandboxstore.StoreStatus(status),
75-
}
76-
err := controller.sandboxStore.Add(sb)
64+
65+
sb := types.NewPodSandbox(sandboxID, sandboxstore.Status{
66+
State: sandboxstore.StateReady,
67+
Pid: pid,
68+
CreatedAt: createdAt,
69+
})
70+
sb.Metadata = sandboxstore.Metadata{ID: sandboxID}
71+
err := controller.store.Save(sb)
7772
if err != nil {
7873
t.Fatal(err)
7974
}
@@ -82,6 +77,20 @@ func Test_Status(t *testing.T) {
8277
t.Fatal(err)
8378
}
8479
assert.Equal(t, s.Pid, pid)
85-
assert.Equal(t, s.ExitedAt, exitedAt)
80+
assert.Equal(t, s.CreatedAt, createdAt)
8681
assert.Equal(t, s.State, sandboxstore.StateReady.String())
82+
83+
sb.Exit(*containerd.NewExitStatus(exitStatus, exitedAt, nil))
84+
exit, err := controller.Wait(context.Background(), sandboxID)
85+
if err != nil {
86+
t.Fatal(err)
87+
}
88+
assert.Equal(t, exit.ExitStatus, exitStatus)
89+
assert.Equal(t, exit.ExitedAt, exitedAt)
90+
91+
s, err = controller.Status(context.Background(), sandboxID, false)
92+
if err != nil {
93+
t.Fatal(err)
94+
}
95+
assert.Equal(t, s.State, sandboxstore.StateNotReady.String())
8796
}

0 commit comments

Comments
 (0)