Move storage allocation sub-commands under 'storage allocation(s)'
This commit is contained in:
parent
aac7b30cf0
commit
5669123c99
@ -1,208 +1,11 @@
|
||||
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{
|
||||
name: "storage",
|
||||
descr: "Sub-commands related to configuration of storage on this host",
|
||||
do: func(ctx subCmdCtx) error {
|
||||
return ctx.doSubCmd(
|
||||
subCmdStorageAllocationAdd,
|
||||
subCmdStorageAllocationList,
|
||||
subCmdStorageAllocationRemove,
|
||||
subCmdStorageAllocation,
|
||||
)
|
||||
},
|
||||
}
|
||||
|
208
go/cmd/entrypoint/storage_allocation.go
Normal file
208
go/cmd/entrypoint/storage_allocation.go
Normal 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,
|
||||
)
|
||||
},
|
||||
}
|
@ -76,7 +76,7 @@ func TestStorageAllocationAdd(t *testing.T) {
|
||||
Return(nil).
|
||||
Once()
|
||||
|
||||
args := []string{"storage", "add-allocation"}
|
||||
args := []string{"storage", "allocation", "add"}
|
||||
args = append(args, test.args...)
|
||||
assert.NoError(t, h.run(t, args...))
|
||||
})
|
||||
@ -155,7 +155,7 @@ func TestStorageAllocationList(t *testing.T) {
|
||||
Return(config, nil).
|
||||
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)
|
||||
}
|
||||
|
||||
args := []string{"storage", "remove-allocation"}
|
||||
args := []string{"storage", "allocation", "remove"}
|
||||
args = append(args, test.args...)
|
||||
err := h.run(t, args...)
|
||||
|
@ -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`.
|
Loading…
Reference in New Issue
Block a user