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

     1  package structs
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"time"
     7  )
     8  
     9  // CSISocketName is the filename that Nomad expects plugins to create inside the
    10  // PluginMountDir.
    11  const CSISocketName = "csi.sock"
    12  
    13  // CSIIntermediaryDirname is the name of the directory inside the PluginMountDir
    14  // where Nomad will expect plugins to create intermediary mounts for volumes.
    15  const CSIIntermediaryDirname = "volumes"
    16  
    17  // VolumeTypeCSI is the type in the volume stanza of a TaskGroup
    18  const VolumeTypeCSI = "csi"
    19  
    20  // CSIPluginType is an enum string that encapsulates the valid options for a
    21  // CSIPlugin stanza's Type. These modes will allow the plugin to be used in
    22  // different ways by the client.
    23  type CSIPluginType string
    24  
    25  const (
    26  	// CSIPluginTypeNode indicates that Nomad should only use the plugin for
    27  	// performing Node RPCs against the provided plugin.
    28  	CSIPluginTypeNode CSIPluginType = "node"
    29  
    30  	// CSIPluginTypeController indicates that Nomad should only use the plugin for
    31  	// performing Controller RPCs against the provided plugin.
    32  	CSIPluginTypeController CSIPluginType = "controller"
    33  
    34  	// CSIPluginTypeMonolith indicates that Nomad can use the provided plugin for
    35  	// both controller and node rpcs.
    36  	CSIPluginTypeMonolith CSIPluginType = "monolith"
    37  )
    38  
    39  // CSIPluginTypeIsValid validates the given CSIPluginType string and returns
    40  // true only when a correct plugin type is specified.
    41  func CSIPluginTypeIsValid(pt CSIPluginType) bool {
    42  	switch pt {
    43  	case CSIPluginTypeNode, CSIPluginTypeController, CSIPluginTypeMonolith:
    44  		return true
    45  	default:
    46  		return false
    47  	}
    48  }
    49  
    50  // TaskCSIPluginConfig contains the data that is required to setup a task as a
    51  // CSI plugin. This will be used by the csi_plugin_supervisor_hook to configure
    52  // mounts for the plugin and initiate the connection to the plugin catalog.
    53  type TaskCSIPluginConfig struct {
    54  	// ID is the identifier of the plugin.
    55  	// Ideally this should be the FQDN of the plugin.
    56  	ID string
    57  
    58  	// Type instructs Nomad on how to handle processing a plugin
    59  	Type CSIPluginType
    60  
    61  	// MountDir is the destination that nomad should mount in its CSI
    62  	// directory for the plugin. It will then expect a file called CSISocketName
    63  	// to be created by the plugin, and will provide references into
    64  	// "MountDir/CSIIntermediaryDirname/{VolumeName}/{AllocID} for mounts.
    65  	MountDir string
    66  }
    67  
    68  func (t *TaskCSIPluginConfig) Copy() *TaskCSIPluginConfig {
    69  	if t == nil {
    70  		return nil
    71  	}
    72  
    73  	nt := new(TaskCSIPluginConfig)
    74  	*nt = *t
    75  
    76  	return nt
    77  }
    78  
    79  // CSIVolumeAttachmentMode chooses the type of storage api that will be used to
    80  // interact with the device.
    81  type CSIVolumeAttachmentMode string
    82  
    83  const (
    84  	CSIVolumeAttachmentModeUnknown     CSIVolumeAttachmentMode = ""
    85  	CSIVolumeAttachmentModeBlockDevice CSIVolumeAttachmentMode = "block-device"
    86  	CSIVolumeAttachmentModeFilesystem  CSIVolumeAttachmentMode = "file-system"
    87  )
    88  
    89  func ValidCSIVolumeAttachmentMode(attachmentMode CSIVolumeAttachmentMode) bool {
    90  	switch attachmentMode {
    91  	case CSIVolumeAttachmentModeBlockDevice, CSIVolumeAttachmentModeFilesystem:
    92  		return true
    93  	default:
    94  		return false
    95  	}
    96  }
    97  
    98  // CSIVolumeAccessMode indicates how a volume should be used in a storage topology
    99  // e.g whether the provider should make the volume available concurrently.
   100  type CSIVolumeAccessMode string
   101  
   102  const (
   103  	CSIVolumeAccessModeUnknown CSIVolumeAccessMode = ""
   104  
   105  	CSIVolumeAccessModeSingleNodeReader CSIVolumeAccessMode = "single-node-reader-only"
   106  	CSIVolumeAccessModeSingleNodeWriter CSIVolumeAccessMode = "single-node-writer"
   107  
   108  	CSIVolumeAccessModeMultiNodeReader       CSIVolumeAccessMode = "multi-node-reader-only"
   109  	CSIVolumeAccessModeMultiNodeSingleWriter CSIVolumeAccessMode = "multi-node-single-writer"
   110  	CSIVolumeAccessModeMultiNodeMultiWriter  CSIVolumeAccessMode = "multi-node-multi-writer"
   111  )
   112  
   113  // ValidCSIVolumeAccessMode checks to see that the provided access mode is a valid,
   114  // non-empty access mode.
   115  func ValidCSIVolumeAccessMode(accessMode CSIVolumeAccessMode) bool {
   116  	switch accessMode {
   117  	case CSIVolumeAccessModeSingleNodeReader, CSIVolumeAccessModeSingleNodeWriter,
   118  		CSIVolumeAccessModeMultiNodeReader, CSIVolumeAccessModeMultiNodeSingleWriter,
   119  		CSIVolumeAccessModeMultiNodeMultiWriter:
   120  		return true
   121  	default:
   122  		return false
   123  	}
   124  }
   125  
   126  // ValidCSIVolumeAccessMode checks for a writable access mode
   127  func ValidCSIVolumeWriteAccessMode(accessMode CSIVolumeAccessMode) bool {
   128  	switch accessMode {
   129  	case CSIVolumeAccessModeSingleNodeWriter,
   130  		CSIVolumeAccessModeMultiNodeSingleWriter,
   131  		CSIVolumeAccessModeMultiNodeMultiWriter:
   132  		return true
   133  	default:
   134  		return false
   135  	}
   136  }
   137  
   138  // CSIMountOptions contain optional additional configuration that can be used
   139  // when specifying that a Volume should be used with VolumeAccessTypeMount.
   140  type CSIMountOptions struct {
   141  	// FSType is an optional field that allows an operator to specify the type
   142  	// of the filesystem.
   143  	FSType string
   144  
   145  	// MountFlags contains additional options that may be used when mounting the
   146  	// volume by the plugin. This may contain sensitive data and should not be
   147  	// leaked.
   148  	MountFlags []string
   149  }
   150  
   151  func (o *CSIMountOptions) Copy() *CSIMountOptions {
   152  	if o == nil {
   153  		return nil
   154  	}
   155  	return &(*o)
   156  }
   157  
   158  func (o *CSIMountOptions) Merge(p *CSIMountOptions) {
   159  	if p == nil {
   160  		return
   161  	}
   162  	if p.FSType != "" {
   163  		o.FSType = p.FSType
   164  	}
   165  	if p.MountFlags != nil {
   166  		o.MountFlags = p.MountFlags
   167  	}
   168  }
   169  
   170  // VolumeMountOptions implements the Stringer and GoStringer interfaces to prevent
   171  // accidental leakage of sensitive mount flags via logs.
   172  var _ fmt.Stringer = &CSIMountOptions{}
   173  var _ fmt.GoStringer = &CSIMountOptions{}
   174  
   175  func (v *CSIMountOptions) String() string {
   176  	mountFlagsString := "nil"
   177  	if len(v.MountFlags) != 0 {
   178  		mountFlagsString = "[REDACTED]"
   179  	}
   180  
   181  	return fmt.Sprintf("csi.CSIOptions(FSType: %s, MountFlags: %s)", v.FSType, mountFlagsString)
   182  }
   183  
   184  func (v *CSIMountOptions) GoString() string {
   185  	return v.String()
   186  }
   187  
   188  // CSISecrets contain optional additional configuration that can be used
   189  // when specifying that a Volume should be used with VolumeAccessTypeMount.
   190  type CSISecrets map[string]string
   191  
   192  // CSISecrets implements the Stringer and GoStringer interfaces to prevent
   193  // accidental leakage of secrets via logs.
   194  var _ fmt.Stringer = &CSISecrets{}
   195  var _ fmt.GoStringer = &CSISecrets{}
   196  
   197  func (s *CSISecrets) String() string {
   198  	redacted := map[string]string{}
   199  	for k := range *s {
   200  		redacted[k] = "[REDACTED]"
   201  	}
   202  	return fmt.Sprintf("csi.CSISecrets(%v)", redacted)
   203  }
   204  
   205  func (s *CSISecrets) GoString() string {
   206  	return s.String()
   207  }
   208  
   209  type CSIVolumeClaim struct {
   210  	AllocationID string
   211  	NodeID       string
   212  	Mode         CSIVolumeClaimMode
   213  	State        CSIVolumeClaimState
   214  }
   215  
   216  type CSIVolumeClaimState int
   217  
   218  const (
   219  	CSIVolumeClaimStateTaken CSIVolumeClaimState = iota
   220  	CSIVolumeClaimStateNodeDetached
   221  	CSIVolumeClaimStateControllerDetached
   222  	CSIVolumeClaimStateReadyToFree
   223  )
   224  
   225  // CSIVolume is the full representation of a CSI Volume
   226  type CSIVolume struct {
   227  	// ID is a namespace unique URL safe identifier for the volume
   228  	ID string
   229  	// Name is a display name for the volume, not required to be unique
   230  	Name string
   231  	// ExternalID identifies the volume for the CSI interface, may be URL unsafe
   232  	ExternalID     string
   233  	Namespace      string
   234  	Topologies     []*CSITopology
   235  	AccessMode     CSIVolumeAccessMode
   236  	AttachmentMode CSIVolumeAttachmentMode
   237  	MountOptions   *CSIMountOptions
   238  	Secrets        CSISecrets
   239  	Parameters     map[string]string
   240  	Context        map[string]string
   241  
   242  	// Allocations, tracking claim status
   243  	ReadAllocs  map[string]*Allocation // AllocID -> Allocation
   244  	WriteAllocs map[string]*Allocation // AllocID -> Allocation
   245  
   246  	ReadClaims  map[string]*CSIVolumeClaim // AllocID -> claim
   247  	WriteClaims map[string]*CSIVolumeClaim // AllocID -> claim
   248  	PastClaims  map[string]*CSIVolumeClaim // AllocID -> claim
   249  
   250  	// Schedulable is true if all the denormalized plugin health fields are true, and the
   251  	// volume has not been marked for garbage collection
   252  	Schedulable         bool
   253  	PluginID            string
   254  	Provider            string
   255  	ProviderVersion     string
   256  	ControllerRequired  bool
   257  	ControllersHealthy  int
   258  	ControllersExpected int
   259  	NodesHealthy        int
   260  	NodesExpected       int
   261  	ResourceExhausted   time.Time
   262  
   263  	CreateIndex uint64
   264  	ModifyIndex uint64
   265  }
   266  
   267  // CSIVolListStub is partial representation of a CSI Volume for inclusion in lists
   268  type CSIVolListStub struct {
   269  	ID                  string
   270  	Namespace           string
   271  	Name                string
   272  	ExternalID          string
   273  	Topologies          []*CSITopology
   274  	AccessMode          CSIVolumeAccessMode
   275  	AttachmentMode      CSIVolumeAttachmentMode
   276  	CurrentReaders      int
   277  	CurrentWriters      int
   278  	Schedulable         bool
   279  	PluginID            string
   280  	Provider            string
   281  	ControllersHealthy  int
   282  	ControllersExpected int
   283  	NodesHealthy        int
   284  	NodesExpected       int
   285  	CreateIndex         uint64
   286  	ModifyIndex         uint64
   287  }
   288  
   289  // NewCSIVolume creates the volume struct. No side-effects
   290  func NewCSIVolume(volumeID string, index uint64) *CSIVolume {
   291  	out := &CSIVolume{
   292  		ID:          volumeID,
   293  		CreateIndex: index,
   294  		ModifyIndex: index,
   295  	}
   296  
   297  	out.newStructs()
   298  	return out
   299  }
   300  
   301  func (v *CSIVolume) newStructs() {
   302  	if v.Topologies == nil {
   303  		v.Topologies = []*CSITopology{}
   304  	}
   305  	if v.Context == nil {
   306  		v.Context = map[string]string{}
   307  	}
   308  	if v.Parameters == nil {
   309  		v.Parameters = map[string]string{}
   310  	}
   311  	if v.Secrets == nil {
   312  		v.Secrets = CSISecrets{}
   313  	}
   314  
   315  	v.ReadAllocs = map[string]*Allocation{}
   316  	v.WriteAllocs = map[string]*Allocation{}
   317  
   318  	v.ReadClaims = map[string]*CSIVolumeClaim{}
   319  	v.WriteClaims = map[string]*CSIVolumeClaim{}
   320  	v.PastClaims = map[string]*CSIVolumeClaim{}
   321  }
   322  
   323  func (v *CSIVolume) RemoteID() string {
   324  	if v.ExternalID != "" {
   325  		return v.ExternalID
   326  	}
   327  	return v.ID
   328  }
   329  
   330  func (v *CSIVolume) Stub() *CSIVolListStub {
   331  	stub := CSIVolListStub{
   332  		ID:                  v.ID,
   333  		Namespace:           v.Namespace,
   334  		Name:                v.Name,
   335  		ExternalID:          v.ExternalID,
   336  		Topologies:          v.Topologies,
   337  		AccessMode:          v.AccessMode,
   338  		AttachmentMode:      v.AttachmentMode,
   339  		CurrentReaders:      len(v.ReadAllocs),
   340  		CurrentWriters:      len(v.WriteAllocs),
   341  		Schedulable:         v.Schedulable,
   342  		PluginID:            v.PluginID,
   343  		Provider:            v.Provider,
   344  		ControllersHealthy:  v.ControllersHealthy,
   345  		ControllersExpected: v.ControllersExpected,
   346  		NodesHealthy:        v.NodesHealthy,
   347  		NodesExpected:       v.NodesExpected,
   348  		CreateIndex:         v.CreateIndex,
   349  		ModifyIndex:         v.ModifyIndex,
   350  	}
   351  
   352  	return &stub
   353  }
   354  
   355  func (v *CSIVolume) ReadSchedulable() bool {
   356  	if !v.Schedulable {
   357  		return false
   358  	}
   359  
   360  	return v.ResourceExhausted == time.Time{}
   361  }
   362  
   363  // WriteSchedulable determines if the volume is schedulable for writes, considering only
   364  // volume health
   365  func (v *CSIVolume) WriteSchedulable() bool {
   366  	if !v.Schedulable {
   367  		return false
   368  	}
   369  
   370  	switch v.AccessMode {
   371  	case CSIVolumeAccessModeSingleNodeWriter, CSIVolumeAccessModeMultiNodeSingleWriter, CSIVolumeAccessModeMultiNodeMultiWriter:
   372  		return v.ResourceExhausted == time.Time{}
   373  	default:
   374  		return false
   375  	}
   376  }
   377  
   378  // WriteFreeClaims determines if there are any free write claims available
   379  func (v *CSIVolume) WriteFreeClaims() bool {
   380  	switch v.AccessMode {
   381  	case CSIVolumeAccessModeSingleNodeWriter, CSIVolumeAccessModeMultiNodeSingleWriter, CSIVolumeAccessModeMultiNodeMultiWriter:
   382  		return len(v.WriteAllocs) == 0
   383  	default:
   384  		return false
   385  	}
   386  }
   387  
   388  // InUse tests whether any allocations are actively using the volume
   389  func (v *CSIVolume) InUse() bool {
   390  	return len(v.ReadAllocs) != 0 ||
   391  		len(v.WriteAllocs) != 0
   392  }
   393  
   394  // Copy returns a copy of the volume, which shares only the Topologies slice
   395  func (v *CSIVolume) Copy() *CSIVolume {
   396  	copy := *v
   397  	out := &copy
   398  	out.newStructs()
   399  	for k, v := range v.Parameters {
   400  		out.Parameters[k] = v
   401  	}
   402  	for k, v := range v.Context {
   403  		out.Context[k] = v
   404  	}
   405  	for k, v := range v.Secrets {
   406  		out.Secrets[k] = v
   407  	}
   408  
   409  	for k, v := range v.ReadAllocs {
   410  		out.ReadAllocs[k] = v
   411  	}
   412  
   413  	for k, v := range v.WriteAllocs {
   414  		out.WriteAllocs[k] = v
   415  	}
   416  
   417  	for k, v := range v.ReadClaims {
   418  		claim := *v
   419  		out.ReadClaims[k] = &claim
   420  	}
   421  	for k, v := range v.WriteClaims {
   422  		claim := *v
   423  		out.WriteClaims[k] = &claim
   424  	}
   425  	for k, v := range v.PastClaims {
   426  		claim := *v
   427  		out.PastClaims[k] = &claim
   428  	}
   429  
   430  	return out
   431  }
   432  
   433  // Claim updates the allocations and changes the volume state
   434  func (v *CSIVolume) Claim(claim *CSIVolumeClaim, alloc *Allocation) error {
   435  	switch claim.Mode {
   436  	case CSIVolumeClaimRead:
   437  		return v.ClaimRead(claim, alloc)
   438  	case CSIVolumeClaimWrite:
   439  		return v.ClaimWrite(claim, alloc)
   440  	case CSIVolumeClaimRelease:
   441  		return v.ClaimRelease(claim)
   442  	}
   443  	return nil
   444  }
   445  
   446  // ClaimRead marks an allocation as using a volume read-only
   447  func (v *CSIVolume) ClaimRead(claim *CSIVolumeClaim, alloc *Allocation) error {
   448  	if _, ok := v.ReadAllocs[claim.AllocationID]; ok {
   449  		return nil
   450  	}
   451  	if alloc == nil {
   452  		return fmt.Errorf("allocation missing: %s", claim.AllocationID)
   453  	}
   454  
   455  	if !v.ReadSchedulable() {
   456  		return fmt.Errorf("unschedulable")
   457  	}
   458  
   459  	// Allocations are copy on write, so we want to keep the id but don't need the
   460  	// pointer. We'll get it from the db in denormalize.
   461  	v.ReadAllocs[claim.AllocationID] = nil
   462  	delete(v.WriteAllocs, claim.AllocationID)
   463  
   464  	v.ReadClaims[claim.AllocationID] = claim
   465  	delete(v.WriteClaims, claim.AllocationID)
   466  	delete(v.PastClaims, claim.AllocationID)
   467  
   468  	return nil
   469  }
   470  
   471  // ClaimWrite marks an allocation as using a volume as a writer
   472  func (v *CSIVolume) ClaimWrite(claim *CSIVolumeClaim, alloc *Allocation) error {
   473  	if _, ok := v.WriteAllocs[claim.AllocationID]; ok {
   474  		return nil
   475  	}
   476  	if alloc == nil {
   477  		return fmt.Errorf("allocation missing: %s", claim.AllocationID)
   478  	}
   479  
   480  	if !v.WriteSchedulable() {
   481  		return fmt.Errorf("unschedulable")
   482  	}
   483  
   484  	if !v.WriteFreeClaims() {
   485  		// Check the blocking allocations to see if they belong to this job
   486  		for _, a := range v.WriteAllocs {
   487  			if a.Namespace != alloc.Namespace || a.JobID != alloc.JobID {
   488  				return fmt.Errorf("volume max claim reached")
   489  			}
   490  		}
   491  	}
   492  
   493  	// Allocations are copy on write, so we want to keep the id but don't need the
   494  	// pointer. We'll get it from the db in denormalize.
   495  	v.WriteAllocs[alloc.ID] = nil
   496  	delete(v.ReadAllocs, alloc.ID)
   497  
   498  	v.WriteClaims[alloc.ID] = claim
   499  	delete(v.ReadClaims, alloc.ID)
   500  	delete(v.PastClaims, alloc.ID)
   501  
   502  	return nil
   503  }
   504  
   505  // ClaimRelease is called when the allocation has terminated and
   506  // already stopped using the volume
   507  func (v *CSIVolume) ClaimRelease(claim *CSIVolumeClaim) error {
   508  	if claim.State == CSIVolumeClaimStateReadyToFree {
   509  		delete(v.ReadAllocs, claim.AllocationID)
   510  		delete(v.WriteAllocs, claim.AllocationID)
   511  		delete(v.ReadClaims, claim.AllocationID)
   512  		delete(v.WriteClaims, claim.AllocationID)
   513  		delete(v.PastClaims, claim.AllocationID)
   514  	} else {
   515  		v.PastClaims[claim.AllocationID] = claim
   516  	}
   517  	return nil
   518  }
   519  
   520  // Equality by value
   521  func (v *CSIVolume) Equal(o *CSIVolume) bool {
   522  	if v == nil || o == nil {
   523  		return v == o
   524  	}
   525  
   526  	// Omit the plugin health fields, their values are controlled by plugin jobs
   527  	if v.ID == o.ID &&
   528  		v.Namespace == o.Namespace &&
   529  		v.AccessMode == o.AccessMode &&
   530  		v.AttachmentMode == o.AttachmentMode &&
   531  		v.PluginID == o.PluginID {
   532  		// Setwise equality of topologies
   533  		var ok bool
   534  		for _, t := range v.Topologies {
   535  			ok = false
   536  			for _, u := range o.Topologies {
   537  				if t.Equal(u) {
   538  					ok = true
   539  					break
   540  				}
   541  			}
   542  			if !ok {
   543  				return false
   544  			}
   545  		}
   546  		return true
   547  	}
   548  	return false
   549  }
   550  
   551  // Validate validates the volume struct, returning all validation errors at once
   552  func (v *CSIVolume) Validate() error {
   553  	errs := []string{}
   554  
   555  	if v.ID == "" {
   556  		errs = append(errs, "missing volume id")
   557  	}
   558  	if v.PluginID == "" {
   559  		errs = append(errs, "missing plugin id")
   560  	}
   561  	if v.Namespace == "" {
   562  		errs = append(errs, "missing namespace")
   563  	}
   564  	if v.AccessMode == "" {
   565  		errs = append(errs, "missing access mode")
   566  	}
   567  	if v.AttachmentMode == "" {
   568  		errs = append(errs, "missing attachment mode")
   569  	}
   570  
   571  	// TODO: Volume Topologies are optional - We should check to see if the plugin
   572  	//       the volume is being registered with requires them.
   573  	// var ok bool
   574  	// for _, t := range v.Topologies {
   575  	// 	if t != nil && len(t.Segments) > 0 {
   576  	// 		ok = true
   577  	// 		break
   578  	// 	}
   579  	// }
   580  	// if !ok {
   581  	// 	errs = append(errs, "missing topology")
   582  	// }
   583  
   584  	if len(errs) > 0 {
   585  		return fmt.Errorf("validation: %s", strings.Join(errs, ", "))
   586  	}
   587  	return nil
   588  }
   589  
   590  // Request and response wrappers
   591  type CSIVolumeRegisterRequest struct {
   592  	Volumes []*CSIVolume
   593  	WriteRequest
   594  }
   595  
   596  type CSIVolumeRegisterResponse struct {
   597  	QueryMeta
   598  }
   599  
   600  type CSIVolumeDeregisterRequest struct {
   601  	VolumeIDs []string
   602  	WriteRequest
   603  }
   604  
   605  type CSIVolumeDeregisterResponse struct {
   606  	QueryMeta
   607  }
   608  
   609  type CSIVolumeClaimMode int
   610  
   611  const (
   612  	CSIVolumeClaimRead CSIVolumeClaimMode = iota
   613  	CSIVolumeClaimWrite
   614  	CSIVolumeClaimRelease
   615  )
   616  
   617  type CSIVolumeClaimBatchRequest struct {
   618  	Claims []CSIVolumeClaimRequest
   619  }
   620  
   621  type CSIVolumeClaimRequest struct {
   622  	VolumeID     string
   623  	AllocationID string
   624  	NodeID       string
   625  	Claim        CSIVolumeClaimMode
   626  	State        CSIVolumeClaimState
   627  	WriteRequest
   628  }
   629  
   630  func (req *CSIVolumeClaimRequest) ToClaim() *CSIVolumeClaim {
   631  	return &CSIVolumeClaim{
   632  		AllocationID: req.AllocationID,
   633  		NodeID:       req.NodeID,
   634  		Mode:         req.Claim,
   635  		State:        req.State,
   636  	}
   637  }
   638  
   639  type CSIVolumeClaimResponse struct {
   640  	// Opaque static publish properties of the volume. SP MAY use this
   641  	// field to ensure subsequent `NodeStageVolume` or `NodePublishVolume`
   642  	// calls calls have contextual information.
   643  	// The contents of this field SHALL be opaque to nomad.
   644  	// The contents of this field SHALL NOT be mutable.
   645  	// The contents of this field SHALL be safe for the nomad to cache.
   646  	// The contents of this field SHOULD NOT contain sensitive
   647  	// information.
   648  	// The contents of this field SHOULD NOT be used for uniquely
   649  	// identifying a volume. The `volume_id` alone SHOULD be sufficient to
   650  	// identify the volume.
   651  	// This field is OPTIONAL and when present MUST be passed to
   652  	// `NodeStageVolume` or `NodePublishVolume` calls on the client
   653  	PublishContext map[string]string
   654  
   655  	// Volume contains the expanded CSIVolume for use on the client after a Claim
   656  	// has completed.
   657  	Volume *CSIVolume
   658  
   659  	QueryMeta
   660  }
   661  
   662  type CSIVolumeListRequest struct {
   663  	PluginID string
   664  	NodeID   string
   665  	QueryOptions
   666  }
   667  
   668  type CSIVolumeListResponse struct {
   669  	Volumes []*CSIVolListStub
   670  	QueryMeta
   671  }
   672  
   673  type CSIVolumeGetRequest struct {
   674  	ID string
   675  	QueryOptions
   676  }
   677  
   678  type CSIVolumeGetResponse struct {
   679  	Volume *CSIVolume
   680  	QueryMeta
   681  }
   682  
   683  // CSIPlugin collects fingerprint info context for the plugin for clients
   684  type CSIPlugin struct {
   685  	ID                 string
   686  	Provider           string // the vendor name from CSI GetPluginInfoResponse
   687  	Version            string // the vendor verson from  CSI GetPluginInfoResponse
   688  	ControllerRequired bool
   689  
   690  	// Map Node.IDs to fingerprint results, split by type. Monolith type plugins have
   691  	// both sets of fingerprinting results.
   692  	Controllers map[string]*CSIInfo
   693  	Nodes       map[string]*CSIInfo
   694  
   695  	// Allocations are populated by denormalize to show running allocations
   696  	Allocations []*AllocListStub
   697  
   698  	// Cache the count of healthy plugins
   699  	ControllersHealthy int
   700  	NodesHealthy       int
   701  
   702  	CreateIndex uint64
   703  	ModifyIndex uint64
   704  }
   705  
   706  // NewCSIPlugin creates the plugin struct. No side-effects
   707  func NewCSIPlugin(id string, index uint64) *CSIPlugin {
   708  	out := &CSIPlugin{
   709  		ID:          id,
   710  		CreateIndex: index,
   711  		ModifyIndex: index,
   712  	}
   713  
   714  	out.newStructs()
   715  	return out
   716  }
   717  
   718  func (p *CSIPlugin) newStructs() {
   719  	p.Controllers = map[string]*CSIInfo{}
   720  	p.Nodes = map[string]*CSIInfo{}
   721  }
   722  
   723  func (p *CSIPlugin) Copy() *CSIPlugin {
   724  	copy := *p
   725  	out := &copy
   726  	out.newStructs()
   727  
   728  	for k, v := range p.Controllers {
   729  		out.Controllers[k] = v
   730  	}
   731  
   732  	for k, v := range p.Nodes {
   733  		out.Nodes[k] = v
   734  	}
   735  
   736  	return out
   737  }
   738  
   739  // AddPlugin adds a single plugin running on the node. Called from state.NodeUpdate in a
   740  // transaction
   741  func (p *CSIPlugin) AddPlugin(nodeID string, info *CSIInfo) error {
   742  	if info.ControllerInfo != nil {
   743  		p.ControllerRequired = info.RequiresControllerPlugin &&
   744  			(info.ControllerInfo.SupportsAttachDetach ||
   745  				info.ControllerInfo.SupportsReadOnlyAttach)
   746  
   747  		prev, ok := p.Controllers[nodeID]
   748  		if ok {
   749  			if prev == nil {
   750  				return fmt.Errorf("plugin missing controller: %s", nodeID)
   751  			}
   752  			if prev.Healthy {
   753  				p.ControllersHealthy -= 1
   754  			}
   755  		}
   756  
   757  		// note: for this to work as expected, only a single
   758  		// controller for a given plugin can be on a given Nomad
   759  		// client, they also conflict on the client so this should be
   760  		// ok
   761  		if prev != nil || info.Healthy {
   762  			p.Controllers[nodeID] = info
   763  		}
   764  		if info.Healthy {
   765  			p.ControllersHealthy += 1
   766  		}
   767  	}
   768  
   769  	if info.NodeInfo != nil {
   770  		prev, ok := p.Nodes[nodeID]
   771  		if ok {
   772  			if prev == nil {
   773  				return fmt.Errorf("plugin missing node: %s", nodeID)
   774  			}
   775  			if prev.Healthy {
   776  				p.NodesHealthy -= 1
   777  			}
   778  		}
   779  		if prev != nil || info.Healthy {
   780  			p.Nodes[nodeID] = info
   781  		}
   782  		if info.Healthy {
   783  			p.NodesHealthy += 1
   784  		}
   785  	}
   786  
   787  	return nil
   788  }
   789  
   790  // DeleteNode removes all plugins from the node. Called from state.DeleteNode in a
   791  // transaction
   792  func (p *CSIPlugin) DeleteNode(nodeID string) error {
   793  	return p.DeleteNodeForType(nodeID, CSIPluginTypeMonolith)
   794  }
   795  
   796  // DeleteNodeForType deletes a client node from the list of controllers or node instance of
   797  // a plugin. Called from deleteJobFromPlugin during job deregistration, in a transaction
   798  func (p *CSIPlugin) DeleteNodeForType(nodeID string, pluginType CSIPluginType) error {
   799  	switch pluginType {
   800  	case CSIPluginTypeController:
   801  		prev, ok := p.Controllers[nodeID]
   802  		if ok {
   803  			if prev == nil {
   804  				return fmt.Errorf("plugin missing controller: %s", nodeID)
   805  			}
   806  			if prev.Healthy {
   807  				p.ControllersHealthy -= 1
   808  			}
   809  		}
   810  		delete(p.Controllers, nodeID)
   811  
   812  	case CSIPluginTypeNode:
   813  		prev, ok := p.Nodes[nodeID]
   814  		if ok {
   815  			if prev == nil {
   816  				return fmt.Errorf("plugin missing node: %s", nodeID)
   817  			}
   818  			if prev.Healthy {
   819  				p.NodesHealthy -= 1
   820  			}
   821  		}
   822  		delete(p.Nodes, nodeID)
   823  
   824  	case CSIPluginTypeMonolith:
   825  		p.DeleteNodeForType(nodeID, CSIPluginTypeController)
   826  		p.DeleteNodeForType(nodeID, CSIPluginTypeNode)
   827  	}
   828  
   829  	return nil
   830  }
   831  
   832  // DeleteAlloc removes the fingerprint info for the allocation
   833  func (p *CSIPlugin) DeleteAlloc(allocID, nodeID string) error {
   834  	prev, ok := p.Controllers[nodeID]
   835  	if ok {
   836  		if prev == nil {
   837  			return fmt.Errorf("plugin missing controller: %s", nodeID)
   838  		}
   839  		if prev.AllocID == allocID {
   840  			if prev.Healthy {
   841  				p.ControllersHealthy -= 1
   842  			}
   843  			delete(p.Controllers, nodeID)
   844  		}
   845  	}
   846  
   847  	prev, ok = p.Nodes[nodeID]
   848  	if ok {
   849  		if prev == nil {
   850  			return fmt.Errorf("plugin missing node: %s", nodeID)
   851  		}
   852  		if prev.AllocID == allocID {
   853  			if prev.Healthy {
   854  				p.NodesHealthy -= 1
   855  			}
   856  			delete(p.Nodes, nodeID)
   857  		}
   858  	}
   859  
   860  	return nil
   861  }
   862  
   863  type CSIPluginListStub struct {
   864  	ID                  string
   865  	Provider            string
   866  	ControllerRequired  bool
   867  	ControllersHealthy  int
   868  	ControllersExpected int
   869  	NodesHealthy        int
   870  	NodesExpected       int
   871  	CreateIndex         uint64
   872  	ModifyIndex         uint64
   873  }
   874  
   875  func (p *CSIPlugin) Stub() *CSIPluginListStub {
   876  	return &CSIPluginListStub{
   877  		ID:                  p.ID,
   878  		Provider:            p.Provider,
   879  		ControllerRequired:  p.ControllerRequired,
   880  		ControllersHealthy:  p.ControllersHealthy,
   881  		ControllersExpected: len(p.Controllers),
   882  		NodesHealthy:        p.NodesHealthy,
   883  		NodesExpected:       len(p.Nodes),
   884  		CreateIndex:         p.CreateIndex,
   885  		ModifyIndex:         p.ModifyIndex,
   886  	}
   887  }
   888  
   889  func (p *CSIPlugin) IsEmpty() bool {
   890  	return len(p.Controllers) == 0 && len(p.Nodes) == 0
   891  }
   892  
   893  type CSIPluginListRequest struct {
   894  	QueryOptions
   895  }
   896  
   897  type CSIPluginListResponse struct {
   898  	Plugins []*CSIPluginListStub
   899  	QueryMeta
   900  }
   901  
   902  type CSIPluginGetRequest struct {
   903  	ID string
   904  	QueryOptions
   905  }
   906  
   907  type CSIPluginGetResponse struct {
   908  	Plugin *CSIPlugin
   909  	QueryMeta
   910  }
   911  
   912  type CSIPluginDeleteRequest struct {
   913  	ID string
   914  	QueryOptions
   915  }
   916  
   917  type CSIPluginDeleteResponse struct {
   918  	QueryMeta
   919  }