Skip to content

Commit 8c7b8f5

Browse files
committed
feat: add support for negative max size
Add support for negative max size values in volume configuration. Negative max size represents the amount of space to be left free on the device, rather than the size the volume should consume. For example, a max size of "-10GiB" means the volume can grow to the device size minus 10GiB. Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
1 parent 77bc3d2 commit 8c7b8f5

File tree

19 files changed

+349
-46
lines changed

19 files changed

+349
-46
lines changed

api/resource/definitions/block/block.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ message PartitionSpec {
169169
string label = 4;
170170
string type_uuid = 5;
171171
uint64 relative_max_size = 6;
172+
bool negative_max_size = 7;
172173
}
173174

174175
// ProvisioningSpec is the spec for volume provisioning.

hack/release.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ This allows easy migration, e.g. by changing `.cluster.controlPlane.endpoint` to
146146
`.cluster.apiServer.extraArgs["service-account-issuer"]`.
147147
"""
148148

149+
[notes.negativeMaxVolumeSize]
150+
title = "Negative Max Volume Size"
151+
description = """\
152+
Negative max size represents the amount of space to be left free on the device, rather than the size the volume should consume.
153+
For example:
154+
* a max size of "-10GiB" means the volume can grow to the available space minus 10GiB.
155+
* a max size of "-25%" means the volume can grow to the available space minus 25%.
156+
"""
157+
149158
[make_deps]
150159

151160
[make_deps.tools]

internal/app/machined/pkg/controllers/block/internal/volumes/partition.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,11 @@ func CreatePartition(ctx context.Context, logger *zap.Logger, diskPath string, v
6565
available := pt.LargestContiguousAllocatable()
6666

6767
size := volumeCfg.TypedSpec().Provisioning.PartitionSpec.MinSize
68-
maxSize := volumeCfg.TypedSpec().Provisioning.PartitionSpec.ResolveMaxSize(available)
68+
69+
maxSize, err := volumeCfg.TypedSpec().Provisioning.PartitionSpec.ResolveMaxSize(available)
70+
if err != nil {
71+
return CreatePartitionResult{}, fmt.Errorf("error resolving max size: %w", err)
72+
}
6973

7074
if available < size {
7175
// should never happen

internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/system_volumes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ func GetEphemeralVolumeTransformer(inContainer bool) volumeConfigTransformer {
116116
MinSize: extraVolumeConfig.Provisioning().MinSize().ValueOr(quirks.New("").PartitionSizes().EphemeralMinSize()),
117117
MaxSize: extraVolumeConfig.Provisioning().MaxSize().ValueOrZero(),
118118
RelativeMaxSize: extraVolumeConfig.Provisioning().RelativeMaxSize().ValueOrZero(),
119+
NegativeMaxSize: extraVolumeConfig.Provisioning().MaxSizeNegative(),
119120
Grow: extraVolumeConfig.Provisioning().Grow().ValueOr(true),
120121
Label: constants.EphemeralPartitionLabel,
121122
TypeUUID: partition.LinuxFilesystemData,

internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/user_volumes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func UserVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) {
105105
MinSize: cmp.Or(userVolumeConfig.Provisioning().MinSize().ValueOrZero(), MinUserVolumeSize),
106106
MaxSize: userVolumeConfig.Provisioning().MaxSize().ValueOrZero(),
107107
RelativeMaxSize: userVolumeConfig.Provisioning().RelativeMaxSize().ValueOrZero(),
108+
NegativeMaxSize: userVolumeConfig.Provisioning().MaxSizeNegative(),
108109
Grow: userVolumeConfig.Provisioning().Grow().ValueOrZero(),
109110
Label: volumeID,
110111
TypeUUID: partition.LinuxFilesystemData,
@@ -317,6 +318,7 @@ func SwapVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) {
317318
PartitionSpec: block.PartitionSpec{
318319
MaxSize: cmp.Or(swapVolumeConfig.Provisioning().MaxSize().ValueOrZero(), MinUserVolumeSize),
319320
RelativeMaxSize: swapVolumeConfig.Provisioning().RelativeMaxSize().ValueOrZero(),
321+
NegativeMaxSize: swapVolumeConfig.Provisioning().MaxSizeNegative(),
320322
Grow: swapVolumeConfig.Provisioning().Grow().ValueOrZero(),
321323
Label: volumeID,
322324
TypeUUID: partition.LinkSwap,

internal/integration/api/volumes.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,19 @@ func (suite *VolumesSuite) TestUserVolumesPartition() {
524524
return doc
525525
})
526526

527-
configDocs = append(configDocs, xslices.Map(volumeIDs[1:], func(volumeID string) any {
527+
configDocs = append(configDocs, xslices.Map(volumeIDs[1:2], func(volumeID string) any {
528+
doc := blockcfg.NewUserVolumeConfigV1Alpha1()
529+
doc.MetaName = volumeID
530+
doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression(
531+
cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()),
532+
)
533+
doc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB")
534+
doc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("-60%")
535+
536+
return doc
537+
})...)
538+
539+
configDocs = append(configDocs, xslices.Map(volumeIDs[2:], func(volumeID string) any {
528540
doc := blockcfg.NewUserVolumeConfigV1Alpha1()
529541
doc.MetaName = volumeID
530542
doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression(
@@ -536,6 +548,8 @@ func (suite *VolumesSuite) TestUserVolumesPartition() {
536548
return doc
537549
})...)
538550

551+
suite.Require().Equal(len(configDocs), len(volumeIDs))
552+
539553
// create user volumes
540554
suite.PatchMachineConfig(ctx, configDocs...)
541555

pkg/machinery/api/resource/definitions/block/block.pb.go

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/machinery/api/resource/definitions/block/block_vtproto.pb.go

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/machinery/config/config/volume.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type VolumeProvisioningConfig interface {
3333
MinSize() optional.Optional[uint64]
3434
MaxSize() optional.Optional[uint64]
3535
RelativeMaxSize() optional.Optional[uint64]
36+
MaxSizeNegative() bool
3637
}
3738

3839
// WrapVolumesConfigList wraps a list of VolumeConfig providing access by name.
@@ -82,12 +83,12 @@ func (emptyVolumeConfig) MaxSize() optional.Optional[uint64] {
8283
return optional.None[uint64]()
8384
}
8485

85-
func (emptyVolumeConfig) RelativeMinSize() optional.Optional[uint64] {
86+
func (emptyVolumeConfig) RelativeMaxSize() optional.Optional[uint64] {
8687
return optional.None[uint64]()
8788
}
8889

89-
func (emptyVolumeConfig) RelativeMaxSize() optional.Optional[uint64] {
90-
return optional.None[uint64]()
90+
func (emptyVolumeConfig) MaxSizeNegative() bool {
91+
return false
9192
}
9293

9394
// UserVolumeConfig defines the interface to access user volume configuration.

pkg/machinery/config/types/block/byte_size.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package block
66

77
import (
8+
"bytes"
89
"encoding"
910
"fmt"
1011
"slices"
@@ -22,11 +23,12 @@ var (
2223
_ yaml.IsZeroer = ByteSize{}
2324
)
2425

25-
// ByteSize is a byte size which can be convienintly represented as a human readable string
26+
// ByteSize is a byte size which can be conveniently represented as a human readable string
2627
// with IEC sizes, e.g. 100MB.
2728
type ByteSize struct {
28-
value *uint64
29-
raw []byte
29+
value *uint64
30+
raw []byte
31+
negative bool
3032
}
3133

3234
// Value returns the value.
@@ -40,8 +42,13 @@ func (bs ByteSize) MarshalText() ([]byte, error) {
4042
return bs.raw, nil
4143
}
4244

45+
negative := ""
46+
if bs.negative {
47+
negative = "-"
48+
}
49+
4350
if bs.value != nil {
44-
return []byte(strconv.FormatUint(*bs.value, 10)), nil
51+
return []byte(negative + strconv.FormatUint(*bs.value, 10)), nil
4552
}
4653

4754
return nil, nil
@@ -53,13 +60,20 @@ func (bs *ByteSize) UnmarshalText(text []byte) error {
5360
return nil
5461
}
5562

63+
raw := slices.Clone(text)
64+
65+
if v, ok := bytes.CutPrefix(text, []byte("-")); ok {
66+
text = v
67+
bs.negative = true
68+
}
69+
5670
value, err := humanize.ParseBytes(string(text))
5771
if err != nil {
5872
return err
5973
}
6074

6175
bs.value = pointer.To(value)
62-
bs.raw = slices.Clone(text)
76+
bs.raw = raw
6377

6478
return nil
6579
}
@@ -81,3 +95,8 @@ func (bs *ByteSize) Merge(other any) error {
8195

8296
return nil
8397
}
98+
99+
// IsNegative returns true if the value is negative.
100+
func (bs ByteSize) IsNegative() bool {
101+
return bs.negative
102+
}

0 commit comments

Comments
 (0)