Skip to content

Commit 1f9c393

Browse files
authored
feat(devbox): fix v2.2 devbox snapshotter
1 parent 83527ea commit 1f9c393

14 files changed

Lines changed: 1036 additions & 117 deletions

File tree

client/container_opts.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"encoding/json"
2222
"errors"
2323
"fmt"
24+
"strings"
2425

2526
"github.com/containerd/containerd/v2/core/containers"
2627
"github.com/containerd/containerd/v2/core/content"
@@ -52,6 +53,11 @@ type InfoConfig struct {
5253
Refresh bool
5354
}
5455

56+
const (
57+
devboxAnnotationPrefix = "devbox.sealos.io/"
58+
devboxSnapshotLabelPrefix = "containerd.io/snapshot/devbox-"
59+
)
60+
5561
// WithRuntime allows a user to specify the runtime name and additional options that should
5662
// be used to create tasks for the container
5763
func WithRuntime(name string, options interface{}) NewContainerOpts {
@@ -255,10 +261,15 @@ func withNewSnapshot(id string, i Image, readonly bool, opts ...snapshots.Opt) N
255261
return err
256262
}
257263

264+
startOpts, err := withDevboxSnapshotLabels(opts...)
265+
if err != nil {
266+
return err
267+
}
268+
258269
if readonly {
259-
_, err = s.View(ctx, id, parent, opts...)
270+
_, err = s.View(ctx, id, parent, startOpts...)
260271
} else {
261-
_, err = s.Prepare(ctx, id, parent, opts...)
272+
_, err = s.Prepare(ctx, id, parent, startOpts...)
262273
}
263274
if err != nil {
264275
return err
@@ -269,6 +280,30 @@ func withNewSnapshot(id string, i Image, readonly bool, opts ...snapshots.Opt) N
269280
}
270281
}
271282

283+
func withDevboxSnapshotLabels(opts ...snapshots.Opt) ([]snapshots.Opt, error) {
284+
base := snapshots.Info{}
285+
for _, opt := range opts {
286+
if err := opt(&base); err != nil {
287+
return nil, fmt.Errorf("error applying snapshot option: %w", err)
288+
}
289+
}
290+
291+
translated := make(map[string]string)
292+
for label, value := range base.Labels {
293+
if strings.HasPrefix(label, devboxAnnotationPrefix) {
294+
translated[devboxSnapshotLabelPrefix+label[len(devboxAnnotationPrefix):]] = value
295+
}
296+
}
297+
if len(translated) == 0 {
298+
return opts, nil
299+
}
300+
301+
startOpts := make([]snapshots.Opt, 0, len(opts)+1)
302+
startOpts = append(startOpts, snapshots.WithLabels(translated))
303+
startOpts = append(startOpts, opts...)
304+
return startOpts, nil
305+
}
306+
272307
// WithContainerExtension appends extension data to the container object.
273308
// Use this to decorate the container object with additional data for the client
274309
// integration.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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 devboxsnapshotter
18+
19+
const (
20+
DevboxSnapshotter = "devbox"
21+
StargzSnapshotter = "stargz"
22+
SealosDevboxContentIDAnnotation = "devbox.sealos.io/content-id"
23+
SealosDevboxStorageLimitAnnotation = "devbox.sealos.io/storage-limit"
24+
)
25+
26+
func LabelsFromAnnotations(annotations map[string]string) map[string]string {
27+
if len(annotations) == 0 {
28+
return nil
29+
}
30+
31+
labels := make(map[string]string)
32+
if contentID := annotations[SealosDevboxContentIDAnnotation]; contentID != "" {
33+
labels[SealosDevboxContentIDAnnotation] = contentID
34+
}
35+
if storageLimit := annotations[SealosDevboxStorageLimitAnnotation]; storageLimit != "" {
36+
labels[SealosDevboxStorageLimitAnnotation] = storageLimit
37+
}
38+
if len(labels) == 0 {
39+
return nil
40+
}
41+
42+
return labels
43+
}
44+
45+
func IsWritableSnapshotter(name string) bool {
46+
switch name {
47+
case DevboxSnapshotter, StargzSnapshotter:
48+
return true
49+
default:
50+
return false
51+
}
52+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package devboxsnapshotter
2+
3+
import "testing"
4+
5+
func TestLabelsFromAnnotations(t *testing.T) {
6+
labels := LabelsFromAnnotations(map[string]string{
7+
SealosDevboxContentIDAnnotation: "workspace-1",
8+
SealosDevboxStorageLimitAnnotation: "20Gi",
9+
"other.annotation": "ignored",
10+
})
11+
12+
if got := labels[SealosDevboxContentIDAnnotation]; got != "workspace-1" {
13+
t.Fatalf("content-id label = %q, want %q", got, "workspace-1")
14+
}
15+
if got := labels[SealosDevboxStorageLimitAnnotation]; got != "20Gi" {
16+
t.Fatalf("storage-limit label = %q, want %q", got, "20Gi")
17+
}
18+
if _, ok := labels["other.annotation"]; ok {
19+
t.Fatalf("unexpected non-devbox label preserved: %+v", labels)
20+
}
21+
}
22+
23+
func TestIsWritableSnapshotter(t *testing.T) {
24+
tests := []struct {
25+
name string
26+
snapshotter string
27+
want bool
28+
}{
29+
{name: "devbox", snapshotter: DevboxSnapshotter, want: true},
30+
{name: "stargz", snapshotter: StargzSnapshotter, want: true},
31+
{name: "overlayfs", snapshotter: "overlayfs", want: false},
32+
}
33+
34+
for _, tt := range tests {
35+
t.Run(tt.name, func(t *testing.T) {
36+
if got := IsWritableSnapshotter(tt.snapshotter); got != tt.want {
37+
t.Fatalf("IsWritableSnapshotter(%q) = %v, want %v", tt.snapshotter, got, tt.want)
38+
}
39+
})
40+
}
41+
}

internal/cri/server/container_create.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"github.com/containerd/containerd/v2/core/snapshots"
4141
"github.com/containerd/containerd/v2/internal/cri/annotations"
4242
criconfig "github.com/containerd/containerd/v2/internal/cri/config"
43+
"github.com/containerd/containerd/v2/internal/cri/devboxsnapshotter"
4344
cio "github.com/containerd/containerd/v2/internal/cri/io"
4445
crilabels "github.com/containerd/containerd/v2/internal/cri/labels"
4546
customopts "github.com/containerd/containerd/v2/internal/cri/opts"
@@ -58,12 +59,15 @@ func init() {
5859
}
5960

6061
func devboxSnapshotterOpts(config *runtime.PodSandboxConfig) (snapshots.Opt, error) {
61-
labels := make(map[string]string)
62-
if config != nil {
63-
for k, v := range config.Annotations {
64-
labels[k] = v
65-
}
62+
if config == nil {
63+
return nil, nil
64+
}
65+
66+
labels := devboxsnapshotter.LabelsFromAnnotations(config.Annotations)
67+
if len(labels) == 0 {
68+
return nil, nil
6669
}
70+
6771
return snapshots.WithLabels(labels), nil
6872
}
6973

@@ -360,8 +364,8 @@ func (c *criService) createContainer(r *createContainerRequest) (_ string, retEr
360364
return "", err
361365
}
362366

363-
// Check if the snapshotter is devbox and add the devbox snapshotter opts.
364-
if c.RuntimeSnapshotter(r.ctx, ociRuntime) == "devbox" {
367+
runtimeSnapshotter := c.RuntimeSnapshotter(r.ctx, ociRuntime)
368+
if devboxsnapshotter.IsWritableSnapshotter(runtimeSnapshotter) {
365369
devboxOpt, err := devboxSnapshotterOpts(r.podSandboxConfig)
366370
if err != nil {
367371
return "", err
@@ -373,7 +377,7 @@ func (c *criService) createContainer(r *createContainerRequest) (_ string, retEr
373377

374378
// Set snapshotter before any other options.
375379
opts := []containerd.NewContainerOpts{
376-
containerd.WithSnapshotter(c.RuntimeSnapshotter(r.ctx, ociRuntime)),
380+
containerd.WithSnapshotter(runtimeSnapshotter),
377381
// Prepare container rootfs. This is always writeable even if
378382
// the container wants a readonly rootfs since we want to give
379383
// the runtime (runc) a chance to modify (e.g. to create mount

internal/cri/server/container_stop.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"time"
2525

2626
eventtypes "github.com/containerd/containerd/api/events"
27+
"github.com/containerd/containerd/v2/internal/cri/devboxsnapshotter"
2728
containerstore "github.com/containerd/containerd/v2/internal/cri/store/container"
2829
ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
2930
"github.com/containerd/containerd/v2/pkg/protobuf"
@@ -85,8 +86,7 @@ func (c *criService) StopContainer(ctx context.Context, r *runtime.StopContainer
8586

8687
log.G(ctx).Infof("Check snapshotter: %s", snapshotter)
8788

88-
// Check if the snapshotter is devbox and update the devbox snapshot.
89-
if snapshotter == "devbox" {
89+
if devboxsnapshotter.IsWritableSnapshotter(snapshotter) {
9090
err = c.client.UpdateDevboxSnapshot(ctx, snapshotter, i.ID, unmountLvm, "true")
9191
if err != nil {
9292
log.G(ctx).WithError(err).Errorf("Failed to update devbox snapshot: %s", err)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package server
2+
3+
import (
4+
"testing"
5+
6+
"github.com/containerd/containerd/v2/core/snapshots"
7+
"github.com/containerd/containerd/v2/internal/cri/devboxsnapshotter"
8+
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
9+
)
10+
11+
func TestDevboxSnapshotterOpts(t *testing.T) {
12+
opt, err := devboxSnapshotterOpts(&runtime.PodSandboxConfig{
13+
Annotations: map[string]string{
14+
devboxsnapshotter.SealosDevboxContentIDAnnotation: "workspace-9",
15+
devboxsnapshotter.SealosDevboxStorageLimitAnnotation: "8Gi",
16+
"other.annotation": "ignored",
17+
},
18+
})
19+
if err != nil {
20+
t.Fatalf("devboxSnapshotterOpts() error = %v", err)
21+
}
22+
if opt == nil {
23+
t.Fatal("devboxSnapshotterOpts() returned nil opt")
24+
}
25+
26+
info := &snapshots.Info{Labels: make(map[string]string)}
27+
if err := opt(info); err != nil {
28+
t.Fatalf("applying snapshot opt error = %v", err)
29+
}
30+
31+
if got := info.Labels[devboxsnapshotter.SealosDevboxContentIDAnnotation]; got != "workspace-9" {
32+
t.Fatalf("content-id label = %q, want %q", got, "workspace-9")
33+
}
34+
if got := info.Labels[devboxsnapshotter.SealosDevboxStorageLimitAnnotation]; got != "8Gi" {
35+
t.Fatalf("storage-limit label = %q, want %q", got, "8Gi")
36+
}
37+
if _, ok := info.Labels["other.annotation"]; ok {
38+
t.Fatalf("unexpected unrelated annotation preserved: %+v", info.Labels)
39+
}
40+
}

internal/cri/server/events.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
apitasks "github.com/containerd/containerd/api/services/tasks/v1"
3131

3232
containerd "github.com/containerd/containerd/v2/client"
33+
"github.com/containerd/containerd/v2/internal/cri/devboxsnapshotter"
3334
containerstore "github.com/containerd/containerd/v2/internal/cri/store/container"
3435
sandboxstore "github.com/containerd/containerd/v2/internal/cri/store/sandbox"
3536
ctrdutil "github.com/containerd/containerd/v2/internal/cri/util"
@@ -251,7 +252,7 @@ func (c *criService) handleContainerExit(ctx context.Context, e *eventtypes.Task
251252
if err != nil {
252253
return status, err
253254
}
254-
if container.Snapshotter == "devbox" {
255+
if devboxsnapshotter.IsWritableSnapshotter(container.Snapshotter) {
255256
if err := c.client.UpdateDevboxSnapshot(ctx, container.Snapshotter, container.ID, unmountLvm, "true"); err != nil {
256257
log.G(ctx).WithError(err).Errorf("failed to update devbox snapshot for container %s", cntr.Container.ID())
257258
}

internal/cri/server/images/image_pull.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
"github.com/containerd/containerd/v2/core/transfer/registry"
5151
"github.com/containerd/containerd/v2/internal/cri/annotations"
5252
criconfig "github.com/containerd/containerd/v2/internal/cri/config"
53+
"github.com/containerd/containerd/v2/internal/cri/devboxsnapshotter"
5354
crilabels "github.com/containerd/containerd/v2/internal/cri/labels"
5455
"github.com/containerd/containerd/v2/internal/cri/util"
5556
snpkg "github.com/containerd/containerd/v2/pkg/snapshotters"
@@ -206,7 +207,7 @@ func (c *CRIImageService) PullImage(ctx context.Context, name string, credential
206207
}
207208

208209
var imageLabels map[string]string
209-
if r == repoTag && snapshotter == "devbox" {
210+
if r == repoTag && devboxsnapshotter.IsWritableSnapshotter(snapshotter) {
210211
imageLabels = make(map[string]string)
211212
for k, v := range labels {
212213
imageLabels[k] = v

0 commit comments

Comments
 (0)