hcnnamespace.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. //go:build windows
  2. package hcn
  3. import (
  4. "encoding/json"
  5. "os"
  6. "syscall"
  7. "github.com/Microsoft/go-winio/pkg/guid"
  8. icni "github.com/Microsoft/hcsshim/internal/cni"
  9. "github.com/Microsoft/hcsshim/internal/interop"
  10. "github.com/Microsoft/hcsshim/internal/regstate"
  11. "github.com/Microsoft/hcsshim/internal/runhcs"
  12. "github.com/sirupsen/logrus"
  13. )
  14. // NamespaceResourceEndpoint represents an Endpoint attached to a Namespace.
  15. type NamespaceResourceEndpoint struct {
  16. Id string `json:"ID,"`
  17. }
  18. // NamespaceResourceContainer represents a Container attached to a Namespace.
  19. type NamespaceResourceContainer struct {
  20. Id string `json:"ID,"`
  21. }
  22. // NamespaceResourceType determines whether the Namespace resource is a Container or Endpoint.
  23. type NamespaceResourceType string
  24. var (
  25. // NamespaceResourceTypeContainer are containers associated with a Namespace.
  26. NamespaceResourceTypeContainer NamespaceResourceType = "Container"
  27. // NamespaceResourceTypeEndpoint are endpoints associated with a Namespace.
  28. NamespaceResourceTypeEndpoint NamespaceResourceType = "Endpoint"
  29. )
  30. // NamespaceResource is associated with a namespace
  31. type NamespaceResource struct {
  32. Type NamespaceResourceType `json:","` // Container, Endpoint
  33. Data json.RawMessage `json:","`
  34. }
  35. // NamespaceType determines whether the Namespace is for a Host or Guest
  36. type NamespaceType string
  37. var (
  38. // NamespaceTypeHost are host namespaces.
  39. NamespaceTypeHost NamespaceType = "Host"
  40. // NamespaceTypeHostDefault are host namespaces in the default compartment.
  41. NamespaceTypeHostDefault NamespaceType = "HostDefault"
  42. // NamespaceTypeGuest are guest namespaces.
  43. NamespaceTypeGuest NamespaceType = "Guest"
  44. // NamespaceTypeGuestDefault are guest namespaces in the default compartment.
  45. NamespaceTypeGuestDefault NamespaceType = "GuestDefault"
  46. )
  47. // HostComputeNamespace represents a namespace (AKA compartment) in
  48. type HostComputeNamespace struct {
  49. Id string `json:"ID,omitempty"`
  50. NamespaceId uint32 `json:",omitempty"`
  51. Type NamespaceType `json:",omitempty"` // Host, HostDefault, Guest, GuestDefault
  52. Resources []NamespaceResource `json:",omitempty"`
  53. SchemaVersion SchemaVersion `json:",omitempty"`
  54. }
  55. // ModifyNamespaceSettingRequest is the structure used to send request to modify a namespace.
  56. // Used to Add/Remove an endpoints and containers to/from a namespace.
  57. type ModifyNamespaceSettingRequest struct {
  58. ResourceType NamespaceResourceType `json:",omitempty"` // Container, Endpoint
  59. RequestType RequestType `json:",omitempty"` // Add, Remove, Update, Refresh
  60. Settings json.RawMessage `json:",omitempty"`
  61. }
  62. func getNamespace(namespaceGUID guid.GUID, query string) (*HostComputeNamespace, error) {
  63. // Open namespace.
  64. var (
  65. namespaceHandle hcnNamespace
  66. resultBuffer *uint16
  67. propertiesBuffer *uint16
  68. )
  69. hr := hcnOpenNamespace(&namespaceGUID, &namespaceHandle, &resultBuffer)
  70. if err := checkForErrors("hcnOpenNamespace", hr, resultBuffer); err != nil {
  71. return nil, err
  72. }
  73. // Query namespace.
  74. hr = hcnQueryNamespaceProperties(namespaceHandle, query, &propertiesBuffer, &resultBuffer)
  75. if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
  76. return nil, err
  77. }
  78. properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
  79. // Close namespace.
  80. hr = hcnCloseNamespace(namespaceHandle)
  81. if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
  82. return nil, err
  83. }
  84. // Convert output to HostComputeNamespace
  85. var outputNamespace HostComputeNamespace
  86. if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
  87. return nil, err
  88. }
  89. return &outputNamespace, nil
  90. }
  91. func enumerateNamespaces(query string) ([]HostComputeNamespace, error) {
  92. // Enumerate all Namespace Guids
  93. var (
  94. resultBuffer *uint16
  95. namespaceBuffer *uint16
  96. )
  97. hr := hcnEnumerateNamespaces(query, &namespaceBuffer, &resultBuffer)
  98. if err := checkForErrors("hcnEnumerateNamespaces", hr, resultBuffer); err != nil {
  99. return nil, err
  100. }
  101. namespaces := interop.ConvertAndFreeCoTaskMemString(namespaceBuffer)
  102. var namespaceIds []guid.GUID
  103. if err := json.Unmarshal([]byte(namespaces), &namespaceIds); err != nil {
  104. return nil, err
  105. }
  106. var outputNamespaces []HostComputeNamespace
  107. for _, namespaceGUID := range namespaceIds {
  108. namespace, err := getNamespace(namespaceGUID, query)
  109. if err != nil {
  110. return nil, err
  111. }
  112. outputNamespaces = append(outputNamespaces, *namespace)
  113. }
  114. return outputNamespaces, nil
  115. }
  116. func createNamespace(settings string) (*HostComputeNamespace, error) {
  117. // Create new namespace.
  118. var (
  119. namespaceHandle hcnNamespace
  120. resultBuffer *uint16
  121. propertiesBuffer *uint16
  122. )
  123. namespaceGUID := guid.GUID{}
  124. hr := hcnCreateNamespace(&namespaceGUID, settings, &namespaceHandle, &resultBuffer)
  125. if err := checkForErrors("hcnCreateNamespace", hr, resultBuffer); err != nil {
  126. return nil, err
  127. }
  128. // Query namespace.
  129. hcnQuery := defaultQuery()
  130. query, err := json.Marshal(hcnQuery)
  131. if err != nil {
  132. return nil, err
  133. }
  134. hr = hcnQueryNamespaceProperties(namespaceHandle, string(query), &propertiesBuffer, &resultBuffer)
  135. if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
  136. return nil, err
  137. }
  138. properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
  139. // Close namespace.
  140. hr = hcnCloseNamespace(namespaceHandle)
  141. if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
  142. return nil, err
  143. }
  144. // Convert output to HostComputeNamespace
  145. var outputNamespace HostComputeNamespace
  146. if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
  147. return nil, err
  148. }
  149. return &outputNamespace, nil
  150. }
  151. func modifyNamespace(namespaceID string, settings string) (*HostComputeNamespace, error) {
  152. namespaceGUID, err := guid.FromString(namespaceID)
  153. if err != nil {
  154. return nil, errInvalidNamespaceID
  155. }
  156. // Open namespace.
  157. var (
  158. namespaceHandle hcnNamespace
  159. resultBuffer *uint16
  160. propertiesBuffer *uint16
  161. )
  162. hr := hcnOpenNamespace(&namespaceGUID, &namespaceHandle, &resultBuffer)
  163. if err := checkForErrors("hcnOpenNamespace", hr, resultBuffer); err != nil {
  164. return nil, err
  165. }
  166. // Modify namespace.
  167. hr = hcnModifyNamespace(namespaceHandle, settings, &resultBuffer)
  168. if err := checkForErrors("hcnModifyNamespace", hr, resultBuffer); err != nil {
  169. return nil, err
  170. }
  171. // Query namespace.
  172. hcnQuery := defaultQuery()
  173. query, err := json.Marshal(hcnQuery)
  174. if err != nil {
  175. return nil, err
  176. }
  177. hr = hcnQueryNamespaceProperties(namespaceHandle, string(query), &propertiesBuffer, &resultBuffer)
  178. if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
  179. return nil, err
  180. }
  181. properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
  182. // Close namespace.
  183. hr = hcnCloseNamespace(namespaceHandle)
  184. if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
  185. return nil, err
  186. }
  187. // Convert output to Namespace
  188. var outputNamespace HostComputeNamespace
  189. if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
  190. return nil, err
  191. }
  192. return &outputNamespace, nil
  193. }
  194. func deleteNamespace(namespaceID string) error {
  195. namespaceGUID, err := guid.FromString(namespaceID)
  196. if err != nil {
  197. return errInvalidNamespaceID
  198. }
  199. var resultBuffer *uint16
  200. hr := hcnDeleteNamespace(&namespaceGUID, &resultBuffer)
  201. if err := checkForErrors("hcnDeleteNamespace", hr, resultBuffer); err != nil {
  202. return err
  203. }
  204. return nil
  205. }
  206. // ListNamespaces makes a call to list all available namespaces.
  207. func ListNamespaces() ([]HostComputeNamespace, error) {
  208. hcnQuery := defaultQuery()
  209. namespaces, err := ListNamespacesQuery(hcnQuery)
  210. if err != nil {
  211. return nil, err
  212. }
  213. return namespaces, nil
  214. }
  215. // ListNamespacesQuery makes a call to query the list of available namespaces.
  216. func ListNamespacesQuery(query HostComputeQuery) ([]HostComputeNamespace, error) {
  217. queryJSON, err := json.Marshal(query)
  218. if err != nil {
  219. return nil, err
  220. }
  221. namespaces, err := enumerateNamespaces(string(queryJSON))
  222. if err != nil {
  223. return nil, err
  224. }
  225. return namespaces, nil
  226. }
  227. // GetNamespaceByID returns the Namespace specified by Id.
  228. func GetNamespaceByID(namespaceID string) (*HostComputeNamespace, error) {
  229. hcnQuery := defaultQuery()
  230. mapA := map[string]string{"ID": namespaceID}
  231. filter, err := json.Marshal(mapA)
  232. if err != nil {
  233. return nil, err
  234. }
  235. hcnQuery.Filter = string(filter)
  236. namespaces, err := ListNamespacesQuery(hcnQuery)
  237. if err != nil {
  238. return nil, err
  239. }
  240. if len(namespaces) == 0 {
  241. return nil, NamespaceNotFoundError{NamespaceID: namespaceID}
  242. }
  243. return &namespaces[0], err
  244. }
  245. // GetNamespaceEndpointIds returns the endpoints of the Namespace specified by Id.
  246. func GetNamespaceEndpointIds(namespaceID string) ([]string, error) {
  247. namespace, err := GetNamespaceByID(namespaceID)
  248. if err != nil {
  249. return nil, err
  250. }
  251. var endpointsIds []string
  252. for _, resource := range namespace.Resources {
  253. if resource.Type == "Endpoint" {
  254. var endpointResource NamespaceResourceEndpoint
  255. if err := json.Unmarshal([]byte(resource.Data), &endpointResource); err != nil {
  256. return nil, err
  257. }
  258. endpointsIds = append(endpointsIds, endpointResource.Id)
  259. }
  260. }
  261. return endpointsIds, nil
  262. }
  263. // GetNamespaceContainerIds returns the containers of the Namespace specified by Id.
  264. func GetNamespaceContainerIds(namespaceID string) ([]string, error) {
  265. namespace, err := GetNamespaceByID(namespaceID)
  266. if err != nil {
  267. return nil, err
  268. }
  269. var containerIds []string
  270. for _, resource := range namespace.Resources {
  271. if resource.Type == "Container" {
  272. var containerResource NamespaceResourceContainer
  273. if err := json.Unmarshal([]byte(resource.Data), &containerResource); err != nil {
  274. return nil, err
  275. }
  276. containerIds = append(containerIds, containerResource.Id)
  277. }
  278. }
  279. return containerIds, nil
  280. }
  281. // NewNamespace creates a new Namespace object
  282. func NewNamespace(nsType NamespaceType) *HostComputeNamespace {
  283. return &HostComputeNamespace{
  284. Type: nsType,
  285. SchemaVersion: V2SchemaVersion(),
  286. }
  287. }
  288. // Create Namespace.
  289. func (namespace *HostComputeNamespace) Create() (*HostComputeNamespace, error) {
  290. logrus.Debugf("hcn::HostComputeNamespace::Create id=%s", namespace.Id)
  291. jsonString, err := json.Marshal(namespace)
  292. if err != nil {
  293. return nil, err
  294. }
  295. logrus.Debugf("hcn::HostComputeNamespace::Create JSON: %s", jsonString)
  296. namespace, hcnErr := createNamespace(string(jsonString))
  297. if hcnErr != nil {
  298. return nil, hcnErr
  299. }
  300. return namespace, nil
  301. }
  302. // Delete Namespace.
  303. func (namespace *HostComputeNamespace) Delete() error {
  304. logrus.Debugf("hcn::HostComputeNamespace::Delete id=%s", namespace.Id)
  305. if err := deleteNamespace(namespace.Id); err != nil {
  306. return err
  307. }
  308. return nil
  309. }
  310. // Sync Namespace endpoints with the appropriate sandbox container holding the
  311. // network namespace open. If no sandbox container is found for this namespace
  312. // this method is determined to be a success and will not return an error in
  313. // this case. If the sandbox container is found and a sync is initiated any
  314. // failures will be returned via this method.
  315. //
  316. // This call initiates a sync between endpoints and the matching UtilityVM
  317. // hosting those endpoints. It is safe to call for any `NamespaceType` but
  318. // `NamespaceTypeGuest` is the only case when a sync will actually occur. For
  319. // `NamespaceTypeHost` the process container will be automatically synchronized
  320. // when the the endpoint is added via `AddNamespaceEndpoint`.
  321. //
  322. // Note: This method sync's both additions and removals of endpoints from a
  323. // `NamespaceTypeGuest` namespace.
  324. func (namespace *HostComputeNamespace) Sync() error {
  325. logrus.WithField("id", namespace.Id).Debugf("hcs::HostComputeNamespace::Sync")
  326. // We only attempt a sync for namespace guest.
  327. if namespace.Type != NamespaceTypeGuest {
  328. return nil
  329. }
  330. // Look in the registry for the key to map from namespace id to pod-id
  331. cfg, err := icni.LoadPersistedNamespaceConfig(namespace.Id)
  332. if err != nil {
  333. if regstate.IsNotFoundError(err) {
  334. return nil
  335. }
  336. return err
  337. }
  338. req := runhcs.VMRequest{
  339. ID: cfg.ContainerID,
  340. Op: runhcs.OpSyncNamespace,
  341. }
  342. shimPath := runhcs.VMPipePath(cfg.HostUniqueID)
  343. if err := runhcs.IssueVMRequest(shimPath, &req); err != nil {
  344. // The shim is likely gone. Simply ignore the sync as if it didn't exist.
  345. if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND {
  346. // Remove the reg key there is no point to try again
  347. _ = cfg.Remove()
  348. return nil
  349. }
  350. f := map[string]interface{}{
  351. "id": namespace.Id,
  352. "container-id": cfg.ContainerID,
  353. }
  354. logrus.WithFields(f).
  355. WithError(err).
  356. Debugf("hcs::HostComputeNamespace::Sync failed to connect to shim pipe: '%s'", shimPath)
  357. return err
  358. }
  359. return nil
  360. }
  361. // ModifyNamespaceSettings updates the Endpoints/Containers of a Namespace.
  362. func ModifyNamespaceSettings(namespaceID string, request *ModifyNamespaceSettingRequest) error {
  363. logrus.Debugf("hcn::HostComputeNamespace::ModifyNamespaceSettings id=%s", namespaceID)
  364. namespaceSettings, err := json.Marshal(request)
  365. if err != nil {
  366. return err
  367. }
  368. _, err = modifyNamespace(namespaceID, string(namespaceSettings))
  369. if err != nil {
  370. return err
  371. }
  372. return nil
  373. }
  374. // AddNamespaceEndpoint adds an endpoint to a Namespace.
  375. func AddNamespaceEndpoint(namespaceID string, endpointID string) error {
  376. logrus.Debugf("hcn::HostComputeEndpoint::AddNamespaceEndpoint id=%s", endpointID)
  377. mapA := map[string]string{"EndpointId": endpointID}
  378. settingsJSON, err := json.Marshal(mapA)
  379. if err != nil {
  380. return err
  381. }
  382. requestMessage := &ModifyNamespaceSettingRequest{
  383. ResourceType: NamespaceResourceTypeEndpoint,
  384. RequestType: RequestTypeAdd,
  385. Settings: settingsJSON,
  386. }
  387. return ModifyNamespaceSettings(namespaceID, requestMessage)
  388. }
  389. // RemoveNamespaceEndpoint removes an endpoint from a Namespace.
  390. func RemoveNamespaceEndpoint(namespaceID string, endpointID string) error {
  391. logrus.Debugf("hcn::HostComputeNamespace::RemoveNamespaceEndpoint id=%s", endpointID)
  392. mapA := map[string]string{"EndpointId": endpointID}
  393. settingsJSON, err := json.Marshal(mapA)
  394. if err != nil {
  395. return err
  396. }
  397. requestMessage := &ModifyNamespaceSettingRequest{
  398. ResourceType: NamespaceResourceTypeEndpoint,
  399. RequestType: RequestTypeRemove,
  400. Settings: settingsJSON,
  401. }
  402. return ModifyNamespaceSettings(namespaceID, requestMessage)
  403. }