1
2
3 package main
4
5 import (
6 "context"
7 "fmt"
8 "os"
9 "path/filepath"
10 "time"
11
12 "github.com/Microsoft/hcsshim/internal/hcs"
13 "github.com/Microsoft/hcsshim/internal/memory"
14 "github.com/Microsoft/hcsshim/internal/oc"
15 "github.com/Microsoft/hcsshim/internal/winapi"
16 "github.com/containerd/containerd/runtime/v2/task"
17 "github.com/gogo/protobuf/proto"
18 "github.com/pkg/errors"
19 "github.com/sirupsen/logrus"
20 "github.com/urfave/cli"
21 )
22
23
24
25 func limitedRead(filePath string, readLimitBytes int64) ([]byte, error) {
26 f, err := os.Open(filePath)
27 if err != nil {
28 return nil, errors.Wrapf(err, "limited read failed to open file: %s", filePath)
29 }
30 defer f.Close()
31 if fi, err := f.Stat(); err == nil {
32 if fi.Size() < readLimitBytes {
33 readLimitBytes = fi.Size()
34 }
35 buf := make([]byte, readLimitBytes)
36 _, err := f.Read(buf)
37 if err != nil {
38 return []byte{}, errors.Wrapf(err, "limited read failed during file read: %s", filePath)
39 }
40 return buf, nil
41 }
42 return []byte{}, errors.Wrapf(err, "limited read failed during file stat: %s", filePath)
43 }
44
45 var deleteCommand = cli.Command{
46 Name: "delete",
47 Usage: `
48 This command allows containerd to delete any container resources created, mounted, and/or run by a shim when containerd can no longer communicate over rpc. This happens if a shim is SIGKILL'd with a running container. These resources will need to be cleaned up when containerd loses the connection to a shim. This is also used when containerd boots and reconnects to shims. If a bundle is still on disk but containerd cannot connect to a shim, the delete command is invoked.
49
50 The delete command will be executed in the container's bundle as its cwd.
51 `,
52 SkipArgReorder: true,
53 Action: func(cCtx *cli.Context) (err error) {
54
55
56
57
58 ctx, span := oc.StartSpan(context.Background(), "delete")
59 defer span.End()
60 defer func() { oc.SetSpanStatus(span, err) }()
61
62 bundleFlag := cCtx.GlobalString("bundle")
63 if bundleFlag == "" {
64 return errors.New("bundle is required")
65 }
66
67
68
69
70
71
72 readLimit := int64(memory.MiB)
73 logBytes, err := limitedRead(filepath.Join(bundleFlag, "panic.log"), readLimit)
74 if err == nil && len(logBytes) > 0 {
75 if int64(len(logBytes)) == readLimit {
76 logrus.Warnf("shim panic log file %s is larger than 1MB, logging only first 1MB", filepath.Join(bundleFlag, "panic.log"))
77 }
78 logrus.WithField("log", string(logBytes)).Warn("found shim panic logs during delete")
79 } else if err != nil && !errors.Is(err, os.ErrNotExist) {
80 logrus.WithError(err).Warn("failed to open shim panic log")
81 }
82
83
84 if sys, _ := hcs.OpenComputeSystem(ctx, idFlag); sys != nil {
85 defer sys.Close()
86 if err := sys.Terminate(ctx); err != nil {
87 fmt.Fprintf(os.Stderr, "failed to terminate '%s': %v", idFlag, err)
88 } else {
89 ch := make(chan error, 1)
90 go func() { ch <- sys.Wait() }()
91 t := time.NewTimer(time.Second * 30)
92 select {
93 case <-t.C:
94 sys.Close()
95 return fmt.Errorf("timed out waiting for '%s' to terminate", idFlag)
96 case err := <-ch:
97 t.Stop()
98 if err != nil {
99 fmt.Fprintf(os.Stderr, "failed to wait for '%s' to terminate: %v", idFlag, err)
100 }
101 }
102 }
103 }
104
105
106
107
108
109
110 username := idFlag
111 if len(username) > winapi.UserNameCharLimit {
112 username = username[:winapi.UserNameCharLimit]
113 }
114
115
116
117 if err := winapi.NetUserDel(
118 "",
119 username,
120 ); err != nil && err != winapi.NERR_UserNotFound {
121 fmt.Fprintf(os.Stderr, "failed to delete user %q: %v", username, err)
122 }
123
124 if data, err := proto.Marshal(&task.DeleteResponse{
125 ExitedAt: time.Now(),
126 ExitStatus: 255,
127 }); err != nil {
128 return err
129 } else {
130 if _, err := os.Stdout.Write(data); err != nil {
131 return err
132 }
133 }
134 return nil
135 },
136 }
137
View as plain text