Skip to content

Commit bb13a55

Browse files
committed
add support for tmpfs based EPHEMERAL volumes
Signed-off-by: Pranav Patil <pranavppatil767@gmail.com>
1 parent 4d0604b commit bb13a55

19 files changed

Lines changed: 374 additions & 21 deletions

File tree

api/resource/definitions/enums/enums.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ enum BlockVolumeType {
736736
VOLUME_TYPE_SYMLINK = 4;
737737
VOLUME_TYPE_OVERLAY = 5;
738738
VOLUME_TYPE_EXTERNAL = 6;
739+
VOLUME_TYPE_MEMORY = 7;
739740
}
740741

741742
// CriImageCacheStatus describes image cache status type.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
// Close the encrypted volumes.
1919
func Close(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error {
2020
switch volumeContext.Cfg.TypedSpec().Type {
21-
case block.VolumeTypeTmpfs, block.VolumeTypeDirectory, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal:
21+
case block.VolumeTypeTmpfs, block.VolumeTypeDirectory, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal, block.VolumeTypeMemory:
2222
// volume types can be always closed
2323
volumeContext.Status.Phase = block.VolumePhaseClosed
2424

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ func LocateAndProvision(ctx context.Context, logger *zap.Logger, volumeContext M
3535
// volume types above are always ready
3636
volumeContext.Status.Phase = block.VolumePhaseReady
3737

38+
return nil
39+
case block.VolumeTypeMemory:
40+
// memory volumes are always ready, but need size from parameters
41+
for _, param := range volumeContext.Cfg.TypedSpec().Mount.Parameters {
42+
if param.Name == "size" && param.String != nil {
43+
var size uint64
44+
if _, err := fmt.Sscanf(*param.String, "%d", &size); err != nil {
45+
return fmt.Errorf("failed to parse size parameter: %w", err)
46+
}
47+
volumeContext.Status.SetSize(size)
48+
49+
break
50+
}
51+
}
52+
53+
volumeContext.Status.Phase = block.VolumePhaseReady
54+
3855
return nil
3956
case block.VolumeTypeExternal:
4057
// volume types above are always ready, but need some additional parameters set
@@ -59,7 +76,7 @@ func LocateAndProvision(ctx context.Context, logger *zap.Logger, volumeContext M
5976
locatorMatch taloscel.Expression
6077
)
6178

62-
matchContext := map[string]any{}
79+
matchContext := map[string]any{"system_disk": false}
6380

6481
switch {
6582
case !volumeContext.Cfg.TypedSpec().Locator.Match.IsZero():
@@ -77,12 +94,18 @@ func LocateAndProvision(ctx context.Context, logger *zap.Logger, volumeContext M
7794
for _, diskCtx := range volumeContext.Disks {
7895
if dv.ParentDevPath != "" && diskCtx.Disk.DevPath == dv.ParentDevPath {
7996
matchContext["disk"] = diskCtx.Disk
97+
if val, ok := diskCtx.SystemDisk.Get(); ok {
98+
matchContext["system_disk"] = val
99+
}
80100

81101
break
82102
}
83103

84104
if dv.ParentDevPath == "" && diskCtx.Disk.DevPath == dv.DevPath {
85105
matchContext["disk"] = diskCtx.Disk
106+
if val, ok := diskCtx.SystemDisk.Get(); ok {
107+
matchContext["system_disk"] = val
108+
}
86109

87110
break
88111
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"os"
1212
"path/filepath"
1313

14+
"github.com/siderolabs/go-pointer"
15+
1416
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes"
1517
"github.com/siderolabs/talos/internal/pkg/partition"
1618
configconfig "github.com/siderolabs/talos/pkg/machinery/config/config"
@@ -105,6 +107,29 @@ func GetEphemeralVolumeTransformer(inContainer bool) volumeConfigTransformer {
105107
volumeConfigurator = func(vc *block.VolumeConfig) error {
106108
extraVolumeConfig, _ := cfg.Volumes().ByName(constants.EphemeralPartitionLabel)
107109

110+
// Check if memory type is specified
111+
if extraVolumeConfig.Type().ValueOr(block.VolumeTypePartition) == block.VolumeTypeMemory {
112+
minSize := extraVolumeConfig.Provisioning().MinSize().ValueOr(quirks.New("").PartitionSizes().EphemeralMinSize())
113+
114+
return NewBuilder().
115+
WithType(block.VolumeTypeMemory).
116+
WithMount(block.MountSpec{
117+
TargetPath: constants.EphemeralMountPoint,
118+
SelinuxLabel: constants.EphemeralSelinuxLabel,
119+
FileMode: 0o755,
120+
UID: 0,
121+
GID: 0,
122+
Parameters: []block.ParameterSpec{
123+
{
124+
Type: block.FSParameterTypeStringValue,
125+
Name: "size",
126+
String: pointer.To(fmt.Sprintf("%d", minSize)),
127+
},
128+
},
129+
}).
130+
Apply(vc.TypedSpec())
131+
}
132+
108133
return NewBuilder().
109134
WithType(block.VolumeTypePartition).
110135
WithProvisioning(block.ProvisioningSpec{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func UserVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) {
125125
WithConvertEncryptionConfiguration(userVolumeConfig.Encryption()).
126126
WriterFunc()
127127

128-
case block.VolumeTypeTmpfs, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal:
128+
case block.VolumeTypeTmpfs, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal, block.VolumeTypeMemory:
129129
fallthrough
130130

131131
default:

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ type DiskContext struct {
6767
// ToCELContext converts the disk context to CEL contexts.
6868
func (d *DiskContext) ToCELContext() map[string]any {
6969
result := map[string]any{
70-
"disk": d.Disk,
70+
"disk": d.Disk,
71+
"system_disk": false,
7172
}
7273

7374
if val, ok := d.SystemDisk.Get(); ok {

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,34 @@ package volumes_test
77
import (
88
"testing"
99

10+
"github.com/siderolabs/gen/optional"
11+
blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block"
1012
"github.com/stretchr/testify/assert"
1113

1214
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes"
1315
"github.com/siderolabs/talos/pkg/machinery/resources/block"
1416
)
1517

18+
func TestDiskContextToCELContext_SystemDiskDefault(t *testing.T) {
19+
t.Parallel()
20+
21+
ctx := (&volumes.DiskContext{Disk: &blockpb.DiskSpec{DevPath: "/dev/vda"}}).ToCELContext()
22+
23+
val, ok := ctx["system_disk"]
24+
assert.True(t, ok)
25+
assert.Equal(t, false, val)
26+
}
27+
28+
func TestDiskContextToCELContext_SystemDiskExplicit(t *testing.T) {
29+
t.Parallel()
30+
31+
ctx := (&volumes.DiskContext{Disk: &blockpb.DiskSpec{DevPath: "/dev/vda"}, SystemDisk: optional.Some(true)}).ToCELContext()
32+
33+
val, ok := ctx["system_disk"]
34+
assert.True(t, ok)
35+
assert.Equal(t, true, val)
36+
}
37+
1638
func TestCompareVolumeConfigs(t *testing.T) {
1739
t.Parallel()
1840

internal/app/machined/pkg/controllers/block/mount.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,9 @@ func (ctrl *MountController) handleMountOperation(
306306
case block.VolumeTypeTmpfs:
307307
return fmt.Errorf("not implemented yet")
308308

309+
case block.VolumeTypeMemory:
310+
return ctrl.handleMemoryMountOperation(logger, filepath.Join(rootPath, mountTarget), mountRequest, volumeStatus)
311+
309312
case block.VolumeTypeExternal:
310313
return ctrl.handleDiskMountOperation(logger, mountSource, filepath.Join(rootPath, mountTarget), mountFilesystem, mountRequest, volumeStatus)
311314

@@ -432,6 +435,81 @@ func (ctrl *MountController) handleBindMountOperation(
432435
return nil
433436
}
434437

438+
func (ctrl *MountController) handleMemoryMountOperation(
439+
logger *zap.Logger,
440+
mountTarget string,
441+
mountRequest *block.MountRequest,
442+
volumeStatus *block.VolumeStatus,
443+
) error {
444+
_, ok := ctrl.activeMounts[mountRequest.Metadata().ID()]
445+
446+
logger = logger.With(zap.String("mount_request.id", mountRequest.Metadata().ID()))
447+
448+
if !ok {
449+
var sizeOpt string
450+
451+
for _, param := range volumeStatus.TypedSpec().MountSpec.Parameters {
452+
if param.Name == "size" && param.String != nil {
453+
sizeOpt = fmt.Sprintf("size=%s", *param.String)
454+
455+
break
456+
}
457+
}
458+
459+
if sizeOpt == "" {
460+
return fmt.Errorf("memory volume requires size parameter")
461+
}
462+
463+
logger.Info("mounting memory volume",
464+
zap.String("target", mountTarget),
465+
zap.String("size", sizeOpt),
466+
)
467+
468+
// Create the mount point directory if it doesn't exist
469+
if err := os.MkdirAll(mountTarget, volumeStatus.TypedSpec().MountSpec.FileMode); err != nil {
470+
return fmt.Errorf("failed to create target path: %w", err)
471+
}
472+
473+
// Mount tmpfs
474+
if err := unix.Mount("tmpfs", mountTarget, "tmpfs", 0, sizeOpt); err != nil {
475+
return fmt.Errorf("failed to mount tmpfs at %s: %w", mountTarget, err)
476+
}
477+
478+
if volumeStatus.TypedSpec().MountSpec.SelinuxLabel != "" {
479+
if err := selinux.SetLabel(mountTarget, volumeStatus.TypedSpec().MountSpec.SelinuxLabel); err != nil {
480+
unix.Unmount(mountTarget, 0) //nolint:errcheck
481+
482+
return fmt.Errorf("failed to set selinux label: %w", err)
483+
}
484+
}
485+
486+
if !mountRequest.TypedSpec().ReadOnly && !mountRequest.TypedSpec().Detached {
487+
if err := ctrl.updateTargetSettings(mountTarget, volumeStatus.TypedSpec().MountSpec); err != nil {
488+
unix.Unmount(mountTarget, 0) //nolint:errcheck
489+
490+
return fmt.Errorf("failed to update target settings: %w", err)
491+
}
492+
}
493+
494+
logger.Info("memory volume mounted successfully",
495+
zap.String("volume", volumeStatus.Metadata().ID()),
496+
zap.String("target", mountTarget),
497+
)
498+
499+
ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{
500+
point: nil, // No mount.Point for direct unix.Mount
501+
readOnly: mountRequest.TypedSpec().ReadOnly,
502+
unmounter: func() error {
503+
return unix.Unmount(mountTarget, 0)
504+
},
505+
}
506+
507+
return nil
508+
}
509+
510+
return nil
511+
}
512+
435513
//nolint:gocyclo
436514
func (ctrl *MountController) handleSymlinkMountOperation(
437515
logger *zap.Logger,
@@ -778,6 +856,9 @@ func (ctrl *MountController) handleUnmountOperation(
778856
case block.VolumeTypeTmpfs:
779857
return fmt.Errorf("not implemented yet")
780858

859+
case block.VolumeTypeMemory:
860+
return ctrl.handleMemoryUnmountOperation(logger, mountRequest, volumeStatus)
861+
781862
case block.VolumeTypeExternal:
782863
return ctrl.handleDiskUnmountOperation(logger, mountRequest, volumeStatus)
783864

@@ -847,6 +928,34 @@ func (ctrl *MountController) handleDirectoryUnmountOperation(
847928
return nil
848929
}
849930

931+
func (ctrl *MountController) handleMemoryUnmountOperation(
932+
logger *zap.Logger,
933+
mountRequest *block.MountRequest,
934+
volumeStatus *block.VolumeStatus,
935+
) error {
936+
mountCtx, ok := ctrl.activeMounts[mountRequest.Metadata().ID()]
937+
if !ok {
938+
return nil
939+
}
940+
941+
logger.Info("unmounting memory volume",
942+
zap.String("volume", volumeStatus.Metadata().ID()),
943+
zap.String("target", volumeStatus.TypedSpec().MountLocation),
944+
)
945+
946+
if err := mountCtx.unmounter(); err != nil {
947+
return fmt.Errorf("failed to unmount memory volume %q: %w", mountRequest.Metadata().ID(), err)
948+
}
949+
950+
delete(ctrl.activeMounts, mountRequest.Metadata().ID())
951+
952+
logger.Info("memory volume unmounted",
953+
zap.String("volume", volumeStatus.Metadata().ID()),
954+
)
955+
956+
return nil
957+
}
958+
850959
func (ctrl *MountController) handleSymlinkUmountOperation(
851960
mountRequest *block.MountRequest,
852961
) error {

internal/app/machined/pkg/controllers/block/volume_manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ func (ctrl *VolumeManagerController) Run(ctx context.Context, r controller.Runti
413413
block.VolumeTypeDirectory,
414414
block.VolumeTypeOverlay,
415415
block.VolumeTypeSymlink,
416+
block.VolumeTypeMemory,
416417
},
417418
volumeStatus.TypedSpec().Type,
418419
)

internal/app/machined/pkg/controllers/k8s/kubelet_service.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ import (
3535
"github.com/siderolabs/talos/internal/app/machined/pkg/system"
3636
"github.com/siderolabs/talos/internal/app/machined/pkg/system/services"
3737
"github.com/siderolabs/talos/pkg/machinery/constants"
38+
"github.com/siderolabs/talos/pkg/machinery/resources/block"
3839
"github.com/siderolabs/talos/pkg/machinery/resources/files"
3940
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
40-
runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
4141
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
4242
)
4343

@@ -83,8 +83,8 @@ func (ctrl *KubeletServiceController) Run(ctx context.Context, r controller.Runt
8383
Kind: controller.InputWeak,
8484
},
8585
{
86-
Namespace: runtimeres.NamespaceName,
87-
Type: runtimeres.MountStatusType,
86+
Namespace: block.NamespaceName,
87+
Type: block.VolumeMountStatusType,
8888
ID: optional.Some(constants.EphemeralPartitionLabel),
8989
Kind: controller.InputWeak,
9090
},
@@ -108,7 +108,7 @@ func (ctrl *KubeletServiceController) Run(ctx context.Context, r controller.Runt
108108
return fmt.Errorf("error getting etc file status: %w", err)
109109
}
110110

111-
_, err = r.Get(ctx, resource.NewMetadata(runtimeres.NamespaceName, runtimeres.MountStatusType, constants.EphemeralPartitionLabel, resource.VersionUndefined))
111+
_, err = r.Get(ctx, resource.NewMetadata(block.NamespaceName, block.VolumeMountStatusType, constants.EphemeralPartitionLabel, resource.VersionUndefined))
112112
if err != nil {
113113
if state.IsNotFoundError(err) {
114114
// in container mode EPHEMERAL is always mounted

0 commit comments

Comments
 (0)