@@ -16,21 +16,31 @@ package containerdshim
1616
1717import (
1818 "context"
19+ "encoding/json"
1920 "errors"
21+ "fmt"
22+ "path/filepath"
2023
2124 taskAPI "github.com/containerd/containerd/api/runtime/task/v2"
25+ "github.com/containerd/containerd/namespaces"
2226 "github.com/containerd/log"
2327 "github.com/containerd/ttrpc"
2428 containerdShim "github.com/urunc-dev/urunc/pkg/containerd-shim/containerd"
29+ "github.com/urunc-dev/urunc/pkg/unikontainers"
2530)
2631
32+ // Internal bundle annotation (duplicated in unikontainers; keep in sync).
33+ const annotRootfsParams = "com.urunc.internal.rootfs.params"
34+
2735// taskService is urunc's shim-side wrapper around containerd's runc task
2836// service. It wires urunc task setup before forwarding calls to the wrapped
2937// service.
3038type taskService struct {
3139 taskAPI.TaskService
3240
3341 containerdAddress string
42+ // Used on Delete, where cwd may no longer be the bundle.
43+ stateRoot string
3444}
3545
3646func (s * taskService ) Create (ctx context.Context , r * taskAPI.CreateTaskRequest ) (* taskAPI.CreateTaskResponse , error ) {
@@ -53,9 +63,8 @@ func (s *taskService) Create(ctx context.Context, r *taskAPI.CreateTaskRequest)
5363 return resp , err
5464 }
5565
56- // ChooseRootfs after inner task Create so bundle rootfs is mounted;
57- // params are persisted in bundle config.json for runtime Exec.
58- if err := chooseGuestRootfs (r ); err != nil {
66+ rootfsChoice , err := chooseGuestRootfs (r )
67+ if err != nil {
5968 if errors .Is (err , errGuestRootfsChoiceSkipped ) {
6069 log .G (ctx ).WithError (err ).Debug ("urunc(shim): guest rootfs choice skipped" )
6170 return resp , nil
@@ -64,14 +73,125 @@ func (s *taskService) Create(ctx context.Context, r *taskAPI.CreateTaskRequest)
6473 return nil , err
6574 }
6675
76+ rootfsViewState , shouldPrepareRootfsView , err := containerdShim .PrepareRootfsView (ctx , session , rootfsChoice , r .Bundle )
77+ rootfsViewPrepared := err == nil && shouldPrepareRootfsView
78+ switch {
79+ case err != nil && shouldPrepareRootfsView :
80+ log .G (ctx ).WithError (err ).Warn ("urunc(shim): failed to prepare rootfs view; falling back to legacy boot artifact extraction" )
81+ case err != nil :
82+ log .G (ctx ).WithError (err ).Warn ("urunc(shim): failed to load urunc config; rootfs view disabled" )
83+ case rootfsViewPrepared :
84+ log .G (ctx ).Debug ("urunc(shim): rootfs view prepared" )
85+ case session != nil :
86+ log .G (ctx ).WithField ("rootfs_type" , rootfsChoice .Type ).Debug ("urunc(shim): rootfs view prepare skipped" )
87+ }
88+
89+ rootfsViewPersisted := false
90+ defer func () {
91+ if rootfsViewPrepared && ! rootfsViewPersisted {
92+ cleanupRootfsView (ctx , session , "" , rootfsViewState .Mountpoint , "create rollback" )
93+ }
94+ }()
95+
96+ rootfsParamsJSON , err := json .Marshal (rootfsChoice )
97+ if err != nil {
98+ log .G (ctx ).WithError (err ).Warn ("urunc(shim): failed to encode rootfs params" )
99+ return nil , err
100+ }
101+
102+ if err := containerdShim .PatchConfigJSON (r .Bundle , map [string ]string {
103+ annotRootfsParams : string (rootfsParamsJSON ),
104+ }); err != nil {
105+ log .G (ctx ).WithError (err ).Warn ("urunc(shim): failed to persist shim create annotations" )
106+ return nil , err
107+ }
108+
109+ if rootfsViewPrepared {
110+ if err := unikontainers .WriteBundleRootfsView (r .Bundle , rootfsViewState ); err != nil {
111+ log .G (ctx ).WithError (err ).Warn ("urunc(shim): failed to persist rootfs view state" )
112+ return nil , err
113+ }
114+ rootfsViewPersisted = true
115+ }
116+
67117 return resp , nil
68118}
69119
120+ func cleanupRootfsView (ctx context.Context , session * containerdShim.Session , snapshotter , mountpoint , reason string ) {
121+ if err := containerdShim .CleanupRootfsView (ctx , session , snapshotter , mountpoint ); err != nil {
122+ log .G (ctx ).WithError (err ).WithField ("reason" , reason ).Warn ("urunc(shim): failed to clean up rootfs view" )
123+ }
124+ }
125+
70126func (s * taskService ) Delete (ctx context.Context , r * taskAPI.DeleteRequest ) (* taskAPI.DeleteResponse , error ) {
71- return s .TaskService .Delete (ctx , r )
127+ shouldCleanup := false
128+ snapshotter := ""
129+ rootfsViewMountpoint := ""
130+ var loadErr error
131+
132+ if r .ExecID == "" {
133+ bundle , err := s .bundlePathFor (ctx , r .ID )
134+ if err != nil {
135+ log .G (ctx ).WithError (err ).Warn ("urunc(shim): resolve bundle path during Delete failed" )
136+ loadErr = err
137+ } else {
138+ // Read view state before inner Delete; snapshotter is taken from bundle
139+ // (written at Prepare) because container metadata may be gone after Delete.
140+ var mountpoint string
141+ shouldCleanup , snapshotter , mountpoint , loadErr = containerdShim .ShouldCleanupRootfsView (bundle )
142+ if loadErr == nil {
143+ rootfsViewMountpoint = mountpoint
144+ }
145+ }
146+ }
147+
148+ // Delete tears down the monitor namespace before removing the view it may pin.
149+ resp , err := s .TaskService .Delete (ctx , r )
150+
151+ if loadErr != nil {
152+ if err != nil {
153+ return resp , err
154+ }
155+ return resp , loadErr
156+ }
157+
158+ if shouldCleanup {
159+ session , sessionErr := containerdShim .OpenSession (ctx , s .containerdAddress , r .ID )
160+ if sessionErr != nil {
161+ log .G (ctx ).WithError (sessionErr ).Warn ("urunc(shim): open containerd session for rootfs view cleanup failed" )
162+ if err == nil {
163+ err = sessionErr
164+ }
165+ } else {
166+ defer func () {
167+ if err := session .Close (); err != nil {
168+ log .G (ctx ).WithError (err ).Warn ("urunc(shim): failed to close containerd session after rootfs view cleanup" )
169+ }
170+ }()
171+ if cleanupErr := containerdShim .CleanupRootfsView (ctx , session , snapshotter , rootfsViewMountpoint ); cleanupErr != nil {
172+ log .G (ctx ).WithError (cleanupErr ).Warn ("urunc(shim): delete rootfs view during Delete failed" )
173+ if err == nil {
174+ err = cleanupErr
175+ }
176+ }
177+ }
178+ }
179+
180+ return resp , err
72181}
73182
74183func (s * taskService ) RegisterTTRPC (server * ttrpc.Server ) error {
75184 taskAPI .RegisterTaskService (server , s )
76185 return nil
77186}
187+
188+ func (s * taskService ) bundlePathFor (ctx context.Context , containerID string ) (string , error ) {
189+ if s .stateRoot == "" {
190+ return "" , fmt .Errorf ("task service state root is empty (shim cwd layout assumption violated)" )
191+ }
192+ ns , err := namespaces .NamespaceRequired (ctx )
193+ if err != nil {
194+ return "" , fmt .Errorf ("namespace required: %w" , err )
195+ }
196+ return filepath .Join (s .stateRoot , ns , containerID ), nil
197+ }
0 commit comments