1
2
3 package hcn
4
5 import (
6 "encoding/json"
7 "os"
8 "syscall"
9
10 "github.com/Microsoft/go-winio/pkg/guid"
11 icni "github.com/Microsoft/hcsshim/internal/cni"
12 "github.com/Microsoft/hcsshim/internal/interop"
13 "github.com/Microsoft/hcsshim/internal/regstate"
14 "github.com/Microsoft/hcsshim/internal/runhcs"
15 "github.com/sirupsen/logrus"
16 )
17
18
19 type NamespaceResourceEndpoint struct {
20 Id string `json:"ID,"`
21 }
22
23
24 type NamespaceResourceContainer struct {
25 Id string `json:"ID,"`
26 }
27
28
29 type NamespaceResourceType string
30
31 var (
32
33 NamespaceResourceTypeContainer NamespaceResourceType = "Container"
34
35 NamespaceResourceTypeEndpoint NamespaceResourceType = "Endpoint"
36 )
37
38
39 type NamespaceResource struct {
40 Type NamespaceResourceType `json:","`
41 Data json.RawMessage `json:","`
42 }
43
44
45 type NamespaceType string
46
47 var (
48
49 NamespaceTypeHost NamespaceType = "Host"
50
51 NamespaceTypeHostDefault NamespaceType = "HostDefault"
52
53 NamespaceTypeGuest NamespaceType = "Guest"
54
55 NamespaceTypeGuestDefault NamespaceType = "GuestDefault"
56 )
57
58
59 type HostComputeNamespace struct {
60 Id string `json:"ID,omitempty"`
61 NamespaceId uint32 `json:",omitempty"`
62 Type NamespaceType `json:",omitempty"`
63 Resources []NamespaceResource `json:",omitempty"`
64 SchemaVersion SchemaVersion `json:",omitempty"`
65 }
66
67
68
69 type ModifyNamespaceSettingRequest struct {
70 ResourceType NamespaceResourceType `json:",omitempty"`
71 RequestType RequestType `json:",omitempty"`
72 Settings json.RawMessage `json:",omitempty"`
73 }
74
75 func getNamespace(namespaceGUID guid.GUID, query string) (*HostComputeNamespace, error) {
76
77 var (
78 namespaceHandle hcnNamespace
79 resultBuffer *uint16
80 propertiesBuffer *uint16
81 )
82 hr := hcnOpenNamespace(&namespaceGUID, &namespaceHandle, &resultBuffer)
83 if err := checkForErrors("hcnOpenNamespace", hr, resultBuffer); err != nil {
84 return nil, err
85 }
86
87 hr = hcnQueryNamespaceProperties(namespaceHandle, query, &propertiesBuffer, &resultBuffer)
88 if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
89 return nil, err
90 }
91 properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
92
93 hr = hcnCloseNamespace(namespaceHandle)
94 if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
95 return nil, err
96 }
97
98 var outputNamespace HostComputeNamespace
99 if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
100 return nil, err
101 }
102 return &outputNamespace, nil
103 }
104
105 func enumerateNamespaces(query string) ([]HostComputeNamespace, error) {
106
107 var (
108 resultBuffer *uint16
109 namespaceBuffer *uint16
110 )
111 hr := hcnEnumerateNamespaces(query, &namespaceBuffer, &resultBuffer)
112 if err := checkForErrors("hcnEnumerateNamespaces", hr, resultBuffer); err != nil {
113 return nil, err
114 }
115
116 namespaces := interop.ConvertAndFreeCoTaskMemString(namespaceBuffer)
117 var namespaceIds []guid.GUID
118 if err := json.Unmarshal([]byte(namespaces), &namespaceIds); err != nil {
119 return nil, err
120 }
121
122 var outputNamespaces []HostComputeNamespace
123 for _, namespaceGUID := range namespaceIds {
124 namespace, err := getNamespace(namespaceGUID, query)
125 if err != nil {
126 return nil, err
127 }
128 outputNamespaces = append(outputNamespaces, *namespace)
129 }
130 return outputNamespaces, nil
131 }
132
133 func createNamespace(settings string) (*HostComputeNamespace, error) {
134
135 var (
136 namespaceHandle hcnNamespace
137 resultBuffer *uint16
138 propertiesBuffer *uint16
139 )
140 namespaceGUID := guid.GUID{}
141 hr := hcnCreateNamespace(&namespaceGUID, settings, &namespaceHandle, &resultBuffer)
142 if err := checkForErrors("hcnCreateNamespace", hr, resultBuffer); err != nil {
143 return nil, err
144 }
145
146 hcnQuery := defaultQuery()
147 query, err := json.Marshal(hcnQuery)
148 if err != nil {
149 return nil, err
150 }
151 hr = hcnQueryNamespaceProperties(namespaceHandle, string(query), &propertiesBuffer, &resultBuffer)
152 if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
153 return nil, err
154 }
155 properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
156
157 hr = hcnCloseNamespace(namespaceHandle)
158 if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
159 return nil, err
160 }
161
162 var outputNamespace HostComputeNamespace
163 if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
164 return nil, err
165 }
166 return &outputNamespace, nil
167 }
168
169 func modifyNamespace(namespaceID string, settings string) (*HostComputeNamespace, error) {
170 namespaceGUID, err := guid.FromString(namespaceID)
171 if err != nil {
172 return nil, errInvalidNamespaceID
173 }
174
175 var (
176 namespaceHandle hcnNamespace
177 resultBuffer *uint16
178 propertiesBuffer *uint16
179 )
180 hr := hcnOpenNamespace(&namespaceGUID, &namespaceHandle, &resultBuffer)
181 if err := checkForErrors("hcnOpenNamespace", hr, resultBuffer); err != nil {
182 return nil, err
183 }
184
185 hr = hcnModifyNamespace(namespaceHandle, settings, &resultBuffer)
186 if err := checkForErrors("hcnModifyNamespace", hr, resultBuffer); err != nil {
187 return nil, err
188 }
189
190 hcnQuery := defaultQuery()
191 query, err := json.Marshal(hcnQuery)
192 if err != nil {
193 return nil, err
194 }
195 hr = hcnQueryNamespaceProperties(namespaceHandle, string(query), &propertiesBuffer, &resultBuffer)
196 if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
197 return nil, err
198 }
199 properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
200
201 hr = hcnCloseNamespace(namespaceHandle)
202 if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
203 return nil, err
204 }
205
206 var outputNamespace HostComputeNamespace
207 if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
208 return nil, err
209 }
210 return &outputNamespace, nil
211 }
212
213 func deleteNamespace(namespaceID string) error {
214 namespaceGUID, err := guid.FromString(namespaceID)
215 if err != nil {
216 return errInvalidNamespaceID
217 }
218 var resultBuffer *uint16
219 hr := hcnDeleteNamespace(&namespaceGUID, &resultBuffer)
220 if err := checkForErrors("hcnDeleteNamespace", hr, resultBuffer); err != nil {
221 return err
222 }
223 return nil
224 }
225
226
227 func ListNamespaces() ([]HostComputeNamespace, error) {
228 hcnQuery := defaultQuery()
229 namespaces, err := ListNamespacesQuery(hcnQuery)
230 if err != nil {
231 return nil, err
232 }
233 return namespaces, nil
234 }
235
236
237 func ListNamespacesQuery(query HostComputeQuery) ([]HostComputeNamespace, error) {
238 queryJSON, err := json.Marshal(query)
239 if err != nil {
240 return nil, err
241 }
242
243 namespaces, err := enumerateNamespaces(string(queryJSON))
244 if err != nil {
245 return nil, err
246 }
247 return namespaces, nil
248 }
249
250
251 func GetNamespaceByID(namespaceID string) (*HostComputeNamespace, error) {
252 hcnQuery := defaultQuery()
253 mapA := map[string]string{"ID": namespaceID}
254 filter, err := json.Marshal(mapA)
255 if err != nil {
256 return nil, err
257 }
258 hcnQuery.Filter = string(filter)
259
260 namespaces, err := ListNamespacesQuery(hcnQuery)
261 if err != nil {
262 return nil, err
263 }
264 if len(namespaces) == 0 {
265 return nil, NamespaceNotFoundError{NamespaceID: namespaceID}
266 }
267
268 return &namespaces[0], err
269 }
270
271
272 func GetNamespaceEndpointIds(namespaceID string) ([]string, error) {
273 namespace, err := GetNamespaceByID(namespaceID)
274 if err != nil {
275 return nil, err
276 }
277 var endpointsIds []string
278 for _, resource := range namespace.Resources {
279 if resource.Type == "Endpoint" {
280 var endpointResource NamespaceResourceEndpoint
281 if err := json.Unmarshal([]byte(resource.Data), &endpointResource); err != nil {
282 return nil, err
283 }
284 endpointsIds = append(endpointsIds, endpointResource.Id)
285 }
286 }
287 return endpointsIds, nil
288 }
289
290
291 func GetNamespaceContainerIds(namespaceID string) ([]string, error) {
292 namespace, err := GetNamespaceByID(namespaceID)
293 if err != nil {
294 return nil, err
295 }
296 var containerIds []string
297 for _, resource := range namespace.Resources {
298 if resource.Type == "Container" {
299 var containerResource NamespaceResourceContainer
300 if err := json.Unmarshal([]byte(resource.Data), &containerResource); err != nil {
301 return nil, err
302 }
303 containerIds = append(containerIds, containerResource.Id)
304 }
305 }
306 return containerIds, nil
307 }
308
309
310 func NewNamespace(nsType NamespaceType) *HostComputeNamespace {
311 return &HostComputeNamespace{
312 Type: nsType,
313 SchemaVersion: V2SchemaVersion(),
314 }
315 }
316
317
318 func (namespace *HostComputeNamespace) Create() (*HostComputeNamespace, error) {
319 logrus.Debugf("hcn::HostComputeNamespace::Create id=%s", namespace.Id)
320
321 jsonString, err := json.Marshal(namespace)
322 if err != nil {
323 return nil, err
324 }
325
326 logrus.Debugf("hcn::HostComputeNamespace::Create JSON: %s", jsonString)
327 namespace, hcnErr := createNamespace(string(jsonString))
328 if hcnErr != nil {
329 return nil, hcnErr
330 }
331 return namespace, nil
332 }
333
334
335 func (namespace *HostComputeNamespace) Delete() error {
336 logrus.Debugf("hcn::HostComputeNamespace::Delete id=%s", namespace.Id)
337
338 if err := deleteNamespace(namespace.Id); err != nil {
339 return err
340 }
341 return nil
342 }
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358 func (namespace *HostComputeNamespace) Sync() error {
359 logrus.WithField("id", namespace.Id).Debugf("hcs::HostComputeNamespace::Sync")
360
361
362 if namespace.Type != NamespaceTypeGuest {
363 return nil
364 }
365
366
367 cfg, err := icni.LoadPersistedNamespaceConfig(namespace.Id)
368 if err != nil {
369 if regstate.IsNotFoundError(err) {
370 return nil
371 }
372 return err
373 }
374 req := runhcs.VMRequest{
375 ID: cfg.ContainerID,
376 Op: runhcs.OpSyncNamespace,
377 }
378 shimPath := runhcs.VMPipePath(cfg.HostUniqueID)
379 if err := runhcs.IssueVMRequest(shimPath, &req); err != nil {
380
381 if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND {
382
383 _ = cfg.Remove()
384 return nil
385 }
386 f := map[string]interface{}{
387 "id": namespace.Id,
388 "container-id": cfg.ContainerID,
389 }
390 logrus.WithFields(f).
391 WithError(err).
392 Debugf("hcs::HostComputeNamespace::Sync failed to connect to shim pipe: '%s'", shimPath)
393 return err
394 }
395 return nil
396 }
397
398
399 func ModifyNamespaceSettings(namespaceID string, request *ModifyNamespaceSettingRequest) error {
400 logrus.Debugf("hcn::HostComputeNamespace::ModifyNamespaceSettings id=%s", namespaceID)
401
402 namespaceSettings, err := json.Marshal(request)
403 if err != nil {
404 return err
405 }
406
407 _, err = modifyNamespace(namespaceID, string(namespaceSettings))
408 if err != nil {
409 return err
410 }
411 return nil
412 }
413
414
415 func AddNamespaceEndpoint(namespaceID string, endpointID string) error {
416 logrus.Debugf("hcn::HostComputeEndpoint::AddNamespaceEndpoint id=%s", endpointID)
417
418 mapA := map[string]string{"EndpointId": endpointID}
419 settingsJSON, err := json.Marshal(mapA)
420 if err != nil {
421 return err
422 }
423 requestMessage := &ModifyNamespaceSettingRequest{
424 ResourceType: NamespaceResourceTypeEndpoint,
425 RequestType: RequestTypeAdd,
426 Settings: settingsJSON,
427 }
428
429 return ModifyNamespaceSettings(namespaceID, requestMessage)
430 }
431
432
433 func RemoveNamespaceEndpoint(namespaceID string, endpointID string) error {
434 logrus.Debugf("hcn::HostComputeNamespace::RemoveNamespaceEndpoint id=%s", endpointID)
435
436 mapA := map[string]string{"EndpointId": endpointID}
437 settingsJSON, err := json.Marshal(mapA)
438 if err != nil {
439 return err
440 }
441 requestMessage := &ModifyNamespaceSettingRequest{
442 ResourceType: NamespaceResourceTypeEndpoint,
443 RequestType: RequestTypeRemove,
444 Settings: settingsJSON,
445 }
446
447 return ModifyNamespaceSettings(namespaceID, requestMessage)
448 }
449
View as plain text