498 lines
16 KiB
Go
498 lines
16 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package structs
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/hashicorp/nomad/plugins/csi"
|
|
)
|
|
|
|
// CSIVolumeMountOptions contains the mount options that should be provided when
|
|
// attaching and mounting a volume with the CSIVolumeAttachmentModeFilesystem
|
|
// attachment mode.
|
|
type CSIVolumeMountOptions struct {
|
|
// Filesystem is the desired filesystem type that should be used by the volume
|
|
// (e.g ext4, aufs, zfs). This field is optional.
|
|
Filesystem string
|
|
|
|
// MountFlags contain the mount options that should be used for the volume.
|
|
// These may contain _sensitive_ data and should not be leaked to logs or
|
|
// returned in debugging data.
|
|
// The total size of this field must be under 4KiB.
|
|
MountFlags []string
|
|
}
|
|
|
|
func (c *CSIVolumeMountOptions) ToCSIMountOptions() *structs.CSIMountOptions {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
return &structs.CSIMountOptions{
|
|
FSType: c.Filesystem,
|
|
MountFlags: c.MountFlags,
|
|
}
|
|
}
|
|
|
|
// CSIControllerRequest interface lets us set embedded CSIControllerQuery
|
|
// fields in the server
|
|
type CSIControllerRequest interface {
|
|
SetControllerNodeID(string)
|
|
}
|
|
|
|
// CSIControllerQuery is used to specify various flags for queries against CSI
|
|
// Controllers
|
|
type CSIControllerQuery struct {
|
|
// ControllerNodeID is the node that should be targeted by the request
|
|
ControllerNodeID string
|
|
|
|
// PluginID is the plugin that should be targeted on the given node.
|
|
PluginID string
|
|
}
|
|
|
|
func (c *CSIControllerQuery) SetControllerNodeID(nodeID string) {
|
|
c.ControllerNodeID = nodeID
|
|
}
|
|
|
|
type ClientCSIControllerValidateVolumeRequest struct {
|
|
VolumeID string // note: this is the external ID
|
|
|
|
VolumeCapabilities []*structs.CSIVolumeCapability
|
|
MountOptions *structs.CSIMountOptions
|
|
Secrets structs.CSISecrets
|
|
|
|
// COMPAT(1.1.1): the AttachmentMode and AccessMode fields are deprecated
|
|
// and replaced by the VolumeCapabilities field above
|
|
AttachmentMode structs.CSIVolumeAttachmentMode
|
|
AccessMode structs.CSIVolumeAccessMode
|
|
|
|
// Parameters as returned by storage provider in CreateVolumeResponse.
|
|
// This field is optional.
|
|
Parameters map[string]string
|
|
|
|
// Volume context as returned by storage provider in CreateVolumeResponse.
|
|
// This field is optional.
|
|
Context map[string]string
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (c *ClientCSIControllerValidateVolumeRequest) ToCSIRequest() (*csi.ControllerValidateVolumeRequest, error) {
|
|
if c == nil {
|
|
return &csi.ControllerValidateVolumeRequest{}, nil
|
|
}
|
|
|
|
creq := &csi.ControllerValidateVolumeRequest{
|
|
ExternalID: c.VolumeID,
|
|
Secrets: c.Secrets,
|
|
Capabilities: []*csi.VolumeCapability{},
|
|
Parameters: c.Parameters,
|
|
Context: c.Context,
|
|
}
|
|
|
|
for _, cap := range c.VolumeCapabilities {
|
|
ccap, err := csi.VolumeCapabilityFromStructs(
|
|
cap.AttachmentMode, cap.AccessMode, c.MountOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
creq.Capabilities = append(creq.Capabilities, ccap)
|
|
}
|
|
return creq, nil
|
|
}
|
|
|
|
type ClientCSIControllerValidateVolumeResponse struct {
|
|
}
|
|
|
|
type ClientCSIControllerAttachVolumeRequest struct {
|
|
// The external ID of the volume to be used on a node.
|
|
// This field is REQUIRED.
|
|
VolumeID string
|
|
|
|
// The ID of the node. This field is REQUIRED. This must match the NodeID that
|
|
// is fingerprinted by the target node for this plugin name.
|
|
ClientCSINodeID string
|
|
|
|
// AttachmentMode indicates how the volume should be attached and mounted into
|
|
// a task.
|
|
AttachmentMode structs.CSIVolumeAttachmentMode
|
|
|
|
// AccessMode indicates the desired concurrent access model for the volume
|
|
AccessMode structs.CSIVolumeAccessMode
|
|
|
|
// MountOptions is an optional field that contains additional configuration
|
|
// when providing an AttachmentMode of CSIVolumeAttachmentModeFilesystem
|
|
MountOptions *CSIVolumeMountOptions
|
|
|
|
// ReadOnly indicates that the volume will be used in a readonly fashion. This
|
|
// only works when the Controller has the PublishReadonly capability.
|
|
ReadOnly bool
|
|
|
|
// Secrets required by plugin to complete the controller publish
|
|
// volume request. This field is OPTIONAL.
|
|
Secrets structs.CSISecrets
|
|
|
|
// Volume context as returned by storage provider in CreateVolumeResponse.
|
|
// This field is optional.
|
|
VolumeContext map[string]string
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (c *ClientCSIControllerAttachVolumeRequest) ToCSIRequest() (*csi.ControllerPublishVolumeRequest, error) {
|
|
if c == nil {
|
|
return &csi.ControllerPublishVolumeRequest{}, nil
|
|
}
|
|
|
|
var opts = c.MountOptions.ToCSIMountOptions()
|
|
caps, err := csi.VolumeCapabilityFromStructs(c.AttachmentMode, c.AccessMode, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &csi.ControllerPublishVolumeRequest{
|
|
ExternalID: c.VolumeID,
|
|
NodeID: c.ClientCSINodeID,
|
|
VolumeCapability: caps,
|
|
ReadOnly: c.ReadOnly,
|
|
Secrets: c.Secrets,
|
|
VolumeContext: c.VolumeContext,
|
|
}, nil
|
|
}
|
|
|
|
// ClientCSIControllerDetachVolumeRequest is the RPC made from the server to
|
|
// a Nomad client to tell a CSI controller plugin on that client to perform
|
|
// ControllerUnpublish for a volume on a specific client.
|
|
type ClientCSIControllerAttachVolumeResponse struct {
|
|
// Opaque static publish properties of the volume. SP MAY use this
|
|
// field to ensure subsequent `NodeStageVolume` or `NodePublishVolume`
|
|
// calls calls have contextual information.
|
|
// The contents of this field SHALL be opaque to nomad.
|
|
// The contents of this field SHALL NOT be mutable.
|
|
// The contents of this field SHALL be safe for the nomad to cache.
|
|
// The contents of this field SHOULD NOT contain sensitive
|
|
// information.
|
|
// The contents of this field SHOULD NOT be used for uniquely
|
|
// identifying a volume. The `volume_id` alone SHOULD be sufficient to
|
|
// identify the volume.
|
|
// This field is OPTIONAL and when present MUST be passed to
|
|
// subsequent `NodeStageVolume` or `NodePublishVolume` calls
|
|
PublishContext map[string]string
|
|
}
|
|
|
|
type ClientCSIControllerDetachVolumeRequest struct {
|
|
// The external ID of the volume to be unpublished for the node
|
|
// This field is REQUIRED.
|
|
VolumeID string
|
|
|
|
// The CSI Node ID for the Node that the volume should be detached from.
|
|
// This field is REQUIRED. This must match the NodeID that is fingerprinted
|
|
// by the target node for this plugin name.
|
|
ClientCSINodeID string
|
|
|
|
// Secrets required by plugin to complete the controller unpublish
|
|
// volume request. This field is OPTIONAL.
|
|
Secrets structs.CSISecrets
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (c *ClientCSIControllerDetachVolumeRequest) ToCSIRequest() *csi.ControllerUnpublishVolumeRequest {
|
|
if c == nil {
|
|
return &csi.ControllerUnpublishVolumeRequest{}
|
|
}
|
|
|
|
return &csi.ControllerUnpublishVolumeRequest{
|
|
ExternalID: c.VolumeID,
|
|
NodeID: c.ClientCSINodeID,
|
|
}
|
|
}
|
|
|
|
type ClientCSIControllerDetachVolumeResponse struct{}
|
|
|
|
// ClientCSIControllerCreateVolumeRequest the RPC made from the server to a
|
|
// Nomad client to tell a CSI controller plugin on that client to perform
|
|
// CreateVolume
|
|
type ClientCSIControllerCreateVolumeRequest struct {
|
|
Name string
|
|
VolumeCapabilities []*structs.CSIVolumeCapability
|
|
MountOptions *structs.CSIMountOptions
|
|
Parameters map[string]string
|
|
Secrets structs.CSISecrets
|
|
CapacityMin int64
|
|
CapacityMax int64
|
|
SnapshotID string
|
|
CloneID string
|
|
RequestedTopologies *structs.CSITopologyRequest
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (req *ClientCSIControllerCreateVolumeRequest) ToCSIRequest() (*csi.ControllerCreateVolumeRequest, error) {
|
|
|
|
creq := &csi.ControllerCreateVolumeRequest{
|
|
Name: req.Name,
|
|
VolumeCapabilities: []*csi.VolumeCapability{},
|
|
Parameters: req.Parameters,
|
|
Secrets: req.Secrets,
|
|
ContentSource: &csi.VolumeContentSource{
|
|
CloneID: req.CloneID,
|
|
SnapshotID: req.SnapshotID,
|
|
},
|
|
AccessibilityRequirements: &csi.TopologyRequirement{
|
|
Requisite: []*csi.Topology{},
|
|
Preferred: []*csi.Topology{},
|
|
},
|
|
}
|
|
|
|
// The CSI spec requires that at least one of the fields in CapacityRange
|
|
// must be defined. Fields set to 0 are considered unspecified and the
|
|
// CreateVolumeRequest should not send an invalid value.
|
|
if req.CapacityMin != 0 || req.CapacityMax != 0 {
|
|
creq.CapacityRange = &csi.CapacityRange{
|
|
RequiredBytes: req.CapacityMin,
|
|
LimitBytes: req.CapacityMax,
|
|
}
|
|
}
|
|
|
|
for _, cap := range req.VolumeCapabilities {
|
|
ccap, err := csi.VolumeCapabilityFromStructs(cap.AttachmentMode, cap.AccessMode, req.MountOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
creq.VolumeCapabilities = append(creq.VolumeCapabilities, ccap)
|
|
}
|
|
|
|
if req.RequestedTopologies != nil {
|
|
for _, topo := range req.RequestedTopologies.Required {
|
|
creq.AccessibilityRequirements.Requisite = append(
|
|
creq.AccessibilityRequirements.Requisite, &csi.Topology{
|
|
Segments: topo.Segments,
|
|
})
|
|
}
|
|
for _, topo := range req.RequestedTopologies.Preferred {
|
|
creq.AccessibilityRequirements.Preferred = append(
|
|
creq.AccessibilityRequirements.Preferred, &csi.Topology{
|
|
Segments: topo.Segments,
|
|
})
|
|
}
|
|
}
|
|
return creq, nil
|
|
}
|
|
|
|
type ClientCSIControllerCreateVolumeResponse struct {
|
|
ExternalVolumeID string
|
|
CapacityBytes int64
|
|
VolumeContext map[string]string
|
|
Topologies []*structs.CSITopology
|
|
}
|
|
|
|
// ClientCSIControllerExpandVolumeRequest is the RPC made from the server to a
|
|
// Nomad client to tell a CSI controller plugin on that client to perform
|
|
// ControllerExpandVolume
|
|
type ClientCSIControllerExpandVolumeRequest struct {
|
|
ExternalVolumeID string
|
|
CapacityRange *csi.CapacityRange
|
|
Secrets structs.CSISecrets
|
|
VolumeCapability *csi.VolumeCapability
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (req *ClientCSIControllerExpandVolumeRequest) ToCSIRequest() *csi.ControllerExpandVolumeRequest {
|
|
csiReq := &csi.ControllerExpandVolumeRequest{
|
|
ExternalVolumeID: req.ExternalVolumeID,
|
|
Capability: req.VolumeCapability,
|
|
Secrets: req.Secrets,
|
|
}
|
|
if req.CapacityRange != nil {
|
|
csiReq.RequiredBytes = req.CapacityRange.RequiredBytes
|
|
csiReq.LimitBytes = req.CapacityRange.LimitBytes
|
|
}
|
|
return csiReq
|
|
}
|
|
|
|
type ClientCSIControllerExpandVolumeResponse struct {
|
|
CapacityBytes int64
|
|
NodeExpansionRequired bool
|
|
}
|
|
|
|
// ClientCSIControllerDeleteVolumeRequest the RPC made from the server to a
|
|
// Nomad client to tell a CSI controller plugin on that client to perform
|
|
// DeleteVolume
|
|
type ClientCSIControllerDeleteVolumeRequest struct {
|
|
ExternalVolumeID string
|
|
Secrets structs.CSISecrets
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (req *ClientCSIControllerDeleteVolumeRequest) ToCSIRequest() *csi.ControllerDeleteVolumeRequest {
|
|
return &csi.ControllerDeleteVolumeRequest{
|
|
ExternalVolumeID: req.ExternalVolumeID,
|
|
Secrets: req.Secrets,
|
|
}
|
|
}
|
|
|
|
type ClientCSIControllerDeleteVolumeResponse struct{}
|
|
|
|
// ClientCSIControllerListVolumesVolumeRequest the RPC made from the server to
|
|
// a Nomad client to tell a CSI controller plugin on that client to perform
|
|
// ListVolumes
|
|
type ClientCSIControllerListVolumesRequest struct {
|
|
// these pagination fields match the pagination fields of the plugins and
|
|
// not Nomad's own fields, for clarity when mapping between the two RPCs
|
|
MaxEntries int32
|
|
StartingToken string
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (req *ClientCSIControllerListVolumesRequest) ToCSIRequest() *csi.ControllerListVolumesRequest {
|
|
return &csi.ControllerListVolumesRequest{
|
|
MaxEntries: req.MaxEntries,
|
|
StartingToken: req.StartingToken,
|
|
}
|
|
}
|
|
|
|
type ClientCSIControllerListVolumesResponse struct {
|
|
Entries []*structs.CSIVolumeExternalStub
|
|
NextToken string
|
|
}
|
|
|
|
// ClientCSIControllerCreateSnapshotRequest the RPC made from the server to a
|
|
// Nomad client to tell a CSI controller plugin on that client to perform
|
|
// CreateSnapshot
|
|
type ClientCSIControllerCreateSnapshotRequest struct {
|
|
ExternalSourceVolumeID string
|
|
Name string
|
|
Secrets structs.CSISecrets
|
|
Parameters map[string]string
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (req *ClientCSIControllerCreateSnapshotRequest) ToCSIRequest() (*csi.ControllerCreateSnapshotRequest, error) {
|
|
return &csi.ControllerCreateSnapshotRequest{
|
|
VolumeID: req.ExternalSourceVolumeID,
|
|
Name: req.Name,
|
|
Secrets: req.Secrets,
|
|
Parameters: req.Parameters,
|
|
}, nil
|
|
}
|
|
|
|
type ClientCSIControllerCreateSnapshotResponse struct {
|
|
ID string
|
|
ExternalSourceVolumeID string
|
|
SizeBytes int64
|
|
CreateTime int64
|
|
IsReady bool
|
|
}
|
|
|
|
// ClientCSIControllerDeleteSnapshotRequest the RPC made from the server to a
|
|
// Nomad client to tell a CSI controller plugin on that client to perform
|
|
// DeleteSnapshot
|
|
type ClientCSIControllerDeleteSnapshotRequest struct {
|
|
ID string
|
|
Secrets structs.CSISecrets
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (req *ClientCSIControllerDeleteSnapshotRequest) ToCSIRequest() *csi.ControllerDeleteSnapshotRequest {
|
|
return &csi.ControllerDeleteSnapshotRequest{
|
|
SnapshotID: req.ID,
|
|
Secrets: req.Secrets,
|
|
}
|
|
}
|
|
|
|
type ClientCSIControllerDeleteSnapshotResponse struct{}
|
|
|
|
// ClientCSIControllerListSnapshotsRequest is the RPC made from the server to
|
|
// a Nomad client to tell a CSI controller plugin on that client to perform
|
|
// ListSnapshots
|
|
type ClientCSIControllerListSnapshotsRequest struct {
|
|
// these pagination fields match the pagination fields of the plugins and
|
|
// not Nomad's own fields, for clarity when mapping between the two RPCs
|
|
MaxEntries int32
|
|
StartingToken string
|
|
Secrets structs.CSISecrets
|
|
|
|
CSIControllerQuery
|
|
}
|
|
|
|
func (req *ClientCSIControllerListSnapshotsRequest) ToCSIRequest() *csi.ControllerListSnapshotsRequest {
|
|
return &csi.ControllerListSnapshotsRequest{
|
|
MaxEntries: req.MaxEntries,
|
|
StartingToken: req.StartingToken,
|
|
Secrets: req.Secrets,
|
|
}
|
|
}
|
|
|
|
type ClientCSIControllerListSnapshotsResponse struct {
|
|
Entries []*structs.CSISnapshot
|
|
NextToken string
|
|
}
|
|
|
|
// ClientCSINodeDetachVolumeRequest is the RPC made from the server to
|
|
// a Nomad client to tell a CSI node plugin on that client to perform
|
|
// NodeUnpublish and NodeUnstage.
|
|
type ClientCSINodeDetachVolumeRequest struct {
|
|
PluginID string // ID of the plugin that manages the volume (required)
|
|
VolumeID string // ID of the volume to be unpublished (required)
|
|
AllocID string // ID of the allocation we're unpublishing for (required)
|
|
NodeID string // ID of the Nomad client targeted
|
|
ExternalID string // External ID of the volume to be unpublished (required)
|
|
|
|
// These fields should match the original volume request so that
|
|
// we can find the mount points on the client
|
|
AttachmentMode structs.CSIVolumeAttachmentMode
|
|
AccessMode structs.CSIVolumeAccessMode
|
|
ReadOnly bool
|
|
}
|
|
|
|
type ClientCSINodeDetachVolumeResponse struct{}
|
|
|
|
// ClientCSINodeExpandVolumeRequest is the RPC made from the server to
|
|
// a Nomad client to tell a CSI node plugin on that client to perform
|
|
// NodeExpandVolume.
|
|
type ClientCSINodeExpandVolumeRequest struct {
|
|
PluginID string // ID of the plugin that manages the volume (required)
|
|
VolumeID string // ID of the volume to be expanded (required)
|
|
ExternalID string // External ID of the volume to be expanded (required)
|
|
|
|
// Capacity range (required) to be sent to the node plugin
|
|
Capacity *csi.CapacityRange
|
|
|
|
// Claim currently held for the allocation (required)
|
|
// used to determine capabilities and the mount point on the client
|
|
Claim *structs.CSIVolumeClaim
|
|
}
|
|
|
|
func (req *ClientCSINodeExpandVolumeRequest) Validate() error {
|
|
var err error
|
|
// These should not occur during normal operations; they're here
|
|
// mainly to catch potential programmer error.
|
|
if req.PluginID == "" {
|
|
err = errors.Join(err, errors.New("PluginID is required"))
|
|
}
|
|
if req.VolumeID == "" {
|
|
err = errors.Join(err, errors.New("VolumeID is required"))
|
|
}
|
|
if req.ExternalID == "" {
|
|
err = errors.Join(err, errors.New("ExternalID is required"))
|
|
}
|
|
if req.Claim == nil {
|
|
err = errors.Join(err, errors.New("Claim is required"))
|
|
} else if req.Claim.AllocationID == "" {
|
|
err = errors.Join(err, errors.New("Claim.AllocationID is required"))
|
|
}
|
|
return err
|
|
}
|
|
|
|
type ClientCSINodeExpandVolumeResponse struct {
|
|
CapacityBytes int64
|
|
}
|