diff --git a/cmd/installer/pkg/install/extensions.go b/cmd/installer/pkg/install/extensions.go index 541c460d73..14c2dc0f8c 100644 --- a/cmd/installer/pkg/install/extensions.go +++ b/cmd/installer/pkg/install/extensions.go @@ -16,11 +16,12 @@ import ( func (i *Installer) installExtensions() error { builder := extensions.Builder{ - InitramfsPath: fmt.Sprintf(constants.InitramfsAssetPath, i.options.Arch), - Arch: i.options.Arch, - ExtensionTreePath: constants.SystemExtensionsPath, - Printf: log.Printf, - Quirks: quirks.New(i.options.Version), + InitramfsPath: fmt.Sprintf(constants.InitramfsAssetPath, i.options.Arch), + Arch: i.options.Arch, + ExtensionTreePath: constants.SystemExtensionsPath, + ExtensionValidateContents: true, + Printf: log.Printf, + Quirks: quirks.New(i.options.Version), } return builder.Build() diff --git a/pkg/imager/extensions/extensions.go b/pkg/imager/extensions/extensions.go index 95d8a46406..0e42fc4441 100644 --- a/pkg/imager/extensions/extensions.go +++ b/pkg/imager/extensions/extensions.go @@ -24,6 +24,8 @@ type Builder struct { Arch string // ExtensionTreePath is a path to the extracted extension tree. ExtensionTreePath string + // ExtensionValidateContents enables validation of the extension contents. + ExtensionValidateContents bool // Printf is used for logging. Printf func(format string, v ...any) // Quirks for the Talos version being used. @@ -95,8 +97,17 @@ func (builder *Builder) Build() error { func (builder *Builder) validateExtensions(extensions []*extensions.Extension) error { builder.Printf("validating system extensions") + opts := []extinterface.ValidationOption{ + extinterface.WithValidateConstraints(), + extinterface.WithTalosVersion(builder.Quirks.Version()), + } + + if builder.ExtensionValidateContents { + opts = append(opts, extinterface.WithValidateContents()) + } + for _, ext := range extensions { - if err := ext.Validate(); err != nil { + if err := ext.Validate(opts...); err != nil { return fmt.Errorf("error validating extension %q: %w", ext.Manifest.Metadata.Name, err) } } diff --git a/pkg/machinery/extensions/extensions_test.go b/pkg/machinery/extensions/extensions_test.go index 576d0926c5..91e0842573 100644 --- a/pkg/machinery/extensions/extensions_test.go +++ b/pkg/machinery/extensions/extensions_test.go @@ -8,11 +8,11 @@ import ( "path/filepath" "testing" + "github.com/blang/semver/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/extensions" - "github.com/siderolabs/talos/pkg/machinery/version" ) func TestLoadValidate(t *testing.T) { @@ -21,25 +21,19 @@ func TestLoadValidate(t *testing.T) { assert.Equal(t, "gvisor", ext.Manifest.Metadata.Name) - // override Talos version to make it predictable - oldVersion := version.Tag - version.Tag = "v1.0.0" - - t.Cleanup(func() { - version.Tag = oldVersion - }) + version, err := semver.Parse("1.0.0") + require.NoError(t, err) - assert.NoError(t, ext.Validate()) + assert.NoError(t, ext.Validate( + extensions.WithValidateConstraints(), + extensions.WithValidateContents(), + extensions.WithTalosVersion(version), + )) } func TestValidateFailures(t *testing.T) { - // override Talos version to make it predictable - oldVersion := version.Tag - version.Tag = "v1.0.0" - - t.Cleanup(func() { - version.Tag = oldVersion - }) + version, err := semver.Parse("1.0.0") + require.NoError(t, err) for _, tt := range []struct { name string @@ -73,7 +67,12 @@ func TestValidateFailures(t *testing.T) { } if err == nil { - err = ext.Validate() + err = ext.Validate( + extensions.WithValidateConstraints(), + extensions.WithValidateContents(), + extensions.WithTalosVersion(version), + ) + assert.EqualError(t, err, tt.validateError) } }) diff --git a/pkg/machinery/extensions/validate.go b/pkg/machinery/extensions/validate.go index 118b397c9b..d295edcc12 100644 --- a/pkg/machinery/extensions/validate.go +++ b/pkg/machinery/extensions/validate.go @@ -16,24 +16,83 @@ import ( "github.com/siderolabs/talos/pkg/machinery/version" ) -// Validate the extension: compatibility, contents, etc. -func (ext *Extension) Validate() error { - if err := ext.validateConstraints(); err != nil { - return err +// ValidationOptions are used to configure the validation process. +type ValidationOptions struct { + // ValidateContstraints enables validation of the extension constraints. + ValidateContstraints bool + // ValidateContents enables validation of the extension contents. + ValidateContents bool + + // TalosVersion is the version of Talos to validate against. + TalosVersion *semver.Version +} + +// WithValidateConstraints enables validation of the extension constraints. +func WithValidateConstraints() ValidationOption { + return func(o *ValidationOptions) error { + o.ValidateContstraints = true + + return nil + } +} + +// WithValidateContents enables validation of the extension contents. +func WithValidateContents() ValidationOption { + return func(o *ValidationOptions) error { + o.ValidateContents = true + + return nil } +} + +// WithTalosVersion sets the Talos version to validate against. +func WithTalosVersion(version semver.Version) ValidationOption { + return func(o *ValidationOptions) error { + o.TalosVersion = &version - return ext.validateContents() + return nil + } } -func (ext *Extension) validateConstraints() error { - constraint := ext.Manifest.Metadata.Compatibility.Talos.Version +// ValidationOption is a function that configures the validation options. +type ValidationOption func(*ValidationOptions) error - if constraint != "" { - talosVersion, err := semver.ParseTolerant(version.Tag) +// Validate the extension: compatibility, contents, etc. +func (ext *Extension) Validate(opts ...ValidationOption) error { + validationOptions := &ValidationOptions{} + + for _, opt := range opts { + if err := opt(validationOptions); err != nil { + panic(err) + } + } + + if validationOptions.TalosVersion == nil { + version, err := semver.ParseTolerant(version.Tag) if err != nil { return err } + validationOptions.TalosVersion = &version + } + + if validationOptions.ValidateContstraints { + if err := ext.validateConstraints(*validationOptions.TalosVersion); err != nil { + return err + } + } + + if validationOptions.ValidateContents { + return ext.validateContents() + } + + return nil +} + +func (ext *Extension) validateConstraints(talosVersion semver.Version) error { + constraint := ext.Manifest.Metadata.Compatibility.Talos.Version + + if constraint != "" { versionConstraint, err := semver.ParseRange(trim(constraint)) if err != nil { return fmt.Errorf("error parsing Talos version constraint: %w", err) diff --git a/pkg/machinery/imager/quirks/quirks.go b/pkg/machinery/imager/quirks/quirks.go index 03cc3793b9..25425795ec 100644 --- a/pkg/machinery/imager/quirks/quirks.go +++ b/pkg/machinery/imager/quirks/quirks.go @@ -29,6 +29,11 @@ func New(talosVersion string) Quirks { var minVersionResetOption = semver.MustParse("1.4.0") +// Version returns the Talos version. +func (q Quirks) Version() semver.Version { + return *q.v +} + // SupportsResetGRUBOption returns true if the Talos version supports the reset option in GRUB menu (image and ISO). func (q Quirks) SupportsResetGRUBOption() bool { // if the version doesn't parse, we assume it's latest Talos