diff --git a/hack/release.toml b/hack/release.toml index 3340d47d6c..f24ebe6c68 100644 --- a/hack/release.toml +++ b/hack/release.toml @@ -18,11 +18,11 @@ preface = """ [notes.updates] title = "Component Updates" description = """\ -Linux: 6.6.59 -containerd: 2.0.0 -Flannel: 0.26.0 -Kubernetes: 1.32.0-beta.0 -runc: 1.2.1 +* Linux: 6.6.59 +* containerd: 2.0.0 +* Flannel: 0.26.0 +* Kubernetes: 1.32.0-beta.0 +* runc: 1.2.1 Talos is built with Go 1.23.2. """ @@ -68,7 +68,7 @@ This command allows you to view the cgroup resource consumption and limits for a [notes.udevd] title = "udevd" description = """\ -Talos previously used `udevd` to provide `udevd`, now it uses `systemd-udevd` instead. +Talos previously used `eudev` to provide `udevd`, now it uses `systemd-udevd` instead. """ [make_deps] diff --git a/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go b/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go index 6e7c9ca578..163b75bc9a 100644 --- a/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go +++ b/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go @@ -179,16 +179,23 @@ func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfig return nil, status.Error(codes.InvalidArgument, err.Error()) } - warnings, err := cfgProvider.Validate( - modeWrapper{ - Mode: s.Controller.Runtime().State().Platform().Mode(), - installed: s.Controller.Runtime().State().Machine().Installed(), - }, - ) + validationMode := modeWrapper{ + Mode: s.Controller.Runtime().State().Platform().Mode(), + installed: s.Controller.Runtime().State().Machine().Installed(), + } + + warnings, err := cfgProvider.Validate(validationMode) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } + warningsRuntime, err := cfgProvider.RuntimeValidate(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), validationMode) + if err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + warnings = slices.Concat(warnings, warningsRuntime) + //nolint:exhaustive switch in.Mode { // --mode=try diff --git a/internal/app/machined/pkg/controllers/block/volume_manager.go b/internal/app/machined/pkg/controllers/block/volume_manager.go index a1b44d0253..9437163c40 100644 --- a/internal/app/machined/pkg/controllers/block/volume_manager.go +++ b/internal/app/machined/pkg/controllers/block/volume_manager.go @@ -22,6 +22,7 @@ import ( "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" + "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" @@ -164,7 +165,7 @@ func (ctrl *VolumeManagerController) Run(ctx context.Context, r controller.Runti discoveredVolumesSpecs, err := safe.Map(discoveredVolumes, func(dv *block.DiscoveredVolume) (*blockpb.DiscoveredVolumeSpec, error) { spec := &blockpb.DiscoveredVolumeSpec{} - return spec, volumes.ResourceSpecToProto(dv, spec) + return spec, proto.ResourceSpecToProto(dv, spec) }) if err != nil { return fmt.Errorf("error mapping discovered volumes: %w", err) @@ -204,7 +205,7 @@ func (ctrl *VolumeManagerController) Run(ctx context.Context, r controller.Runti diskSpecs, err := safe.Map(disks, func(d *block.Disk) (volumes.DiskContext, error) { spec := &blockpb.DiskSpec{} - if err := volumes.ResourceSpecToProto(d, spec); err != nil { + if err := proto.ResourceSpecToProto(d, spec); err != nil { return volumes.DiskContext{}, err } diff --git a/internal/app/machined/pkg/controllers/config/acquire.go b/internal/app/machined/pkg/controllers/config/acquire.go index a9b4bd9038..9e5aa58e76 100644 --- a/internal/app/machined/pkg/controllers/config/acquire.go +++ b/internal/app/machined/pkg/controllers/config/acquire.go @@ -14,6 +14,7 @@ import ( "io" "net/http" "os" + "slices" "strings" "github.com/cosi-project/runtime/pkg/controller" @@ -68,6 +69,7 @@ type AcquireController struct { EventPublisher talosruntime.Publisher ValidationMode validation.RuntimeMode ConfigPath string + ResourceState state.State configSourcesUsed []string } @@ -345,7 +347,12 @@ func (ctrl *AcquireController) loadFromPlatform(ctx context.Context, logger *zap return nil, fmt.Errorf("failed to validate config acquired via platform %s: %w", platformName, err) } - for _, warning := range warnings { + warningsRuntime, err := cfg.RuntimeValidate(ctx, ctrl.ResourceState, ctrl.ValidationMode) + if err != nil { + return nil, fmt.Errorf("failed to runtime validate config acquired via platform %s: %w", platformName, err) + } + + for _, warning := range slices.Concat(warnings, warningsRuntime) { logger.Warn("config validation warning", zap.String("platform", platformName), zap.String("warning", warning)) } @@ -364,7 +371,7 @@ func (ctrl *AcquireController) stateCmdline(ctx context.Context, r controller.Ru return ctrl.stateMaintenanceEnter, nil, nil } - cfg, err := ctrl.loadFromCmdline(logger) + cfg, err := ctrl.loadFromCmdline(ctx, logger) if err != nil { return nil, nil, err } @@ -386,7 +393,9 @@ func (ctrl *AcquireController) stateCmdline(ctx context.Context, r controller.Ru } // loadFromCmdline is a helper function for stateCmdline. -func (ctrl *AcquireController) loadFromCmdline(logger *zap.Logger) (config.Provider, error) { +// +//nolint:gocyclo +func (ctrl *AcquireController) loadFromCmdline(ctx context.Context, logger *zap.Logger) (config.Provider, error) { cmdline := ctrl.CmdlineGetter() param := cmdline.Get(constants.KernelParamConfigInline) @@ -435,7 +444,12 @@ func (ctrl *AcquireController) loadFromCmdline(logger *zap.Logger) (config.Provi return nil, fmt.Errorf("failed to validate config acquired via cmdline %s: %w", constants.KernelParamConfigInline, err) } - for _, warning := range warnings { + warningsRuntime, err := cfg.RuntimeValidate(ctx, ctrl.ResourceState, ctrl.ValidationMode) + if err != nil { + return nil, fmt.Errorf("failed to validate config acquired via cmdline %s: %w", constants.KernelParamConfigInline, err) + } + + for _, warning := range slices.Concat(warnings, warningsRuntime) { logger.Warn("config validation warning", zap.String("cmdline", constants.KernelParamConfigInline), zap.String("warning", warning)) } diff --git a/internal/app/machined/pkg/controllers/config/acquire_test.go b/internal/app/machined/pkg/controllers/config/acquire_test.go index dface32b5f..9ecf7f73d9 100644 --- a/internal/app/machined/pkg/controllers/config/acquire_test.go +++ b/internal/app/machined/pkg/controllers/config/acquire_test.go @@ -199,6 +199,7 @@ func TestAcquireSuite(t *testing.T) { EventPublisher: s.eventPublisher, ValidationMode: validationModeMock{}, ConfigPath: s.configPath, + ResourceState: s.State(), })) } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go index 38a44f65ae..e3e0d706a8 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go @@ -71,6 +71,7 @@ import ( "github.com/siderolabs/talos/pkg/kubernetes" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config/machine" + "github.com/siderolabs/talos/pkg/machinery/config/types/block/blockhelpers" "github.com/siderolabs/talos/pkg/machinery/constants" metamachinery "github.com/siderolabs/talos/pkg/machinery/meta" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" @@ -2015,9 +2016,27 @@ func Install(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { var disk string - disk, err = r.Config().Machine().Install().Disk() + matchExpr, err := r.Config().Machine().Install().DiskMatchExpression() if err != nil { - return err + return fmt.Errorf("failed to get disk match expression: %w", err) + } + + switch { + case matchExpr != nil: + logger.Printf("using disk match expression: %s", matchExpr) + + matchedDisks, err := blockhelpers.MatchDisks(ctx, r.State().V1Alpha2().Resources(), matchExpr) + if err != nil { + return err + } + + if len(matchedDisks) == 0 { + return fmt.Errorf("no disks matched the expression: %s", matchExpr) + } + + disk = matchedDisks[0].TypedSpec().DevPath + case r.Config().Machine().Install().Disk() != "": + disk = r.Config().Machine().Install().Disk() } disk, err = filepath.EvalSymlinks(disk) @@ -2025,6 +2044,8 @@ func Install(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return err } + logger.Printf("installing Talos to disk %s", disk) + err = install.RunInstallerContainer( disk, r.State().Platform().Name(), diff --git a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go index 561c1a505c..aacc2b9bef 100644 --- a/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go +++ b/internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go @@ -125,6 +125,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error ConfigSetter: ctrl.v1alpha1Runtime, EventPublisher: ctrl.v1alpha1Runtime.Events(), ValidationMode: ctrl.v1alpha1Runtime.State().Platform().Mode(), + ResourceState: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(), }, &config.MachineTypeController{}, &cri.SeccompProfileController{}, diff --git a/internal/app/maintenance/server.go b/internal/app/maintenance/server.go index 2c5c469537..00ade1f2df 100644 --- a/internal/app/maintenance/server.go +++ b/internal/app/maintenance/server.go @@ -10,6 +10,7 @@ import ( "fmt" "io/fs" "log" + "slices" "strings" cosiv1alpha1 "github.com/cosi-project/runtime/api/v1alpha1" @@ -75,7 +76,7 @@ func (s *Server) Register(obj *grpc.Server) { } // ApplyConfiguration implements [machine.MachineServiceServer]. -func (s *Server) ApplyConfiguration(_ context.Context, in *machine.ApplyConfigurationRequest) (*machine.ApplyConfigurationResponse, error) { +func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfigurationRequest) (*machine.ApplyConfigurationResponse, error) { if s.mode.IsAgent() { return nil, status.Error(codes.Unimplemented, "API is not implemented in agent mode") } @@ -102,10 +103,15 @@ func (s *Server) ApplyConfiguration(_ context.Context, in *machine.ApplyConfigur return nil, status.Errorf(codes.InvalidArgument, "configuration validation failed: %s", err) } + warningsRuntime, err := cfgProvider.RuntimeValidate(ctx, s.controller.Runtime().State().V1Alpha2().Resources(), s.controller.Runtime().State().Platform().Mode()) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "runtime configuration validation failed: %s", err) + } + reply := &machine.ApplyConfigurationResponse{ Messages: []*machine.ApplyConfiguration{ { - Warnings: warnings, + Warnings: slices.Concat(warnings, warningsRuntime), }, }, } diff --git a/internal/integration/api/generate-config.go b/internal/integration/api/generate-config.go index c26b2b0a31..fd00359c93 100644 --- a/internal/integration/api/generate-config.go +++ b/internal/integration/api/generate-config.go @@ -95,9 +95,6 @@ func (suite *GenerateConfigSuite) TestGenerate() { suite.Require().NoError(err) - disk, err := config.Machine().Install().Disk() - suite.Require().NoError(err) - suite.Require().EqualValues(request.MachineConfig.Type, config.Machine().Type()) suite.Require().EqualValues(request.ClusterConfig.Name, config.Cluster().Name()) suite.Require().EqualValues(request.ClusterConfig.ControlPlane.Endpoint, config.Cluster().Endpoint().String()) @@ -114,7 +111,7 @@ func (suite *GenerateConfigSuite) TestGenerate() { fmt.Sprintf("%s:v%s", constants.KubeletImage, request.MachineConfig.KubernetesVersion), config.Machine().Kubelet().Image(), ) - suite.Require().EqualValues(request.MachineConfig.InstallConfig.InstallDisk, disk) + suite.Require().EqualValues(request.MachineConfig.InstallConfig.InstallDisk, config.Machine().Install().Disk()) suite.Require().EqualValues(request.MachineConfig.InstallConfig.InstallImage, config.Machine().Install().Image()) suite.Require().EqualValues(request.MachineConfig.NetworkConfig.Hostname, config.Machine().Network().Hostname()) suite.Require().EqualValues(request.MachineConfig.NetworkConfig.Hostname, config.Machine().Network().Hostname()) @@ -149,9 +146,6 @@ func (suite *GenerateConfigSuite) TestGenerate() { suite.Require().NoError(err) - disk, err = config.Machine().Install().Disk() - suite.Require().NoError(err) - suite.Require().EqualValues(request.MachineConfig.Type, joinedConfig.Machine().Type()) suite.Require().EqualValues(request.ClusterConfig.Name, joinedConfig.Cluster().Name()) suite.Require().EqualValues(request.ClusterConfig.ControlPlane.Endpoint, joinedConfig.Cluster().Endpoint().String()) @@ -163,7 +157,7 @@ func (suite *GenerateConfigSuite) TestGenerate() { fmt.Sprintf("%s:v%s", constants.KubeletImage, request.MachineConfig.KubernetesVersion), joinedConfig.Machine().Kubelet().Image(), ) - suite.Require().EqualValues(request.MachineConfig.InstallConfig.InstallDisk, disk) + suite.Require().EqualValues(request.MachineConfig.InstallConfig.InstallDisk, config.Machine().Install().Disk()) suite.Require().EqualValues( request.MachineConfig.InstallConfig.InstallImage, joinedConfig.Machine().Install().Image(), diff --git a/pkg/machinery/cel/build.go b/pkg/machinery/cel/build.go new file mode 100644 index 0000000000..066c3a811d --- /dev/null +++ b/pkg/machinery/cel/build.go @@ -0,0 +1,66 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package cel + +import ( + "fmt" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" +) + +// Builder allows building CEL expressions programmatically. +type Builder struct { + ast.ExprFactory + env *cel.Env + nextID int64 +} + +// NewBuilder creates a new builder. +func NewBuilder(env *cel.Env) *Builder { + return &Builder{ + ExprFactory: ast.NewExprFactory(), + env: env, + } +} + +// NextID returns the next unique ID. +func (b *Builder) NextID() int64 { + b.nextID++ + + return b.nextID +} + +// ToBooleanExpression converts the AST to a boolean expression. +func (b *Builder) ToBooleanExpression(expr ast.Expr) (*Expression, error) { + rawAst := ast.NewAST(expr, nil) + + pbAst, err := ast.ToProto(rawAst) + if err != nil { + return nil, err + } + + celAst, err := cel.CheckedExprToAstWithSource(pbAst, common.NewTextSource("")) + if err != nil { + return nil, err + } + + var issues *cel.Issues + + celAst, issues = b.env.Check(celAst) + if issues != nil && issues.Err() != nil { + return nil, issues.Err() + } + + if outputType := celAst.OutputType(); !outputType.IsExactType(types.BoolType) { + return nil, fmt.Errorf("expression output type is %s, expected bool", outputType) + } + + return &Expression{ + ast: celAst, + }, nil +} diff --git a/pkg/machinery/cel/build_test.go b/pkg/machinery/cel/build_test.go new file mode 100644 index 0000000000..f49b0de446 --- /dev/null +++ b/pkg/machinery/cel/build_test.go @@ -0,0 +1,32 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package cel_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/siderolabs/talos/pkg/machinery/cel" + "github.com/siderolabs/talos/pkg/machinery/cel/celenv" +) + +func TestBuildDiskExpression(t *testing.T) { + t.Parallel() + + builder := cel.NewBuilder(celenv.DiskLocator()) + + expr := builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + "rotational", + ) + + out, err := builder.ToBooleanExpression(expr) + require.NoError(t, err) + + assert.Equal(t, "disk.rotational", out.String()) +} diff --git a/pkg/machinery/cel/celenv/celenv.go b/pkg/machinery/cel/celenv/celenv.go index b63797db49..91cf810a5c 100644 --- a/pkg/machinery/cel/celenv/celenv.go +++ b/pkg/machinery/cel/celenv/celenv.go @@ -11,6 +11,8 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/ryanuber/go-glob" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" @@ -36,6 +38,13 @@ var DiskLocator = sync.OnceValue(func() *cel.Env { cel.Types(&diskSpec), cel.Variable("disk", cel.ObjectType(string(diskSpec.ProtoReflect().Descriptor().FullName()))), cel.Variable("system_disk", types.BoolType), + cel.Function("glob", // glob(pattern, string) + cel.Overload("glob_string_string", []*cel.Type{cel.StringType, cel.StringType}, cel.BoolType, + cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val { + return types.Bool(glob.Glob(string(arg1.(types.String)), string(arg2.(types.String)))) + }), + ), + ), }, celUnitMultipliersConstants(), )..., diff --git a/pkg/machinery/cel/celenv/celenv_test.go b/pkg/machinery/cel/celenv/celenv_test.go index 0c3069e8fa..e921f5139a 100644 --- a/pkg/machinery/cel/celenv/celenv_test.go +++ b/pkg/machinery/cel/celenv/celenv_test.go @@ -30,6 +30,10 @@ func TestDiskLocator(t *testing.T) { name: "disk size", expression: "disk.size > 1000u * GiB && !disk.rotational", }, + { + name: "glob", + expression: "glob('sd[a-z]', disk.dev_path)", + }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() diff --git a/pkg/machinery/config/config/machine.go b/pkg/machinery/config/config/machine.go index 5fb5dfb08f..8a220a2987 100644 --- a/pkg/machinery/config/config/machine.go +++ b/pkg/machinery/config/config/machine.go @@ -13,6 +13,7 @@ import ( "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/crypto/x509" + "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) @@ -91,7 +92,8 @@ type File interface { type Install interface { Image() string Extensions() []Extension - Disk() (string, error) + Disk() string + DiskMatchExpression() (*cel.Expression, error) ExtraKernelArgs() []string Zero() bool LegacyBIOSSupport() bool diff --git a/pkg/machinery/config/config/validate.go b/pkg/machinery/config/config/validate.go index bd244b5cfb..6fcc8808cd 100644 --- a/pkg/machinery/config/config/validate.go +++ b/pkg/machinery/config/config/validate.go @@ -4,7 +4,13 @@ package config -import "github.com/siderolabs/talos/pkg/machinery/config/validation" +import ( + "context" + + "github.com/cosi-project/runtime/pkg/state" + + "github.com/siderolabs/talos/pkg/machinery/config/validation" +) // Validator is the interface to validate configuration. // @@ -13,3 +19,15 @@ type Validator interface { // Validate checks configuration and returns warnings and fatal errors (as multierror). Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) } + +// RuntimeValidator is the interface to validate configuration in the runtime context. +// +// This interface is used by Talos itself to validate configuration on the machine (vs. the Validator interface). +// +// The errors reported by Validator & RuntimeValidator are different. +type RuntimeValidator interface { + // RuntimeValidate validates the config in the runtime context. + // + // The method returns warnings and fatal errors (as multierror). + RuntimeValidate(context.Context, state.State, validation.RuntimeMode, ...validation.Option) ([]string, error) +} diff --git a/pkg/machinery/config/configloader/configloader_test.go b/pkg/machinery/config/configloader/configloader_test.go index b93b846a35..d570e93045 100644 --- a/pkg/machinery/config/configloader/configloader_test.go +++ b/pkg/machinery/config/configloader/configloader_test.go @@ -21,7 +21,7 @@ import ( func callMethods(t testing.TB, obj reflect.Value, chain ...string) { t.Helper() - if obj.Kind() == reflect.Interface && obj.IsNil() { + if (obj.Kind() == reflect.Interface || obj.Kind() == reflect.Pointer) && obj.IsNil() { return } diff --git a/pkg/machinery/config/container/container.go b/pkg/machinery/config/container/container.go index d537fc4c77..55e8eb5e60 100644 --- a/pkg/machinery/config/container/container.go +++ b/pkg/machinery/config/container/container.go @@ -7,10 +7,12 @@ package container import ( "bytes" + "context" "errors" "fmt" "strings" + "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/xslices" @@ -305,6 +307,35 @@ func (container *Container) Validate(mode validation.RuntimeMode, opt ...validat return warnings, multiErr.ErrorOrNil() } +// RuntimeValidate validates the config in the runtime context. +func (container *Container) RuntimeValidate(ctx context.Context, st state.State, mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) { + var ( + warnings []string + err error + ) + + if container.v1alpha1Config != nil { + warnings, err = container.v1alpha1Config.RuntimeValidate(ctx, st, mode, opt...) + } + + var multiErr *multierror.Error + + if err != nil { + multiErr = multierror.Append(multiErr, err) + } + + for _, doc := range container.documents { + if validatableDoc, ok := doc.(config.RuntimeValidator); ok { + docWarnings, docErr := validatableDoc.RuntimeValidate(ctx, st, mode, opt...) + + warnings = append(warnings, docWarnings...) + multiErr = multierror.Append(multiErr, docErr) + } + } + + return warnings, multiErr.ErrorOrNil() +} + // RedactSecrets returns a copy of the Provider with all secrets replaced with the given string. func (container *Container) RedactSecrets(replacement string) coreconfig.Provider { clone := container.clone() diff --git a/pkg/machinery/config/provider.go b/pkg/machinery/config/provider.go index d198bdcdbd..1264c6c1dc 100644 --- a/pkg/machinery/config/provider.go +++ b/pkg/machinery/config/provider.go @@ -15,6 +15,9 @@ type Encoder = config.Encoder // Validator provides the interface to validate configuration. type Validator = config.Validator +// RuntimeValidator provides the interface to validate configuration in the runtime context. +type RuntimeValidator = config.RuntimeValidator + // Container provides the interface to access configuration documents. // // Container might contain multiple config documents, supporting encoding/decoding, @@ -22,6 +25,7 @@ type Validator = config.Validator type Container interface { Encoder Validator + RuntimeValidator Readonly() bool diff --git a/pkg/machinery/config/schemas/config.schema.json b/pkg/machinery/config/schemas/config.schema.json index a65352f266..97c0c0a11e 100644 --- a/pkg/machinery/config/schemas/config.schema.json +++ b/pkg/machinery/config/schemas/config.schema.json @@ -2245,9 +2245,9 @@ "busPath": { "type": "string", "title": "busPath", - "description": "Disk bus path.\nWarning: This requires special configuration for NVMe drives. For details, see https://github.com/siderolabs/go-blockdevice/issues/114.\n", - "markdownDescription": "Disk bus path.\nWarning: This requires special configuration for NVMe drives. For details, see https://github.com/siderolabs/go-blockdevice/issues/114.", - "x-intellij-html-description": "\u003cp\u003eDisk bus path.\nWarning: This requires special configuration for NVMe drives. For details, see \u003ca href=\"https://github.com/siderolabs/go-blockdevice/issues/114\" target=\"_blank\"\u003ehttps://github.com/siderolabs/go-blockdevice/issues/114\u003c/a\u003e.\u003c/p\u003e\n" + "description": "Disk bus path.\n", + "markdownDescription": "Disk bus path.", + "x-intellij-html-description": "\u003cp\u003eDisk bus path.\u003c/p\u003e\n" } }, "additionalProperties": false, diff --git a/pkg/machinery/config/types/block/blockhelpers/blockhelpers.go b/pkg/machinery/config/types/block/blockhelpers/blockhelpers.go new file mode 100644 index 0000000000..553a8836bc --- /dev/null +++ b/pkg/machinery/config/types/block/blockhelpers/blockhelpers.go @@ -0,0 +1,51 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package blockhelpers provides helper functions for working with block resources. +package blockhelpers + +import ( + "context" + + "github.com/cosi-project/runtime/pkg/safe" + "github.com/cosi-project/runtime/pkg/state" + + blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" + "github.com/siderolabs/talos/pkg/machinery/cel" + "github.com/siderolabs/talos/pkg/machinery/cel/celenv" + "github.com/siderolabs/talos/pkg/machinery/proto" + "github.com/siderolabs/talos/pkg/machinery/resources/block" +) + +// MatchDisks returns a list of disks that match the given expression. +func MatchDisks(ctx context.Context, st state.State, expression *cel.Expression) ([]*block.Disk, error) { + disks, err := safe.StateListAll[*block.Disk](ctx, st) + if err != nil { + return nil, err + } + + var matchedDisks []*block.Disk + + for disk := range disks.All() { + spec := &blockpb.DiskSpec{} + + if err = proto.ResourceSpecToProto(disk, spec); err != nil { + return nil, err + } + + matches, err := expression.EvalBool(celenv.DiskLocator(), map[string]any{ + "disk": spec, + "system_disk": false, + }) + if err != nil { + return nil, err + } + + if matches { + matchedDisks = append(matchedDisks, disk) + } + } + + return matchedDisks, nil +} diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_marshal_test.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_marshal_test.go index 9aa8f0d46e..8a4f5ee326 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_marshal_test.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_marshal_test.go @@ -8,8 +8,6 @@ import ( "fmt" "testing" - humanize "github.com/dustin/go-humanize" - "github.com/siderolabs/go-blockdevice/blockdevice/util/disk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" yaml "gopkg.in/yaml.v3" @@ -125,25 +123,11 @@ func TestDiskSizeMatcherUnmarshal(t *testing.T) { match: false, }, } { - var ( - size uint64 - err error - ) - - if test.size != "" { - size, err = humanize.ParseBytes(test.size) - require.NoError(t, err) - } - - err = yaml.Unmarshal([]byte(fmt.Sprintf("m: '%s'\n", test.condition)), &obj) + err := yaml.Unmarshal([]byte(fmt.Sprintf("m: '%s'\n", test.condition)), &obj) if test.err { require.Error(t, err) } else { require.NoError(t, err) } - - if test.size != "" { - require.Equal(t, obj.M.Matcher(&disk.Disk{Size: size}), test.match, test.size) - } } } diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go index f9406aa0e7..860340d8f8 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go @@ -7,20 +7,23 @@ package v1alpha1 import ( "crypto/tls" stdx509 "crypto/x509" - "errors" "fmt" "os" "slices" "strings" "time" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/operators" + "github.com/google/cel-go/common/types" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" - "github.com/siderolabs/go-blockdevice/blockdevice/util/disk" "github.com/siderolabs/go-blockdevice/v2/encryption" "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" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" @@ -1217,72 +1220,174 @@ func (i *InstallConfig) Extensions() []config.Extension { } // Disk implements the config.Provider interface. -func (i *InstallConfig) Disk() (string, error) { - matchers := i.DiskMatchers() - if len(matchers) > 0 { - d, err := disk.Find(matchers...) - if err != nil { - return "", err - } - - if d != nil { - return d.DeviceName, nil - } - - return "", errors.New("no disk found matching provided parameters") - } - - return i.InstallDisk, nil +func (i *InstallConfig) Disk() string { + return i.InstallDisk } -// DiskMatchers implements the config.Provider interface. +// DiskMatchExpression returns the disk matcher expression by inspecting the InstallDiskSelector. // //nolint:gocyclo -func (i *InstallConfig) DiskMatchers() []disk.Matcher { - if i.InstallDiskSelector != nil { - selector := i.InstallDiskSelector - - var matchers []disk.Matcher - if selector.Size != nil { - matchers = append(matchers, selector.Size.Matcher) - } - - if selector.UUID != "" { - matchers = append(matchers, disk.WithUUID(selector.UUID)) - } - - if selector.WWID != "" { - matchers = append(matchers, disk.WithWWID(selector.WWID)) - } - - if selector.Model != "" { - matchers = append(matchers, disk.WithModel(selector.Model)) - } - - if selector.Name != "" { - matchers = append(matchers, disk.WithName(selector.Name)) - } +func (i *InstallConfig) DiskMatchExpression() (*cel.Expression, error) { + if i.InstallDiskSelector == nil { + return nil, nil + } - if selector.Serial != "" { - matchers = append(matchers, disk.WithSerial(selector.Serial)) - } + var exprs []ast.Expr - if selector.Modalias != "" { - matchers = append(matchers, disk.WithModalias(selector.Modalias)) - } + builder := cel.NewBuilder(celenv.DiskLocator()) + selector := i.InstallDiskSelector - if disk.Type(selector.Type) != disk.TypeUnknown { - matchers = append(matchers, disk.WithType(disk.Type(selector.Type))) + if selector.Size != nil { + op := selector.Size.MatchData.Op + if op == "" { + op = "==" } - if selector.BusPath != "" { - matchers = append(matchers, disk.WithBusPath(selector.BusPath)) + exprs = append(exprs, // disk.size op value + builder.NewCall( + builder.NextID(), + "_"+op+"_", + builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + "size", + ), + builder.NewLiteral( + builder.NextID(), + types.Uint(selector.Size.MatchData.Size), + ), + ), + ) + } + + patternMatcherExpr := func(pattern, field string) ast.Expr { // glob(pattern, disk.$field) + return builder.NewCall( + builder.NextID(), + "glob", + builder.NewLiteral(builder.NextID(), types.String(pattern)), + builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + field, + ), + ) + } + + directMatchExpr := func(value, field string) ast.Expr { // disk.$field == value + return builder.NewCall( + builder.NextID(), + operators.Equals, + builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + field, + ), + builder.NewLiteral(builder.NextID(), types.String(value)), + ) + } + + if selector.UUID != "" { + // not supported + return nil, fmt.Errorf("selector on uuid is not supported") + } + + if selector.WWID != "" { + exprs = append(exprs, patternMatcherExpr(selector.WWID, "wwid")) + } + + if selector.Model != "" { + exprs = append(exprs, patternMatcherExpr(selector.Model, "model")) + } + + if selector.Name != "" { + // not supported + return nil, fmt.Errorf("selector on name is not supported") + } + + if selector.Serial != "" { + exprs = append(exprs, patternMatcherExpr(selector.Serial, "serial")) + } + + if selector.Modalias != "" { + exprs = append(exprs, patternMatcherExpr(selector.Modalias, "modalias")) + } + + if selector.Type != "" { + switch selector.Type { + case "nvme": // disk.transport == "nvme" + exprs = append(exprs, directMatchExpr("nvme", "transport")) + case "sd": // disk.transport == "mmc" + exprs = append(exprs, directMatchExpr("mmc", "transport")) + case "hdd": // disk.rotational + exprs = append(exprs, builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + "rotational", + )) + case "ssd": // disk.transport != "" && !disk.rotational + exprs = append(exprs, + builder.NewCall( + builder.NextID(), + operators.NotEquals, + builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + "transport", + ), + builder.NewLiteral(builder.NextID(), types.String("")), + ), + builder.NewCall( + builder.NextID(), + operators.LogicalNot, + builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + "rotational", + ), + ), + ) + default: + return nil, fmt.Errorf("unsupported disk type %q", selector.Type) } - - return matchers } - return nil + if selector.BusPath != "" { + exprs = append(exprs, patternMatcherExpr(selector.BusPath, "buspath")) + } + + // exclude readonly disks: !disk.readonly + exprs = append(exprs, builder.NewCall( + builder.NextID(), + operators.LogicalNot, + builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + "readonly", + ), + )) + + // exclude CD-ROMs: !disk.cdrom + exprs = append(exprs, builder.NewCall( + builder.NextID(), + operators.LogicalNot, + builder.NewSelect( + builder.NextID(), + builder.NewIdent(builder.NextID(), "disk"), + "cdrom", + ), + )) + + // reduce all expressions to a single one with && + for len(exprs) > 1 { + exprs = append(exprs[:len(exprs)-2], builder.NewCall( + builder.NextID(), + operators.LogicalAnd, + exprs[len(exprs)-2], + exprs[len(exprs)-1], + )) + } + + return builder.ToBooleanExpression(exprs[0]) } // ExtraKernelArgs implements the config.Provider interface. diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider_test.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider_test.go new file mode 100644 index 0000000000..7328613c04 --- /dev/null +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider_test.go @@ -0,0 +1,93 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package v1alpha1_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" +) + +func TestInstallDiskSelector(t *testing.T) { + t.Parallel() + + for _, test := range []struct { + name string + + selector v1alpha1.InstallDiskSelector + + expected string + }{ + { + name: "size", + + selector: v1alpha1.InstallDiskSelector{ + Size: &v1alpha1.InstallDiskSizeMatcher{ + MatchData: v1alpha1.InstallDiskSizeMatchData{ + Op: "<=", + Size: 256 * 1024, + }, + }, + }, + + expected: `disk.size <= 262144u && !disk.readonly && !disk.cdrom`, + }, + { + name: "size and type", + + selector: v1alpha1.InstallDiskSelector{ + Size: &v1alpha1.InstallDiskSizeMatcher{ + MatchData: v1alpha1.InstallDiskSizeMatchData{ + Size: 1024 * 1024, + }, + }, + Type: v1alpha1.InstallDiskType("nvme"), + }, + + expected: `disk.size == 1048576u && disk.transport == "nvme" && !disk.readonly && !disk.cdrom`, + }, + { + name: "size and type and modalias", + + selector: v1alpha1.InstallDiskSelector{ + Size: &v1alpha1.InstallDiskSizeMatcher{ + MatchData: v1alpha1.InstallDiskSizeMatchData{ + Size: 1024 * 1024, + }, + }, + Type: v1alpha1.InstallDiskType("hdd"), + Modalias: "pci:1234:5678*", + }, + + expected: `disk.size == 1048576u && glob("pci:1234:5678*", disk.modalias) && disk.rotational && +!disk.readonly && !disk.cdrom`, + }, + { + name: "ssd", + + selector: v1alpha1.InstallDiskSelector{ + Type: v1alpha1.InstallDiskType("ssd"), + }, + + expected: `disk.transport != "" && !disk.rotational && !disk.readonly && !disk.cdrom`, + }, + } { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + installCfg := &v1alpha1.InstallConfig{ + InstallDiskSelector: &test.selector, + } + + expr, err := installCfg.DiskMatchExpression() + require.NoError(t, err) + + assert.Equal(t, test.expected, expr.String()) + }) + } +} diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go index 0834a6a908..9d07311d09 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go @@ -30,7 +30,6 @@ import ( "github.com/dustin/go-humanize" "github.com/siderolabs/crypto/x509" - "github.com/siderolabs/go-blockdevice/blockdevice/util/disk" "gopkg.in/yaml.v3" "github.com/siderolabs/talos/pkg/machinery/config/config" @@ -879,11 +878,6 @@ func (m *InstallDiskSizeMatcher) UnmarshalYAML(unmarshal func(any) error) error return nil } -// Matcher is a method that can handle some custom disk matching logic. -func (m *InstallDiskSizeMatcher) Matcher(d *disk.Disk) bool { - return m.MatchData.Compare(d) -} - // InstallDiskSizeMatchData contains data for comparison - Op and Size. // //docgen:nodoc @@ -892,53 +886,8 @@ type InstallDiskSizeMatchData struct { Size uint64 } -// Compare is the method to compare disk size. -func (in *InstallDiskSizeMatchData) Compare(d *disk.Disk) bool { - switch in.Op { - case ">=": - return d.Size >= in.Size - case "<=": - return d.Size <= in.Size - case ">": - return d.Size > in.Size - case "<": - return d.Size < in.Size - case "": - fallthrough - case "==": - return d.Size == in.Size - default: - return false - } -} - // InstallDiskType custom type for disk type selector. -type InstallDiskType disk.Type - -// MarshalYAML is a custom marshaller for `InstallDiskSizeMatcher`. -func (it InstallDiskType) MarshalYAML() (any, error) { - return disk.Type(it).String(), nil -} - -// UnmarshalYAML is a custom unmarshaler for `InstallDiskType`. -func (it *InstallDiskType) UnmarshalYAML(unmarshal func(any) error) error { - var ( - t string - err error - ) - - if err = unmarshal(&t); err != nil { - return err - } - - if dt, err := disk.ParseType(t); err == nil { - *it = InstallDiskType(dt) - } else { - return err - } - - return nil -} +type InstallDiskType string // InstallDiskSelector represents a disk query parameters for the install disk lookup. type InstallDiskSelector struct { @@ -974,7 +923,6 @@ type InstallDiskSelector struct { Type InstallDiskType `yaml:"type,omitempty"` // description: | // Disk bus path. - // Warning: This requires special configuration for NVMe drives. For details, see https://github.com/siderolabs/go-blockdevice/issues/114. // examples: // - value: '"/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0"' // - value: '"/pci0000:00/*"' diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go index c2e97e798f..42e1385e00 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go @@ -1150,7 +1150,7 @@ func (InstallDiskSelector) Doc() *encoder.Doc { Name: "busPath", Type: "string", Note: "", - Description: "Disk bus path.\nWarning: This requires special configuration for NVMe drives. For details, see https://github.com/siderolabs/go-blockdevice/issues/114.", + Description: "Disk bus path.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk bus path." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go index b27cfd5eac..3a58b6e508 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go @@ -5,23 +5,25 @@ package v1alpha1 import ( + "context" "encoding/base64" "errors" "fmt" "net" "net/url" - "os" "reflect" "regexp" "strconv" "strings" "sync" + "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" sideronet "github.com/siderolabs/net" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" + "github.com/siderolabs/talos/pkg/machinery/config/types/block/blockhelpers" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/kubelet" @@ -94,24 +96,13 @@ func (c *Config) Validate(mode validation.RuntimeMode, options ...validation.Opt if c.MachineConfig.MachineInstall == nil { result = multierror.Append(result, fmt.Errorf("install instructions are required in %q mode", mode)) } else { - if opts.Local { - if c.MachineConfig.MachineInstall.InstallDisk == "" && len(c.MachineConfig.MachineInstall.DiskMatchers()) == 0 { - result = multierror.Append(result, errors.New("either install disk or diskSelector should be defined")) - } - } else { - disk, err := c.MachineConfig.MachineInstall.Disk() + matcher, err := c.MachineConfig.MachineInstall.DiskMatchExpression() + if err != nil { + result = multierror.Append(result, fmt.Errorf("install disk selector is invalid: %w", err)) + } - if err != nil { - result = multierror.Append(result, err) - } else { - if disk == "" { - result = multierror.Append(result, fmt.Errorf("an install disk is required in %q mode", mode)) - } - - if _, err := os.Stat(disk); os.IsNotExist(err) { - result = multierror.Append(result, fmt.Errorf("specified install disk does not exist: %q", disk)) - } - } + if c.MachineConfig.MachineInstall.InstallDisk == "" && matcher == nil { + result = multierror.Append(result, errors.New("either install disk or diskSelector should be defined")) } } } @@ -916,3 +907,33 @@ func (e *EtcdConfig) Validate() error { return result.ErrorOrNil() } + +// RuntimeValidate validates the config in runtime context. +// +// In runtime context, resource state is available. +func (c *Config) RuntimeValidate(ctx context.Context, st state.State, mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) { + var ( + warnings []string + result *multierror.Error + ) + + if c.MachineConfig != nil { + if mode.RequiresInstall() && c.MachineConfig.MachineInstall != nil { + diskExpr, err := c.MachineConfig.MachineInstall.DiskMatchExpression() + if err != nil { + result = multierror.Append(result, fmt.Errorf("install disk selector is invalid: %w", err)) + } else if diskExpr != nil { + matchedDisks, err := blockhelpers.MatchDisks(ctx, st, diskExpr) + if err != nil { + result = multierror.Append(result, err) + } + + if len(matchedDisks) == 0 { + result = multierror.Append(result, fmt.Errorf("no disks matched the expression: %s", diskExpr)) + } + } + } + } + + return warnings, result.ErrorOrNil() +} diff --git a/pkg/machinery/go.mod b/pkg/machinery/go.mod index 34115618cf..aac650bdeb 100644 --- a/pkg/machinery/go.mod +++ b/pkg/machinery/go.mod @@ -22,11 +22,11 @@ require ( github.com/mdlayher/ethtool v0.2.0 github.com/opencontainers/runtime-spec v1.2.0 github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 + github.com/ryanuber/go-glob v1.0.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/siderolabs/crypto v0.5.0 github.com/siderolabs/gen v0.7.0 github.com/siderolabs/go-api-signature v0.3.6 - github.com/siderolabs/go-blockdevice v0.4.8 github.com/siderolabs/go-blockdevice/v2 v2.0.2 github.com/siderolabs/go-pointer v1.0.0 github.com/siderolabs/net v0.4.0 @@ -63,7 +63,6 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.28.0 // indirect diff --git a/pkg/machinery/go.sum b/pkg/machinery/go.sum index 085d709c1d..e0c53b1aab 100644 --- a/pkg/machinery/go.sum +++ b/pkg/machinery/go.sum @@ -109,8 +109,6 @@ github.com/siderolabs/gen v0.7.0 h1:uHAt3WD0dof28NHFuguWBbDokaXQraR/HyVxCLw2QCU= github.com/siderolabs/gen v0.7.0/go.mod h1:an3a2Y53O7kUjnnK8Bfu3gewtvnIOu5RTU6HalFtXQQ= github.com/siderolabs/go-api-signature v0.3.6 h1:wDIsXbpl7Oa/FXvxB6uz4VL9INA9fmr3EbmjEZYFJrU= github.com/siderolabs/go-api-signature v0.3.6/go.mod h1:hoH13AfunHflxbXfh+NoploqV13ZTDfQ1mQJWNVSW9U= -github.com/siderolabs/go-blockdevice v0.4.8 h1:KfdWvIx0Jft5YVuCsFIJFwjWEF1oqtzkgX9PeU9cX4c= -github.com/siderolabs/go-blockdevice v0.4.8/go.mod h1:4PeOuk71pReJj1JQEXDE7kIIQJPVe8a+HZQa+qjxSEA= github.com/siderolabs/go-blockdevice/v2 v2.0.2 h1:GIdOBrCLQ7X9jbr0P/+7paw5SIfp/LL+dx9mTOzmw8w= github.com/siderolabs/go-blockdevice/v2 v2.0.2/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w= github.com/siderolabs/go-pointer v1.0.0 h1:6TshPKep2doDQJAAtHUuHWXbca8ZfyRySjSBT/4GsMU= diff --git a/internal/app/machined/pkg/controllers/block/internal/volumes/proto.go b/pkg/machinery/proto/resource.go similarity index 71% rename from internal/app/machined/pkg/controllers/block/internal/volumes/proto.go rename to pkg/machinery/proto/resource.go index 610d19099c..8ea399fa2b 100644 --- a/internal/app/machined/pkg/controllers/block/internal/volumes/proto.go +++ b/pkg/machinery/proto/resource.go @@ -2,21 +2,20 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package volumes +// Package proto defines a functions to work with proto messages. +package proto import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/siderolabs/protoenc" - - "github.com/siderolabs/talos/pkg/machinery/proto" ) // ResourceSpecToProto converts a resource spec to a proto message. -func ResourceSpecToProto(i resource.Resource, o proto.Message) error { +func ResourceSpecToProto(i resource.Resource, o Message) error { marshaled, err := protoenc.Marshal(i.Spec()) if err != nil { return err } - return proto.Unmarshal(marshaled, o) + return Unmarshal(marshaled, o) } diff --git a/website/content/v1.9/reference/configuration/v1alpha1/config.md b/website/content/v1.9/reference/configuration/v1alpha1/config.md index 72f134184c..57ea9f9b38 100644 --- a/website/content/v1.9/reference/configuration/v1alpha1/config.md +++ b/website/content/v1.9/reference/configuration/v1alpha1/config.md @@ -1976,7 +1976,7 @@ size: <= 2TB |`uuid` |string |Disk UUID `/sys/block//uuid`. | | |`wwid` |string |Disk WWID `/sys/block//wwid`. | | |`type` |InstallDiskType |Disk Type. |`ssd`
`hdd`
`nvme`
`sd`
| -|`busPath` |string |
Disk bus path.Warning: This requires special configuration for NVMe drives. For details, see https://github.com/siderolabs/go-blockdevice/issues/114.
Show example(s){{< highlight yaml >}} +|`busPath` |string |Disk bus path.
Show example(s){{< highlight yaml >}} busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0 {{< /highlight >}}{{< highlight yaml >}} busPath: /pci0000:00/* diff --git a/website/content/v1.9/schemas/config.schema.json b/website/content/v1.9/schemas/config.schema.json index a65352f266..97c0c0a11e 100644 --- a/website/content/v1.9/schemas/config.schema.json +++ b/website/content/v1.9/schemas/config.schema.json @@ -2245,9 +2245,9 @@ "busPath": { "type": "string", "title": "busPath", - "description": "Disk bus path.\nWarning: This requires special configuration for NVMe drives. For details, see https://github.com/siderolabs/go-blockdevice/issues/114.\n", - "markdownDescription": "Disk bus path.\nWarning: This requires special configuration for NVMe drives. For details, see https://github.com/siderolabs/go-blockdevice/issues/114.", - "x-intellij-html-description": "\u003cp\u003eDisk bus path.\nWarning: This requires special configuration for NVMe drives. For details, see \u003ca href=\"https://github.com/siderolabs/go-blockdevice/issues/114\" target=\"_blank\"\u003ehttps://github.com/siderolabs/go-blockdevice/issues/114\u003c/a\u003e.\u003c/p\u003e\n" + "description": "Disk bus path.\n", + "markdownDescription": "Disk bus path.", + "x-intellij-html-description": "\u003cp\u003eDisk bus path.\u003c/p\u003e\n" } }, "additionalProperties": false,