Artem Chernyshev 0cdb5a58c8
feat: support raw bytes in the inline fields for manifests/patches
Now inline supports all three variants:
- a single inline map (backward compatibility for config patches).
- a list of inline maps
- raw bytes, that can also contain multiple documents.

`omnictl cluster template export` command was updated to export config
patches/manifests as raw bytes to ensure that multiple values are
properly supported.

Fixes: https://github.com/siderolabs/omni/issues/2683

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
2026-04-30 19:07:46 +03:00

182 lines
4.5 KiB
Go

// 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 models
import (
"fmt"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/google/uuid"
"github.com/hashicorp/go-multierror"
"github.com/siderolabs/gen/pair"
"github.com/siderolabs/omni/client/pkg/constants"
"github.com/siderolabs/omni/client/pkg/omni/resources/omni"
)
// MachineIDList is a list of Machine UUIDs.
type MachineIDList []MachineID
// MachineID is a Machine UUID.
type MachineID string
// KindMachine is a Machine model kind.
const KindMachine = "Machine"
// Machine provides customization for a specific machine.
type Machine struct { //nolint:govet
Meta `yaml:",inline"`
SystemExtensions `yaml:",inline"`
// Machine name (ID).
Name MachineID `yaml:"name"`
// Descriptors are the user descriptors to apply to the cluster.
Descriptors Descriptors `yaml:",inline"`
// Locked locks the machine, so no config updates, upgrades and downgrades will be performed on the machine.
Locked bool `yaml:"locked,omitempty"`
// Install specification.
Install MachineInstall `yaml:"install,omitempty"`
// ClusterMachine patches.
Patches PatchList `yaml:"patches,omitempty"`
// KernelArgs are the additional kernel arguments.
KernelArgs KernelArgs `yaml:",inline"`
}
// MachineInstall provides machine install configuration.
type MachineInstall struct {
// Disk device name.
Disk string `yaml:"disk"`
}
// Validate the list of machines.
func (l MachineIDList) Validate() error {
var multiErr error
for _, id := range l {
multiErr = joinErrors(multiErr, id.Validate())
}
return multiErr
}
// Validate the model.
func (id MachineID) Validate() error {
if _, err := uuid.Parse(string(id)); err != nil {
return fmt.Errorf("invalid machine ID %q: %w", id, err)
}
return nil
}
// Validate the model.
func (install *MachineInstall) Validate() error {
return nil
}
// Validate the model.
func (machine *Machine) Validate(opts ValidateOptions) error {
var multiErr error
if machine.Name == "" {
multiErr = multierror.Append(multiErr, fmt.Errorf("name is required for machine"))
}
if err := machine.Descriptors.Validate(); err != nil {
multiErr = multierror.Append(multiErr, err)
}
multiErr = joinErrors(multiErr, machine.Name.Validate(), machine.Install.Validate(), machine.Patches.Validate(opts))
if multiErr != nil {
return fmt.Errorf("machine %q is invalid: %w", machine.Name, multiErr)
}
return nil
}
// Translate the model.
func (machine *Machine) Translate(ctx TranslateContext) ([]resource.Resource, error) {
var resourceList []resource.Resource
if machine.Install.Disk != "" {
patch := Patch{
Name: "install-disk",
Inline: NewInlineContent(map[string]any{
"machine": map[string]any{
"install": map[string]any{
"disk": machine.Install.Disk,
},
},
}),
}
patchResource, err := patch.Translate(
ctx,
fmt.Sprintf("cm-%s", machine.Name),
constants.PatchWeightInstallDisk,
pair.MakePair(omni.LabelCluster, ctx.ClusterName),
pair.MakePair(omni.LabelClusterMachine, string(machine.Name)),
pair.MakePair(omni.LabelSystemPatch, ""),
)
if err != nil {
return nil, err
}
resourceList = append(resourceList, patchResource)
}
patches, err := machine.Patches.Translate(
ctx,
fmt.Sprintf("cm-%s", machine.Name),
constants.PatchBaseWeightClusterMachine,
pair.MakePair(omni.LabelCluster, ctx.ClusterName),
pair.MakePair(omni.LabelClusterMachine, string(machine.Name)),
)
if err != nil {
return nil, err
}
resourceList = append(resourceList, patches...)
schematicConfigurations := machine.translate(
ctx,
string(machine.Name),
pair.MakePair(omni.LabelClusterMachine, string(machine.Name)),
)
res, defined := machine.translateKernelArgs(ctx)
if defined {
resourceList = append(resourceList, res)
}
return append(resourceList, schematicConfigurations...), nil
}
func (machine *Machine) translateKernelArgs(ctx TranslateContext) (res resource.Resource, defined bool) {
if args, ok := machine.KernelArgs.Get(); ok {
return buildKernelArgsResource(machine.Name, args), true
}
msKernelArgs := ctx.MachineSetLevelKernelArgs[machine.Name]
if args, ok := msKernelArgs.Get(); ok {
return buildKernelArgsResource(machine.Name, args), true
}
if args, ok := ctx.ClusterLevelKernelArgs.Get(); ok {
return buildKernelArgsResource(machine.Name, args), true
}
return nil, false
}
func init() {
register[Machine](KindMachine)
}