@@ -21,7 +21,9 @@ import (
21
21
"os/exec"
22
22
"strconv"
23
23
"strings"
24
+ "sync"
24
25
"syscall"
26
+ "time"
25
27
26
28
"github.com/containerd/containerd/identifiers"
27
29
"github.com/containerd/containerd/log"
@@ -30,6 +32,7 @@ import (
30
32
"github.com/containerd/containerd/runtime/v2/shim"
31
33
"github.com/containerd/containerd/sys"
32
34
"github.com/golang/protobuf/ptypes/empty"
35
+ "github.com/hashicorp/go-multierror"
33
36
"github.com/pkg/errors"
34
37
"github.com/sirupsen/logrus"
35
38
"google.golang.org/grpc/codes"
@@ -47,6 +50,7 @@ import (
47
50
var (
48
51
_ fccontrolTtrpc.FirecrackerService = (* local )(nil )
49
52
ttrpcAddressEnv = "TTRPC_ADDRESS"
53
+ stopVMInterval = 10 * time .Millisecond
50
54
)
51
55
52
56
func init () {
@@ -64,6 +68,9 @@ type local struct {
64
68
containerdAddress string
65
69
logger * logrus.Entry
66
70
config * config.Config
71
+
72
+ processesMu sync.Mutex
73
+ processes map [string ]int32
67
74
}
68
75
69
76
func newLocal (ic * plugin.InitContext ) (* local , error ) {
@@ -80,6 +87,7 @@ func newLocal(ic *plugin.InitContext) (*local, error) {
80
87
containerdAddress : ic .Address ,
81
88
logger : log .G (ic .Context ),
82
89
config : cfg ,
90
+ processes : make (map [string ]int32 ),
83
91
}, nil
84
92
}
85
93
@@ -116,7 +124,7 @@ func (s *local) CreateVM(requestCtx context.Context, req *proto.CreateVMRequest)
116
124
117
125
shimSocket , err := shim .NewSocket (shimSocketAddress )
118
126
if isEADDRINUSE (err ) {
119
- return nil , status .Errorf (codes .AlreadyExists , "VM with ID %q already exists" , id )
127
+ return nil , status .Errorf (codes .AlreadyExists , "VM with ID %q already exists (socket: %q) " , id , shimSocketAddress )
120
128
} else if err != nil {
121
129
err = errors .Wrapf (err , "failed to open shim socket at address %q" , shimSocketAddress )
122
130
s .logger .WithError (err ).Error ()
@@ -199,9 +207,17 @@ func (s *local) CreateVM(requestCtx context.Context, req *proto.CreateVMRequest)
199
207
return nil , err
200
208
}
201
209
210
+ s .addShim (shimSocketAddress , cmd )
211
+
202
212
return resp , nil
203
213
}
204
214
215
+ func (s * local ) addShim (address string , cmd * exec.Cmd ) {
216
+ s .processesMu .Lock ()
217
+ defer s .processesMu .Unlock ()
218
+ s .processes [address ] = int32 (cmd .Process .Pid )
219
+ }
220
+
205
221
func (s * local ) shimFirecrackerClient (requestCtx context.Context , vmID string ) (* fcclient.Client , error ) {
206
222
if err := identifiers .Validate (vmID ); err != nil {
207
223
return nil , errors .Wrap (err , "invalid id" )
@@ -224,16 +240,34 @@ func (s *local) StopVM(requestCtx context.Context, req *proto.StopVMRequest) (*e
224
240
if err != nil {
225
241
return nil , err
226
242
}
227
-
228
243
defer client .Close ()
229
244
230
- resp , err := client .StopVM (requestCtx , req )
245
+ resp , shimErr := client .StopVM (requestCtx , req )
246
+ waitErr := s .waitForShimToExit (requestCtx , req .VMID )
247
+
248
+ // Assuming the shim is returning containerd's error code, return the error as is if possible.
249
+ if waitErr == nil {
250
+ return resp , shimErr
251
+ }
252
+ return resp , multierror .Append (shimErr , waitErr ).ErrorOrNil ()
253
+ }
254
+
255
+ func (s * local ) waitForShimToExit (ctx context.Context , vmID string ) error {
256
+ socketAddr , err := fcShim .SocketAddress (ctx , vmID )
231
257
if err != nil {
232
- s .logger .WithError (err ).Error ()
233
- return nil , err
258
+ return err
259
+ }
260
+
261
+ s .processesMu .Lock ()
262
+ defer s .processesMu .Unlock ()
263
+
264
+ pid , ok := s .processes [socketAddr ]
265
+ if ! ok {
266
+ return errors .Errorf ("failed to find a shim process for %q" , socketAddr )
234
267
}
268
+ defer delete (s .processes , socketAddr )
235
269
236
- return resp , err
270
+ return internal . WaitForPidToExit ( ctx , stopVMInterval , pid )
237
271
}
238
272
239
273
// GetVMInfo returns metadata for the VM with the given VMID.
@@ -387,6 +421,15 @@ func (s *local) newShim(ns, vmID, containerdAddress string, shimSocket *net.Unix
387
421
logger .WithError (err ).Error ("shim has been unexpectedly terminated" )
388
422
}
389
423
}
424
+
425
+ // Close all Unix abstract sockets.
426
+ if err := shimSocketFile .Close (); err != nil {
427
+ logger .WithError (err ).Errorf ("failed to close %q" , shimSocketFile .Name ())
428
+ }
429
+ if err := fcSocketFile .Close (); err != nil {
430
+ logger .WithError (err ).Errorf ("failed to close %q" , fcSocketFile .Name ())
431
+ }
432
+
390
433
if err := os .RemoveAll (shimDir .RootPath ()); err != nil {
391
434
logger .WithError (err ).Errorf ("failed to remove %q" , shimDir .RootPath ())
392
435
}
0 commit comments