Move storage allocation sub-commands under 'storage allocation(s)'

This commit is contained in:
Brian Picciano 2024-12-13 15:45:48 +01:00
parent aac7b30cf0
commit 5669123c99
4 changed files with 212 additions and 209 deletions

View File

@ -1,208 +1,11 @@
package main package main
import (
"cmp"
"errors"
"fmt"
"isle/daemon/daecommon"
"slices"
"golang.org/x/exp/maps"
)
type storageAllocation struct {
Index int `yaml:"index"`
daecommon.ConfigStorageAllocation `yaml:",inline"`
}
func indexStorageAllocations(
config daecommon.NetworkConfig,
) []storageAllocation {
slices.SortFunc(
config.Storage.Allocations,
func(i, j daecommon.ConfigStorageAllocation) int {
return cmp.Compare(i.RPCPort, j.RPCPort)
},
)
allocs := make([]storageAllocation, len(config.Storage.Allocations))
for i := range config.Storage.Allocations {
allocs[i] = storageAllocation{i, config.Storage.Allocations[i]}
}
return allocs
}
var subCmdStorageAllocationAdd = subCmd{
name: "add-allocation",
descr: "Adds a new storage allocation to the host",
do: func(ctx subCmdCtx) error {
var alloc daecommon.ConfigStorageAllocation
ctx.flags.StringVar(
&alloc.DataPath,
"data-path",
"",
"Path to the directory data should be stored in",
)
ctx.flags.StringVar(
&alloc.MetaPath,
"meta-path",
"",
"Path to the directory metadata should be stored in. This is a"+
" smaller dataset which benefits from a faster drive, if"+
" possible.",
)
ctx.flags.IntVar(
&alloc.Capacity,
"capacity",
0,
"How many gigabytes to allocate.",
)
ctx.flags.IntVar(
&alloc.S3APIPort,
"s3-api-port",
0,
"Which port of the VPN network interface to serve the S3 API on."+
" Will be automatically assigned if not given.",
)
ctx.flags.IntVar(
&alloc.RPCPort,
"rpc-port",
0,
"Which port of the VPN network interface to serve RPC requests on."+
" Will be automatically assigned if not given. Once this port"+
" is defined for an allocation it cannot be changed.",
)
ctx.flags.IntVar(
&alloc.AdminPort,
"admin-port",
0,
"Which port of the VPN network interface to serve admin requests"+
" on. Will be automatically assigned if not given.",
)
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
if alloc.DataPath == "" || alloc.MetaPath == "" || alloc.Capacity == 0 {
return errors.New(
"--data-path, --meta-path, and --capacity are required",
)
}
config, err := ctx.daemonRPC.GetConfig(ctx)
if err != nil {
return fmt.Errorf("getting network config: %w", err)
}
config.Storage.Allocations = append(config.Storage.Allocations, alloc)
if err := ctx.daemonRPC.SetConfig(ctx, config); err != nil {
return fmt.Errorf("updating the network config: %w", err)
}
return nil
},
}
var subCmdStorageAllocationList = subCmd{
name: "list-allocation",
plural: "s",
descr: "Lists all storage which is currently allocated on this host",
do: doWithOutput(func(ctx subCmdCtx) (any, error) {
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return nil, fmt.Errorf("parsing flags: %w", err)
}
config, err := ctx.daemonRPC.GetConfig(ctx)
if err != nil {
return nil, fmt.Errorf("getting network config: %w", err)
}
return indexStorageAllocations(config), nil
}),
}
var subCmdStorageAllocationRemove = subCmd{
name: "remove-allocation",
descr: "Removes an allocation which has been previously added. " +
"Allocations are identified by their index field from the output of " +
"`storage list-allocation(s)`.",
do: func(ctx subCmdCtx) error {
indexes := ctx.flags.IntSlice(
"index", nil,
"Index of the storage allocation which should be removed. Can be "+
"specified more than once",
)
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
if len(*indexes) == 0 {
return errors.New("At least one --index must be specified")
}
config, err := ctx.daemonRPC.GetConfig(ctx)
if err != nil {
return fmt.Errorf("getting network config: %w", err)
}
var (
allocs = indexStorageAllocations(config)
allocsByIndex = map[int]daecommon.ConfigStorageAllocation{}
)
for _, alloc := range allocs {
allocsByIndex[alloc.Index] = alloc.ConfigStorageAllocation
}
for _, index := range *indexes {
if _, ok := allocsByIndex[index]; !ok {
return fmt.Errorf(
"Index %d not found in configured storage allocations: %w",
index, err,
)
}
delete(allocsByIndex, index)
}
// we sort the new allocation set so that tests are deterministic
newAllocs := maps.Values(allocsByIndex)
slices.SortFunc(
newAllocs,
func(i, j daecommon.ConfigStorageAllocation) int {
return cmp.Compare(i.RPCPort, j.RPCPort)
},
)
config.Storage.Allocations = newAllocs
if err := ctx.daemonRPC.SetConfig(ctx, config); err != nil {
return fmt.Errorf("updating the network config: %w", err)
}
return nil
},
}
var subCmdStorage = subCmd{ var subCmdStorage = subCmd{
name: "storage", name: "storage",
descr: "Sub-commands related to configuration of storage on this host", descr: "Sub-commands related to configuration of storage on this host",
do: func(ctx subCmdCtx) error { do: func(ctx subCmdCtx) error {
return ctx.doSubCmd( return ctx.doSubCmd(
subCmdStorageAllocationAdd, subCmdStorageAllocation,
subCmdStorageAllocationList,
subCmdStorageAllocationRemove,
) )
}, },
} }

View File

@ -0,0 +1,208 @@
package main
import (
"cmp"
"errors"
"fmt"
"isle/daemon/daecommon"
"slices"
"golang.org/x/exp/maps"
)
type storageAllocation struct {
Index int `yaml:"index"`
daecommon.ConfigStorageAllocation `yaml:",inline"`
}
func indexStorageAllocations(
config daecommon.NetworkConfig,
) []storageAllocation {
slices.SortFunc(
config.Storage.Allocations,
func(i, j daecommon.ConfigStorageAllocation) int {
return cmp.Compare(i.RPCPort, j.RPCPort)
},
)
allocs := make([]storageAllocation, len(config.Storage.Allocations))
for i := range config.Storage.Allocations {
allocs[i] = storageAllocation{i, config.Storage.Allocations[i]}
}
return allocs
}
var subCmdStorageAllocationAdd = subCmd{
name: "add",
descr: "Adds a new storage allocation to the host",
do: func(ctx subCmdCtx) error {
var alloc daecommon.ConfigStorageAllocation
ctx.flags.StringVar(
&alloc.DataPath,
"data-path",
"",
"Path to the directory data should be stored in",
)
ctx.flags.StringVar(
&alloc.MetaPath,
"meta-path",
"",
"Path to the directory metadata should be stored in. This is a"+
" smaller dataset which benefits from a faster drive, if"+
" possible.",
)
ctx.flags.IntVar(
&alloc.Capacity,
"capacity",
0,
"How many gigabytes to allocate.",
)
ctx.flags.IntVar(
&alloc.S3APIPort,
"s3-api-port",
0,
"Which port of the VPN network interface to serve the S3 API on."+
" Will be automatically assigned if not given.",
)
ctx.flags.IntVar(
&alloc.RPCPort,
"rpc-port",
0,
"Which port of the VPN network interface to serve RPC requests on."+
" Will be automatically assigned if not given. Once this port"+
" is defined for an allocation it cannot be changed.",
)
ctx.flags.IntVar(
&alloc.AdminPort,
"admin-port",
0,
"Which port of the VPN network interface to serve admin requests"+
" on. Will be automatically assigned if not given.",
)
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
if alloc.DataPath == "" || alloc.MetaPath == "" || alloc.Capacity == 0 {
return errors.New(
"--data-path, --meta-path, and --capacity are required",
)
}
config, err := ctx.daemonRPC.GetConfig(ctx)
if err != nil {
return fmt.Errorf("getting network config: %w", err)
}
config.Storage.Allocations = append(config.Storage.Allocations, alloc)
if err := ctx.daemonRPC.SetConfig(ctx, config); err != nil {
return fmt.Errorf("updating the network config: %w", err)
}
return nil
},
}
var subCmdStorageAllocationList = subCmd{
name: "list",
descr: "Lists all storage which is currently allocated on this host",
do: doWithOutput(func(ctx subCmdCtx) (any, error) {
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return nil, fmt.Errorf("parsing flags: %w", err)
}
config, err := ctx.daemonRPC.GetConfig(ctx)
if err != nil {
return nil, fmt.Errorf("getting network config: %w", err)
}
return indexStorageAllocations(config), nil
}),
}
var subCmdStorageAllocationRemove = subCmd{
name: "remove",
descr: "Removes an allocation which has been previously added. " +
"Allocations are identified by their index field from the output of " +
"`storage allocation list`.",
do: func(ctx subCmdCtx) error {
indexes := ctx.flags.IntSlice(
"index", nil,
"Index of the storage allocation which should be removed. Can be "+
"specified more than once",
)
ctx, err := ctx.withParsedFlags(nil)
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
if len(*indexes) == 0 {
return errors.New("At least one --index must be specified")
}
config, err := ctx.daemonRPC.GetConfig(ctx)
if err != nil {
return fmt.Errorf("getting network config: %w", err)
}
var (
allocs = indexStorageAllocations(config)
allocsByIndex = map[int]daecommon.ConfigStorageAllocation{}
)
for _, alloc := range allocs {
allocsByIndex[alloc.Index] = alloc.ConfigStorageAllocation
}
for _, index := range *indexes {
if _, ok := allocsByIndex[index]; !ok {
return fmt.Errorf(
"Index %d not found in configured storage allocations: %w",
index, err,
)
}
delete(allocsByIndex, index)
}
// we sort the new allocation set so that tests are deterministic
newAllocs := maps.Values(allocsByIndex)
slices.SortFunc(
newAllocs,
func(i, j daecommon.ConfigStorageAllocation) int {
return cmp.Compare(i.RPCPort, j.RPCPort)
},
)
config.Storage.Allocations = newAllocs
if err := ctx.daemonRPC.SetConfig(ctx, config); err != nil {
return fmt.Errorf("updating the network config: %w", err)
}
return nil
},
}
var subCmdStorageAllocation = subCmd{
name: "allocation",
descr: "Manage storage allocations configured on this host",
plural: "s",
do: func(ctx subCmdCtx) error {
return ctx.doSubCmd(
subCmdStorageAllocationAdd,
subCmdStorageAllocationList,
subCmdStorageAllocationRemove,
)
},
}

View File

@ -76,7 +76,7 @@ func TestStorageAllocationAdd(t *testing.T) {
Return(nil). Return(nil).
Once() Once()
args := []string{"storage", "add-allocation"} args := []string{"storage", "allocation", "add"}
args = append(args, test.args...) args = append(args, test.args...)
assert.NoError(t, h.run(t, args...)) assert.NoError(t, h.run(t, args...))
}) })
@ -155,7 +155,7 @@ func TestStorageAllocationList(t *testing.T) {
Return(config, nil). Return(config, nil).
Once() Once()
h.runAssertStdout(t, test.want, "storage", "list-allocations") h.runAssertStdout(t, test.want, "storage", "allocation", "list")
}) })
} }
} }
@ -275,7 +275,7 @@ func TestStorageAllocationRemove(t *testing.T) {
test.setExpectations(h.daemonRPC) test.setExpectations(h.daemonRPC)
} }
args := []string{"storage", "remove-allocation"} args := []string{"storage", "allocation", "remove"}
args = append(args, test.args...) args = append(args, test.args...)
err := h.run(t, args...) err := h.run(t, args...)

View File

@ -1,8 +0,0 @@
---
type: task
---
# Rename Storage Allocation-Related Sub-Commands
Rather than having sub-commands like `storage list-allocations` there should
instead be `storage allocation(s) list`.