diff --git a/go/cmd/entrypoint/storage.go b/go/cmd/entrypoint/storage.go index e578f3e..981b641 100644 --- a/go/cmd/entrypoint/storage.go +++ b/go/cmd/entrypoint/storage.go @@ -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, ) }, } diff --git a/go/cmd/entrypoint/storage_allocation.go b/go/cmd/entrypoint/storage_allocation.go new file mode 100644 index 0000000..de2d969 --- /dev/null +++ b/go/cmd/entrypoint/storage_allocation.go @@ -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, + ) + }, +} diff --git a/go/cmd/entrypoint/storage_test.go b/go/cmd/entrypoint/storage_allocation_test.go similarity index 97% rename from go/cmd/entrypoint/storage_test.go rename to go/cmd/entrypoint/storage_allocation_test.go index b386910..2180351 100644 --- a/go/cmd/entrypoint/storage_test.go +++ b/go/cmd/entrypoint/storage_allocation_test.go @@ -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...) diff --git a/tasks/v0.0.3/code/alloc-subcmd-names.md b/tasks/v0.0.3/code/alloc-subcmd-names.md deleted file mode 100644 index 3e6aedc..0000000 --- a/tasks/v0.0.3/code/alloc-subcmd-names.md +++ /dev/null @@ -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`.