Skip to content

Commit 3e3c0dc

Browse files
committed
feat(shim): add containerd access session
Signed-off-by: sidneychang <2190206983@qq.com>
1 parent 05b0d3e commit 3e3c0dc

3 files changed

Lines changed: 232 additions & 1 deletion

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ URUNC_SRC += $(wildcard $(CURDIR)/pkg/unikontainers/initrd/*.go)
7373
URUNC_SRC += $(wildcard $(CURDIR)/pkg/network/*.go)
7474
SHIM_SRC := $(wildcard $(CURDIR)/cmd/containerd-shim-urunc-v2/*.go)
7575
SHIM_SRC += $(wildcard $(CURDIR)/pkg/containerd-shim/*.go)
76+
SHIM_SRC += $(wildcard $(CURDIR)/pkg/containerd-shim/containerd/*.go)
7677

7778
#? CNTR_TOOL Tool to run the linter container (default: docker)
7879
CNTR_TOOL ?= docker

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ require (
2828
github.com/vishvananda/netlink v1.3.1
2929
github.com/vishvananda/netns v0.0.5
3030
golang.org/x/sys v0.43.0
31+
google.golang.org/grpc v1.79.3
3132
k8s.io/cri-api v0.35.4
3233
)
3334

@@ -79,7 +80,6 @@ require (
7980
golang.org/x/tools v0.41.0 // indirect
8081
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
8182
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
82-
google.golang.org/grpc v1.79.3 // indirect
8383
google.golang.org/protobuf v1.36.11 // indirect
8484
gopkg.in/yaml.v3 v3.0.1 // indirect
8585
)
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// Copyright (c) 2023-2026, Nubificus LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package containerd
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"time"
21+
22+
containersapi "github.com/containerd/containerd/api/services/containers/v1"
23+
contentapi "github.com/containerd/containerd/api/services/content/v1"
24+
imagesapi "github.com/containerd/containerd/api/services/images/v1"
25+
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
26+
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
27+
"github.com/containerd/containerd/defaults"
28+
"github.com/containerd/containerd/errdefs"
29+
"github.com/containerd/containerd/namespaces"
30+
"github.com/containerd/containerd/pkg/dialer"
31+
"google.golang.org/grpc"
32+
"google.golang.org/grpc/backoff"
33+
"google.golang.org/grpc/credentials/insecure"
34+
)
35+
36+
const defaultConnectTimeout = 10 * time.Second
37+
38+
type Session struct {
39+
conn *grpc.ClientConn
40+
41+
namespace string
42+
containerID string
43+
container *containersapi.Container
44+
}
45+
46+
func OpenSession(ctx context.Context, address, containerID string) (*Session, error) {
47+
if address == "" {
48+
return nil, fmt.Errorf("containerd address is empty")
49+
}
50+
if containerID == "" {
51+
return nil, fmt.Errorf("container id is empty")
52+
}
53+
54+
namespace, err := namespaces.NamespaceRequired(ctx)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
backoffConfig := backoff.DefaultConfig
60+
backoffConfig.MaxDelay = 3 * time.Second
61+
dialOptions := []grpc.DialOption{
62+
grpc.WithBlock(),
63+
grpc.WithTransportCredentials(insecure.NewCredentials()),
64+
grpc.FailOnNonTempDialError(true),
65+
grpc.WithConnectParams(grpc.ConnectParams{Backoff: backoffConfig}),
66+
grpc.WithContextDialer(dialer.ContextDialer),
67+
grpc.WithReturnConnectionError(),
68+
grpc.WithDefaultCallOptions(
69+
grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize),
70+
grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize),
71+
),
72+
}
73+
74+
dialCtx, cancel := context.WithTimeout(ctx, defaultConnectTimeout)
75+
defer cancel()
76+
77+
conn, err := grpc.DialContext(dialCtx, dialer.DialAddress(address), dialOptions...)
78+
if err != nil {
79+
return nil, fmt.Errorf("dial containerd at %q: %w", address, err)
80+
}
81+
82+
session := &Session{
83+
conn: conn,
84+
namespace: namespace,
85+
containerID: containerID,
86+
}
87+
88+
container, err := loadContainer(ctx, namespace, containerID, session.containersClient())
89+
if err != nil {
90+
if closeErr := conn.Close(); closeErr != nil {
91+
return nil, fmt.Errorf("loadContainer failed: %w; close containerd connection: %v", err, closeErr)
92+
}
93+
return nil, fmt.Errorf("loadContainer failed: %w", err)
94+
}
95+
session.container = container
96+
97+
return session, nil
98+
}
99+
100+
func (s *Session) Close() error {
101+
if s == nil || s.conn == nil {
102+
return nil
103+
}
104+
return s.conn.Close()
105+
}
106+
107+
type BaseAccess struct {
108+
namespace string
109+
containerID string
110+
container *containersapi.Container
111+
}
112+
113+
func newBaseAccess(s *Session) BaseAccess {
114+
return BaseAccess{
115+
namespace: s.namespace,
116+
containerID: s.containerID,
117+
container: s.container,
118+
}
119+
}
120+
121+
func (a BaseAccess) Namespace() string {
122+
return a.namespace
123+
}
124+
125+
func (a BaseAccess) ContainerID() string {
126+
return a.containerID
127+
}
128+
129+
func (a BaseAccess) Container() *containersapi.Container {
130+
return a.container
131+
}
132+
133+
func (a BaseAccess) WithNamespace(ctx context.Context) context.Context {
134+
return withNamespace(ctx, a.namespace)
135+
}
136+
137+
type AnnotationAccess struct {
138+
BaseAccess
139+
140+
images imagesapi.ImagesClient
141+
content contentapi.ContentClient
142+
}
143+
144+
func NewAnnotationAccess(s *Session) AnnotationAccess {
145+
return AnnotationAccess{
146+
BaseAccess: newBaseAccess(s),
147+
images: s.imagesClient(),
148+
content: s.contentClient(),
149+
}
150+
}
151+
152+
func (a AnnotationAccess) Images() imagesapi.ImagesClient {
153+
return a.images
154+
}
155+
156+
func (a AnnotationAccess) Content() contentapi.ContentClient {
157+
return a.content
158+
}
159+
160+
type SnapshotViewAccess struct {
161+
BaseAccess
162+
163+
snapshots snapshotsapi.SnapshotsClient
164+
leases leasesapi.LeasesClient
165+
}
166+
167+
func NewSnapshotViewAccess(s *Session) SnapshotViewAccess {
168+
return SnapshotViewAccess{
169+
BaseAccess: newBaseAccess(s),
170+
snapshots: s.snapshotsClient(),
171+
leases: s.leasesClient(),
172+
}
173+
}
174+
175+
func (a SnapshotViewAccess) Snapshots() snapshotsapi.SnapshotsClient {
176+
return a.snapshots
177+
}
178+
179+
func (a SnapshotViewAccess) Leases() leasesapi.LeasesClient {
180+
return a.leases
181+
}
182+
183+
func loadContainer(ctx context.Context, namespace, containerID string, client containersapi.ContainersClient) (*containersapi.Container, error) {
184+
resp, err := client.Get(withNamespace(ctx, namespace), &containersapi.GetContainerRequest{
185+
ID: containerID,
186+
})
187+
if err != nil {
188+
return nil, fmt.Errorf("get container %q: %w", containerID, containerdErr(err))
189+
}
190+
container := resp.GetContainer()
191+
if container == nil {
192+
return nil, fmt.Errorf("get container %q: empty response", containerID)
193+
}
194+
195+
return container, nil
196+
}
197+
198+
func withNamespace(ctx context.Context, namespace string) context.Context {
199+
if ctx == nil {
200+
ctx = context.Background()
201+
}
202+
return namespaces.WithNamespace(ctx, namespace)
203+
}
204+
205+
func containerdErr(err error) error {
206+
if err == nil {
207+
return nil
208+
}
209+
return errdefs.FromGRPC(err)
210+
}
211+
212+
func (s *Session) containersClient() containersapi.ContainersClient {
213+
return containersapi.NewContainersClient(s.conn)
214+
}
215+
216+
func (s *Session) imagesClient() imagesapi.ImagesClient {
217+
return imagesapi.NewImagesClient(s.conn)
218+
}
219+
220+
func (s *Session) contentClient() contentapi.ContentClient {
221+
return contentapi.NewContentClient(s.conn)
222+
}
223+
224+
func (s *Session) snapshotsClient() snapshotsapi.SnapshotsClient {
225+
return snapshotsapi.NewSnapshotsClient(s.conn)
226+
}
227+
228+
func (s *Session) leasesClient() leasesapi.LeasesClient {
229+
return leasesapi.NewLeasesClient(s.conn)
230+
}

0 commit comments

Comments
 (0)