123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- package hcn
- import (
- "encoding/json"
- "os"
- "syscall"
- icni "github.com/Microsoft/hcsshim/internal/cni"
- "github.com/Microsoft/hcsshim/internal/guid"
- "github.com/Microsoft/hcsshim/internal/interop"
- "github.com/Microsoft/hcsshim/internal/regstate"
- "github.com/Microsoft/hcsshim/internal/runhcs"
- "github.com/sirupsen/logrus"
- )
- // NamespaceResourceEndpoint represents an Endpoint attached to a Namespace.
- type NamespaceResourceEndpoint struct {
- Id string `json:"ID,"`
- }
- // NamespaceResourceContainer represents a Container attached to a Namespace.
- type NamespaceResourceContainer struct {
- Id string `json:"ID,"`
- }
- // NamespaceResourceType determines whether the Namespace resource is a Container or Endpoint.
- type NamespaceResourceType string
- var (
- // NamespaceResourceTypeContainer are contianers associated with a Namespace.
- NamespaceResourceTypeContainer NamespaceResourceType = "Container"
- // NamespaceResourceTypeEndpoint are endpoints associated with a Namespace.
- NamespaceResourceTypeEndpoint NamespaceResourceType = "Endpoint"
- )
- // NamespaceResource is associated with a namespace
- type NamespaceResource struct {
- Type NamespaceResourceType `json:","` // Container, Endpoint
- Data json.RawMessage `json:","`
- }
- // NamespaceType determines whether the Namespace is for a Host or Guest
- type NamespaceType string
- var (
- // NamespaceTypeHost are host namespaces.
- NamespaceTypeHost NamespaceType = "Host"
- // NamespaceTypeHostDefault are host namespaces in the default compartment.
- NamespaceTypeHostDefault NamespaceType = "HostDefault"
- // NamespaceTypeGuest are guest namespaces.
- NamespaceTypeGuest NamespaceType = "Guest"
- // NamespaceTypeGuestDefault are guest namespaces in the default compartment.
- NamespaceTypeGuestDefault NamespaceType = "GuestDefault"
- )
- // HostComputeNamespace represents a namespace (AKA compartment) in
- type HostComputeNamespace struct {
- Id string `json:"ID,omitempty"`
- NamespaceId uint32 `json:",omitempty"`
- Type NamespaceType `json:",omitempty"` // Host, HostDefault, Guest, GuestDefault
- Resources []NamespaceResource `json:",omitempty"`
- SchemaVersion SchemaVersion `json:",omitempty"`
- }
- // ModifyNamespaceSettingRequest is the structure used to send request to modify a namespace.
- // Used to Add/Remove an endpoints and containers to/from a namespace.
- type ModifyNamespaceSettingRequest struct {
- ResourceType NamespaceResourceType `json:",omitempty"` // Container, Endpoint
- RequestType RequestType `json:",omitempty"` // Add, Remove, Update, Refresh
- Settings json.RawMessage `json:",omitempty"`
- }
- func getNamespace(namespaceGuid guid.GUID, query string) (*HostComputeNamespace, error) {
- // Open namespace.
- var (
- namespaceHandle hcnNamespace
- resultBuffer *uint16
- propertiesBuffer *uint16
- )
- hr := hcnOpenNamespace(&namespaceGuid, &namespaceHandle, &resultBuffer)
- if err := checkForErrors("hcnOpenNamespace", hr, resultBuffer); err != nil {
- return nil, err
- }
- // Query namespace.
- hr = hcnQueryNamespaceProperties(namespaceHandle, query, &propertiesBuffer, &resultBuffer)
- if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
- return nil, err
- }
- properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
- // Close namespace.
- hr = hcnCloseNamespace(namespaceHandle)
- if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
- return nil, err
- }
- // Convert output to HostComputeNamespace
- var outputNamespace HostComputeNamespace
- if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
- return nil, err
- }
- return &outputNamespace, nil
- }
- func enumerateNamespaces(query string) ([]HostComputeNamespace, error) {
- // Enumerate all Namespace Guids
- var (
- resultBuffer *uint16
- namespaceBuffer *uint16
- )
- hr := hcnEnumerateNamespaces(query, &namespaceBuffer, &resultBuffer)
- if err := checkForErrors("hcnEnumerateNamespaces", hr, resultBuffer); err != nil {
- return nil, err
- }
- namespaces := interop.ConvertAndFreeCoTaskMemString(namespaceBuffer)
- var namespaceIds []guid.GUID
- if err := json.Unmarshal([]byte(namespaces), &namespaceIds); err != nil {
- return nil, err
- }
- var outputNamespaces []HostComputeNamespace
- for _, namespaceGuid := range namespaceIds {
- namespace, err := getNamespace(namespaceGuid, query)
- if err != nil {
- return nil, err
- }
- outputNamespaces = append(outputNamespaces, *namespace)
- }
- return outputNamespaces, nil
- }
- func createNamespace(settings string) (*HostComputeNamespace, error) {
- // Create new namespace.
- var (
- namespaceHandle hcnNamespace
- resultBuffer *uint16
- propertiesBuffer *uint16
- )
- namespaceGuid := guid.GUID{}
- hr := hcnCreateNamespace(&namespaceGuid, settings, &namespaceHandle, &resultBuffer)
- if err := checkForErrors("hcnCreateNamespace", hr, resultBuffer); err != nil {
- return nil, err
- }
- // Query namespace.
- hcnQuery := defaultQuery()
- query, err := json.Marshal(hcnQuery)
- if err != nil {
- return nil, err
- }
- hr = hcnQueryNamespaceProperties(namespaceHandle, string(query), &propertiesBuffer, &resultBuffer)
- if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
- return nil, err
- }
- properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
- // Close namespace.
- hr = hcnCloseNamespace(namespaceHandle)
- if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
- return nil, err
- }
- // Convert output to HostComputeNamespace
- var outputNamespace HostComputeNamespace
- if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
- return nil, err
- }
- return &outputNamespace, nil
- }
- func modifyNamespace(namespaceId string, settings string) (*HostComputeNamespace, error) {
- namespaceGuid := guid.FromString(namespaceId)
- // Open namespace.
- var (
- namespaceHandle hcnNamespace
- resultBuffer *uint16
- propertiesBuffer *uint16
- )
- hr := hcnOpenNamespace(&namespaceGuid, &namespaceHandle, &resultBuffer)
- if err := checkForErrors("hcnOpenNamespace", hr, resultBuffer); err != nil {
- return nil, err
- }
- // Modify namespace.
- hr = hcnModifyNamespace(namespaceHandle, settings, &resultBuffer)
- if err := checkForErrors("hcnModifyNamespace", hr, resultBuffer); err != nil {
- return nil, err
- }
- // Query namespace.
- hcnQuery := defaultQuery()
- query, err := json.Marshal(hcnQuery)
- if err != nil {
- return nil, err
- }
- hr = hcnQueryNamespaceProperties(namespaceHandle, string(query), &propertiesBuffer, &resultBuffer)
- if err := checkForErrors("hcnQueryNamespaceProperties", hr, resultBuffer); err != nil {
- return nil, err
- }
- properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
- // Close namespace.
- hr = hcnCloseNamespace(namespaceHandle)
- if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
- return nil, err
- }
- // Convert output to Namespace
- var outputNamespace HostComputeNamespace
- if err := json.Unmarshal([]byte(properties), &outputNamespace); err != nil {
- return nil, err
- }
- return &outputNamespace, nil
- }
- func deleteNamespace(namespaceId string) error {
- namespaceGuid := guid.FromString(namespaceId)
- var resultBuffer *uint16
- hr := hcnDeleteNamespace(&namespaceGuid, &resultBuffer)
- if err := checkForErrors("hcnDeleteNamespace", hr, resultBuffer); err != nil {
- return err
- }
- return nil
- }
- // ListNamespaces makes a call to list all available namespaces.
- func ListNamespaces() ([]HostComputeNamespace, error) {
- hcnQuery := defaultQuery()
- namespaces, err := ListNamespacesQuery(hcnQuery)
- if err != nil {
- return nil, err
- }
- return namespaces, nil
- }
- // ListNamespacesQuery makes a call to query the list of available namespaces.
- func ListNamespacesQuery(query HostComputeQuery) ([]HostComputeNamespace, error) {
- queryJson, err := json.Marshal(query)
- if err != nil {
- return nil, err
- }
- namespaces, err := enumerateNamespaces(string(queryJson))
- if err != nil {
- return nil, err
- }
- return namespaces, nil
- }
- // GetNamespaceByID returns the Namespace specified by Id.
- func GetNamespaceByID(namespaceId string) (*HostComputeNamespace, error) {
- return getNamespace(guid.FromString(namespaceId), defaultQueryJson())
- }
- // GetNamespaceEndpointIds returns the endpoints of the Namespace specified by Id.
- func GetNamespaceEndpointIds(namespaceId string) ([]string, error) {
- namespace, err := GetNamespaceByID(namespaceId)
- if err != nil {
- return nil, err
- }
- var endpointsIds []string
- for _, resource := range namespace.Resources {
- if resource.Type == "Endpoint" {
- var endpointResource NamespaceResourceEndpoint
- if err := json.Unmarshal([]byte(resource.Data), &endpointResource); err != nil {
- return nil, err
- }
- endpointsIds = append(endpointsIds, endpointResource.Id)
- }
- }
- return endpointsIds, nil
- }
- // GetNamespaceContainerIds returns the containers of the Namespace specified by Id.
- func GetNamespaceContainerIds(namespaceId string) ([]string, error) {
- namespace, err := GetNamespaceByID(namespaceId)
- if err != nil {
- return nil, err
- }
- var containerIds []string
- for _, resource := range namespace.Resources {
- if resource.Type == "Container" {
- var contaienrResource NamespaceResourceContainer
- if err := json.Unmarshal([]byte(resource.Data), &contaienrResource); err != nil {
- return nil, err
- }
- containerIds = append(containerIds, contaienrResource.Id)
- }
- }
- return containerIds, nil
- }
- // NewNamespace creates a new Namespace object
- func NewNamespace(nsType NamespaceType) *HostComputeNamespace {
- return &HostComputeNamespace{
- Type: nsType,
- SchemaVersion: V2SchemaVersion(),
- }
- }
- // Create Namespace.
- func (namespace *HostComputeNamespace) Create() (*HostComputeNamespace, error) {
- logrus.Debugf("hcn::HostComputeNamespace::Create id=%s", namespace.Id)
- jsonString, err := json.Marshal(namespace)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("hcn::HostComputeNamespace::Create JSON: %s", jsonString)
- namespace, hcnErr := createNamespace(string(jsonString))
- if hcnErr != nil {
- return nil, hcnErr
- }
- return namespace, nil
- }
- // Delete Namespace.
- func (namespace *HostComputeNamespace) Delete() error {
- logrus.Debugf("hcn::HostComputeNamespace::Delete id=%s", namespace.Id)
- if err := deleteNamespace(namespace.Id); err != nil {
- return err
- }
- return nil
- }
- // Sync Namespace endpoints with the appropriate sandbox container holding the
- // network namespace open. If no sandbox container is found for this namespace
- // this method is determined to be a success and will not return an error in
- // this case. If the sandbox container is found and a sync is initiated any
- // failures will be returned via this method.
- //
- // This call initiates a sync between endpoints and the matching UtilityVM
- // hosting those endpoints. It is safe to call for any `NamespaceType` but
- // `NamespaceTypeGuest` is the only case when a sync will actually occur. For
- // `NamespaceTypeHost` the process container will be automatically synchronized
- // when the the endpoint is added via `AddNamespaceEndpoint`.
- //
- // Note: This method sync's both additions and removals of endpoints from a
- // `NamespaceTypeGuest` namespace.
- func (namespace *HostComputeNamespace) Sync() error {
- logrus.WithField("id", namespace.Id).Debugf("hcs::HostComputeNamespace::Sync")
- // We only attempt a sync for namespace guest.
- if namespace.Type != NamespaceTypeGuest {
- return nil
- }
- // Look in the registry for the key to map from namespace id to pod-id
- cfg, err := icni.LoadPersistedNamespaceConfig(namespace.Id)
- if err != nil {
- if regstate.IsNotFoundError(err) {
- return nil
- }
- return err
- }
- req := runhcs.VMRequest{
- ID: cfg.ContainerID,
- Op: runhcs.OpSyncNamespace,
- }
- shimPath := runhcs.VMPipePath(cfg.HostUniqueID)
- if err := runhcs.IssueVMRequest(shimPath, &req); err != nil {
- // The shim is likey gone. Simply ignore the sync as if it didn't exist.
- if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND {
- // Remove the reg key there is no point to try again
- cfg.Remove()
- return nil
- }
- f := map[string]interface{}{
- "id": namespace.Id,
- "container-id": cfg.ContainerID,
- }
- logrus.WithFields(f).
- WithError(err).
- Debugf("hcs::HostComputeNamespace::Sync failed to connect to shim pipe: '%s'", shimPath)
- return err
- }
- return nil
- }
- // ModifyNamespaceSettings updates the Endpoints/Containers of a Namespace.
- func ModifyNamespaceSettings(namespaceId string, request *ModifyNamespaceSettingRequest) error {
- logrus.Debugf("hcn::HostComputeNamespace::ModifyNamespaceSettings id=%s", namespaceId)
- namespaceSettings, err := json.Marshal(request)
- if err != nil {
- return err
- }
- _, err = modifyNamespace(namespaceId, string(namespaceSettings))
- if err != nil {
- return err
- }
- return nil
- }
- // AddNamespaceEndpoint adds an endpoint to a Namespace.
- func AddNamespaceEndpoint(namespaceId string, endpointId string) error {
- logrus.Debugf("hcn::HostComputeEndpoint::AddNamespaceEndpoint id=%s", endpointId)
- mapA := map[string]string{"EndpointId": endpointId}
- settingsJson, err := json.Marshal(mapA)
- if err != nil {
- return err
- }
- requestMessage := &ModifyNamespaceSettingRequest{
- ResourceType: NamespaceResourceTypeEndpoint,
- RequestType: RequestTypeAdd,
- Settings: settingsJson,
- }
- return ModifyNamespaceSettings(namespaceId, requestMessage)
- }
- // RemoveNamespaceEndpoint removes an endpoint from a Namespace.
- func RemoveNamespaceEndpoint(namespaceId string, endpointId string) error {
- logrus.Debugf("hcn::HostComputeNamespace::RemoveNamespaceEndpoint id=%s", endpointId)
- mapA := map[string]string{"EndpointId": endpointId}
- settingsJson, err := json.Marshal(mapA)
- if err != nil {
- return err
- }
- requestMessage := &ModifyNamespaceSettingRequest{
- ResourceType: NamespaceResourceTypeEndpoint,
- RequestType: RequestTypeRemove,
- Settings: settingsJson,
- }
- return ModifyNamespaceSettings(namespaceId, requestMessage)
- }
|