...

Source file src/github.com/Microsoft/hcsshim/hcn/hcnnamespace.go

Documentation: github.com/Microsoft/hcsshim/hcn

     1  //go:build windows
     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  // NamespaceResourceEndpoint represents an Endpoint attached to a Namespace.
    19  type NamespaceResourceEndpoint struct {
    20  	Id string `json:"ID,"`
    21  }
    22  
    23  // NamespaceResourceContainer represents a Container attached to a Namespace.
    24  type NamespaceResourceContainer struct {
    25  	Id string `json:"ID,"`
    26  }
    27  
    28  // NamespaceResourceType determines whether the Namespace resource is a Container or Endpoint.
    29  type NamespaceResourceType string
    30  
    31  var (
    32  	// NamespaceResourceTypeContainer are containers associated with a Namespace.
    33  	NamespaceResourceTypeContainer NamespaceResourceType = "Container"
    34  	// NamespaceResourceTypeEndpoint are endpoints associated with a Namespace.
    35  	NamespaceResourceTypeEndpoint NamespaceResourceType = "Endpoint"
    36  )
    37  
    38  // NamespaceResource is associated with a namespace
    39  type NamespaceResource struct {
    40  	Type NamespaceResourceType `json:","` // Container, Endpoint
    41  	Data json.RawMessage       `json:","`
    42  }
    43  
    44  // NamespaceType determines whether the Namespace is for a Host or Guest
    45  type NamespaceType string
    46  
    47  var (
    48  	// NamespaceTypeHost are host namespaces.
    49  	NamespaceTypeHost NamespaceType = "Host"
    50  	// NamespaceTypeHostDefault are host namespaces in the default compartment.
    51  	NamespaceTypeHostDefault NamespaceType = "HostDefault"
    52  	// NamespaceTypeGuest are guest namespaces.
    53  	NamespaceTypeGuest NamespaceType = "Guest"
    54  	// NamespaceTypeGuestDefault are guest namespaces in the default compartment.
    55  	NamespaceTypeGuestDefault NamespaceType = "GuestDefault"
    56  )
    57  
    58  // HostComputeNamespace represents a namespace (AKA compartment) in
    59  type HostComputeNamespace struct {
    60  	Id            string              `json:"ID,omitempty"`
    61  	NamespaceId   uint32              `json:",omitempty"`
    62  	Type          NamespaceType       `json:",omitempty"` // Host, HostDefault, Guest, GuestDefault
    63  	Resources     []NamespaceResource `json:",omitempty"`
    64  	SchemaVersion SchemaVersion       `json:",omitempty"`
    65  }
    66  
    67  // ModifyNamespaceSettingRequest is the structure used to send request to modify a namespace.
    68  // Used to Add/Remove an endpoints and containers to/from a namespace.
    69  type ModifyNamespaceSettingRequest struct {
    70  	ResourceType NamespaceResourceType `json:",omitempty"` // Container, Endpoint
    71  	RequestType  RequestType           `json:",omitempty"` // Add, Remove, Update, Refresh
    72  	Settings     json.RawMessage       `json:",omitempty"`
    73  }
    74  
    75  func getNamespace(namespaceGUID guid.GUID, query string) (*HostComputeNamespace, error) {
    76  	// Open namespace.
    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  	// Query namespace.
    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  	// Close namespace.
    93  	hr = hcnCloseNamespace(namespaceHandle)
    94  	if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
    95  		return nil, err
    96  	}
    97  	// Convert output to HostComputeNamespace
    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  	// Enumerate all Namespace Guids
   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  	// Create new namespace.
   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  	// Query namespace.
   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  	// Close namespace.
   157  	hr = hcnCloseNamespace(namespaceHandle)
   158  	if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
   159  		return nil, err
   160  	}
   161  	// Convert output to HostComputeNamespace
   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  	// Open namespace.
   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  	// Modify namespace.
   185  	hr = hcnModifyNamespace(namespaceHandle, settings, &resultBuffer)
   186  	if err := checkForErrors("hcnModifyNamespace", hr, resultBuffer); err != nil {
   187  		return nil, err
   188  	}
   189  	// Query namespace.
   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  	// Close namespace.
   201  	hr = hcnCloseNamespace(namespaceHandle)
   202  	if err := checkForErrors("hcnCloseNamespace", hr, nil); err != nil {
   203  		return nil, err
   204  	}
   205  	// Convert output to Namespace
   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  // ListNamespaces makes a call to list all available namespaces.
   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  // ListNamespacesQuery makes a call to query the list of available namespaces.
   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  // GetNamespaceByID returns the Namespace specified by Id.
   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  // GetNamespaceEndpointIds returns the endpoints of the Namespace specified by Id.
   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  // GetNamespaceContainerIds returns the containers of the Namespace specified by Id.
   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  // NewNamespace creates a new Namespace object
   310  func NewNamespace(nsType NamespaceType) *HostComputeNamespace {
   311  	return &HostComputeNamespace{
   312  		Type:          nsType,
   313  		SchemaVersion: V2SchemaVersion(),
   314  	}
   315  }
   316  
   317  // Create Namespace.
   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  // Delete Namespace.
   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  // Sync Namespace endpoints with the appropriate sandbox container holding the
   345  // network namespace open. If no sandbox container is found for this namespace
   346  // this method is determined to be a success and will not return an error in
   347  // this case. If the sandbox container is found and a sync is initiated any
   348  // failures will be returned via this method.
   349  //
   350  // This call initiates a sync between endpoints and the matching UtilityVM
   351  // hosting those endpoints. It is safe to call for any `NamespaceType` but
   352  // `NamespaceTypeGuest` is the only case when a sync will actually occur. For
   353  // `NamespaceTypeHost` the process container will be automatically synchronized
   354  // when the the endpoint is added via `AddNamespaceEndpoint`.
   355  //
   356  // Note: This method sync's both additions and removals of endpoints from a
   357  // `NamespaceTypeGuest` namespace.
   358  func (namespace *HostComputeNamespace) Sync() error {
   359  	logrus.WithField("id", namespace.Id).Debugf("hcs::HostComputeNamespace::Sync")
   360  
   361  	// We only attempt a sync for namespace guest.
   362  	if namespace.Type != NamespaceTypeGuest {
   363  		return nil
   364  	}
   365  
   366  	// Look in the registry for the key to map from namespace id to pod-id
   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  		// The shim is likely gone. Simply ignore the sync as if it didn't exist.
   381  		if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND {
   382  			// Remove the reg key there is no point to try again
   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  // ModifyNamespaceSettings updates the Endpoints/Containers of a Namespace.
   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  // AddNamespaceEndpoint adds an endpoint to a Namespace.
   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  // RemoveNamespaceEndpoint removes an endpoint from a Namespace.
   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