1
2
3 package gcs
4
5 import (
6 "context"
7 "errors"
8 "sync"
9 "time"
10
11 "github.com/Microsoft/hcsshim/internal/cow"
12 "github.com/Microsoft/hcsshim/internal/hcs/schema1"
13 hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2"
14 "github.com/Microsoft/hcsshim/internal/log"
15 "github.com/Microsoft/hcsshim/internal/oc"
16 "go.opencensus.io/trace"
17 )
18
19 const hrComputeSystemDoesNotExist = 0xc037010e
20
21
22
23 type Container struct {
24 gc *GuestConnection
25 id string
26 notifyCh chan struct{}
27 closeCh chan struct{}
28 closeOnce sync.Once
29
30 waitBlock chan struct{}
31
32 waitError error
33 }
34
35 var _ cow.Container = &Container{}
36
37
38
39 func (gc *GuestConnection) CreateContainer(ctx context.Context, cid string, config interface{}) (_ *Container, err error) {
40 ctx, span := oc.StartSpan(ctx, "gcs::GuestConnection::CreateContainer", oc.WithClientSpanKind)
41 defer span.End()
42 defer func() { oc.SetSpanStatus(span, err) }()
43 span.AddAttributes(trace.StringAttribute("cid", cid))
44
45 c := &Container{
46 gc: gc,
47 id: cid,
48 notifyCh: make(chan struct{}),
49 closeCh: make(chan struct{}),
50 waitBlock: make(chan struct{}),
51 }
52 err = gc.requestNotify(cid, c.notifyCh)
53 if err != nil {
54 return nil, err
55 }
56 req := containerCreate{
57 requestBase: makeRequest(ctx, cid),
58 ContainerConfig: anyInString{config},
59 }
60 var resp containerCreateResponse
61 err = gc.brdg.RPC(ctx, rpcCreate, &req, &resp, false)
62 if err != nil {
63 return nil, err
64 }
65 go c.waitBackground()
66 return c, nil
67 }
68
69
70
71 func (gc *GuestConnection) CloneContainer(ctx context.Context, cid string) (_ *Container, err error) {
72 c := &Container{
73 gc: gc,
74 id: cid,
75 notifyCh: make(chan struct{}),
76 closeCh: make(chan struct{}),
77 waitBlock: make(chan struct{}),
78 }
79 err = gc.requestNotify(cid, c.notifyCh)
80 if err != nil {
81 return nil, err
82 }
83 go c.waitBackground()
84 return c, nil
85 }
86
87
88 func (c *Container) OS() string {
89 return c.gc.os
90 }
91
92
93
94 func (c *Container) IsOCI() bool {
95 return c.gc.os != "windows"
96 }
97
98
99 func (c *Container) Close() error {
100 c.closeOnce.Do(func() {
101 _, span := oc.StartSpan(context.Background(), "gcs::Container::Close")
102 defer span.End()
103 span.AddAttributes(trace.StringAttribute("cid", c.id))
104
105 close(c.closeCh)
106 })
107 return nil
108 }
109
110
111 func (c *Container) CreateProcess(ctx context.Context, config interface{}) (_ cow.Process, err error) {
112 ctx, span := oc.StartSpan(ctx, "gcs::Container::CreateProcess", oc.WithClientSpanKind)
113 defer span.End()
114 defer func() { oc.SetSpanStatus(span, err) }()
115 span.AddAttributes(trace.StringAttribute("cid", c.id))
116
117 return c.gc.exec(ctx, c.id, config)
118 }
119
120
121 func (c *Container) ID() string {
122 return c.id
123 }
124
125
126 func (c *Container) Modify(ctx context.Context, config interface{}) (err error) {
127 ctx, span := oc.StartSpan(ctx, "gcs::Container::Modify", oc.WithClientSpanKind)
128 defer span.End()
129 defer func() { oc.SetSpanStatus(span, err) }()
130 span.AddAttributes(trace.StringAttribute("cid", c.id))
131
132 req := containerModifySettings{
133 requestBase: makeRequest(ctx, c.id),
134 Request: config,
135 }
136 var resp responseBase
137 return c.gc.brdg.RPC(ctx, rpcModifySettings, &req, &resp, false)
138 }
139
140
141 func (c *Container) Properties(ctx context.Context, types ...schema1.PropertyType) (_ *schema1.ContainerProperties, err error) {
142 ctx, span := oc.StartSpan(ctx, "gcs::Container::Properties", oc.WithClientSpanKind)
143 defer span.End()
144 defer func() { oc.SetSpanStatus(span, err) }()
145 span.AddAttributes(trace.StringAttribute("cid", c.id))
146
147 req := containerGetProperties{
148 requestBase: makeRequest(ctx, c.id),
149 Query: containerPropertiesQuery{PropertyTypes: types},
150 }
151 var resp containerGetPropertiesResponse
152 err = c.gc.brdg.RPC(ctx, rpcGetProperties, &req, &resp, true)
153 if err != nil {
154 return nil, err
155 }
156 return (*schema1.ContainerProperties)(&resp.Properties), nil
157 }
158
159
160 func (c *Container) PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (_ *hcsschema.Properties, err error) {
161 ctx, span := oc.StartSpan(ctx, "gcs::Container::PropertiesV2", oc.WithClientSpanKind)
162 defer span.End()
163 defer func() { oc.SetSpanStatus(span, err) }()
164 span.AddAttributes(trace.StringAttribute("cid", c.id))
165
166 req := containerGetPropertiesV2{
167 requestBase: makeRequest(ctx, c.id),
168 Query: containerPropertiesQueryV2{PropertyTypes: types},
169 }
170 var resp containerGetPropertiesResponseV2
171 err = c.gc.brdg.RPC(ctx, rpcGetProperties, &req, &resp, true)
172 if err != nil {
173 return nil, err
174 }
175 return (*hcsschema.Properties)(&resp.Properties), nil
176 }
177
178
179 func (c *Container) Start(ctx context.Context) (err error) {
180 ctx, span := oc.StartSpan(ctx, "gcs::Container::Start", oc.WithClientSpanKind)
181 defer span.End()
182 defer func() { oc.SetSpanStatus(span, err) }()
183 span.AddAttributes(trace.StringAttribute("cid", c.id))
184
185 req := makeRequest(ctx, c.id)
186 var resp responseBase
187 return c.gc.brdg.RPC(ctx, rpcStart, &req, &resp, false)
188 }
189
190 func (c *Container) shutdown(ctx context.Context, proc rpcProc) error {
191 req := makeRequest(ctx, c.id)
192 var resp responseBase
193 err := c.gc.brdg.RPC(ctx, proc, &req, &resp, true)
194 if err != nil {
195 if uint32(resp.Result) != hrComputeSystemDoesNotExist {
196 return err
197 }
198 select {
199 case <-c.notifyCh:
200 default:
201 log.G(ctx).WithError(err).Warn("ignoring missing container")
202 }
203 }
204 return nil
205 }
206
207
208
209
210 func (c *Container) Shutdown(ctx context.Context) (err error) {
211 ctx, span := oc.StartSpan(ctx, "gcs::Container::Shutdown", oc.WithClientSpanKind)
212 defer span.End()
213 defer func() { oc.SetSpanStatus(span, err) }()
214 span.AddAttributes(trace.StringAttribute("cid", c.id))
215
216 ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
217 defer cancel()
218 return c.shutdown(ctx, rpcShutdownGraceful)
219 }
220
221
222
223
224 func (c *Container) Terminate(ctx context.Context) (err error) {
225 ctx, span := oc.StartSpan(ctx, "gcs::Container::Terminate", oc.WithClientSpanKind)
226 defer span.End()
227 defer func() { oc.SetSpanStatus(span, err) }()
228 span.AddAttributes(trace.StringAttribute("cid", c.id))
229
230 ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
231 defer cancel()
232 return c.shutdown(ctx, rpcShutdownForced)
233 }
234
235 func (c *Container) WaitChannel() <-chan struct{} {
236 return c.waitBlock
237 }
238
239 func (c *Container) WaitError() error {
240 return c.waitError
241 }
242
243
244
245 func (c *Container) Wait() error {
246 <-c.WaitChannel()
247 return c.WaitError()
248 }
249
250 func (c *Container) waitBackground() {
251 ctx, span := oc.StartSpan(context.Background(), "gcs::Container::waitBackground")
252 defer span.End()
253 span.AddAttributes(trace.StringAttribute("cid", c.id))
254
255 select {
256 case <-c.notifyCh:
257 case <-c.closeCh:
258 c.waitError = errors.New("container closed")
259 }
260 close(c.waitBlock)
261
262 log.G(ctx).Debug("container exited")
263 oc.SetSpanStatus(span, c.waitError)
264 }
265
View as plain text