diff --git a/internal/app/machined/pkg/controllers/block/user_volume_config.go b/internal/app/machined/pkg/controllers/block/user_volume_config.go
index 64aba1bcd..2c36736df 100644
--- a/internal/app/machined/pkg/controllers/block/user_volume_config.go
+++ b/internal/app/machined/pkg/controllers/block/user_volume_config.go
@@ -275,12 +275,13 @@ func (ctrl *UserVolumeConfigController) handleUserVolumeConfig(
},
}
v.TypedSpec().Mount = block.MountSpec{
- TargetPath: userVolumeConfig.Name(),
- ParentID: constants.UserVolumeMountPoint,
- SelinuxLabel: constants.EphemeralSelinuxLabel,
- FileMode: 0o755,
- UID: 0,
- GID: 0,
+ TargetPath: userVolumeConfig.Name(),
+ ParentID: constants.UserVolumeMountPoint,
+ SelinuxLabel: constants.EphemeralSelinuxLabel,
+ FileMode: 0o755,
+ UID: 0,
+ GID: 0,
+ ProjectQuotaSupport: userVolumeConfig.Filesystem().ProjectQuotaSupport(),
}
if err := convertEncryptionConfiguration(userVolumeConfig.Encryption(), v.TypedSpec()); err != nil {
diff --git a/internal/integration/api/volumes.go b/internal/integration/api/volumes.go
index cf35a15db..4e0c32f0d 100644
--- a/internal/integration/api/volumes.go
+++ b/internal/integration/api/volumes.go
@@ -23,6 +23,7 @@ import (
"github.com/cosi-project/runtime/pkg/state"
"github.com/google/uuid"
"github.com/siderolabs/gen/xslices"
+ "github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/assert"
"github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers"
@@ -598,7 +599,8 @@ func (suite *VolumesSuite) TestUserVolumes() {
// wait for the discovered volume to disappear
rtestutils.AssertNoResource[*block.DiscoveredVolume](ctx, suite.T(), suite.Client.COSI, filepath.Base(vs.TypedSpec().Location))
- // re-create the volume
+ // re-create the volume with project quota support
+ configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).FilesystemSpec.ProjectQuotaSupportConfig = pointer.To(true)
suite.PatchMachineConfig(ctx, configDocs[0])
rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs,
@@ -608,7 +610,15 @@ func (suite *VolumesSuite) TestUserVolumes() {
)
rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs,
- func(vs *block.MountStatus, _ *assert.Assertions) {})
+ func(vs *block.MountStatus, asrt *assert.Assertions) {
+ if vs.Metadata().ID() == userVolumeIDs[0] {
+ // check that the project quota support is enabled
+ asrt.True(vs.TypedSpec().ProjectQuotaSupport, "project quota support should be enabled for %s", vs.Metadata().ID())
+ } else {
+ // check that the project quota support is disabled
+ asrt.False(vs.TypedSpec().ProjectQuotaSupport, "project quota support should be disabled for %s", vs.Metadata().ID())
+ }
+ })
// clean up
suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeIDs...)
diff --git a/pkg/machinery/config/config/volume.go b/pkg/machinery/config/config/volume.go
index e222bc06e..44d788c69 100644
--- a/pkg/machinery/config/config/volume.go
+++ b/pkg/machinery/config/config/volume.go
@@ -89,6 +89,8 @@ type UserVolumeConfig interface {
type FilesystemConfig interface {
// Type returns the filesystem type.
Type() block.FilesystemType
+ // ProjectQuotaSupport returns true if the filesystem should support project quotas.
+ ProjectQuotaSupport() bool
}
// SwapVolumeConfig defines the interface to access swap volume configuration.
diff --git a/pkg/machinery/config/schemas/config.schema.json b/pkg/machinery/config/schemas/config.schema.json
index 3d259d8d7..6a2f28249 100644
--- a/pkg/machinery/config/schemas/config.schema.json
+++ b/pkg/machinery/config/schemas/config.schema.json
@@ -179,6 +179,13 @@
"description": "Filesystem type. Default is xfs.\n",
"markdownDescription": "Filesystem type. Default is `xfs`.",
"x-intellij-html-description": "\u003cp\u003eFilesystem type. Default is \u003ccode\u003exfs\u003c/code\u003e.\u003c/p\u003e\n"
+ },
+ "projectQuotaSupport": {
+ "type": "boolean",
+ "title": "projectQuotaSupport",
+ "description": "Enables project quota support, valid only for ‘xfs’ filesystem.\n\nNote: changing this value might require a full remount of the filesystem.\n",
+ "markdownDescription": "Enables project quota support, valid only for 'xfs' filesystem.\n\nNote: changing this value might require a full remount of the filesystem.",
+ "x-intellij-html-description": "\u003cp\u003eEnables project quota support, valid only for \u0026lsquo;xfs\u0026rsquo; filesystem.\u003c/p\u003e\n\n\u003cp\u003eNote: changing this value might require a full remount of the filesystem.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
diff --git a/pkg/machinery/config/types/block/block_doc.go b/pkg/machinery/config/types/block/block_doc.go
index e1055d232..5fa517ffb 100644
--- a/pkg/machinery/config/types/block/block_doc.go
+++ b/pkg/machinery/config/types/block/block_doc.go
@@ -342,6 +342,13 @@ func (FilesystemSpec) Doc() *encoder.Doc {
"xfs",
},
},
+ {
+ Name: "projectQuotaSupport",
+ Type: "bool",
+ Note: "",
+ Description: "Enables project quota support, valid only for 'xfs' filesystem.\n\nNote: changing this value might require a full remount of the filesystem.",
+ Comments: [3]string{"" /* encoder.HeadComment */, "Enables project quota support, valid only for 'xfs' filesystem." /* encoder.LineComment */, "" /* encoder.FootComment */},
+ },
},
}
diff --git a/pkg/machinery/config/types/block/deep_copy.generated.go b/pkg/machinery/config/types/block/deep_copy.generated.go
index 47ad4e53e..f82539de2 100644
--- a/pkg/machinery/config/types/block/deep_copy.generated.go
+++ b/pkg/machinery/config/types/block/deep_copy.generated.go
@@ -85,6 +85,10 @@ func (o *UserVolumeConfigV1Alpha1) DeepCopy() *UserVolumeConfigV1Alpha1 {
cp.ProvisioningSpec.ProvisioningMaxSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.raw))
copy(cp.ProvisioningSpec.ProvisioningMaxSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.raw)
}
+ if o.FilesystemSpec.ProjectQuotaSupportConfig != nil {
+ cp.FilesystemSpec.ProjectQuotaSupportConfig = new(bool)
+ *cp.FilesystemSpec.ProjectQuotaSupportConfig = *o.FilesystemSpec.ProjectQuotaSupportConfig
+ }
if o.EncryptionSpec.EncryptionKeys != nil {
cp.EncryptionSpec.EncryptionKeys = make([]EncryptionKey, len(o.EncryptionSpec.EncryptionKeys))
copy(cp.EncryptionSpec.EncryptionKeys, o.EncryptionSpec.EncryptionKeys)
diff --git a/pkg/machinery/config/types/block/testdata/uservolumeconfig_prjquota.yaml b/pkg/machinery/config/types/block/testdata/uservolumeconfig_prjquota.yaml
new file mode 100644
index 000000000..a7ab35ae7
--- /dev/null
+++ b/pkg/machinery/config/types/block/testdata/uservolumeconfig_prjquota.yaml
@@ -0,0 +1,10 @@
+apiVersion: v1alpha1
+kind: UserVolumeConfig
+name: secret-store
+provisioning:
+ diskSelector:
+ match: '!system_disk'
+ minSize: 10GiB
+filesystem:
+ type: xfs
+ projectQuotaSupport: true
diff --git a/pkg/machinery/config/types/block/user_volume_config.go b/pkg/machinery/config/types/block/user_volume_config.go
index 7797aca98..8907e508b 100644
--- a/pkg/machinery/config/types/block/user_volume_config.go
+++ b/pkg/machinery/config/types/block/user_volume_config.go
@@ -11,6 +11,8 @@ import (
"fmt"
"strings"
+ "github.com/siderolabs/go-pointer"
+
"github.com/siderolabs/talos/pkg/machinery/cel"
"github.com/siderolabs/talos/pkg/machinery/cel/celenv"
"github.com/siderolabs/talos/pkg/machinery/config/config"
@@ -206,6 +208,11 @@ type FilesystemSpec struct {
// - ext4
// - xfs
FilesystemType block.FilesystemType `yaml:"type,omitempty"`
+ // description: |
+ // Enables project quota support, valid only for 'xfs' filesystem.
+ //
+ // Note: changing this value might require a full remount of the filesystem.
+ ProjectQuotaSupportConfig *bool `yaml:"projectQuotaSupport,omitempty"`
}
// Type implements config.FilesystemConfig interface.
@@ -217,6 +224,11 @@ func (s FilesystemSpec) Type() block.FilesystemType {
return s.FilesystemType
}
+// ProjectQuotaSupport implements config.FilesysteemConfig interface.
+func (s FilesystemSpec) ProjectQuotaSupport() bool {
+ return pointer.SafeDeref(s.ProjectQuotaSupportConfig)
+}
+
// Validate implements config.Validator interface.
func (s FilesystemSpec) Validate() ([]string, error) {
switch s.FilesystemType { //nolint:exhaustive
@@ -227,5 +239,9 @@ func (s FilesystemSpec) Validate() ([]string, error) {
return nil, fmt.Errorf("unsupported filesystem type: %s", s.FilesystemType)
}
+ if pointer.SafeDeref(s.ProjectQuotaSupportConfig) && s.Type() != block.FilesystemTypeXFS {
+ return nil, fmt.Errorf("project quota support is only available for xfs filesystem")
+ }
+
return nil, nil
}
diff --git a/pkg/machinery/config/types/block/user_volume_config_test.go b/pkg/machinery/config/types/block/user_volume_config_test.go
index e706316d9..a212e82be 100644
--- a/pkg/machinery/config/types/block/user_volume_config_test.go
+++ b/pkg/machinery/config/types/block/user_volume_config_test.go
@@ -11,6 +11,7 @@ import (
"strings"
"testing"
+ "github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -69,6 +70,21 @@ func TestUserVolumeConfigMarshalUnmarshal(t *testing.T) {
},
}
+ return c
+ },
+ },
+ {
+ name: "prjquota",
+ filename: "uservolumeconfig_prjquota.yaml",
+ cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 {
+ c := block.NewUserVolumeConfigV1Alpha1()
+ c.MetaName = "secret-store"
+
+ require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`)))
+ c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
+ c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeXFS
+ c.FilesystemSpec.ProjectQuotaSupportConfig = pointer.To(true)
+
return c
},
},
@@ -269,6 +285,23 @@ func TestUserVolumeConfigValidate(t *testing.T) {
expectedErrors: "at least one encryption key type must be specified for slot 0\nduplicate key slot 1",
},
+ {
+ name: "prjquota not supported",
+
+ cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 {
+ c := block.NewUserVolumeConfigV1Alpha1()
+ c.MetaName = constants.EphemeralPartitionLabel
+
+ require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`)))
+ c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
+ c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeEXT4
+ c.FilesystemSpec.ProjectQuotaSupportConfig = pointer.To(true)
+
+ return c
+ },
+
+ expectedErrors: "project quota support is only available for xfs filesystem",
+ },
{
name: "valid",
diff --git a/website/content/v1.11/reference/configuration/block/uservolumeconfig.md b/website/content/v1.11/reference/configuration/block/uservolumeconfig.md
index e5a3c42c9..5da3e9ddc 100644
--- a/website/content/v1.11/reference/configuration/block/uservolumeconfig.md
+++ b/website/content/v1.11/reference/configuration/block/uservolumeconfig.md
@@ -130,6 +130,7 @@ FilesystemSpec configures the filesystem for the volume.
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`type` |FilesystemType |Filesystem type. Default is `xfs`. |`ext4`
`xfs`
|
+|`projectQuotaSupport` |bool |Enables project quota support, valid only for 'xfs' filesystem.
Note: changing this value might require a full remount of the filesystem. | |
diff --git a/website/content/v1.11/schemas/config.schema.json b/website/content/v1.11/schemas/config.schema.json
index 3d259d8d7..6a2f28249 100644
--- a/website/content/v1.11/schemas/config.schema.json
+++ b/website/content/v1.11/schemas/config.schema.json
@@ -179,6 +179,13 @@
"description": "Filesystem type. Default is xfs.\n",
"markdownDescription": "Filesystem type. Default is `xfs`.",
"x-intellij-html-description": "\u003cp\u003eFilesystem type. Default is \u003ccode\u003exfs\u003c/code\u003e.\u003c/p\u003e\n"
+ },
+ "projectQuotaSupport": {
+ "type": "boolean",
+ "title": "projectQuotaSupport",
+ "description": "Enables project quota support, valid only for ‘xfs’ filesystem.\n\nNote: changing this value might require a full remount of the filesystem.\n",
+ "markdownDescription": "Enables project quota support, valid only for 'xfs' filesystem.\n\nNote: changing this value might require a full remount of the filesystem.",
+ "x-intellij-html-description": "\u003cp\u003eEnables project quota support, valid only for \u0026lsquo;xfs\u0026rsquo; filesystem.\u003c/p\u003e\n\n\u003cp\u003eNote: changing this value might require a full remount of the filesystem.\u003c/p\u003e\n"
}
},
"additionalProperties": false,