Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/linters/urunc-dict.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ TUNTAP
Timestamping
Tmpfs
Tuntap
TTRPC
UNBINDABLE
URUNC
Unikernel
Expand Down Expand Up @@ -127,8 +128,11 @@ clis
cmainas
cmdline
cntr
containerdshim
containerd
containerd's
containersapi
contentapi
cpio
creack
crictl
Expand All @@ -150,6 +154,7 @@ elevateprivileges
elfloader
endmacro
epoll
errdefs
etest
etesting
etfs
Expand Down Expand Up @@ -177,6 +182,7 @@ httpreply
iface
ifaces
ifname
imagesapi
initpipe
initrds
inlinehilite
Expand All @@ -198,6 +204,7 @@ kwds
lazytime
lenag
lenpg
leasesapi
levarage
libc
libcontainer
Expand Down Expand Up @@ -296,6 +303,7 @@ setgroup
settime
sgid
sharedfs
snapshotsapi
sigaction
sigreturn
sigstr
Expand All @@ -318,6 +326,7 @@ tmpl
tomlq
traefik
triger
ttrpc
twemoji
uidmap
ukernel
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ URUNC_SRC += $(wildcard $(CURDIR)/pkg/unikontainers/types/*.go)
URUNC_SRC += $(wildcard $(CURDIR)/pkg/unikontainers/initrd/*.go)
URUNC_SRC += $(wildcard $(CURDIR)/pkg/network/*.go)
SHIM_SRC := $(wildcard $(CURDIR)/cmd/containerd-shim-urunc-v2/*.go)
SHIM_SRC += $(wildcard $(CURDIR)/pkg/containerd-shim/*.go)
SHIM_SRC += $(wildcard $(CURDIR)/pkg/containerd-shim/containerd/*.go)

#? CNTR_TOOL Tool to run the linter container (default: docker)
CNTR_TOOL ?= docker
Expand Down
2 changes: 1 addition & 1 deletion cmd/containerd-shim-urunc-v2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import (
"context"

"github.com/containerd/containerd/runtime/v2/runc/manager"
_ "github.com/containerd/containerd/runtime/v2/runc/task/plugin"
"github.com/containerd/containerd/runtime/v2/shim"
_ "github.com/urunc-dev/urunc/pkg/containerd-shim"
)

func main() {
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/cavaliergopher/cpio v1.0.1
github.com/containerd/containerd v1.7.30
github.com/containerd/containerd/api v1.10.0
github.com/containerd/ttrpc v1.2.7
github.com/creack/pty v1.1.24
github.com/elastic/go-seccomp-bpf v1.6.0
github.com/hashicorp/go-version v1.9.0
Expand All @@ -26,6 +28,7 @@ require (
github.com/vishvananda/netlink v1.3.1
github.com/vishvananda/netns v0.0.5
golang.org/x/sys v0.43.0
google.golang.org/grpc v1.79.3
k8s.io/cri-api v0.35.4
)

Expand All @@ -36,15 +39,13 @@ require (
github.com/cilium/ebpf v0.20.0 // indirect
github.com/containerd/cgroups/v3 v3.1.0 // indirect
github.com/containerd/console v1.0.5 // indirect
github.com/containerd/containerd/api v1.10.0 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/go-runc v1.0.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
Expand Down Expand Up @@ -79,7 +80,6 @@ require (
golang.org/x/tools v0.41.0 // indirect
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
171 changes: 171 additions & 0 deletions pkg/containerd-shim/containerd/session.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright (c) 2023-2026, Nubificus LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package containerd

import (
"context"
"fmt"
"time"

containersapi "github.com/containerd/containerd/api/services/containers/v1"
contentapi "github.com/containerd/containerd/api/services/content/v1"
imagesapi "github.com/containerd/containerd/api/services/images/v1"
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
"github.com/containerd/containerd/defaults"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/dialer"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials/insecure"
)

const defaultConnectTimeout = 10 * time.Second

type Session struct {
conn *grpc.ClientConn

namespace string
containerID string
container *containersapi.Container
}

// OpenSession opens a containerd session and loads the task container metadata.
func OpenSession(ctx context.Context, address, containerID string) (*Session, error) {
if address == "" {
return nil, fmt.Errorf("containerd address is empty")
}
if containerID == "" {
return nil, fmt.Errorf("container id is empty")
}

namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}

backoffConfig := backoff.DefaultConfig
backoffConfig.MaxDelay = 3 * time.Second
dialOptions := []grpc.DialOption{
grpc.WithBlock(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.FailOnNonTempDialError(true),
grpc.WithConnectParams(grpc.ConnectParams{Backoff: backoffConfig}),
grpc.WithContextDialer(dialer.ContextDialer),
grpc.WithReturnConnectionError(),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize),
grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize),
),
}

dialCtx, cancel := context.WithTimeout(ctx, defaultConnectTimeout)
defer cancel()

conn, err := grpc.DialContext(dialCtx, dialer.DialAddress(address), dialOptions...)
if err != nil {
return nil, fmt.Errorf("dial containerd at %q: %w", address, err)
}

session := &Session{
conn: conn,
namespace: namespace,
containerID: containerID,
}

container, err := loadContainer(ctx, namespace, containerID, session.containersClient())
if err != nil {
if closeErr := conn.Close(); closeErr != nil {
return nil, fmt.Errorf("loadContainer failed: %w; close containerd connection: %v", err, closeErr)
}
return nil, fmt.Errorf("loadContainer failed: %w", err)
}
session.container = container

return session, nil
}

func (s *Session) Close() error {
if s == nil || s.conn == nil {
return nil
}
Comment thread
cmainas marked this conversation as resolved.
return s.conn.Close()
}

func (s *Session) GetNamespace() string {
return s.namespace
}

func (s *Session) GetContainerID() string {
return s.containerID
}

func (s *Session) GetContainer() *containersapi.Container {
return s.container
}

func loadContainer(ctx context.Context, namespace, containerID string, client containersapi.ContainersClient) (*containersapi.Container, error) {
resp, err := client.Get(withNamespace(ctx, namespace), &containersapi.GetContainerRequest{
ID: containerID,
})
if err != nil {
return nil, fmt.Errorf("get container %q: %w", containerID, containerdErr(err))
}
container := resp.GetContainer()
if container == nil {
return nil, fmt.Errorf("get container %q: response missing container", containerID)
}

return container, nil
}

func withNamespace(ctx context.Context, namespace string) context.Context {
if ctx == nil {
ctx = context.Background()
}
return namespaces.WithNamespace(ctx, namespace)
}

func containerdErr(err error) error {
if err == nil {
return nil
}
return errdefs.FromGRPC(err)
}

func (s *Session) containersClient() containersapi.ContainersClient {
return containersapi.NewContainersClient(s.conn)
}

//nolint:unused // Used by follow-up feature-specific access constructors.
func (s *Session) imagesClient() imagesapi.ImagesClient {
return imagesapi.NewImagesClient(s.conn)
}

//nolint:unused // Used by follow-up feature-specific access constructors.
func (s *Session) contentClient() contentapi.ContentClient {
return contentapi.NewContentClient(s.conn)
}

//nolint:unused // Used by follow-up feature-specific access constructors.
func (s *Session) snapshotsClient() snapshotsapi.SnapshotsClient {
return snapshotsapi.NewSnapshotsClient(s.conn)
}

//nolint:unused // Used by follow-up feature-specific access constructors.
func (s *Session) leasesClient() leasesapi.LeasesClient {
return leasesapi.NewLeasesClient(s.conn)
}
54 changes: 54 additions & 0 deletions pkg/containerd-shim/task_plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2023-2026, Nubificus LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package containerdshim

import (
"github.com/containerd/containerd/pkg/shutdown"
"github.com/containerd/containerd/plugin"
runcTask "github.com/containerd/containerd/runtime/v2/runc/task"
"github.com/containerd/containerd/runtime/v2/shim"
)

func init() {
plugin.Register(&plugin.Registration{
Type: plugin.TTRPCPlugin,
ID: "task",
Requires: []plugin.Type{
plugin.EventPlugin,
plugin.InternalPlugin,
},
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
pp, err := ic.GetByID(plugin.EventPlugin, "publisher")
if err != nil {
return nil, err
}

ss, err := ic.GetByID(plugin.InternalPlugin, "shutdown")
if err != nil {
return nil, err
}

inner, err := runcTask.NewTaskService(ic.Context, pp.(shim.Publisher), ss.(shutdown.Service))
if err != nil {
return nil, err
}

return &taskService{
TaskService: inner,
containerdAddress: ic.Address,
}, nil
},
})
}
44 changes: 44 additions & 0 deletions pkg/containerd-shim/task_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2023-2026, Nubificus LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package containerdshim

import (
"context"

taskAPI "github.com/containerd/containerd/api/runtime/task/v2"
"github.com/containerd/ttrpc"
)

// taskService is urunc's shim-side wrapper around containerd's runc task
// service. It currently forwards calls to the wrapped service while keeping a
// urunc-owned place for task-level feature wiring.
type taskService struct {
taskAPI.TaskService

containerdAddress string
}

func (s *taskService) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*taskAPI.CreateTaskResponse, error) {
return s.TaskService.Create(ctx, r)
}

func (s *taskService) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAPI.DeleteResponse, error) {
return s.TaskService.Delete(ctx, r)
}

func (s *taskService) RegisterTTRPC(server *ttrpc.Server) error {
taskAPI.RegisterTaskService(server, s)
return nil
}
Loading