osbuild2: add support for LVM stages
Stage options implement a validate() method for checking values against schema constraints.
This commit is contained in:
parent
b777444319
commit
39b0cf39d6
6 changed files with 230 additions and 0 deletions
51
internal/osbuild2/lvm2_create_stage.go
Normal file
51
internal/osbuild2/lvm2_create_stage.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const lvmVolNameRegex = "^[a-zA-Z0-9+_.][a-zA-Z0-9+_.-]*$"
|
||||
|
||||
// Create LVM2 physical volumes, volume groups, and logical volumes
|
||||
|
||||
type LVM2CreateStageOptions struct {
|
||||
Volumes []LogicalVolume `json:"volumes"`
|
||||
}
|
||||
|
||||
func (LVM2CreateStageOptions) isStageOptions() {}
|
||||
|
||||
func (o LVM2CreateStageOptions) validate() error {
|
||||
if len(o.Volumes) == 0 {
|
||||
return fmt.Errorf("at least one volume is required")
|
||||
}
|
||||
|
||||
nameRegex := regexp.MustCompile(lvmVolNameRegex)
|
||||
for _, volume := range o.Volumes {
|
||||
fmt.Printf("testing volume %q\n", volume.Name)
|
||||
if !nameRegex.MatchString(volume.Name) {
|
||||
return fmt.Errorf("volume name %q doesn't conform to schema (%s)", volume.Name, nameRegex.String())
|
||||
} else {
|
||||
fmt.Printf("volume.Name %q is ok\n", volume.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type LogicalVolume struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
func NewLVM2CreateStage(options *LVM2CreateStageOptions, device *Device) *Stage {
|
||||
if err := options.validate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &Stage{
|
||||
Type: "org.osbuild.lvm2.create",
|
||||
Options: options,
|
||||
Devices: Devices{"device": *device},
|
||||
}
|
||||
}
|
||||
60
internal/osbuild2/lvm2_create_stage_test.go
Normal file
60
internal/osbuild2/lvm2_create_stage_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewLVM2CreateStageValidation(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
okOptions := LVM2CreateStageOptions{
|
||||
Volumes: []LogicalVolume{
|
||||
{
|
||||
Name: "a_volume_name",
|
||||
Size: "",
|
||||
},
|
||||
{
|
||||
Name: "good-volume.name",
|
||||
Size: "10G",
|
||||
},
|
||||
{
|
||||
Name: "99-luft+volumes",
|
||||
Size: "10737418240",
|
||||
},
|
||||
{
|
||||
Name: "++",
|
||||
Size: "1337",
|
||||
},
|
||||
{
|
||||
Name: "_",
|
||||
Size: "0",
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NoError(okOptions.validate())
|
||||
|
||||
badVolumes := []LogicalVolume{
|
||||
{
|
||||
Name: "!bad-bad-volume-name",
|
||||
Size: "1337",
|
||||
},
|
||||
{
|
||||
Name: "even worse",
|
||||
},
|
||||
{
|
||||
Name: "-",
|
||||
},
|
||||
}
|
||||
|
||||
for _, vol := range badVolumes {
|
||||
options := LVM2CreateStageOptions{
|
||||
Volumes: []LogicalVolume{vol},
|
||||
}
|
||||
assert.Error(options.validate(), vol.Name)
|
||||
}
|
||||
|
||||
empty := LVM2CreateStageOptions{}
|
||||
assert.Error(empty.validate())
|
||||
}
|
||||
17
internal/osbuild2/lvm2_lv_device.go
Normal file
17
internal/osbuild2/lvm2_lv_device.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package osbuild2
|
||||
|
||||
// Provide access to LVM2 Logical Volume (LV)
|
||||
|
||||
type LVM2LVDeviceOptions struct {
|
||||
// Logical volume to activate
|
||||
Volume string `json:"volume"`
|
||||
}
|
||||
|
||||
func (LVM2LVDeviceOptions) isDeviceOptions() {}
|
||||
|
||||
func NewLVM2LVDevice(options *LoopbackDeviceOptions) *Device {
|
||||
return &Device{
|
||||
Type: "org.osbuild.lvm2.lv",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
1
internal/osbuild2/lvm2_lv_device_test.go
Normal file
1
internal/osbuild2/lvm2_lv_device_test.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package osbuild2
|
||||
46
internal/osbuild2/lvm2_metadata_stage.go
Normal file
46
internal/osbuild2/lvm2_metadata_stage.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Set LVM2 Volume Group metadata
|
||||
|
||||
type LVM2MetadataStageOptions struct {
|
||||
CreationHost string `json:"creation_host,omitempty"`
|
||||
|
||||
// Creation time (uint64 represented as string)
|
||||
CreationTime string `json:"creation_time,omitempty"`
|
||||
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
VGName string `json:"vg_name"`
|
||||
}
|
||||
|
||||
func (LVM2MetadataStageOptions) isStageOptions() {}
|
||||
|
||||
func (o LVM2MetadataStageOptions) validate() error {
|
||||
nameRegex := regexp.MustCompile(lvmVolNameRegex)
|
||||
if !nameRegex.MatchString(o.VGName) {
|
||||
return fmt.Errorf("volume group name %q doesn't conform to schema (%s)", o.VGName, nameRegex.String())
|
||||
}
|
||||
|
||||
if _, err := strconv.ParseUint(o.CreationTime, 10, 64); err != nil {
|
||||
return fmt.Errorf("invalid volume creation time: %s", o.CreationTime)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLVM2MetadataStage(options *LVM2MetadataStageOptions, device *Device) *Stage {
|
||||
if err := options.validate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &Stage{
|
||||
Type: "org.osbuild.lvm2.metadata",
|
||||
Options: options,
|
||||
Devices: Devices{"device": *device},
|
||||
}
|
||||
}
|
||||
55
internal/osbuild2/lvm2_metadata_stage_test.go
Normal file
55
internal/osbuild2/lvm2_metadata_stage_test.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewLVM2MetadataStageValidation(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
okOptions := []LVM2MetadataStageOptions{
|
||||
{
|
||||
VGName: "a_volume_name",
|
||||
CreationTime: "0",
|
||||
},
|
||||
{
|
||||
VGName: "good-volume.name",
|
||||
CreationTime: "1629282647",
|
||||
},
|
||||
{
|
||||
VGName: "99-luft+volumes",
|
||||
CreationTime: "2147483648",
|
||||
},
|
||||
{
|
||||
VGName: "++",
|
||||
CreationTime: "42",
|
||||
},
|
||||
{
|
||||
VGName: "_",
|
||||
CreationTime: "4294967297",
|
||||
},
|
||||
}
|
||||
for _, o := range okOptions {
|
||||
assert.NoError(o.validate(), o)
|
||||
}
|
||||
|
||||
badOptions := []LVM2MetadataStageOptions{
|
||||
{
|
||||
VGName: "ok-name-bad-time",
|
||||
CreationTime: "-10",
|
||||
},
|
||||
{
|
||||
VGName: "!bad-name",
|
||||
CreationTime: "1629282647",
|
||||
},
|
||||
{
|
||||
VGName: "worse.time",
|
||||
CreationTime: "TIME",
|
||||
},
|
||||
}
|
||||
for _, o := range badOptions {
|
||||
assert.Error(o.validate(), o)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue