github.com/uchennaokeke444/nomad@v0.11.8/nomad/structs/node.go (about)

     1  package structs
     2  
     3  import (
     4  	"reflect"
     5  	"time"
     6  
     7  	"github.com/hashicorp/nomad/helper"
     8  )
     9  
    10  // CSITopology is a map of topological domains to topological segments.
    11  // A topological domain is a sub-division of a cluster, like "region",
    12  // "zone", "rack", etc.
    13  //
    14  // According to CSI, there are a few requirements for the keys within this map:
    15  // - Valid keys have two segments: an OPTIONAL prefix and name, separated
    16  //   by a slash (/), for example: "com.company.example/zone".
    17  // - The key name segment is REQUIRED. The prefix is OPTIONAL.
    18  // - The key name MUST be 63 characters or less, begin and end with an
    19  //   alphanumeric character ([a-z0-9A-Z]), and contain only dashes (-),
    20  //   underscores (_), dots (.), or alphanumerics in between, for example
    21  //   "zone".
    22  // - The key prefix MUST be 63 characters or less, begin and end with a
    23  //   lower-case alphanumeric character ([a-z0-9]), contain only
    24  //   dashes (-), dots (.), or lower-case alphanumerics in between, and
    25  //   follow domain name notation format
    26  //   (https://tools.ietf.org/html/rfc1035#section-2.3.1).
    27  // - The key prefix SHOULD include the plugin's host company name and/or
    28  //   the plugin name, to minimize the possibility of collisions with keys
    29  //   from other plugins.
    30  // - If a key prefix is specified, it MUST be identical across all
    31  //   topology keys returned by the SP (across all RPCs).
    32  // - Keys MUST be case-insensitive. Meaning the keys "Zone" and "zone"
    33  //   MUST not both exist.
    34  // - Each value (topological segment) MUST contain 1 or more strings.
    35  // - Each string MUST be 63 characters or less and begin and end with an
    36  //   alphanumeric character with '-', '_', '.', or alphanumerics in
    37  //   between.
    38  //
    39  // However, Nomad applies lighter restrictions to these, as they are already
    40  // only referenced by plugin within the scheduler and as such collisions and
    41  // related concerns are less of an issue. We may implement these restrictions
    42  // in the future.
    43  type CSITopology struct {
    44  	Segments map[string]string
    45  }
    46  
    47  func (t *CSITopology) Copy() *CSITopology {
    48  	if t == nil {
    49  		return nil
    50  	}
    51  
    52  	return &CSITopology{
    53  		Segments: helper.CopyMapStringString(t.Segments),
    54  	}
    55  }
    56  
    57  func (t *CSITopology) Equal(o *CSITopology) bool {
    58  	if t == nil || o == nil {
    59  		return t == o
    60  	}
    61  
    62  	return helper.CompareMapStringString(t.Segments, o.Segments)
    63  }
    64  
    65  // CSINodeInfo is the fingerprinted data from a CSI Plugin that is specific to
    66  // the Node API.
    67  type CSINodeInfo struct {
    68  	// ID is the identity of a given nomad client as observed by the storage
    69  	// provider.
    70  	ID string
    71  
    72  	// MaxVolumes is the maximum number of volumes that can be attached to the
    73  	// current host via this provider.
    74  	// If 0 then unlimited volumes may be attached.
    75  	MaxVolumes int64
    76  
    77  	// AccessibleTopology specifies where (regions, zones, racks, etc.) the node is
    78  	// accessible from within the storage provider.
    79  	//
    80  	// A plugin that returns this field MUST also set the `RequiresTopologies`
    81  	// property.
    82  	//
    83  	// This field is OPTIONAL. If it is not specified, then we assume that the
    84  	// the node is not subject to any topological constraint, and MAY
    85  	// schedule workloads that reference any volume V, such that there are
    86  	// no topological constraints declared for V.
    87  	//
    88  	// Example 1:
    89  	//   accessible_topology =
    90  	//     {"region": "R1", "zone": "Z2"}
    91  	// Indicates the node exists within the "region" "R1" and the "zone"
    92  	// "Z2" within the storage provider.
    93  	AccessibleTopology *CSITopology
    94  
    95  	// RequiresNodeStageVolume indicates whether the client should Stage/Unstage
    96  	// volumes on this node.
    97  	RequiresNodeStageVolume bool
    98  }
    99  
   100  func (n *CSINodeInfo) Copy() *CSINodeInfo {
   101  	if n == nil {
   102  		return nil
   103  	}
   104  
   105  	nc := new(CSINodeInfo)
   106  	*nc = *n
   107  	nc.AccessibleTopology = n.AccessibleTopology.Copy()
   108  
   109  	return nc
   110  }
   111  
   112  // CSIControllerInfo is the fingerprinted data from a CSI Plugin that is specific to
   113  // the Controller API.
   114  type CSIControllerInfo struct {
   115  	// SupportsReadOnlyAttach is set to true when the controller returns the
   116  	// ATTACH_READONLY capability.
   117  	SupportsReadOnlyAttach bool
   118  
   119  	// SupportsPublishVolume is true when the controller implements the methods
   120  	// required to attach and detach volumes. If this is false Nomad should skip
   121  	// the controller attachment flow.
   122  	SupportsAttachDetach bool
   123  
   124  	// SupportsListVolumes is true when the controller implements the ListVolumes
   125  	// RPC. NOTE: This does not guaruntee that attached nodes will be returned
   126  	// unless SupportsListVolumesAttachedNodes is also true.
   127  	SupportsListVolumes bool
   128  
   129  	// SupportsListVolumesAttachedNodes indicates whether the plugin will return
   130  	// attached nodes data when making ListVolume RPCs
   131  	SupportsListVolumesAttachedNodes bool
   132  }
   133  
   134  func (c *CSIControllerInfo) Copy() *CSIControllerInfo {
   135  	if c == nil {
   136  		return nil
   137  	}
   138  
   139  	nc := new(CSIControllerInfo)
   140  	*nc = *c
   141  
   142  	return nc
   143  }
   144  
   145  // CSIInfo is the current state of a single CSI Plugin. This is updated regularly
   146  // as plugin health changes on the node.
   147  type CSIInfo struct {
   148  	PluginID          string
   149  	AllocID           string
   150  	Healthy           bool
   151  	HealthDescription string
   152  	UpdateTime        time.Time
   153  
   154  	Provider        string // vendor name from CSI GetPluginInfoResponse
   155  	ProviderVersion string // vendor version from CSI GetPluginInfoResponse
   156  
   157  	// RequiresControllerPlugin is set when the CSI Plugin returns the
   158  	// CONTROLLER_SERVICE capability. When this is true, the volumes should not be
   159  	// scheduled on this client until a matching controller plugin is available.
   160  	RequiresControllerPlugin bool
   161  
   162  	// RequiresTopologies is set when the CSI Plugin returns the
   163  	// VOLUME_ACCESSIBLE_CONSTRAINTS capability. When this is true, we must
   164  	// respect the Volume and Node Topology information.
   165  	RequiresTopologies bool
   166  
   167  	// CSI Specific metadata
   168  	ControllerInfo *CSIControllerInfo `json:",omitempty"`
   169  	NodeInfo       *CSINodeInfo       `json:",omitempty"`
   170  }
   171  
   172  func (c *CSIInfo) Copy() *CSIInfo {
   173  	if c == nil {
   174  		return nil
   175  	}
   176  
   177  	nc := new(CSIInfo)
   178  	*nc = *c
   179  	nc.ControllerInfo = c.ControllerInfo.Copy()
   180  	nc.NodeInfo = c.NodeInfo.Copy()
   181  
   182  	return nc
   183  }
   184  
   185  func (c *CSIInfo) SetHealthy(hs bool) {
   186  	c.Healthy = hs
   187  	if hs {
   188  		c.HealthDescription = "healthy"
   189  	} else {
   190  		c.HealthDescription = "unhealthy"
   191  	}
   192  }
   193  
   194  func (c *CSIInfo) Equal(o *CSIInfo) bool {
   195  	if c == nil && o == nil {
   196  		return c == o
   197  	}
   198  
   199  	nc := *c
   200  	nc.UpdateTime = time.Time{}
   201  	no := *o
   202  	no.UpdateTime = time.Time{}
   203  
   204  	return reflect.DeepEqual(nc, no)
   205  }
   206  
   207  func (c *CSIInfo) IsController() bool {
   208  	if c == nil || c.ControllerInfo == nil {
   209  		return false
   210  	}
   211  	return true
   212  }
   213  
   214  func (c *CSIInfo) IsNode() bool {
   215  	if c == nil || c.NodeInfo == nil {
   216  		return false
   217  	}
   218  	return true
   219  }
   220  
   221  // DriverInfo is the current state of a single driver. This is updated
   222  // regularly as driver health changes on the node.
   223  type DriverInfo struct {
   224  	Attributes        map[string]string
   225  	Detected          bool
   226  	Healthy           bool
   227  	HealthDescription string
   228  	UpdateTime        time.Time
   229  }
   230  
   231  func (di *DriverInfo) Copy() *DriverInfo {
   232  	if di == nil {
   233  		return nil
   234  	}
   235  
   236  	cdi := new(DriverInfo)
   237  	*cdi = *di
   238  	cdi.Attributes = helper.CopyMapStringString(di.Attributes)
   239  	return cdi
   240  }
   241  
   242  // MergeHealthCheck merges information from a health check for a drier into a
   243  // node's driver info
   244  func (di *DriverInfo) MergeHealthCheck(other *DriverInfo) {
   245  	di.Healthy = other.Healthy
   246  	di.HealthDescription = other.HealthDescription
   247  	di.UpdateTime = other.UpdateTime
   248  }
   249  
   250  // MergeFingerprint merges information from fingerprinting a node for a driver
   251  // into a node's driver info for that driver.
   252  func (di *DriverInfo) MergeFingerprintInfo(other *DriverInfo) {
   253  	di.Detected = other.Detected
   254  	di.Attributes = other.Attributes
   255  }
   256  
   257  // DriverInfo determines if two driver info objects are equal..As this is used
   258  // in the process of health checking, we only check the fields that are
   259  // computed by the health checker. In the future, this will be merged.
   260  func (di *DriverInfo) HealthCheckEquals(other *DriverInfo) bool {
   261  	if di == nil && other == nil {
   262  		return true
   263  	}
   264  
   265  	if di.Healthy != other.Healthy {
   266  		return false
   267  	}
   268  
   269  	if di.HealthDescription != other.HealthDescription {
   270  		return false
   271  	}
   272  
   273  	return true
   274  }