open-nomad/command/volume_init.go

260 lines
6.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package command
import (
"fmt"
"os"
"strings"
"github.com/posener/complete"
)
const (
// DefaultHclVolumeInitName is the default name we use when initializing
// the example volume file in HCL format
DefaultHclVolumeInitName = "volume.hcl"
// DefaultHclVolumeInitName is the default name we use when initializing
// the example volume file in JSON format
DefaultJsonVolumeInitName = "volume.json"
)
// VolumeInitCommand generates a new volume spec that you can customize to
// your liking, like vagrant init
type VolumeInitCommand struct {
Meta
}
func (c *VolumeInitCommand) Help() string {
helpText := `
Usage: nomad volume init <filename>
Creates an example volume specification file that can be used as a starting
point to customize further. If no filename is give, the default "volume.json"
or "volume.hcl" will be used.
Init Options:
-json
Create an example JSON volume specification.
`
return strings.TrimSpace(helpText)
}
func (c *VolumeInitCommand) Synopsis() string {
return "Create an example volume specification file"
}
func (c *VolumeInitCommand) AutocompleteFlags() complete.Flags {
return complete.Flags{
"-json": complete.PredictNothing,
}
}
func (c *VolumeInitCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictNothing
}
func (c *VolumeInitCommand) Name() string { return "volume init" }
func (c *VolumeInitCommand) Run(args []string) int {
var jsonOutput bool
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
flags.Usage = func() { c.Ui.Output(c.Help()) }
flags.BoolVar(&jsonOutput, "json", false, "")
if err := flags.Parse(args); err != nil {
return 1
}
// Check that we get no arguments
args = flags.Args()
if l := len(args); l > 1 {
c.Ui.Error("This command takes no arguments or one: <filename>")
c.Ui.Error(commandErrorText(c))
return 1
}
fileName := DefaultHclVolumeInitName
fileContent := defaultHclVolumeSpec
if jsonOutput {
fileName = DefaultJsonVolumeInitName
fileContent = defaultJsonVolumeSpec
}
if len(args) == 1 {
fileName = args[0]
}
// Check if the file already exists
_, err := os.Stat(fileName)
if err != nil && !os.IsNotExist(err) {
c.Ui.Error(fmt.Sprintf("Failed to stat %q: %v", fileName, err))
return 1
}
if !os.IsNotExist(err) {
c.Ui.Error(fmt.Sprintf("Volume specification %q already exists", fileName))
return 1
}
// Write out the example
err = os.WriteFile(fileName, []byte(fileContent), 0660)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to write %q: %v", fileName, err))
return 1
}
// Success
c.Ui.Output(fmt.Sprintf("Example volume specification written to %s", fileName))
return 0
}
var defaultHclVolumeSpec = strings.TrimSpace(`
id = "ebs_prod_db1"
namespace = "default"
name = "database"
type = "csi"
plugin_id = "plugin_id"
# For 'nomad volume register', provide the external ID from the storage
# provider. This field should be omitted when creating a volume with
# 'nomad volume create'
external_id = "vol-23452345"
# For 'nomad volume create', specify a snapshot ID or volume to clone. You can
# specify only one of these two fields.
snapshot_id = "snap-12345"
# clone_id = "vol-abcdef"
# Optional: for 'nomad volume create', specify a maximum and minimum capacity.
# Registering an existing volume will record but ignore these fields.
capacity_min = "10GiB"
capacity_max = "20G"
# Required (at least one): for 'nomad volume create', specify one or more
# capabilities to validate. Registering an existing volume will record but
# ignore these fields.
capability {
access_mode = "single-node-writer"
attachment_mode = "file-system"
}
capability {
access_mode = "single-node-reader"
attachment_mode = "block-device"
}
# Optional: for 'nomad volume create', specify mount options to validate for
# 'attachment_mode = "file-system". Registering an existing volume will record
# but ignore these fields.
mount_options {
fs_type = "ext4"
mount_flags = ["ro"]
}
# Optional: specify one or more locations where the volume must be accessible
# from. Refer to the plugin documentation for what segment values are supported.
topology_request {
preferred {
topology { segments { rack = "R1" } }
}
required {
topology { segments { rack = "R1" } }
topology { segments { rack = "R2", zone = "us-east-1a" } }
}
}
# Optional: provide any secrets specified by the plugin.
secrets {
example_secret = "xyzzy"
}
# Optional: provide a map of keys to string values expected by the plugin.
parameters {
skuname = "Premium_LRS"
}
# Optional: for 'nomad volume register', provide a map of keys to string
# values expected by the plugin. This field will populated automatically by
# 'nomad volume create'.
context {
endpoint = "http://192.168.1.101:9425"
}
`)
var defaultJsonVolumeSpec = strings.TrimSpace(`
{
"id": "ebs_prod_db1",
"namespace": "default",
"name": "database",
"type": "csi",
"plugin_id": "plugin_id",
"external_id": "vol-23452345",
"snapshot_id": "snap-12345",
"capacity_min": "10GiB",
"capacity_max": "20G",
"capability": [
{
"access_mode": "single-node-writer",
"attachment_mode": "file-system"
},
{
"access_mode": "single-node-reader",
"attachment_mode": "block-device"
}
],
"context": [
{
"endpoint": "http://192.168.1.101:9425"
}
],
"mount_options": [
{
"fs_type": "ext4",
"mount_flags": [
"ro"
]
}
],
"topology_request": {
"preferred": [
{
"topology": {
"segments": {
"rack": "R1"
}
}
}
],
"required": [
{
"topology": {
"segments": {
"rack": "R1"
}
}
},
{
"topology": {
"segments": {
"rack": "R2",
"zone": "us-east-1a"
}
}
}
]
},
"parameters": [
{
"skuname": "Premium_LRS"
}
],
"secrets": [
{
"example_secret": "xyzzy"
}
]
}
`)