-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dynamic host volumes: basic CLI CRUD operations
- Loading branch information
Showing
15 changed files
with
1,295 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
// 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 | ||
|
||
// ID is a UUID-like string generated by the server. | ||
ID string | ||
|
||
// Name is the name that group.volume will use to identify the volume | ||
// source. Not expected to be unique. | ||
Name string | ||
|
||
// 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 | ||
RequestedCapacityMin int64 `mapstructure:"capacity_min" hcl:"capacity_min"` // bytes | ||
RequestedCapacityMax int64 `mapstructure:"capacity_max" hcl:"capacity_max"` // bytes | ||
Capacity int64 // bytes | ||
|
||
// 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 | ||
Capacity int64 // bytes | ||
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 { | ||
Volumes []*HostVolume | ||
} | ||
|
||
type HostVolumeRegisterRequest struct { | ||
Volumes []*HostVolume | ||
} | ||
|
||
type HostVolumeListRequest struct { | ||
NodeID string | ||
NodePool string | ||
} | ||
|
||
type HostVolumeDeleteRequest struct { | ||
VolumeIDs []string | ||
} | ||
|
||
// Create forwards to client agents so host volumes can be created on those | ||
// hosts, and registers the volumes with Nomad servers. | ||
func (hv *HostVolumes) Create(req *HostVolumeCreateRequest, opts *WriteOptions) ([]*HostVolume, *WriteMeta, error) { | ||
var out struct { | ||
Volumes []*HostVolume | ||
} | ||
wm, err := hv.client.put("/v1/volume/host/create", req, &out, opts) | ||
if err != nil { | ||
return nil, wm, err | ||
} | ||
return out.Volumes, wm, nil | ||
} | ||
|
||
// Register registers host volumes that were created out-of-band with the Nomad | ||
// servers. | ||
func (hv *HostVolumes) Register(req *HostVolumeRegisterRequest, opts *WriteOptions) ([]*HostVolume, *WriteMeta, error) { | ||
var out struct { | ||
Volumes []*HostVolume | ||
} | ||
wm, err := hv.client.put("/v1/volume/host/register", req, &out, opts) | ||
if err != nil { | ||
return nil, wm, err | ||
} | ||
return out.Volumes, 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(id string, opts *WriteOptions) (*WriteMeta, error) { | ||
path, err := url.JoinPath("/v1/volume/host/", url.PathEscape(id)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
wm, err := hv.client.delete(path, nil, nil, opts) | ||
return wm, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.