Skip to content

Commit

Permalink
chore: fixup extents
Browse files Browse the repository at this point in the history
Signed-off-by: Jakob Möller <jmoller@redhat.com>
  • Loading branch information
jakobmoellerdev committed Jul 22, 2024
1 parent 5fad8b9 commit f44f044
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 69 deletions.
25 changes: 0 additions & 25 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
ci:
skip: ['golangci-lint', 'go-test']

repos:
- repo: local
hooks:
- id: go-test
name: Go Test
language: system
entry: go
args: ['test', '-v', '-skip-rootful-tests']
pass_filenames: true

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
Expand All @@ -28,19 +16,6 @@ repos:
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace

- repo: https://github.com/golangci/golangci-lint
rev: v1.59.1
hooks:
- id: golangci-lint
entry: golangci-lint run --fix
args:
- "--modules-download-mode=vendor"
- "--timeout=20m"
types: [go]
language: golang
require_serial: true
pass_filenames: false
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
Expand Down
2 changes: 1 addition & 1 deletion command.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var waitDelayKey = struct{}{}
// DefaultWaitDelay for Commands
// If WaitDelay is zero (the default), I/ O pipes will be read until EOF, which might not occur until orphaned subprocesses of the command have also closed their descriptors for the pipes
// see exec.Cmd.Wait for more information
var DefaultWaitDelay = 10 * time.Second
var DefaultWaitDelay = time.Duration(0)

func SetProcessCancelWaitDelay(ctx context.Context, delay time.Duration) context.Context {
return context.WithValue(ctx, waitDelayKey, delay)
Expand Down
47 changes: 35 additions & 12 deletions extents.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"slices"
"strconv"
"strings"
"unicode"
)

var ErrInvalidExtentsGTZero = errors.New("invalid extents specified, must be set")
Expand All @@ -20,13 +21,13 @@ type ExtentPercent string

const (
// ExtentPercentFree determines percentage of remaining free space in the VG
ExtentPercentFree ExtentPercent = "FREE"
ExtentPercentFree ExtentPercent = ExtentPercentSymbol + "FREE"
// ExtentPercentOrigin determines percentage of the total size of the origin LV
ExtentPercentOrigin ExtentPercent = "ORIGIN"
ExtentPercentOrigin ExtentPercent = ExtentPercentSymbol + "ORIGIN"
// ExtentPercentPVS determines percentage of the total size of the specified PVs
ExtentPercentPVS ExtentPercent = "PVS"
ExtentPercentPVS ExtentPercent = ExtentPercentSymbol + "PVS"
// ExtentPercentVG determines percentage of the total size of the VG
ExtentPercentVG ExtentPercent = "VG"
ExtentPercentVG ExtentPercent = ExtentPercentSymbol + "VG"
)

var percentCandidates = []ExtentPercent{
Expand Down Expand Up @@ -74,8 +75,8 @@ func ParseExtents(extents string) (Extents, error) {
return e, ErrInvalidMultiplePercent
}
if pidx > 0 {
percent := extents[pidx+1:]
if percent == "" || !slices.Contains(percentCandidates, ExtentPercent(percent)) {
percent := extents[pidx:]
if !slices.Contains(percentCandidates, ExtentPercent(percent)) {
return e, ErrInvalidPercentDefinition
}

Expand All @@ -98,11 +99,14 @@ func (opt Extents) ApplyToArgs(args Arguments) error {
if err := opt.Validate(); err != nil {
return err
}
if opt.Val == 0 {
return nil
}

args.AddOrReplace("--extents", fmt.Sprintf("%s%s",
args.AddOrReplace(fmt.Sprintf("--extents=%s%s",
strconv.FormatUint(opt.Val, 10),
map[bool]string{
true: fmt.Sprintf("%s%s", ExtentPercentSymbol, opt.ExtentPercent),
true: string(opt.ExtentPercent),
false: "",
}[len(opt.ExtentPercent) > 0],
))
Expand Down Expand Up @@ -143,6 +147,15 @@ func MustParsePrefixedExtents(str string) PrefixedExtents {

func ParsePrefixedExtents(str string) (PrefixedExtents, error) {
prefix := SizePrefix(str[0])

if unicode.IsDigit(rune(prefix)) {
extents, err := ParseExtents(str)
if err != nil {
return PrefixedExtents{}, err
}
return NewPrefixedExtents(SizePrefixNone, extents), nil
}

if !slices.Contains(prefixCandidates, prefix) {
return PrefixedExtents{}, ErrInvalidSizePrefix
}
Expand All @@ -160,7 +173,7 @@ func (opt PrefixedExtents) Validate() error {
return err
}

if !slices.Contains(prefixCandidates, opt.SizePrefix) {
if opt.SizePrefix != SizePrefixNone && !slices.Contains(prefixCandidates, opt.SizePrefix) {
return ErrInvalidSizePrefix
}

Expand All @@ -171,15 +184,25 @@ func (opt PrefixedExtents) ApplyToArgs(args Arguments) error {
if err := opt.Validate(); err != nil {
return err
}
if opt.Val == 0 {
return nil
}

args.AddOrReplace("--extents", fmt.Sprintf("%s%s%s",
string(opt.SizePrefix),
args.AddOrReplace(fmt.Sprintf("--extents=%s%s%s",
map[bool]string{
true: string(opt.SizePrefix),
false: "",
}[opt.SizePrefix != SizePrefixNone],
strconv.FormatUint(opt.Val, 10),
map[bool]string{
true: fmt.Sprintf("%s%s", ExtentPercentSymbol, opt.ExtentPercent),
true: string(opt.ExtentPercent),
false: "",
}[len(opt.ExtentPercent) > 0],
))

return nil
}

func (opt PrefixedExtents) ApplyToLVExtendOptions(opts *LVExtendOptions) {
opts.PrefixedExtents = opt
}
8 changes: 4 additions & 4 deletions extents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ func Test_Extents(t *testing.T) {
if err := extents.ApplyToArgs(args); err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(args.GetRaw(), []string{"--extents", "1%ORIGIN"}) {
t.Errorf("unexpected args: %v", args.GetRaw())
if act, exp := args.GetRaw(), "--extents=1%ORIGIN"; len(act) != 1 || act[0] != exp {
t.Errorf("unexpected args: expected %s but got %s(%v)", exp, act, len(act))
}
})

Expand All @@ -175,8 +175,8 @@ func Test_Extents(t *testing.T) {
if err := extents.ApplyToArgs(args); err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(args.GetRaw(), []string{"--extents", "+1%ORIGIN"}) {
t.Errorf("unexpected args: %v", args.GetRaw())
if act, exp := args.GetRaw(), "--extents=+1%ORIGIN"; len(act) != 1 || act[0] != exp {
t.Errorf("unexpected args: expected %s but got %s(%v)", exp, act, len(act))
}
})

Expand Down
4 changes: 4 additions & 0 deletions logical_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ func (opt LogicalVolumeName) ApplyToLVRenameOptions(opts *LVRenameOptions) {
opts.SetOldOrNew(opt)
}

func (opt LogicalVolumeName) ApplyToLVExtendOptions(opts *LVExtendOptions) {
opts.LogicalVolumeName = opt
}

func (opt LogicalVolumeName) ApplyToLVCreateOptions(opts *LVCreateOptions) {
opts.LogicalVolumeName = opt
}
Expand Down
22 changes: 15 additions & 7 deletions lvextend.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ type (
LogicalVolumeName

PoolMetadataPrefixedSize
Size
Extents
PrefixedSize
PrefixedExtents

CommonOptions
}
Expand Down Expand Up @@ -61,21 +61,29 @@ func (opts *LVExtendOptions) ApplyToArgs(args Arguments) error {
return err
}

if opts.Extents.Val > 0 && opts.Size.Val > 0 {
if opts.Extents.Val > 0 && opts.PrefixedSize.Val > 0 {
return fmt.Errorf("size and extents are mutually exclusive")
} else if opts.Extents.Val <= 0 && opts.Size.Val <= 0 {
} else if opts.Extents.Val <= 0 && opts.PrefixedSize.Val <= 0 {
return fmt.Errorf("size or extents must be specified")
}

if opts.PoolMetadataPrefixedSize.Val == 0 && opts.Size.Val == 0 && opts.Extents.Val == 0 {
if opts.PrefixedSize.SizePrefix == SizePrefixMinus {
return fmt.Errorf("size prefix must be positive")
} else if opts.PrefixedExtents.SizePrefix == SizePrefixMinus {
return fmt.Errorf("extents prefix must be positive")
} else if opts.PoolMetadataPrefixedSize.SizePrefix == SizePrefixMinus {
return fmt.Errorf("pool metadata size prefix must be positive")
}

if opts.PoolMetadataPrefixedSize.Val == 0 && opts.PrefixedSize.Val == 0 && opts.Extents.Val == 0 {
return errors.New("PoolMetadataPrefixedSize, Size or Extents is required")
}

for _, arg := range []Argument{
id,
opts.Size,
opts.PrefixedSize,
opts.PrefixedExtents,
opts.PoolMetadataPrefixedSize,
opts.Extents,
opts.CommonOptions,
} {
if err := arg.ApplyToArgs(args); err != nil {
Expand Down
38 changes: 38 additions & 0 deletions lvextend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package lvm2go

import (
"context"
"testing"
)

func TestLVExtend(t *testing.T) {
FailTestIfNotRoot(t)

clnt := NewClient()
ctx := context.Background()

test := test{
LoopDevices: []Size{
MustParseSize("100M"),
},
Volumes: []TestLogicalVolume{{
Options: LVCreateOptionList{
MustParseExtents("10%FREE"),
},
}},
}

infra := test.SetupDevicesAndVolumeGroup(t)

for _, lv := range infra.lvs {
if err := clnt.LVExtend(
ctx,
infra.volumeGroup.Name,
lv.LogicalVolumeName(),
MustParsePrefixedExtents("100%FREE"),
); err != nil {
t.Fatal(err)
}
}

}
2 changes: 1 addition & 1 deletion path.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func GetLVMPath() string {

var resolveLVMPathFromHost = sync.OnceValue(func() string {
if path, err := exec.LookPath("lvm"); err != nil {
return "/sbin/lvm"
return "/usr/sbin/lvm"
} else {
return path
}
Expand Down
12 changes: 10 additions & 2 deletions run_lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ func (c *client) RunLVM(ctx context.Context, args ...string) error {
// RunLVMInto calls lvm2 sub-commands and decodes the output via JSON into the provided struct pointer.
// if the struct pointer is nil, the output will be printed to the log instead.
func (c *client) RunLVMInto(ctx context.Context, into any, args ...string) error {
output, err := StreamedCommand(ctx, CommandContext(ctx, GetLVMPath(), args...))
cmd := CommandContext(ctx, GetLVMPath(), args...)

slog.DebugContext(ctx, "running command", slog.String("command", strings.Join(cmd.Args, " ")))

output, err := StreamedCommand(ctx, cmd)
if err != nil {
return fmt.Errorf("failed to execute command: %v", err)
}
Expand Down Expand Up @@ -70,7 +74,11 @@ func (c *client) RunRaw(ctx context.Context, process RawOutputProcessor, args ..
if len(args) == 0 {
return fmt.Errorf("no command provided")
}
output, err := StreamedCommand(ctx, CommandContext(ctx, args[0], args[1:]...))
cmd := CommandContext(ctx, args[0], args[1:]...)

slog.DebugContext(ctx, "running command", slog.String("command", strings.Join(cmd.Args, " ")))

output, err := StreamedCommand(ctx, cmd)
if err != nil {
return fmt.Errorf("failed to execute command: %v", err)
}
Expand Down
Loading

0 comments on commit f44f044

Please sign in to comment.