disk: add RequiredSizes to ImageOptions

These RequiredSizes are a map that is passed on to the partition table
logic which had hardcoded defaults. This makes it possible to define
either no RequiredSizes (`nil`) or empty RequiredSizes which means no
further constraint checks or partition resizes will be done.
This commit is contained in:
Simon de Vlieger 2023-01-26 11:21:00 +01:00 committed by Ondřej Budai
parent d7f5fac183
commit 39879a9f60
17 changed files with 188 additions and 57 deletions

View file

@ -39,7 +39,7 @@ func (img *MyImage) InstantiateManifest(m *manifest.Manifest,
}
// TODO: add helper
pt, err := disk.NewPartitionTable(&basePT, nil, 0, false, rng)
pt, err := disk.NewPartitionTable(&basePT, nil, 0, false, nil, rng)
if err != nil {
panic(err)
}

View file

@ -74,7 +74,7 @@ func TestDisk_DynamicallyResizePartitionTable(t *testing.T) {
// math/rand is good enough in this case
/* #nosec G404 */
rng := rand.New(rand.NewSource(0))
newpt, err := NewPartitionTable(&pt, mountpoints, 1024, false, rng)
newpt, err := NewPartitionTable(&pt, mountpoints, 1024, false, nil, rng)
assert.NoError(t, err)
assert.GreaterOrEqual(t, newpt.Size, expectedSize)
}
@ -449,7 +449,7 @@ func TestCreatePartitionTable(t *testing.T) {
for ptName := range testPartitionTables {
pt := testPartitionTables[ptName]
for bpName, bp := range testBlueprints {
mpt, err := NewPartitionTable(&pt, bp, uint64(13*MiB), false, rng)
mpt, err := NewPartitionTable(&pt, bp, uint64(13*MiB), false, nil, rng)
assert.NoError(err, "Partition table generation failed: PT %q BP %q (%s)", ptName, bpName, err)
assert.NotNil(mpt, "Partition table generation failed: PT %q BP %q (nil partition table)", ptName, bpName)
assert.Greater(mpt.GetSize(), sumSizes(bp))
@ -475,12 +475,12 @@ func TestCreatePartitionTableLVMify(t *testing.T) {
if tbp != nil && (ptName == "btrfs" || ptName == "luks") {
assert.Panics(func() {
_, _ = NewPartitionTable(&pt, tbp, uint64(13*MiB), true, rng)
_, _ = NewPartitionTable(&pt, tbp, uint64(13*MiB), true, nil, rng)
}, fmt.Sprintf("PT %q BP %q: should panic", ptName, bpName))
continue
}
mpt, err := NewPartitionTable(&pt, tbp, uint64(13*MiB), true, rng)
mpt, err := NewPartitionTable(&pt, tbp, uint64(13*MiB), true, nil, rng)
assert.NoError(err, "PT %q BP %q: Partition table generation failed: (%s)", ptName, bpName, err)
rootPath := entityPath(mpt, "/")
@ -588,7 +588,7 @@ func TestMinimumSizes(t *testing.T) {
for idx, tc := range testCases {
{ // without LVM
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), false, rng)
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), false, nil, rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := entityPath(mpt, mnt)
@ -602,7 +602,7 @@ func TestMinimumSizes(t *testing.T) {
}
{ // with LVM
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), true, rng)
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), true, nil, rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := entityPath(mpt, mnt)
@ -689,7 +689,7 @@ func TestLVMExtentAlignment(t *testing.T) {
}
for idx, tc := range testCases {
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), true, rng)
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), true, nil, rng)
assert.NoError(err)
for mnt, expSize := range tc.ExpectedSizes {
path := entityPath(mpt, mnt)
@ -718,7 +718,7 @@ func TestNewBootWithSizeLVMify(t *testing.T) {
},
}
mpt, err := NewPartitionTable(&pt, custom, uint64(3*GiB), true, rng)
mpt, err := NewPartitionTable(&pt, custom, uint64(3*GiB), true, nil, rng)
assert.NoError(err)
for idx, c := range custom {
@ -971,3 +971,118 @@ func TestEnsureDirectorySizes(t *testing.T) {
}
}
func TestMinimumSizesWithRequiredSizes(t *testing.T) {
assert := assert.New(t)
// math/rand is good enough in this case
/* #nosec G404 */
rng := rand.New(rand.NewSource(13))
pt := testPartitionTables["plain"]
type testCase struct {
Blueprint []blueprint.FilesystemCustomization
ExpectedMinSizes map[string]uint64
}
testCases := []testCase{
{ // specify small /usr -> / and /usr get default size
Blueprint: []blueprint.FilesystemCustomization{
{
Mountpoint: "/usr",
MinSize: 1 * MiB,
},
},
ExpectedMinSizes: map[string]uint64{
"/usr": 3 * GiB,
"/": 1 * GiB,
},
},
{ // specify small / and /usr -> / and /usr get default size
Blueprint: []blueprint.FilesystemCustomization{
{
Mountpoint: "/",
MinSize: 1 * MiB,
},
{
Mountpoint: "/usr",
MinSize: 1 * KiB,
},
},
ExpectedMinSizes: map[string]uint64{
"/usr": 3 * GiB,
"/": 1 * GiB,
},
},
{ // big /usr -> / gets default size
Blueprint: []blueprint.FilesystemCustomization{
{
Mountpoint: "/usr",
MinSize: 10 * GiB,
},
},
ExpectedMinSizes: map[string]uint64{
"/usr": 10 * GiB,
"/": 1 * GiB,
},
},
{
Blueprint: []blueprint.FilesystemCustomization{
{
Mountpoint: "/",
MinSize: 10 * GiB,
},
{
Mountpoint: "/home",
MinSize: 1 * MiB,
},
},
ExpectedMinSizes: map[string]uint64{
"/": 10 * GiB,
"/home": 1 * GiB,
},
},
{ // no separate /usr and no size for / -> / gets sum of default sizes for / and /usr
Blueprint: []blueprint.FilesystemCustomization{
{
Mountpoint: "/opt",
MinSize: 10 * GiB,
},
},
ExpectedMinSizes: map[string]uint64{
"/opt": 10 * GiB,
"/": 1 * GiB,
},
},
}
for idx, tc := range testCases {
{ // without LVM
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), false, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := entityPath(mpt, mnt)
assert.NotNil(path, "[%d] mountpoint %q not found", idx, mnt)
parent := path[1]
part, ok := parent.(*Partition)
assert.True(ok, "%q parent (%v) is not a partition", mnt, parent)
assert.GreaterOrEqual(part.GetSize(), minSize,
"[%d] %q size %d should be greater or equal to %d", idx, mnt, part.GetSize(), minSize)
}
}
{ // with LVM
mpt, err := NewPartitionTable(&pt, tc.Blueprint, uint64(3*GiB), true, map[string]uint64{"/": 1 * GiB, "/usr": 3 * GiB}, rng)
assert.NoError(err)
for mnt, minSize := range tc.ExpectedMinSizes {
path := entityPath(mpt, mnt)
assert.NotNil(path, "[%d] mountpoint %q not found", idx, mnt)
parent := path[1]
part, ok := parent.(*LVMLogicalVolume)
assert.True(ok, "[%d] %q parent (%v) is not an LVM logical volume", idx, mnt, parent)
assert.GreaterOrEqual(part.GetSize(), minSize,
"[%d] %q size %d should be greater or equal to %d", idx, mnt, part.GetSize(), minSize)
}
}
}
}

View file

@ -17,9 +17,11 @@ type PartitionTable struct {
SectorSize uint64 // Sector size in bytes
ExtraPadding uint64 // Extra space at the end of the partition table (sectors)
RequiredSizes map[string]uint64
}
func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, lvmify bool, rng *rand.Rand) (*PartitionTable, error) {
func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, lvmify bool, requiredSizes map[string]uint64, rng *rand.Rand) (*PartitionTable, error) {
newPT := basePT.Clone().(*PartitionTable)
// first pass: enlarge existing mountpoints and collect new ones
@ -40,11 +42,19 @@ func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.Filesyste
return nil, err
}
// TODO: make these overrideable for each image type
newPT.EnsureDirectorySizes(map[string]uint64{
"/": 1073741824,
"/usr": 2147483648,
})
// If no separate requiredSizes are given then we use our defaults
if requiredSizes == nil {
newPT.RequiredSizes = map[string]uint64{
"/": 1073741824,
"/usr": 2147483648,
}
} else {
newPT.RequiredSizes = requiredSizes
}
if len(newPT.RequiredSizes) != 0 {
newPT.EnsureDirectorySizes(newPT.RequiredSizes)
}
// Calculate partition table offsets and sizes
newPT.relayout(imageSize)
@ -65,12 +75,13 @@ func (pt *PartitionTable) Clone() Entity {
}
clone := &PartitionTable{
Size: pt.Size,
UUID: pt.UUID,
Type: pt.Type,
Partitions: make([]Partition, len(pt.Partitions)),
SectorSize: pt.SectorSize,
ExtraPadding: pt.ExtraPadding,
Size: pt.Size,
UUID: pt.UUID,
Type: pt.Type,
Partitions: make([]Partition, len(pt.Partitions)),
SectorSize: pt.SectorSize,
ExtraPadding: pt.ExtraPadding,
RequiredSizes: pt.RequiredSizes,
}
for idx, partition := range pt.Partitions {

View file

@ -125,10 +125,11 @@ type ImageType interface {
// The ImageOptions specify options for a specific image build
type ImageOptions struct {
Size uint64
OSTree OSTreeImageOptions
Subscription *SubscriptionImageOptions
Facts *FactsImageOptions
Size uint64
OSTree OSTreeImageOptions
Subscription *SubscriptionImageOptions
Facts *FactsImageOptions
RequiredSizes map[string]uint64
}
// The OSTreeImageOptions specify an ostree ref, checksum, URL, ContentURL, and RHSM. The meaning of

View file

@ -653,7 +653,7 @@ func (t *imageType) getPartitionTable(
lvmify := !t.rpmOstree
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, rng)
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, options.RequiredSizes, rng)
}
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {

View file

@ -402,6 +402,10 @@ func iotRawImage(workload workload.Workload,
}
img.OSName = "fedora-iot"
// the iot raw image is laid out quite specifically, so we set no constraints; this means the
// default constraints don't apply either
options.RequiredSizes = map[string]uint64{}
// TODO: move generation into LiveImage
pt, err := t.getPartitionTable(customizations.GetFilesystems(), options, rng)
if err != nil {

View file

@ -310,7 +310,7 @@ func (t *imageType) getPartitionTable(
imageSize := t.Size(options.Size)
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, true, rng)
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, true, nil, rng)
}
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {

View file

@ -157,7 +157,7 @@ func (t *imageType) getPartitionTable(
lvmify := !t.rpmOstree
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, rng)
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, nil, rng)
}
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {

View file

@ -160,7 +160,7 @@ func (t *imageType) getPartitionTable(
lvmify := !t.rpmOstree
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, rng)
return disk.NewPartitionTable(&basePartitionTable, mountpoints, imageSize, lvmify, nil, rng)
}
func (t *imageType) getDefaultImageConfig() *distro.ImageConfig {

View file

@ -18,7 +18,7 @@ func TestGenDeviceCreationStages(t *testing.T) {
luks_lvm := testPartitionTables["luks+lvm"]
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, rng)
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, make(map[string]uint64), rng)
assert.NoError(err)
stages := GenDeviceCreationStages(pt, "image.raw")
@ -81,7 +81,7 @@ func TestGenDeviceFinishStages(t *testing.T) {
luks_lvm := testPartitionTables["luks+lvm"]
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, rng)
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, make(map[string]uint64), rng)
assert.NoError(err)
stages := GenDeviceFinishStages(pt, "image.raw")
@ -124,7 +124,7 @@ func TestGenDeviceFinishStagesOrderWithLVMClevisBind(t *testing.T) {
luks_lvm := testPartitionTables["luks+lvm+clevisBind"]
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, rng)
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, make(map[string]uint64), rng)
assert.NoError(err)
stages := GenDeviceFinishStages(pt, "image.raw")

View file

@ -18,7 +18,7 @@ func TestGenImageKernelOptions(t *testing.T) {
luks_lvm := testPartitionTables["luks+lvm"]
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, rng)
pt, err := disk.NewPartitionTable(&luks_lvm, []blueprint.FilesystemCustomization{}, 0, false, make(map[string]uint64), rng)
assert.NoError(err)
var uuid string

View file

@ -2283,7 +2283,7 @@
"type": "org.osbuild.truncate",
"options": {
"filename": "disk.img",
"size": "4822401024"
"size": "4294967296"
}
},
{
@ -2305,7 +2305,7 @@
"uuid": "CB07C243-BC44-4717-853E-28852021225B"
},
{
"size": 6293471,
"size": 5263327,
"start": 3125248,
"type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
@ -2369,7 +2369,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471,
"size": 5263327,
"lock": true
}
}
@ -2416,7 +2416,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471
"size": 5263327
}
}
},

View file

@ -2291,7 +2291,7 @@
"type": "org.osbuild.truncate",
"options": {
"filename": "disk.img",
"size": "4822401024"
"size": "4294967296"
}
},
{
@ -2313,7 +2313,7 @@
"uuid": "CB07C243-BC44-4717-853E-28852021225B"
},
{
"size": 6293471,
"size": 5263327,
"start": 3125248,
"type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
@ -2377,7 +2377,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471,
"size": 5263327,
"lock": true
}
}
@ -2424,7 +2424,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471
"size": 5263327
}
}
},

View file

@ -2299,7 +2299,7 @@
"type": "org.osbuild.truncate",
"options": {
"filename": "disk.img",
"size": "4822401024"
"size": "4294967296"
}
},
{
@ -2321,7 +2321,7 @@
"uuid": "CB07C243-BC44-4717-853E-28852021225B"
},
{
"size": 6293471,
"size": 5263327,
"start": 3125248,
"type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
@ -2385,7 +2385,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471,
"size": 5263327,
"lock": true
}
}
@ -2432,7 +2432,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471
"size": 5263327
}
}
},

View file

@ -2307,7 +2307,7 @@
"type": "org.osbuild.truncate",
"options": {
"filename": "disk.img",
"size": "4822401024"
"size": "4294967296"
}
},
{
@ -2329,7 +2329,7 @@
"uuid": "CB07C243-BC44-4717-853E-28852021225B"
},
{
"size": 6293471,
"size": 5263327,
"start": 3125248,
"type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
@ -2393,7 +2393,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471,
"size": 5263327,
"lock": true
}
}
@ -2440,7 +2440,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471
"size": 5263327
}
}
},

View file

@ -2114,7 +2114,7 @@
"type": "org.osbuild.truncate",
"options": {
"filename": "disk.img",
"size": "4822401024"
"size": "4294967296"
}
},
{
@ -2136,7 +2136,7 @@
"uuid": "CB07C243-BC44-4717-853E-28852021225B"
},
{
"size": 6293471,
"size": 5263327,
"start": 3125248,
"type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
@ -2200,7 +2200,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471,
"size": 5263327,
"lock": true
}
}
@ -2247,7 +2247,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471
"size": 5263327
}
}
},

View file

@ -2122,7 +2122,7 @@
"type": "org.osbuild.truncate",
"options": {
"filename": "disk.img",
"size": "4822401024"
"size": "4294967296"
}
},
{
@ -2144,7 +2144,7 @@
"uuid": "CB07C243-BC44-4717-853E-28852021225B"
},
{
"size": 6293471,
"size": 5263327,
"start": 3125248,
"type": "0FC63DAF-8483-4772-8E79-3D69D8477DE4",
"uuid": "6264D520-3FB9-423F-8AB8-7A0A8E3D3562"
@ -2208,7 +2208,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471,
"size": 5263327,
"lock": true
}
}
@ -2255,7 +2255,7 @@
"options": {
"filename": "disk.img",
"start": 3125248,
"size": 6293471
"size": 5263327
}
}
},