feat: udev rules support
Brings udev rules as first class citizens in the machine config Allows for customizing how udev devices are presented in the system. My case in particular, I need to change the group and permissions for the Intel QuickSync renderer device to enable non-root containers to make use of the hardware transcoding capabilities of the Intel GPU. Signed-off-by: Branden Cash <ammmze@gmail.com> Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
parent
5237fdc957
commit
41299cae99
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
||||
bin
|
||||
_out
|
||||
.vscode
|
||||
.idea
|
||||
*.code-workspace
|
||||
init.yaml
|
||||
controlplane.yaml
|
||||
|
@ -232,6 +232,9 @@ func (*Sequencer) Boot(r runtime.Runtime) []runtime.Phase {
|
||||
r.State().Platform().Mode() != runtime.ModeContainer,
|
||||
"overlay",
|
||||
MountOverlayFilesystems,
|
||||
).Append(
|
||||
"udevSetup",
|
||||
WriteUdevRules,
|
||||
).AppendWhen(
|
||||
r.State().Platform().Mode() != runtime.ModeContainer,
|
||||
"udevd",
|
||||
|
@ -589,6 +589,30 @@ func StartContainerd(seq runtime.Sequence, data interface{}) (runtime.TaskExecut
|
||||
}, "startContainerd"
|
||||
}
|
||||
|
||||
// WriteUdevRules is the task that writes udev rules to a udev rules file.
|
||||
func WriteUdevRules(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
|
||||
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
|
||||
rules := r.Config().Machine().Udev().Rules()
|
||||
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var content strings.Builder
|
||||
|
||||
for _, rule := range rules {
|
||||
content.WriteString(strings.ReplaceAll(rule, "\n", "\\\n"))
|
||||
content.WriteByte('\n')
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(constants.UdevRulesPath, []byte(content.String()), 0o644); err != nil {
|
||||
return fmt.Errorf("failed writing custom udev rules: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, "writeUdevRules"
|
||||
}
|
||||
|
||||
// StartUdevd represents the task to start udevd.
|
||||
func StartUdevd(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
|
||||
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
|
||||
|
@ -49,6 +49,7 @@ type MachineConfig interface {
|
||||
Registries() Registries
|
||||
SystemDiskEncryption() SystemDiskEncryption
|
||||
Features() Features
|
||||
Udev() UdevConfig
|
||||
}
|
||||
|
||||
// Disk represents the options available for partitioning, formatting, and
|
||||
@ -478,3 +479,8 @@ type ServiceRegistry interface {
|
||||
Enabled() bool
|
||||
Endpoint() string
|
||||
}
|
||||
|
||||
// UdevConfig describes configuration for udev.
|
||||
type UdevConfig interface {
|
||||
Rules() []string
|
||||
}
|
||||
|
@ -285,6 +285,15 @@ func (m *MachineConfig) Features() config.Features {
|
||||
return m.MachineFeatures
|
||||
}
|
||||
|
||||
// Udev implements the config.MachineConfig interface.
|
||||
func (m *MachineConfig) Udev() config.UdevConfig {
|
||||
if m.MachineUdev == nil {
|
||||
return &UdevConfig{}
|
||||
}
|
||||
|
||||
return m.MachineUdev
|
||||
}
|
||||
|
||||
// Image implements the config.Provider interface.
|
||||
func (k *KubeletConfig) Image() string {
|
||||
image := k.KubeletImage
|
||||
@ -1202,3 +1211,8 @@ func (v VolumeMountConfig) Name() string {
|
||||
func (v VolumeMountConfig) ReadOnly() bool {
|
||||
return v.VolumeReadOnly
|
||||
}
|
||||
|
||||
// Rules implements config.Udev interface.
|
||||
func (u *UdevConfig) Rules() []string {
|
||||
return u.UdevRules
|
||||
}
|
||||
|
@ -250,6 +250,10 @@ var (
|
||||
RBAC: pointer.ToBool(true),
|
||||
}
|
||||
|
||||
machineUdevExample = &UdevConfig{
|
||||
UdevRules: []string{"SUBSYSTEM==\"drm\", KERNEL==\"renderD*\", GROUP=\"44\", MODE=\"0660\""},
|
||||
}
|
||||
|
||||
clusterConfigExample = struct {
|
||||
ControlPlane *ControlPlaneConfig `yaml:"controlPlane"`
|
||||
ClusterName string `yaml:"clusterName"`
|
||||
@ -651,6 +655,11 @@ type MachineConfig struct {
|
||||
// examples:
|
||||
// - value: machineFeaturesExample
|
||||
MachineFeatures *FeaturesConfig `yaml:"features,omitempty"`
|
||||
// description: |
|
||||
// Configures the udev system.
|
||||
// examples:
|
||||
// - value: machineUdevExample
|
||||
MachineUdev *UdevConfig `yaml:"udev,omitempty"`
|
||||
}
|
||||
|
||||
// ClusterConfig represents the cluster-wide config values.
|
||||
@ -2006,3 +2015,10 @@ type RegistryServiceConfig struct {
|
||||
// - value: constants.DefaultDiscoveryServiceEndpoint
|
||||
RegistryEndpoint string `yaml:"endpoint,omitempty"`
|
||||
}
|
||||
|
||||
// UdevConfig describes how the udev system should be configured.
|
||||
type UdevConfig struct {
|
||||
// description: |
|
||||
// List of udev rules to apply to the udev system
|
||||
UdevRules []string `yaml:"rules,omitempty"`
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ var (
|
||||
DiscoveryRegistriesConfigDoc encoder.Doc
|
||||
RegistryKubernetesConfigDoc encoder.Doc
|
||||
RegistryServiceConfigDoc encoder.Doc
|
||||
UdevConfigDoc encoder.Doc
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -128,7 +129,7 @@ func init() {
|
||||
FieldName: "machine",
|
||||
},
|
||||
}
|
||||
MachineConfigDoc.Fields = make([]encoder.Doc, 15)
|
||||
MachineConfigDoc.Fields = make([]encoder.Doc, 16)
|
||||
MachineConfigDoc.Fields[0].Name = "type"
|
||||
MachineConfigDoc.Fields[0].Type = "string"
|
||||
MachineConfigDoc.Fields[0].Note = ""
|
||||
@ -248,6 +249,13 @@ func init() {
|
||||
MachineConfigDoc.Fields[14].Comments[encoder.LineComment] = "Features describe individual Talos features that can be switched on or off."
|
||||
|
||||
MachineConfigDoc.Fields[14].AddExample("", machineFeaturesExample)
|
||||
MachineConfigDoc.Fields[15].Name = "udev"
|
||||
MachineConfigDoc.Fields[15].Type = "UdevConfig"
|
||||
MachineConfigDoc.Fields[15].Note = ""
|
||||
MachineConfigDoc.Fields[15].Description = "Configures the udev system."
|
||||
MachineConfigDoc.Fields[15].Comments[encoder.LineComment] = "Configures the udev system."
|
||||
|
||||
MachineConfigDoc.Fields[15].AddExample("", machineUdevExample)
|
||||
|
||||
ClusterConfigDoc.Type = "ClusterConfig"
|
||||
ClusterConfigDoc.Comments[encoder.LineComment] = "ClusterConfig represents the cluster-wide config values."
|
||||
@ -2136,6 +2144,24 @@ func init() {
|
||||
RegistryServiceConfigDoc.Fields[1].Comments[encoder.LineComment] = "External service endpoint."
|
||||
|
||||
RegistryServiceConfigDoc.Fields[1].AddExample("", constants.DefaultDiscoveryServiceEndpoint)
|
||||
|
||||
UdevConfigDoc.Type = "UdevConfig"
|
||||
UdevConfigDoc.Comments[encoder.LineComment] = "UdevConfig describes how the udev system should be configured."
|
||||
UdevConfigDoc.Description = "UdevConfig describes how the udev system should be configured."
|
||||
|
||||
UdevConfigDoc.AddExample("", machineUdevExample)
|
||||
UdevConfigDoc.AppearsIn = []encoder.Appearance{
|
||||
{
|
||||
TypeName: "MachineConfig",
|
||||
FieldName: "udev",
|
||||
},
|
||||
}
|
||||
UdevConfigDoc.Fields = make([]encoder.Doc, 1)
|
||||
UdevConfigDoc.Fields[0].Name = "rules"
|
||||
UdevConfigDoc.Fields[0].Type = "[]string"
|
||||
UdevConfigDoc.Fields[0].Note = ""
|
||||
UdevConfigDoc.Fields[0].Description = "List of udev rules to apply to the udev system"
|
||||
UdevConfigDoc.Fields[0].Comments[encoder.LineComment] = "List of udev rules to apply to the udev system"
|
||||
}
|
||||
|
||||
func (_ Config) Doc() *encoder.Doc {
|
||||
@ -2358,6 +2384,10 @@ func (_ RegistryServiceConfig) Doc() *encoder.Doc {
|
||||
return &RegistryServiceConfigDoc
|
||||
}
|
||||
|
||||
func (_ UdevConfig) Doc() *encoder.Doc {
|
||||
return &UdevConfigDoc
|
||||
}
|
||||
|
||||
// GetConfigurationDoc returns documentation for the file ./v1alpha1_types_doc.go.
|
||||
func GetConfigurationDoc() *encoder.FileDoc {
|
||||
return &encoder.FileDoc{
|
||||
@ -2419,6 +2449,7 @@ func GetConfigurationDoc() *encoder.FileDoc {
|
||||
&DiscoveryRegistriesConfigDoc,
|
||||
&RegistryKubernetesConfigDoc,
|
||||
&RegistryServiceConfigDoc,
|
||||
&UdevConfigDoc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -974,6 +974,11 @@ func (in *MachineConfig) DeepCopyInto(out *MachineConfig) {
|
||||
*out = new(FeaturesConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.MachineUdev != nil {
|
||||
in, out := &in.MachineUdev, &out.MachineUdev
|
||||
*out = new(UdevConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -1386,6 +1391,27 @@ func (in *TimeConfig) DeepCopy() *TimeConfig {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *UdevConfig) DeepCopyInto(out *UdevConfig) {
|
||||
*out = *in
|
||||
if in.UdevRules != nil {
|
||||
in, out := &in.UdevRules, &out.UdevRules
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UdevConfig.
|
||||
func (in *UdevConfig) DeepCopy() *UdevConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(UdevConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VIPEquinixMetalConfig) DeepCopyInto(out *VIPEquinixMetalConfig) {
|
||||
*out = *in
|
||||
|
@ -512,6 +512,9 @@ const (
|
||||
|
||||
// KubeSpanLinkName is the link name for the KubeSpan Wireguard interface.
|
||||
KubeSpanLinkName = "kubespan"
|
||||
|
||||
// UdevRulesPath rules file path.
|
||||
UdevRulesPath = "/usr/etc/udev/rules.d/99-talos.rules"
|
||||
)
|
||||
|
||||
// See https://linux.die.net/man/3/klogctl
|
||||
|
@ -735,6 +735,31 @@ features:
|
||||
```
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div class="dd">
|
||||
|
||||
<code>udev</code> <i><a href="#udevconfig">UdevConfig</a></i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Configures the udev system.
|
||||
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
``` yaml
|
||||
udev:
|
||||
# List of udev rules to apply to the udev system
|
||||
rules:
|
||||
- SUBSYSTEM=="drm", KERNEL=="renderD*", GROUP="44", MODE="0660"
|
||||
```
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
@ -5531,3 +5556,34 @@ endpoint: https://discovery.talos.dev/
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
## UdevConfig
|
||||
UdevConfig describes how the udev system should be configured.
|
||||
|
||||
Appears in:
|
||||
|
||||
- <code><a href="#machineconfig">MachineConfig</a>.udev</code>
|
||||
|
||||
|
||||
``` yaml
|
||||
# List of udev rules to apply to the udev system
|
||||
rules:
|
||||
- SUBSYSTEM=="drm", KERNEL=="renderD*", GROUP="44", MODE="0660"
|
||||
```
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>rules</code> <i>[]string</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
List of udev rules to apply to the udev system
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user