Skip to content

Commit

Permalink
feat: vgchange and pointer returns
Browse files Browse the repository at this point in the history
  • Loading branch information
jakobmoellerdev committed Jul 28, 2024
1 parent 6cebfb5 commit b3a3405
Show file tree
Hide file tree
Showing 23 changed files with 257 additions and 47 deletions.
2 changes: 2 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ const (
ArgsTypePVs ArgsType = "pvs"
ArgsTypeVGs ArgsType = "vgs"
ArgsTypeLVCreate ArgsType = "lvcreate"
ArgsTypeLVChange ArgsType = "lvchange"
ArgsTypeVGCreate ArgsType = "vgcreate"
ArgsTypeVGChange ArgsType = "vgchange"
ArgsTypeLVRename ArgsType = "lvrename"
)

Expand Down
31 changes: 28 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package lvm2go

import (
"context"
"errors"
)

var (
ErrVolumeGroupNotFound = errors.New("volume group not found")
ErrLogicalVolumeNotFound = errors.New("logical volume not found")
)

type client struct{}
Expand Down Expand Up @@ -41,14 +47,23 @@ type MetaClient interface {

// VolumeGroupClient is a client that provides operations on lvm2 volume groups.
type VolumeGroupClient interface {
// VG returns a volume group that matches the given options.
//
// If no VolumeGroupName is defined, ErrVolumeGroupNameRequired is returned.
// If no volume group is found, ErrVolumeGroupNotFound is returned.
//
// It is equivalent to calling VGs with the same options and returning the first volume group in the list.
// see VGs for more information.
VG(ctx context.Context, opts ...VGsOption) (*VolumeGroup, error)

// VGs return a list of volume groups that match the given options.
//
// If no volume groups are found, an empty slice is returned.
// If options limit the number of volume groups returned,
// the slice may be shorter than the total number of volume groups.
//
// See man lvm vgs for more information.
VGs(ctx context.Context, opts ...VGsOption) ([]VolumeGroup, error)
VGs(ctx context.Context, opts ...VGsOption) ([]*VolumeGroup, error)

// VGCreate creates a new volume group with the given options.
//
Expand Down Expand Up @@ -83,14 +98,24 @@ type VolumeGroupClient interface {

// LogicalVolumeClient is a client that provides operations on lvm2 logical volumes.
type LogicalVolumeClient interface {
// LV returns a logical volume that matches the given options.
//
// If no LogicalVolumeName is defined, ErrLogicalVolumeNameRequired is returned.
// If no VolumeGroupName is defined, ErrVolumeGroupNameRequired is returned.
// If no logical volume is found in the volume group, ErrLogicalVolumeNotFound is returned.
//
// It is equivalent to calling LVs with the same options and returning the first logical volume in the list.
// see LVs for more information.
LV(ctx context.Context, opts ...LVsOption) (*LogicalVolume, error)

// LVs return a list of logical volumes that match the given options.
//
// If no logical volumes are found, an empty slice is returned.
// If options limit the number of volume groups returned,
// the slice may be shorter than the total number of logical volumes.
//
// See man lvm lvs for more information.
LVs(ctx context.Context, opts ...LVsOption) ([]LogicalVolume, error)
LVs(ctx context.Context, opts ...LVsOption) ([]*LogicalVolume, error)

// LVCreate creates a new logical volume with the given options.
//
Expand Down Expand Up @@ -137,7 +162,7 @@ type PhysicalVolumeClient interface {
// the slice may be shorter than the total number of physical volumes.
//
// See man lvm pvs for more information.
PVs(ctx context.Context, opts ...PVsOption) ([]PhysicalVolume, error)
PVs(ctx context.Context, opts ...PVsOption) ([]*PhysicalVolume, error)

// PVCreate creates a new physical volume with the given options.
//
Expand Down
10 changes: 1 addition & 9 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@ func TestLVs(t *testing.T) {
t.Fatal(err)
}

if len(lvs) != len(tc.Volumes) {
t.Fatalf("Expected %d logical volumes, got %d", len(tc.Volumes), len(lvs))
}

for _, expected := range infra.lvs {
found := false
for _, lv := range lvs {
Expand All @@ -97,14 +93,10 @@ func TestLVs(t *testing.T) {
}
}

vgs, err := clnt.VGs(ctx, infra.volumeGroup.Name)
vg, err := clnt.VG(ctx, infra.volumeGroup.Name)
if err != nil {
t.Fatal(err)
}
if len(vgs) != 1 {
t.Fatalf("Expected 1 volume group, got %d", len(vgs))
}
vg := vgs[0]

if vg.Name != infra.volumeGroup.Name {
t.Fatalf("Expected volume group %s, got %s", infra.volumeGroup.Name, vg.Name)
Expand Down
1 change: 1 addition & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
)

func Test_RawConfig(t *testing.T) {
t.Parallel()
FailTestIfNotRoot(t)
slog.SetDefault(slog.New(NewContextPropagatingSlogHandler(NewTestingHandler(t))))
slog.SetLogLoggerLevel(slog.LevelDebug)
Expand Down
4 changes: 3 additions & 1 deletion json_convert_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ func unmarshalAndConvertToStrings(raw map[string]json.RawMessage, key string, fi
return err
}

*fieldPtr = strings.Split(str, ",")
if len(str) > 0 {
*fieldPtr = strings.Split(str, ",")
}

return nil
}
Expand Down
20 changes: 18 additions & 2 deletions logical_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type LogicalVolume struct {
Major int64 `json:"lv_kernel_major"`
Minor int64 `json:"lv_kernel_minor"`

Tags string `json:"lv_tags"`
Tags Tags `json:"lv_tags"`
Attr LVAttributes `json:"lv_attr"`
Size Size `json:"lv_size"`

Expand All @@ -43,7 +43,6 @@ func (lv *LogicalVolume) UnmarshalJSON(data []byte) error {
"lv_name": (*string)(&lv.Name),
"lv_full_name": &lv.FullName,
"lv_path": &lv.Path,
"lv_tags": &lv.Tags,
"origin": &lv.Origin,
"pool_lv": &lv.PoolLogicalVolume,
"vg_name": (*string)(&lv.VolumeGroupName),
Expand All @@ -55,6 +54,14 @@ func (lv *LogicalVolume) UnmarshalJSON(data []byte) error {
}
}

for key, fieldPtr := range map[string]*Tags{
"lv_tags": &lv.Tags,
} {
if err := unmarshalAndConvertToStrings(raw, key, (*[]string)(fieldPtr)); err != nil {
return err
}
}

for key, fieldPtr := range map[string]*int64{
"lv_kernel_major": &lv.Major,
"lv_kernel_minor": &lv.Minor,
Expand Down Expand Up @@ -119,6 +126,10 @@ func (opt LogicalVolumeName) ApplyToLVChangeOptions(opts *LVChangeOptions) {
opts.LogicalVolumeName = opt
}

func (opt LogicalVolumeName) ApplyToLVsOptions(opts *LVsOptions) {
opts.LogicalVolumeName = opt
}

type FQLogicalVolumeName struct {
VolumeGroupName
LogicalVolumeName
Expand Down Expand Up @@ -147,11 +158,16 @@ func (opt *FQLogicalVolumeName) ApplyToLVResizeOptions(opts *LVResizeOptions) {
func (opt *FQLogicalVolumeName) ApplyToLVReduceOptions(opts *LVReduceOptions) {
opts.VolumeGroupName, opts.LogicalVolumeName = opt.VolumeGroupName, opt.LogicalVolumeName
}

func (opt *FQLogicalVolumeName) ApplyToLVRenameOptions(opts *LVRenameOptions) {
opts.VolumeGroupName = opt.VolumeGroupName
opts.SetOldOrNew(opt.LogicalVolumeName)
}

func (opt *FQLogicalVolumeName) ApplyToLVsOptions(opts *LVsOptions) {
opts.VolumeGroupName, opts.LogicalVolumeName = opt.VolumeGroupName, opt.LogicalVolumeName
}

func (opt *FQLogicalVolumeName) Split() (VolumeGroupName, LogicalVolumeName) {
return opt.VolumeGroupName, opt.LogicalVolumeName
}
Expand Down
1 change: 1 addition & 0 deletions lv_attr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
)

func TestLVAttributes(t *testing.T) {
t.Parallel()
type args struct {
raw string
}
Expand Down
2 changes: 1 addition & 1 deletion lvchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (c *client) LVChange(ctx context.Context, opts ...LVChangeOption) error {
}

func (list LVChangeOptionsList) AsArgs() (Arguments, error) {
args := NewArgs(ArgsTypeGeneric)
args := NewArgs(ArgsTypeLVChange)
options := LVChangeOptions{}
for _, opt := range list {
opt.ApplyToLVChangeOptions(&options)
Expand Down
1 change: 1 addition & 0 deletions lvextend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
)

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

clnt := NewClient()
Expand Down
1 change: 1 addition & 0 deletions lvmdevices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

func TestLVMDevices(t *testing.T) {
t.Parallel()
FailTestIfNotRoot(t)

_, err := exec.LookPath("lvmdevices")
Expand Down
1 change: 1 addition & 0 deletions lvrename_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
)

func TestLVRename(t *testing.T) {
t.Parallel()
FailTestIfNotRoot(t)

clnt := NewClient()
Expand Down
38 changes: 36 additions & 2 deletions lvs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
type (
LVsOptions struct {
VolumeGroupName
LogicalVolumeName
Tags
Select

Expand All @@ -27,10 +28,10 @@ var (
// LVs returns a list of logical volumes that match the given options.
// If no logical volumes are found, nil is returned.
// It is really just a wrapper around the `lvs --reportformat json` command.
func (c *client) LVs(ctx context.Context, opts ...LVsOption) ([]LogicalVolume, error) {
func (c *client) LVs(ctx context.Context, opts ...LVsOption) ([]*LogicalVolume, error) {
type lvReport struct {
Report []struct {
LV []LogicalVolume `json:"lv"`
LV []*LogicalVolume `json:"lv"`
} `json:"report"`
}

Expand Down Expand Up @@ -67,6 +68,39 @@ func (c *client) LVs(ctx context.Context, opts ...LVsOption) ([]LogicalVolume, e
return lvs, nil
}

func (c *client) LV(ctx context.Context, opts ...LVsOption) (*LogicalVolume, error) {
foundVG := false
foundLV := false
for _, opt := range opts {
if _, ok := opt.(VolumeGroupName); ok {
foundVG = true
}
if _, ok := opt.(LogicalVolumeName); ok {
foundLV = true
}
if foundVG && foundLV {
break
}
}
if !foundVG {
return nil, ErrVolumeGroupNameRequired
}
if !foundLV {
return nil, ErrLogicalVolumeNameRequired
}

lvs, err := c.LVs(ctx, opts...)
if err != nil {
return nil, err
}

if len(lvs) == 0 {
return nil, ErrLogicalVolumeNotFound
}

return lvs[0], nil
}

func (opts *LVsOptions) ApplyToArgs(args Arguments) error {
for _, arg := range []Argument{
opts.VolumeGroupName,
Expand Down
3 changes: 3 additions & 0 deletions physical_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package lvm2go

import (
"encoding/json"
"errors"
)

var ErrPhysicalVolumeNameRequired = errors.New("PhysicalVolumeName is required for a fully qualified physical volume")

type PhysicalVolume struct {
UUID string `json:"pv_uuid"`
Name PhysicalVolumeName `json:"pv_name"`
Expand Down
22 changes: 11 additions & 11 deletions pvs.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ var (
// PVs returns a list of logical volumes that match the given options.
// If no logical volumes are found, nil is returned.
// It is really just a wrapper around the `lvs --reportformat json` command.
func (c *client) PVs(ctx context.Context, opts ...PVsOption) ([]PhysicalVolume, error) {
func (c *client) PVs(ctx context.Context, opts ...PVsOption) ([]*PhysicalVolume, error) {
type lvReport struct {
Report []struct {
PV []PhysicalVolume `json:"pv"`
PV []*PhysicalVolume `json:"pv"`
} `json:"report"`
}

Expand Down Expand Up @@ -68,16 +68,16 @@ func (c *client) PVs(ctx context.Context, opts ...PVsOption) ([]PhysicalVolume,
}

func (opts *PVsOptions) ApplyToArgs(args Arguments) error {
if err := opts.VolumeGroupName.ApplyToArgs(args); err != nil {
return err
}

if err := opts.CommonOptions.ApplyToArgs(args); err != nil {
return err
}

if err := opts.ColumnOptions.ApplyToArgs(args); err != nil {
return err
for _, arg := range []Argument{
opts.VolumeGroupName,
opts.Tags,
opts.CommonOptions,
opts.ColumnOptions,
} {
if err := arg.ApplyToArgs(args); err != nil {
return err
}
}

return nil
Expand Down
1 change: 1 addition & 0 deletions size_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func init() {
}

func Test_Size(t *testing.T) {
t.Parallel()
for _, tc := range DefaultSizeTestCases {
t.Run(tc.InputToParse, func(t *testing.T) {
actual, err := ParseSize(tc.InputToParse)
Expand Down
7 changes: 7 additions & 0 deletions tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ func (opt Tags) ApplyToLVCreateOptions(opts *LVCreateOptions) {
func (opt Tags) ApplyToVGRemoveOptions(opts *VGRemoveOptions) {
opts.Tags = opt
}
func (opt Tags) ApplyToVGChangeOptions(opts *VGChangeOptions) {
opts.Tags = opt
}
func (opt Tags) ApplyToLVRemoveOptions(opts *LVRemoveOptions) {
opts.Tags = opt
}
Expand All @@ -29,6 +32,10 @@ func (opt Tags) ApplyToArgs(args Arguments) error {
}

switch args.GetType() {
case ArgsTypeLVChange:
fallthrough
case ArgsTypeVGChange:
fallthrough
case ArgsTypeVGCreate:
fallthrough
case ArgsTypeLVCreate:
Expand Down
8 changes: 7 additions & 1 deletion util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,15 @@ func (t LoopbackDevices) PhysicalVolumeNames() PhysicalVolumeNames {

}

// testLoopbackCreationSync is a mutex to synchronize the creation of loopback devices in tests
// so that they don't interfere with each other by requesting the same free loopback device
var testLoopbackCreationSync = sync.Mutex{}

func MakeTestLoopbackDevice(t *testing.T, size Size) LoopbackDevice {
t.Helper()
ctx := context.Background()
testLoopbackCreationSync.Lock()
defer testLoopbackCreationSync.Unlock()

backingFilePath := filepath.Join(t.TempDir(), fmt.Sprintf("%s.img", NewNonDeterministicTestID(t)))

Expand Down Expand Up @@ -132,7 +139,6 @@ func MakeTestLoopbackDevice(t *testing.T, size Size) LoopbackDevice {
}
logger = logger.With("loop", loop)
logger.DebugContext(ctx, "created test loopback device successfully")

t.Cleanup(func() {
logger.DebugContext(ctx, "cleaning up test loopback device")
if err := loop.Close(); err != nil {
Expand Down
Loading

0 comments on commit b3a3405

Please sign in to comment.