// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package api import "net/url" // HostVolume represents a Dynamic Host Volume: a volume associated with a // specific Nomad client agent but created via API. type HostVolume struct { // Namespace is the Nomad namespace for the host volume, which constrains // which jobs can mount it. Namespace string `mapstructure:"namespace" hcl:"namespace"` // ID is a UUID-like string generated by the server. ID string `mapstructure:"id" hcl:"id"` // Name is the name that group.volume will use to identify the volume // source. Not expected to be unique. Name string `mapstructure:"name" hcl:"name"` // PluginID is the name of the host volume plugin on the client that will be // used for creating the volume. If omitted, the client will use its default // built-in plugin. PluginID string `mapstructure:"plugin_id" hcl:"plugin_id"` // NodePool is the node pool of the node where the volume is placed. If the // user doesn't provide a node ID, a node will be selected using the // NodePool and Constraints. If the user provides both NodePool and NodeID, // NodePool will be used to validate the request. If omitted, the server // will populate this value in before writing the volume to Raft. NodePool string `mapstructure:"node_pool" hcl:"node_pool"` // NodeID is the node where the volume is placed. If the user doesn't // provide a NodeID, one will be selected using the NodePool and // Constraints. If omitted, this field will then be populated by the server // before writing the volume to Raft. NodeID string `mapstructure:"node_id" hcl:"node_id"` // Constraints are optional. If the NodeID is not provided, the NodePool and // Constraints are used to select a node. If the NodeID is provided, // Constraints are used to validate that the node meets those constraints at // the time of volume creation. Constraints []*Constraint `json:",omitempty" hcl:"constraint"` // Because storage may allow only specific intervals of size, we accept a // min and max and return the actual capacity when the volume is created or // updated on the client RequestedCapacityMinBytes int64 `mapstructure:"capacity_min" hcl:"capacity_min"` RequestedCapacityMaxBytes int64 `mapstructure:"capacity_max" hcl:"capacity_max"` CapacityBytes int64 // RequestedCapabilities defines the options available to group.volume // blocks. The scheduler checks against the listed capability blocks and // selects a node for placement if *any* capability block works. RequestedCapabilities []*HostVolumeCapability `hcl:"capability"` // Parameters are an opaque map of parameters for the host volume plugin. Parameters map[string]string `json:",omitempty"` // HostPath is the path on disk where the volume's mount point was // created. We record this to make debugging easier. HostPath string `mapstructure:"host_path" hcl:"host_path"` // State represents the overall state of the volume. One of pending, ready, // deleted. State HostVolumeState CreateIndex uint64 CreateTime int64 ModifyIndex uint64 ModifyTime int64 // Allocations is the list of non-client-terminal allocations with claims on // this host volume. They are denormalized on read and this field will be // never written to Raft Allocations []*AllocationListStub `json:",omitempty" mapstructure:"-" hcl:"-"` } // HostVolume state reports the current status of the host volume type HostVolumeState string const ( HostVolumeStatePending HostVolumeState = "pending" HostVolumeStateReady HostVolumeState = "ready" HostVolumeStateDeleted HostVolumeState = "deleted" ) // HostVolumeCapability is the requested attachment and access mode for a volume type HostVolumeCapability struct { AttachmentMode HostVolumeAttachmentMode `mapstructure:"attachment_mode" hcl:"attachment_mode"` AccessMode HostVolumeAccessMode `mapstructure:"access_mode" hcl:"access_mode"` } // HostVolumeAttachmentMode chooses the type of storage API that will be used to // interact with the device. type HostVolumeAttachmentMode string const ( HostVolumeAttachmentModeUnknown HostVolumeAttachmentMode = "" HostVolumeAttachmentModeBlockDevice HostVolumeAttachmentMode = "block-device" HostVolumeAttachmentModeFilesystem HostVolumeAttachmentMode = "file-system" ) // HostVolumeAccessMode indicates how Nomad should make the volume available to // concurrent allocations. type HostVolumeAccessMode string const ( HostVolumeAccessModeUnknown HostVolumeAccessMode = "" HostVolumeAccessModeSingleNodeReader HostVolumeAccessMode = "single-node-reader-only" HostVolumeAccessModeSingleNodeWriter HostVolumeAccessMode = "single-node-writer" HostVolumeAccessModeMultiNodeReader HostVolumeAccessMode = "multi-node-reader-only" HostVolumeAccessModeMultiNodeSingleWriter HostVolumeAccessMode = "multi-node-single-writer" HostVolumeAccessModeMultiNodeMultiWriter HostVolumeAccessMode = "multi-node-multi-writer" ) // HostVolumeStub is used for responses for the List Volumes endpoint type HostVolumeStub struct { Namespace string ID string Name string PluginID string NodePool string NodeID string CapacityBytes int64 State HostVolumeState CreateIndex uint64 CreateTime int64 ModifyIndex uint64 ModifyTime int64 } // HostVolumes is used to access the host volumes API. type HostVolumes struct { client *Client } // HostVolumes returns a new handle on the host volumes API. func (c *Client) HostVolumes() *HostVolumes { return &HostVolumes{client: c} } type HostVolumeCreateRequest struct { Volume *HostVolume // PolicyOverride overrides Sentinel soft-mandatory policy enforcement PolicyOverride bool } type HostVolumeRegisterRequest struct { Volume *HostVolume // PolicyOverride overrides Sentinel soft-mandatory policy enforcement PolicyOverride bool } type HostVolumeCreateResponse struct { Volume *HostVolume Warnings string } type HostVolumeRegisterResponse struct { Volume *HostVolume Warnings string } type HostVolumeListRequest struct { NodeID string NodePool string } type HostVolumeDeleteRequest struct { ID string } // Create forwards to client agents so a host volume can be created on those // hosts, and registers the volume with Nomad servers. func (hv *HostVolumes) Create(req *HostVolumeCreateRequest, opts *WriteOptions) (*HostVolumeCreateResponse, *WriteMeta, error) { var out *HostVolumeCreateResponse wm, err := hv.client.put("/v1/volume/host/create", req, &out, opts) if err != nil { return nil, wm, err } return out, wm, nil } // Register registers a host volume that was created out-of-band with the Nomad // servers. func (hv *HostVolumes) Register(req *HostVolumeRegisterRequest, opts *WriteOptions) (*HostVolumeRegisterResponse, *WriteMeta, error) { var out *HostVolumeRegisterResponse wm, err := hv.client.put("/v1/volume/host/register", req, &out, opts) if err != nil { return nil, wm, err } return out, wm, nil } // Get queries for a single host volume, by ID func (hv *HostVolumes) Get(id string, opts *QueryOptions) (*HostVolume, *QueryMeta, error) { var out *HostVolume path, err := url.JoinPath("/v1/volume/host/", url.PathEscape(id)) if err != nil { return nil, nil, err } qm, err := hv.client.query(path, &out, opts) if err != nil { return nil, qm, err } return out, qm, nil } // List queries for a set of host volumes, by namespace, node, node pool, or // name prefix. func (hv *HostVolumes) List(req *HostVolumeListRequest, opts *QueryOptions) ([]*HostVolumeStub, *QueryMeta, error) { var out []*HostVolumeStub qv := url.Values{} qv.Set("type", "host") if req != nil { if req.NodeID != "" { qv.Set("node_id", req.NodeID) } if req.NodePool != "" { qv.Set("node_pool", req.NodePool) } } qm, err := hv.client.query("/v1/volumes?"+qv.Encode(), &out, opts) if err != nil { return nil, qm, err } return out, qm, nil } // Delete deletes a host volume func (hv *HostVolumes) Delete(req *HostVolumeDeleteRequest, opts *WriteOptions) (*WriteMeta, error) { path, err := url.JoinPath("/v1/volume/host/", url.PathEscape(req.ID)) if err != nil { return nil, err } wm, err := hv.client.delete(path, nil, nil, opts) return wm, err }