mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
The `-type` option for `volume status` is a UX papercut because for many clusters there will be only one sort of volume in use. Update the CLI so that the default behavior is to query CSI and/or DHV. This behavior is subtly different when the user provides an ID or not. If the user doesn't provide an ID, we query both CSI and DHV and show both tables. If the user provides an ID, we query DHV first and then CSI, and show only the appropriate volume. Because DHV IDs are UUIDs, we're sure we won't have collisions between the two. We only show errors if both queries return an error. Fixes: https://hashicorp.atlassian.net/browse/NET-12214
173 lines
4.5 KiB
Go
173 lines
4.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/cli"
|
|
"github.com/hashicorp/nomad/api"
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/posener/complete"
|
|
"github.com/shoenig/test/must"
|
|
)
|
|
|
|
func TestHostVolumeStatusCommand_Args(t *testing.T) {
|
|
ci.Parallel(t)
|
|
ui := cli.NewMockUi()
|
|
cmd := &VolumeStatusCommand{Meta: Meta{Ui: ui}}
|
|
|
|
code := cmd.Run([]string{
|
|
"-type", "host",
|
|
"-node", "6063016a-9d4c-11ef-85fc-9be98efe7e76",
|
|
"-node-pool", "prod",
|
|
"6e3e80f2-9d4c-11ef-97b1-d38cf64416a4",
|
|
})
|
|
must.One(t, code)
|
|
|
|
out := ui.ErrorWriter.String()
|
|
must.StrContains(t, out, "-node or -node-pool options can only be used when no ID is provided")
|
|
}
|
|
|
|
func TestHostVolumeStatusCommand_List(t *testing.T) {
|
|
ci.Parallel(t)
|
|
srv, client, url := testServer(t, true, nil)
|
|
t.Cleanup(srv.Shutdown)
|
|
|
|
waitForNodes(t, client)
|
|
|
|
_, err := client.Namespaces().Register(&api.Namespace{Name: "prod"}, nil)
|
|
must.NoError(t, err)
|
|
|
|
nodes, _, err := client.Nodes().List(nil)
|
|
must.NoError(t, err)
|
|
must.Len(t, 1, nodes)
|
|
nodeID := nodes[0].ID
|
|
|
|
ui := cli.NewMockUi()
|
|
|
|
vols := []api.NamespacedID{
|
|
{Namespace: "prod", ID: "database"},
|
|
{Namespace: "prod", ID: "certs"},
|
|
{Namespace: "default", ID: "example"},
|
|
}
|
|
|
|
for _, vol := range vols {
|
|
hclTestFile := fmt.Sprintf(`
|
|
namespace = "%s"
|
|
name = "%s"
|
|
type = "host"
|
|
plugin_id = "mkdir"
|
|
node_id = "%s"
|
|
node_pool = "default"
|
|
capability {
|
|
access_mode = "single-node-reader-only"
|
|
attachment_mode = "file-system"
|
|
}
|
|
`, vol.Namespace, vol.ID, nodeID)
|
|
|
|
file, err := os.CreateTemp(t.TempDir(), "volume-test-*.hcl")
|
|
must.NoError(t, err)
|
|
_, err = file.WriteString(hclTestFile)
|
|
must.NoError(t, err)
|
|
|
|
args := []string{"-address", url, "-detach", file.Name()}
|
|
cmd := &VolumeCreateCommand{Meta: Meta{Ui: ui}}
|
|
code := cmd.Run(args)
|
|
must.Eq(t, 0, code, must.Sprintf("got error: %s", ui.ErrorWriter.String()))
|
|
|
|
out := ui.OutputWriter.String()
|
|
must.StrContains(t, out, "Created host volume")
|
|
ui.OutputWriter.Reset()
|
|
}
|
|
|
|
cmd := &VolumeStatusCommand{Meta: Meta{Ui: ui}}
|
|
args := []string{"-address", url, "-type", "host", "-namespace", "prod"}
|
|
code := cmd.Run(args)
|
|
must.Eq(t, 0, code, must.Sprintf("got error: %s", ui.ErrorWriter.String()))
|
|
out := ui.OutputWriter.String()
|
|
must.StrContains(t, out, "certs")
|
|
must.StrContains(t, out, "database")
|
|
must.StrNotContains(t, out, "example")
|
|
}
|
|
|
|
func TestHostVolumeStatusCommand_Get(t *testing.T) {
|
|
ci.Parallel(t)
|
|
srv, client, url := testServer(t, true, nil)
|
|
t.Cleanup(srv.Shutdown)
|
|
|
|
waitForNodes(t, client)
|
|
|
|
_, err := client.Namespaces().Register(&api.Namespace{Name: "prod"}, nil)
|
|
must.NoError(t, err)
|
|
|
|
nodes, _, err := client.Nodes().List(nil)
|
|
must.NoError(t, err)
|
|
must.Len(t, 1, nodes)
|
|
nodeID := nodes[0].ID
|
|
hostPath := t.TempDir()
|
|
|
|
ui := cli.NewMockUi()
|
|
|
|
hclTestFile := fmt.Sprintf(`
|
|
namespace = "prod"
|
|
name = "example"
|
|
type = "host"
|
|
plugin_id = "mkdir"
|
|
node_id = "%s"
|
|
node_pool = "default"
|
|
host_path = "%s"
|
|
capability {
|
|
access_mode = "single-node-reader-only"
|
|
attachment_mode = "file-system"
|
|
}
|
|
`, nodeID, hostPath)
|
|
|
|
file, err := os.CreateTemp(t.TempDir(), "volume-test-*.hcl")
|
|
must.NoError(t, err)
|
|
_, err = file.WriteString(hclTestFile)
|
|
must.NoError(t, err)
|
|
|
|
args := []string{"-address", url, file.Name()}
|
|
regCmd := &VolumeRegisterCommand{Meta: Meta{Ui: ui}}
|
|
code := regCmd.Run(args)
|
|
must.Eq(t, 0, code, must.Sprintf("got error: %s", ui.ErrorWriter.String()))
|
|
|
|
out := ui.OutputWriter.String()
|
|
must.StrContains(t, out, "Registered host volume")
|
|
parts := strings.Split(out, " ")
|
|
id := strings.TrimSpace(parts[len(parts)-1])
|
|
|
|
ui.OutputWriter.Reset()
|
|
|
|
// autocomplete
|
|
cmd := &VolumeStatusCommand{Meta: Meta{Ui: ui, namespace: "*", flagAddress: url}}
|
|
cmd.Meta.namespace = "*"
|
|
prefix := id[:len(id)-5]
|
|
cargs := complete.Args{Last: prefix}
|
|
predictor := cmd.AutocompleteArgs()
|
|
|
|
res := predictor.Predict(cargs)
|
|
must.SliceLen(t, 1, res)
|
|
must.Eq(t, id, res[0])
|
|
|
|
// missing the namespace
|
|
cmd = &VolumeStatusCommand{Meta: Meta{Ui: ui}}
|
|
args = []string{"-address", url, "-type", "host", id}
|
|
code = cmd.Run(args)
|
|
must.Eq(t, 1, code)
|
|
must.StrContains(t, ui.ErrorWriter.String(),
|
|
"Error listing host volumes: no volumes with prefix or ID")
|
|
ui.ErrorWriter.Reset()
|
|
|
|
args = []string{"-address", url, "-type", "host", "-namespace", "prod", id}
|
|
code = cmd.Run(args)
|
|
must.Eq(t, 0, code, must.Sprintf("got error: %s", ui.ErrorWriter.String()))
|
|
out = ui.OutputWriter.String()
|
|
must.StrContains(t, out, "example")
|
|
}
|