1
2
3
4 package hcsv2
5
6 import (
7 "context"
8 "fmt"
9 "os"
10 "sync"
11 "sync/atomic"
12 "syscall"
13
14 "github.com/containerd/cgroups"
15 v1 "github.com/containerd/cgroups/stats/v1"
16 oci "github.com/opencontainers/runtime-spec/specs-go"
17 "github.com/pkg/errors"
18 "github.com/sirupsen/logrus"
19 "go.opencensus.io/trace"
20
21 "github.com/Microsoft/hcsshim/internal/guest/gcserr"
22 "github.com/Microsoft/hcsshim/internal/guest/prot"
23 "github.com/Microsoft/hcsshim/internal/guest/runtime"
24 specInternal "github.com/Microsoft/hcsshim/internal/guest/spec"
25 "github.com/Microsoft/hcsshim/internal/guest/stdio"
26 "github.com/Microsoft/hcsshim/internal/guest/storage"
27 "github.com/Microsoft/hcsshim/internal/guest/transport"
28 "github.com/Microsoft/hcsshim/internal/log"
29 "github.com/Microsoft/hcsshim/internal/logfields"
30 "github.com/Microsoft/hcsshim/internal/oc"
31 "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
32 "github.com/Microsoft/hcsshim/internal/protocol/guestresource"
33 )
34
35
36 type containerStatus uint32
37
38 const (
39
40
41 containerCreating containerStatus = iota
42
43
44 containerCreated
45 )
46
47 type Container struct {
48 id string
49 vsock transport.Transport
50
51 spec *oci.Spec
52 ociBundlePath string
53 isSandbox bool
54
55 container runtime.Container
56 initProcess *containerProcess
57
58 etL sync.Mutex
59 exitType prot.NotificationType
60
61 processesMutex sync.Mutex
62 processes map[uint32]*containerProcess
63
64
65 status containerStatus
66
67
68
69
70 scratchDirPath string
71 }
72
73 func (c *Container) Start(ctx context.Context, conSettings stdio.ConnectionSettings) (int, error) {
74 log.G(ctx).WithField(logfields.ContainerID, c.id).Info("opengcs::Container::Start")
75 stdioSet, err := stdio.Connect(c.vsock, conSettings)
76 if err != nil {
77 return -1, err
78 }
79 if c.initProcess.spec.Terminal {
80 ttyr := c.container.Tty()
81 ttyr.ReplaceConnectionSet(stdioSet)
82 ttyr.Start()
83 } else {
84 pr := c.container.PipeRelay()
85 pr.ReplaceConnectionSet(stdioSet)
86 pr.CloseUnusedPipes()
87 pr.Start()
88 }
89 err = c.container.Start()
90 if err != nil {
91 stdioSet.Close()
92 }
93 return int(c.initProcess.pid), err
94 }
95
96 func (c *Container) ExecProcess(ctx context.Context, process *oci.Process, conSettings stdio.ConnectionSettings) (int, error) {
97 log.G(ctx).WithField(logfields.ContainerID, c.id).Info("opengcs::Container::ExecProcess")
98 stdioSet, err := stdio.Connect(c.vsock, conSettings)
99 if err != nil {
100 return -1, err
101 }
102
103
104
105 process.Rlimits = c.spec.Process.Rlimits
106
107
108
109
110
111 if process.User.Username != "" {
112
113 if err := setUserStr(&oci.Spec{Root: c.spec.Root, Process: process}, process.User.Username); err != nil {
114 return -1, err
115 }
116
117 process.User.Username = ""
118 } else if c.spec.Process.User.Username != "" {
119 process.User = c.spec.Process.User
120 }
121
122 p, err := c.container.ExecProcess(process, stdioSet)
123 if err != nil {
124 stdioSet.Close()
125 return -1, err
126 }
127
128 pid := p.Pid()
129 c.processesMutex.Lock()
130 c.processes[uint32(pid)] = newProcess(c, process, p, uint32(pid), false)
131 c.processesMutex.Unlock()
132 return pid, nil
133 }
134
135
136 func (c *Container) InitProcess() Process {
137 return c.initProcess
138 }
139
140
141
142 func (c *Container) GetProcess(pid uint32) (Process, error) {
143
144 logrus.WithFields(logrus.Fields{
145 logfields.ContainerID: c.id,
146 logfields.ProcessID: pid,
147 }).Info("opengcs::Container::GetProcess")
148 if c.initProcess.pid == pid {
149 return c.initProcess, nil
150 }
151
152 c.processesMutex.Lock()
153 defer c.processesMutex.Unlock()
154
155 p, ok := c.processes[pid]
156 if !ok {
157 return nil, gcserr.NewHresultError(gcserr.HrErrNotFound)
158 }
159 return p, nil
160 }
161
162
163 func (c *Container) GetAllProcessPids(ctx context.Context) ([]int, error) {
164 log.G(ctx).WithField(logfields.ContainerID, c.id).Info("opengcs::Container::GetAllProcessPids")
165 state, err := c.container.GetAllProcesses()
166 if err != nil {
167 return nil, err
168 }
169 pids := make([]int, len(state))
170 for i, s := range state {
171 pids[i] = s.Pid
172 }
173 return pids, nil
174 }
175
176
177 func (c *Container) Kill(ctx context.Context, signal syscall.Signal) error {
178 log.G(ctx).WithField(logfields.ContainerID, c.id).Info("opengcs::Container::Kill")
179 err := c.container.Kill(signal)
180 if err != nil {
181 return err
182 }
183 c.setExitType(signal)
184 return nil
185 }
186
187 func (c *Container) Delete(ctx context.Context) error {
188 entity := log.G(ctx).WithField(logfields.ContainerID, c.id)
189 entity.Info("opengcs::Container::Delete")
190 if c.isSandbox {
191
192 if err := storage.UnmountAllInPath(ctx, specInternal.SandboxMountsDir(c.id), true); err != nil {
193 entity.WithError(err).Error("failed to unmount sandbox mounts")
194 }
195
196
197 if err := storage.UnmountAllInPath(ctx, specInternal.HugePagesMountsDir(c.id), true); err != nil {
198 entity.WithError(err).Error("failed to unmount hugepages mounts")
199 }
200 }
201
202 var retErr error
203 if err := c.container.Delete(); err != nil {
204 retErr = err
205 }
206
207 if err := os.RemoveAll(c.scratchDirPath); err != nil {
208 if retErr != nil {
209 retErr = fmt.Errorf("errors deleting container state, %s & %s", retErr, err)
210 } else {
211 retErr = err
212 }
213 }
214
215 if err := os.RemoveAll(c.ociBundlePath); err != nil {
216 if retErr != nil {
217 retErr = fmt.Errorf("errors deleting container oci bundle dir, %s & %s", retErr, err)
218 } else {
219 retErr = err
220 }
221 }
222
223 return retErr
224 }
225
226 func (c *Container) Update(ctx context.Context, resources interface{}) error {
227 log.G(ctx).WithField(logfields.ContainerID, c.id).Info("opengcs::Container::Update")
228 return c.container.Update(resources)
229 }
230
231
232 func (c *Container) Wait() prot.NotificationType {
233 _, span := oc.StartSpan(context.Background(), "opengcs::Container::Wait")
234 defer span.End()
235 span.AddAttributes(trace.StringAttribute(logfields.ContainerID, c.id))
236
237 c.initProcess.writersWg.Wait()
238 c.etL.Lock()
239 defer c.etL.Unlock()
240 return c.exitType
241 }
242
243
244
245 func (c *Container) setExitType(signal syscall.Signal) {
246 c.etL.Lock()
247 defer c.etL.Unlock()
248
249 if signal == syscall.SIGTERM {
250 c.exitType = prot.NtGracefulExit
251 } else if signal == syscall.SIGKILL {
252 c.exitType = prot.NtForcedExit
253 }
254 }
255
256
257 func (c *Container) GetStats(ctx context.Context) (*v1.Metrics, error) {
258 _, span := oc.StartSpan(ctx, "opengcs::Container::GetStats")
259 defer span.End()
260 span.AddAttributes(trace.StringAttribute("cid", c.id))
261
262 cgroupPath := c.spec.Linux.CgroupsPath
263 cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(cgroupPath))
264 if err != nil {
265 return nil, errors.Errorf("failed to get container stats for %v: %v", c.id, err)
266 }
267
268 return cg.Stat(cgroups.IgnoreNotExist)
269 }
270
271 func (c *Container) modifyContainerConstraints(ctx context.Context, rt guestrequest.RequestType, cc *guestresource.LCOWContainerConstraints) (err error) {
272 return c.Update(ctx, cc.Linux)
273 }
274
275 func (c *Container) getStatus() containerStatus {
276 val := atomic.LoadUint32((*uint32)(&c.status))
277 return containerStatus(val)
278 }
279
280 func (c *Container) setStatus(st containerStatus) {
281 atomic.StoreUint32((*uint32)(&c.status), uint32(st))
282 }
283
284 func (c *Container) ID() string {
285 return c.id
286 }
287
View as plain text