1
2
3 package main
4
5 import (
6 gocontext "context"
7 "encoding/json"
8 "fmt"
9 "io"
10 "os"
11 "os/exec"
12 "path/filepath"
13
14 "github.com/Microsoft/go-winio"
15 "github.com/Microsoft/hcsshim/internal/oci"
16 "github.com/Microsoft/hcsshim/pkg/annotations"
17 "github.com/containerd/containerd/runtime/v2/shim"
18 "github.com/containerd/containerd/runtime/v2/task"
19 "github.com/containerd/ttrpc"
20 "github.com/pkg/errors"
21 "github.com/sirupsen/logrus"
22 "github.com/urfave/cli"
23 )
24
25 var startCommand = cli.Command{
26 Name: "start",
27 Usage: `
28 This command will launch new shims.
29
30 The start command, as well as all binary calls to the shim, has the bundle for the container set as the cwd.
31
32 The start command MUST return an address to a shim for containerd to issue API requests for container operations.
33
34 The start command can either start a new shim or return an address to an existing shim based on the shim's logic.
35 `,
36 SkipArgReorder: true,
37 Action: func(context *cli.Context) (err error) {
38
39 logrus.SetOutput(io.Discard)
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 const addrFmt = "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\containerd-shim-%s-%s-pipe"
58
59 var (
60 address string
61 pid int
62 )
63
64 cwd, err := os.Getwd()
65 if err != nil {
66 return err
67 }
68
69 a, err := getSpecAnnotations(cwd)
70 if err != nil {
71 return err
72 }
73
74 ct, sbid, err := oci.GetSandboxTypeAndID(a)
75 if err != nil {
76 return err
77 }
78
79 if ct == oci.KubernetesContainerTypeContainer {
80 address = fmt.Sprintf(addrFmt, namespaceFlag, sbid)
81
82
83 c, err := winio.DialPipe(address, nil)
84 if err != nil {
85 return errors.Wrap(err, "failed to connect to hosting shim")
86 }
87 cl := ttrpc.NewClient(c, ttrpc.WithOnClose(func() { c.Close() }))
88 t := task.NewTaskClient(cl)
89 ctx := gocontext.Background()
90 req := &task.ConnectRequest{ID: sbid}
91 cr, err := t.Connect(ctx, req)
92
93 cl.Close()
94 c.Close()
95 if err != nil {
96 return errors.Wrap(err, "failed to get shim pid from hosting shim")
97 }
98 pid = int(cr.ShimPid)
99 }
100
101
102 if address == "" {
103 isSandbox := ct == oci.KubernetesContainerTypeSandbox
104 if isSandbox && idFlag != sbid {
105 return errors.Errorf(
106 "'id' and '%s' must match for '%s=%s'",
107 annotations.KubernetesSandboxID,
108 annotations.KubernetesContainerType,
109 oci.KubernetesContainerTypeSandbox)
110 }
111
112 self, err := os.Executable()
113 if err != nil {
114 return err
115 }
116
117 r, w, err := os.Pipe()
118 if err != nil {
119 return err
120 }
121 defer r.Close()
122 defer w.Close()
123
124 f, err := os.Create(filepath.Join(cwd, "panic.log"))
125 if err != nil {
126 return err
127 }
128 defer f.Close()
129
130 address = fmt.Sprintf(addrFmt, namespaceFlag, idFlag)
131 args := []string{
132 self,
133 "--namespace", namespaceFlag,
134 "--address", addressFlag,
135 "--publish-binary", containerdBinaryFlag,
136 "--id", idFlag,
137 "serve",
138 "--socket", address,
139 }
140 if isSandbox {
141 args = append(args, "--is-sandbox")
142 }
143 cmd := &exec.Cmd{
144 Path: self,
145 Args: args,
146 Env: os.Environ(),
147 Dir: cwd,
148 Stdin: os.Stdin,
149 Stdout: w,
150 Stderr: f,
151 }
152
153 if err := cmd.Start(); err != nil {
154 return err
155 }
156 w.Close()
157 defer func() {
158 if err != nil {
159 _ = cmd.Process.Kill()
160 }
161 }()
162
163
164 _, err = io.Copy(os.Stderr, r)
165 if err != nil {
166 return err
167 }
168 pid = cmd.Process.Pid
169 }
170
171 if err := shim.WritePidFile(filepath.Join(cwd, "shim.pid"), pid); err != nil {
172 return err
173 }
174 if err := shim.WriteAddress(filepath.Join(cwd, "address"), address); err != nil {
175 return err
176 }
177
178
179 if _, err := fmt.Fprint(os.Stdout, address); err != nil {
180 return err
181 }
182 return nil
183 },
184 }
185
186 func getSpecAnnotations(bundlePath string) (map[string]string, error) {
187
188
189 type specAnnotations struct {
190
191 Annotations map[string]string `json:"annotations,omitempty"`
192 }
193 f, err := os.OpenFile(filepath.Join(bundlePath, "config.json"), os.O_RDONLY, 0)
194 if err != nil {
195 return nil, err
196 }
197 defer f.Close()
198 var spec specAnnotations
199 if err := json.NewDecoder(f).Decode(&spec); err != nil {
200 return nil, errors.Wrap(err, "failed to deserialize valid OCI spec")
201 }
202 return spec.Annotations, nil
203 }
204
View as plain text