mirror of
https://github.com/siderolabs/omni.git
synced 2025-08-08 02:27:00 +02:00
Some checks are pending
default / default (push) Waiting to run
default / e2e-backups (push) Blocked by required conditions
default / e2e-forced-removal (push) Blocked by required conditions
default / e2e-scaling (push) Blocked by required conditions
default / e2e-short (push) Blocked by required conditions
default / e2e-short-secureboot (push) Blocked by required conditions
default / e2e-templates (push) Blocked by required conditions
default / e2e-upgrades (push) Blocked by required conditions
default / e2e-workload-proxy (push) Blocked by required conditions
When a machine is connected to SideroLink over the GRPC tunnel in maintenance mode, its tunnel config gets overwritten when a machine config is applied to it. Instead, check the link to determine the tunnel setting and preserve it on allocation. Since we are not explicit about this setting and not always leave it to the Omni-wide configuration, rework the Omni-wide configuration to - allow opt-in if it is disabled instance-wide - do not allow opt-out if it is enabled instance-wide This allows us to preserve the current behavior of "forcing" the machines without explicit configuration to use the system default. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
169 lines
5.3 KiB
Go
169 lines
5.3 KiB
Go
// Copyright (c) 2025 Sidero Labs, Inc.
|
|
//
|
|
// Use of this software is governed by the Business Source License
|
|
// included in the LICENSE file.
|
|
|
|
package grpc
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/cosi-project/runtime/pkg/safe"
|
|
"github.com/siderolabs/image-factory/pkg/client"
|
|
"github.com/siderolabs/image-factory/pkg/schematic"
|
|
"github.com/siderolabs/talos/pkg/machinery/imager/quirks"
|
|
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"github.com/siderolabs/omni/client/api/omni/management"
|
|
"github.com/siderolabs/omni/client/pkg/meta"
|
|
"github.com/siderolabs/omni/client/pkg/omni/resources"
|
|
"github.com/siderolabs/omni/client/pkg/omni/resources/omni"
|
|
"github.com/siderolabs/omni/client/pkg/omni/resources/siderolink"
|
|
"github.com/siderolabs/omni/internal/pkg/auth"
|
|
"github.com/siderolabs/omni/internal/pkg/auth/role"
|
|
"github.com/siderolabs/omni/internal/pkg/config"
|
|
)
|
|
|
|
// CreateSchematic implements ManagementServer.
|
|
func (s *managementServer) CreateSchematic(ctx context.Context, request *management.CreateSchematicRequest) (*management.CreateSchematicResponse, error) {
|
|
// creating a schematic is equivalent to creating a machine
|
|
if _, err := auth.CheckGRPC(ctx, auth.WithRole(role.Operator)); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
baseKernelArgs, tunnelEnabled, err := s.getBaseKernelArgs(ctx, request.SiderolinkGrpcTunnelMode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
customization := schematic.Customization{
|
|
ExtraKernelArgs: append(baseKernelArgs, request.ExtraKernelArgs...),
|
|
SystemExtensions: schematic.SystemExtensions{
|
|
OfficialExtensions: request.Extensions,
|
|
},
|
|
}
|
|
|
|
for key, value := range request.MetaValues {
|
|
if !meta.CanSetMetaKey(int(key)) {
|
|
return nil, status.Errorf(codes.InvalidArgument, "meta key %s is not allowed to be set in the schematic, as it's reserved by Talos", runtime.MetaKeyTagToID(uint8(key)))
|
|
}
|
|
|
|
customization.Meta = append(customization.Meta, schematic.MetaValue{
|
|
Key: uint8(key),
|
|
Value: value,
|
|
})
|
|
|
|
// validate that labels meta has the correct format
|
|
if key == meta.LabelsMeta {
|
|
var labels *meta.ImageLabels
|
|
|
|
labels, err = meta.ParseLabels([]byte(value))
|
|
if err != nil {
|
|
return nil, status.Errorf(codes.InvalidArgument, "failed to parse machine labels: %s", err)
|
|
}
|
|
|
|
if labels.Labels == nil {
|
|
return nil, status.Errorf(codes.InvalidArgument, "machine labels are empty")
|
|
}
|
|
|
|
if labels.LegacyLabels != nil {
|
|
return nil, status.Errorf(codes.InvalidArgument, "'machineInitialLabels' is deprecated")
|
|
}
|
|
}
|
|
}
|
|
|
|
slices.SortFunc(customization.Meta, func(a, b schematic.MetaValue) int {
|
|
switch {
|
|
case a.Key < b.Key:
|
|
return 1
|
|
case a.Key > b.Key:
|
|
return -1
|
|
}
|
|
|
|
return 0
|
|
})
|
|
|
|
schematicRequest := schematic.Schematic{
|
|
Customization: customization,
|
|
}
|
|
|
|
media, err := safe.ReaderGetByID[*omni.InstallationMedia](ctx, s.omniState, request.MediaId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
supportsOverlays := quirks.New(request.TalosVersion).SupportsOverlay()
|
|
|
|
if media.TypedSpec().Value.Overlay != "" && supportsOverlays {
|
|
var overlays []client.OverlayInfo
|
|
|
|
overlays, err = s.imageFactoryClient.Client.OverlaysVersions(ctx, request.TalosVersion)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get overlays list for the Talos version: %w", err)
|
|
}
|
|
|
|
index := slices.IndexFunc(overlays, func(value client.OverlayInfo) bool {
|
|
return value.Name == media.TypedSpec().Value.Overlay
|
|
})
|
|
|
|
if index == -1 {
|
|
return nil, fmt.Errorf("failed to find overlay with name %q", media.TypedSpec().Value.Overlay)
|
|
}
|
|
|
|
overlay := overlays[index]
|
|
|
|
schematicRequest.Overlay = schematic.Overlay{
|
|
Name: overlay.Name,
|
|
Image: overlay.Image,
|
|
}
|
|
}
|
|
|
|
s.logger.Info("ensure schematic", zap.Reflect("schematic", schematicRequest))
|
|
|
|
schematicInfo, err := s.imageFactoryClient.EnsureSchematic(ctx, schematicRequest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to ensure schematic: %w", err)
|
|
}
|
|
|
|
pxeURL, err := config.Config.GetImageFactoryPXEBaseURL()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
filename := media.TypedSpec().Value.GenerateFilename(!supportsOverlays, request.SecureBoot, false)
|
|
|
|
return &management.CreateSchematicResponse{
|
|
SchematicId: schematicInfo.FullID,
|
|
PxeUrl: pxeURL.JoinPath("pxe", schematicInfo.FullID, request.TalosVersion, filename).String(),
|
|
GrpcTunnelEnabled: tunnelEnabled,
|
|
}, nil
|
|
}
|
|
|
|
func (s *managementServer) getBaseKernelArgs(ctx context.Context, grpcTunnelMode management.CreateSchematicRequest_SiderolinkGRPCTunnelMode) (args []string, tunnelEnabled bool, err error) {
|
|
params, err := safe.StateGet[*siderolink.ConnectionParams](ctx, s.omniState, siderolink.NewConnectionParams(
|
|
resources.DefaultNamespace,
|
|
siderolink.ConfigID,
|
|
).Metadata())
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("failed to get Omni connection params for the extra kernel arguments: %w", err)
|
|
}
|
|
|
|
// If the tunnel is enabled instance-wide or in the request, the final state is enabled
|
|
grpcTunnelEnabled := params.TypedSpec().Value.UseGrpcTunnel || grpcTunnelMode == management.CreateSchematicRequest_ENABLED
|
|
|
|
if grpcTunnelEnabled {
|
|
if args, err = siderolink.KernelArgsWithGRPCRTunnelMode(params, true); err != nil {
|
|
return nil, false, err
|
|
}
|
|
} else {
|
|
args = siderolink.KernelArgs(params)
|
|
}
|
|
|
|
return args, grpcTunnelEnabled, nil
|
|
}
|