mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-04 20:06:18 +02:00
feat: stop mounting state partition
Fixes #11608 Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
This commit is contained in:
parent
53ce93aaed
commit
9db6dc06c3
@ -121,6 +121,7 @@ message MountRequestSpec {
|
||||
repeated string requesters = 3;
|
||||
repeated string requester_i_ds = 4;
|
||||
bool read_only = 5;
|
||||
bool detached = 6;
|
||||
}
|
||||
|
||||
// MountSpec is the spec for volume mount.
|
||||
@ -144,6 +145,7 @@ message MountStatusSpec {
|
||||
bool read_only = 5;
|
||||
bool project_quota_support = 6;
|
||||
talos.resource.definitions.enums.BlockEncryptionProviderType encryption_provider = 7;
|
||||
bool detached = 8;
|
||||
}
|
||||
|
||||
// PartitionSpec is the spec for volume partitioning.
|
||||
@ -219,6 +221,7 @@ message VolumeMountRequestSpec {
|
||||
string volume_id = 1;
|
||||
string requester = 2;
|
||||
bool read_only = 3;
|
||||
bool detached = 4;
|
||||
}
|
||||
|
||||
// VolumeMountStatusSpec is the spec for VolumeMountStatus.
|
||||
@ -227,6 +230,7 @@ message VolumeMountStatusSpec {
|
||||
string requester = 2;
|
||||
string target = 3;
|
||||
bool read_only = 4;
|
||||
bool detached = 5;
|
||||
}
|
||||
|
||||
// VolumeStatusSpec is the spec for VolumeStatus resource.
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
@ -781,7 +782,7 @@ func mergeKubeconfig(ctx context.Context, clusterAccess *access.Adapter) error {
|
||||
|
||||
_, err = os.Stat(kubeconfigPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,10 @@ package talos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -67,7 +69,7 @@ captures ownership and permission bits.`,
|
||||
return fmt.Errorf("local path %q should be a directory", args[1])
|
||||
}
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("failed to stat local path: %w", err)
|
||||
}
|
||||
if err = os.MkdirAll(localPath, 0o777); err != nil {
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -114,7 +115,7 @@ func editFn(c *client.Client) func(context.Context, string, resource.Resource, e
|
||||
|
||||
// If we're retrying the loop because of an error, and no change was made in the file, short-circuit
|
||||
if lastError != "" && bytes.Equal(yamlstrip.Comments(editedDiff), yamlstrip.Comments(edited)) {
|
||||
if _, err = os.Stat(path); !os.IsNotExist(err) {
|
||||
if _, err = os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||
message := addEditingComment(lastError)
|
||||
message += fmt.Sprintf("A copy of your changes has been stored to %q\nEdit canceled, no valid changes were saved.\n", path)
|
||||
|
||||
|
||||
@ -7,7 +7,9 @@ package talos
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -70,7 +72,7 @@ If merge flag is false and [local-path] is "-", config will be written to stdout
|
||||
|
||||
st, err := os.Stat(localPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("error checking path %q: %w", localPath, err)
|
||||
}
|
||||
|
||||
@ -87,7 +89,7 @@ If merge flag is false and [local-path] is "-", config will be written to stdout
|
||||
if err == nil && !(kubeconfigFlags.force || kubeconfigFlags.merge) {
|
||||
return fmt.Errorf("kubeconfig file already exists, use --force to overwrite: %q", localPath)
|
||||
} else if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
// merge doesn't make sense if target path doesn't exist
|
||||
kubeconfigFlags.merge = false
|
||||
} else {
|
||||
|
||||
@ -8,6 +8,7 @@ package main
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
@ -124,7 +125,7 @@ func mountRootFS() error {
|
||||
var extensionsConfig extensions.Config
|
||||
|
||||
if err := extensionsConfig.Read(constants.ExtensionsConfigFile); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("failed to read extensions config: %w", err)
|
||||
}
|
||||
}
|
||||
@ -210,7 +211,7 @@ func mountRootFS() error {
|
||||
func bindMountFirmware() error {
|
||||
firmwarePath := quirks.New("").FirmwarePath()
|
||||
if _, err := os.Stat(firmwarePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -224,7 +225,7 @@ func bindMountFirmware() error {
|
||||
|
||||
func bindMountExtra() error {
|
||||
if _, err := os.Stat(constants.SDStubDynamicInitrdPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,8 @@ package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"errors"
|
||||
"io/fs"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@ -36,7 +37,7 @@ func (s *Server) MetaWrite(ctx context.Context, req *machine.MetaWriteRequest) (
|
||||
}
|
||||
|
||||
err = s.Controller.Runtime().State().Machine().Meta().Flush()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
// ignore not exist error, as it's possible that the meta partition is not created yet
|
||||
return nil, err
|
||||
}
|
||||
@ -69,7 +70,7 @@ func (s *Server) MetaDelete(ctx context.Context, req *machine.MetaDeleteRequest)
|
||||
}
|
||||
|
||||
err = s.Controller.Runtime().State().Machine().Meta().Flush()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
// ignore not exist error, as it's possible that the meta partition is not created yet
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
@ -1350,7 +1351,7 @@ func getContainerInspector(ctx context.Context, namespace string, driver common.
|
||||
func (s *Server) Read(in *machine.ReadRequest, srv machine.MachineService_ReadServer) (err error) {
|
||||
stat, err := os.Stat(in.Path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return status.Error(codes.NotFound, err.Error())
|
||||
}
|
||||
|
||||
@ -1938,7 +1939,7 @@ func (s *Server) EtcdSnapshot(in *machine.EtcdSnapshotRequest, srv machine.Machi
|
||||
//nolint:gocyclo
|
||||
func (s *Server) EtcdRecover(srv machine.MachineService_EtcdRecoverServer) error {
|
||||
if _, err := os.Stat(filepath.Dir(constants.EtcdRecoverySnapshotPath)); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return status.Error(codes.FailedPrecondition, "etcd service is not ready for recovery yet")
|
||||
}
|
||||
|
||||
|
||||
@ -228,12 +228,7 @@ func runEntrypoint(ctx context.Context, c *v1alpha1runtime.Controller) error {
|
||||
}
|
||||
}()
|
||||
|
||||
controllerWaitGroup.Add(1)
|
||||
|
||||
// Start v2 controller runtime.
|
||||
go func() {
|
||||
defer controllerWaitGroup.Done()
|
||||
|
||||
controllerWaitGroup.Go(func() {
|
||||
if e := c.V1Alpha2().Run(ctx, drainer); e != nil {
|
||||
ctrlErr := fmt.Errorf("fatal controller runtime error: %s", e)
|
||||
|
||||
@ -243,7 +238,7 @@ func runEntrypoint(ctx context.Context, c *v1alpha1runtime.Controller) error {
|
||||
}
|
||||
|
||||
log.Printf("controller runtime finished")
|
||||
}()
|
||||
})
|
||||
|
||||
// Inject controller into maintenance service.
|
||||
maintenance.InjectController(c)
|
||||
|
||||
6
internal/app/machined/pkg/adapters/block/mount.go
Normal file
6
internal/app/machined/pkg/adapters/block/mount.go
Normal file
@ -0,0 +1,6 @@
|
||||
// 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 block implements adapters wrapping resources/block to provide additional functionality.
|
||||
package block
|
||||
@ -0,0 +1,52 @@
|
||||
// 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 block
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/opentree"
|
||||
)
|
||||
|
||||
// VolumeMountStatus adapter provides conversion from MountStatus.
|
||||
//
|
||||
//nolint:revive,golint
|
||||
func VolumeMountStatus(r *block.VolumeMountStatus) volumeMountStatus {
|
||||
return volumeMountStatus{
|
||||
VolumeMountStatus: r,
|
||||
}
|
||||
}
|
||||
|
||||
type volumeMountStatus struct {
|
||||
VolumeMountStatus *block.VolumeMountStatus
|
||||
}
|
||||
|
||||
// WithRoot adapts VolumeMountStatus to xfs.Root and calls the provided callback with it.
|
||||
func (a volumeMountStatus) WithRoot(logger *zap.Logger, callback func(root xfs.Root) error) error {
|
||||
var root xfs.Root
|
||||
|
||||
root, ok := a.VolumeMountStatus.TypedSpec().Root().(xfs.Root)
|
||||
|
||||
if !ok || root == nil || !a.VolumeMountStatus.TypedSpec().Detached {
|
||||
root = &xfs.UnixRoot{
|
||||
FS: opentree.NewFromPath(a.VolumeMountStatus.TypedSpec().Target),
|
||||
}
|
||||
if err := root.OpenFS(); err != nil {
|
||||
return fmt.Errorf("error opening filesystem: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := root.Close(); err != nil {
|
||||
logger.Error("error closing filesystem", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return callback(root)
|
||||
}
|
||||
@ -6,7 +6,9 @@ package network
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"os"
|
||||
"slices"
|
||||
@ -469,7 +471,7 @@ func (a nftablesRule) Compile() (*NfTablesCompiled, error) {
|
||||
match := a.NfTablesRule.MatchSourceAddress
|
||||
|
||||
if err := addressMatchExpression(match, "source", 12, 8); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return &NfTablesCompiled{}, nil
|
||||
}
|
||||
|
||||
@ -481,7 +483,7 @@ func (a nftablesRule) Compile() (*NfTablesCompiled, error) {
|
||||
match := a.NfTablesRule.MatchDestinationAddress
|
||||
|
||||
if err := addressMatchExpression(match, "destination", 16, 24); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return &NfTablesCompiled{}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ type VolumeMounterAutomaton = *automaton.ControllerAutomaton[volumeMountContext]
|
||||
// VolumeMounterOptions is the options for the volume mounter controller state machine.
|
||||
type VolumeMounterOptions struct {
|
||||
ReadOnly bool
|
||||
Detached bool
|
||||
}
|
||||
|
||||
// VolumeMounterOption is a function that configures the volume mounter controller state machine.
|
||||
@ -48,6 +49,13 @@ func WithReadOnly(readOnly bool) VolumeMounterOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDetached sets the volume mounter controller state machine to detached mode.
|
||||
func WithDetached(detached bool) VolumeMounterOption {
|
||||
return func(options *VolumeMounterOptions) {
|
||||
options.Detached = detached
|
||||
}
|
||||
}
|
||||
|
||||
// NewVolumeMounter creates a new volume mounter controller state machine.
|
||||
//
|
||||
// It ensures that the volume is mounted, and calls the callback function when the volume is mounted,
|
||||
@ -77,6 +85,7 @@ func createVolumeMountRequest(ctx context.Context, r controller.ReaderWriter, lo
|
||||
req.TypedSpec().VolumeID = mountContext.volumeID
|
||||
req.TypedSpec().Requester = mountContext.requester
|
||||
req.TypedSpec().ReadOnly = mountContext.options.ReadOnly
|
||||
req.TypedSpec().Detached = mountContext.options.Detached
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
||||
@ -7,7 +7,9 @@ package sysblock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -29,7 +31,7 @@ func Walk(root string) ([]*kobject.Event, error) {
|
||||
for _, entry := range entries {
|
||||
fi, err := entry.Info()
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -42,7 +44,7 @@ func Walk(root string) ([]*kobject.Event, error) {
|
||||
|
||||
path, err := filepath.EvalSymlinks(filepath.Join(root, entry.Name()))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -51,7 +53,7 @@ func Walk(root string) ([]*kobject.Event, error) {
|
||||
|
||||
uevent, err := readUevent(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -103,7 +105,7 @@ func readUevent(path string) (map[string]string, error) {
|
||||
func readPartitions(path string) ([]*kobject.Event, error) {
|
||||
entries, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -126,7 +128,7 @@ func readPartitions(path string) ([]*kobject.Event, error) {
|
||||
|
||||
uevent, err := readUevent(partitionPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@ -18,10 +18,10 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
mountv3 "github.com/siderolabs/talos/internal/pkg/mount/v3"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/machinery/imager/quirks"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/makefs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
// Format establishes a filesystem on a block device.
|
||||
@ -164,7 +164,6 @@ func GrowFilesystem(logger *zap.Logger, volumeContext ManagerContext) error {
|
||||
mountv3.WithFsopen(
|
||||
volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type.String(),
|
||||
fsopen.WithSource(volumeContext.Status.MountLocation),
|
||||
fsopen.WithPrinter(logger.Sugar().Infof),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@ -6,7 +6,9 @@ package block
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@ -19,14 +21,14 @@ import (
|
||||
"github.com/siderolabs/gen/xslices"
|
||||
"github.com/siderolabs/go-blockdevice/v2/swap"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/mount/v3"
|
||||
"github.com/siderolabs/talos/internal/pkg/selinux"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/filetree"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
type mountContext struct {
|
||||
@ -232,6 +234,15 @@ func (ctrl *MountController) Run(ctx context.Context, r controller.Runtime, logg
|
||||
mountStatus.TypedSpec().EncryptionProvider = volumeStatus.TypedSpec().EncryptionProvider
|
||||
mountStatus.TypedSpec().ReadOnly = mountRequest.TypedSpec().ReadOnly
|
||||
mountStatus.TypedSpec().ProjectQuotaSupport = volumeStatus.TypedSpec().MountSpec.ProjectQuotaSupport
|
||||
mountStatus.TypedSpec().Detached = mountRequest.TypedSpec().Detached
|
||||
|
||||
// This needs to be set through accessor, and is not guaranteed to resolve to a valid root.
|
||||
mount, ok := ctrl.activeMounts[mountRequest.Metadata().ID()]
|
||||
if ok && mount.point != nil {
|
||||
mountStatus.TypedSpec().SetRoot(mount.point.Root())
|
||||
} else {
|
||||
mountStatus.TypedSpec().SetRoot(&xfs.OSRoot{Shadow: filepath.Join(rootPath, mountTarget)})
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
@ -341,7 +352,7 @@ func (ctrl *MountController) handleSymlinkMountOperation(
|
||||
targetPath := filepath.Join(rootPath, target)
|
||||
|
||||
st, err := os.Lstat(targetPath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("failed to stat target path: %w", err)
|
||||
}
|
||||
|
||||
@ -450,7 +461,7 @@ func (ctrl *MountController) updateTargetSettings(
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting label %q: %w", targetPath, err)
|
||||
return fmt.Errorf("error setting label on %q: %w", currentLabel, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -475,7 +486,6 @@ func (ctrl *MountController) handleDiskMountOperation(
|
||||
|
||||
fsOpts = append(fsOpts,
|
||||
fsopen.WithSource(mountSource),
|
||||
fsopen.WithPrinter(logger.Sugar().With(zap.String("volume", volumeStatus.Metadata().ID())).Infof),
|
||||
fsopen.WithProjectQuota(volumeStatus.TypedSpec().MountSpec.ProjectQuotaSupport),
|
||||
)
|
||||
|
||||
@ -484,7 +494,11 @@ func (ctrl *MountController) handleDiskMountOperation(
|
||||
)
|
||||
|
||||
if mountRequest.TypedSpec().ReadOnly {
|
||||
opts = append(opts, mount.WithMountAttributes(unix.MOUNT_ATTR_RDONLY))
|
||||
opts = append(opts, mount.WithReadOnly())
|
||||
}
|
||||
|
||||
if mountRequest.TypedSpec().Detached {
|
||||
opts = append(opts, mount.WithDetached())
|
||||
}
|
||||
|
||||
manager := mount.NewManager(slices.Concat(
|
||||
@ -504,7 +518,7 @@ func (ctrl *MountController) handleDiskMountOperation(
|
||||
return fmt.Errorf("failed to mount %q: %w", mountRequest.Metadata().ID(), err)
|
||||
}
|
||||
|
||||
if !mountRequest.TypedSpec().ReadOnly {
|
||||
if !mountRequest.TypedSpec().ReadOnly && !mountRequest.TypedSpec().Detached {
|
||||
if err = ctrl.updateTargetSettings(mountTarget, volumeStatus.TypedSpec().MountSpec); err != nil {
|
||||
manager.Unmount() //nolint:errcheck
|
||||
|
||||
@ -518,6 +532,7 @@ func (ctrl *MountController) handleDiskMountOperation(
|
||||
zap.String("target", mountTarget),
|
||||
zap.Stringer("filesystem", mountFilesystem),
|
||||
zap.Bool("read_only", mountRequest.TypedSpec().ReadOnly),
|
||||
zap.Bool("detached", mountRequest.TypedSpec().Detached),
|
||||
)
|
||||
|
||||
ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{
|
||||
|
||||
@ -105,6 +105,7 @@ func (ctrl *MountRequestController) Run(ctx context.Context, r controller.Runtim
|
||||
desiredMountRequests[volumeID] = &block.MountRequestSpec{
|
||||
VolumeID: volumeID,
|
||||
ReadOnly: volumeMountRequest.TypedSpec().ReadOnly,
|
||||
Detached: volumeMountRequest.TypedSpec().Detached,
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,6 +113,7 @@ func (ctrl *MountRequestController) Run(ctx context.Context, r controller.Runtim
|
||||
desiredMountRequest.Requesters = append(desiredMountRequest.Requesters, volumeMountRequest.TypedSpec().Requester)
|
||||
desiredMountRequest.RequesterIDs = append(desiredMountRequest.RequesterIDs, volumeMountRequest.Metadata().ID())
|
||||
desiredMountRequest.ReadOnly = desiredMountRequest.ReadOnly && volumeMountRequest.TypedSpec().ReadOnly // read-only if all requesters are read-only
|
||||
desiredMountRequest.Detached = desiredMountRequest.Detached && volumeMountRequest.TypedSpec().Detached // detached if all requesters are detached
|
||||
desiredMountRequest.ParentMountID = volumeStatus.TypedSpec().MountSpec.ParentID
|
||||
}
|
||||
|
||||
|
||||
@ -90,6 +90,10 @@ func (ctrl *MountStatusController) Run(ctx context.Context, r controller.Runtime
|
||||
vms.TypedSpec().Target = mountStatus.TypedSpec().Target
|
||||
vms.TypedSpec().VolumeID = mountStatus.TypedSpec().Spec.VolumeID
|
||||
vms.TypedSpec().ReadOnly = mountStatus.TypedSpec().Spec.ReadOnly
|
||||
vms.TypedSpec().Detached = mountStatus.TypedSpec().Detached
|
||||
|
||||
// This needs to be set through accessor, and is not guaranteed to resolve to a valid root.
|
||||
vms.TypedSpec().SetRoot(mountStatus.TypedSpec().Root())
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
@ -12,15 +12,15 @@ import (
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"go.uber.org/zap"
|
||||
|
||||
blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block"
|
||||
clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/opentree"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/cluster"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/files"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// NodeIdentityController manages runtime.Identity caching identity in the STATE.
|
||||
@ -79,7 +79,12 @@ func (ctrl *NodeIdentityController) Run(ctx context.Context, r controller.Runtim
|
||||
}
|
||||
|
||||
if ctrl.stateMachine == nil {
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(ctrl.Name(), constants.StatePartitionLabel, ctrl.establishNodeIdentity)
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(
|
||||
ctrl.Name(),
|
||||
constants.StatePartitionLabel,
|
||||
ctrl.establishNodeIdentity,
|
||||
blockautomaton.WithDetached(true),
|
||||
)
|
||||
}
|
||||
|
||||
if err := ctrl.stateMachine.Run(ctx, r, logger); err != nil {
|
||||
@ -91,48 +96,39 @@ func (ctrl *NodeIdentityController) Run(ctx context.Context, r controller.Runtim
|
||||
}
|
||||
|
||||
func (ctrl *NodeIdentityController) establishNodeIdentity(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
|
||||
root := &xfs.UnixRoot{FS: opentree.NewFromPath(mountStatus.TypedSpec().Target)}
|
||||
if err := root.OpenFS(); err != nil {
|
||||
return fmt.Errorf("error opening filesystem: %w", err)
|
||||
}
|
||||
return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error {
|
||||
var localIdentity cluster.IdentitySpec
|
||||
|
||||
defer func() {
|
||||
if err := root.Close(); err != nil {
|
||||
logger.Error("error closing filesystem", zap.Error(err))
|
||||
if err := controllers.LoadOrNewFromFile(root, constants.NodeIdentityFilename, &localIdentity, func(v *cluster.IdentitySpec) error {
|
||||
return clusteradapter.IdentitySpec(v).Generate()
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error caching node identity: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var localIdentity cluster.IdentitySpec
|
||||
if err := safe.WriterModify(ctx, r, cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity), func(r *cluster.Identity) error {
|
||||
*r.TypedSpec() = localIdentity
|
||||
|
||||
if err := controllers.LoadOrNewFromFile(root, constants.NodeIdentityFilename, &localIdentity, func(v *cluster.IdentitySpec) error {
|
||||
return clusteradapter.IdentitySpec(v).Generate()
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error caching node identity: %w", err)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error modifying resource: %w", err)
|
||||
}
|
||||
|
||||
if err := safe.WriterModify(ctx, r, cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity), func(r *cluster.Identity) error {
|
||||
*r.TypedSpec() = localIdentity
|
||||
// generate `/etc/machine-id` from node identity
|
||||
if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "machine-id"),
|
||||
func(r *files.EtcFileSpec) error {
|
||||
var err error
|
||||
|
||||
r.TypedSpec().Contents, err = clusteradapter.IdentitySpec(&localIdentity).ConvertMachineID()
|
||||
r.TypedSpec().Mode = 0o444
|
||||
r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel
|
||||
|
||||
return err
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error modifying machine-id: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("node identity established", zap.String("node_id", localIdentity.NodeID))
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error modifying resource: %w", err)
|
||||
}
|
||||
|
||||
// generate `/etc/machine-id` from node identity
|
||||
if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "machine-id"),
|
||||
func(r *files.EtcFileSpec) error {
|
||||
var err error
|
||||
|
||||
r.TypedSpec().Contents, err = clusteradapter.IdentitySpec(&localIdentity).ConvertMachineID()
|
||||
r.TypedSpec().Mode = 0o444
|
||||
r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel
|
||||
|
||||
return err
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error modifying machine-id: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("node identity established", zap.String("node_id", localIdentity.NodeID))
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@ -12,9 +12,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@ -26,6 +25,7 @@ import (
|
||||
"github.com/siderolabs/go-procfs/procfs"
|
||||
"go.uber.org/zap"
|
||||
|
||||
blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
|
||||
talosruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
@ -40,6 +40,7 @@ import (
|
||||
configresource "github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// PlatformConfigurator is a reduced interface of runtime.Platform.
|
||||
@ -297,8 +298,12 @@ func (validationModeDiskConfig) String() string {
|
||||
// loadFromDisk is a helper function for stateDisk.
|
||||
func (ctrl *AcquireController) loadFromDisk(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger) (config.Provider, bool, error) {
|
||||
if ctrl.stateMachine == nil {
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(ctrl.Name(), constants.StatePartitionLabel, ctrl.loadConfigFromDisk,
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(
|
||||
ctrl.Name(),
|
||||
constants.StatePartitionLabel,
|
||||
ctrl.loadConfigFromDisk,
|
||||
blockautomaton.WithReadOnly(true),
|
||||
blockautomaton.WithDetached(true),
|
||||
)
|
||||
}
|
||||
|
||||
@ -321,39 +326,41 @@ func (ctrl *AcquireController) loadFromDisk(ctx context.Context, r controller.Re
|
||||
}
|
||||
|
||||
func (ctrl *AcquireController) loadConfigFromDisk(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
|
||||
configPath := filepath.Join(mountStatus.TypedSpec().Target, constants.ConfigFilename)
|
||||
return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error {
|
||||
configPath := constants.ConfigFilename
|
||||
|
||||
logger.Debug("loading config from STATE", zap.String("path", configPath))
|
||||
logger.Debug("loading config from STATE", zap.String("path", configPath))
|
||||
|
||||
_, err := os.Stat(configPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// no saved machine config
|
||||
return nil
|
||||
_, err := xfs.Stat(root, configPath)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
// no saved machine config
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to stat %s: %w", configPath, err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to stat %s: %w", configPath, err)
|
||||
}
|
||||
cfg, err := loadConfig(root, configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := configloader.NewFromFile(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config from STATE: %w", err)
|
||||
}
|
||||
// if the STATE partition is present & contains machine config, Talos is already installed
|
||||
warnings, err := cfg.Validate(validationModeDiskConfig{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to validate on-disk config: %w", err)
|
||||
}
|
||||
|
||||
// if the STATE partition is present & contains machine config, Talos is already installed
|
||||
warnings, err := cfg.Validate(validationModeDiskConfig{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to validate on-disk config: %w", err)
|
||||
}
|
||||
for _, warning := range warnings {
|
||||
logger.Warn("config validation warning", zap.String("warning", warning))
|
||||
}
|
||||
|
||||
for _, warning := range warnings {
|
||||
logger.Warn("config validation warning", zap.String("warning", warning))
|
||||
}
|
||||
// we can't return the value directly
|
||||
ctrl.diskConfig = cfg
|
||||
|
||||
// we can't return the value directly
|
||||
ctrl.diskConfig = cfg
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// stateCmdlineEarly acquires machine configuration from the kernel cmdline source (talos.config.early).
|
||||
@ -687,3 +694,18 @@ func (ctrl *AcquireController) stateDone(ctx context.Context, r controller.Runti
|
||||
func (ctrl *AcquireController) stateFinal(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func loadConfig(root xfs.Root, configPath string) (config.Provider, error) {
|
||||
f, err := xfs.Open(root, configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open %q from STATE: %w", configPath, err)
|
||||
}
|
||||
defer f.Close() //nolint:errcheck
|
||||
|
||||
cfg, err := configloader.NewFromReader(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load %q from STATE: %w", configPath, err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@ -336,10 +336,10 @@ func (suite *AcquireSuite) TestFromDiskFailure() {
|
||||
ev := suite.platformEvent.getEvents()[0]
|
||||
suite.Assert().Equal(platform.EventTypeFailure, ev.Type)
|
||||
suite.Assert().Equal("Error loading and validating Talos machine config.", ev.Message)
|
||||
suite.Assert().Equal("failed to load config from STATE: unknown keys found during decoding:\naaaversion: v1alpha1 # Indicates the schema used to decode the contents.\n", ev.Error.Error())
|
||||
suite.Assert().Equal("failed to load \"config.yaml\" from STATE: unknown keys found during decoding:\naaaversion: v1alpha1 # Indicates the schema used to decode the contents.\n", ev.Error.Error())
|
||||
|
||||
suite.Assert().Equal(&machineapi.ConfigLoadErrorEvent{
|
||||
Error: "failed to load config from STATE: unknown keys found during decoding:\naaaversion: v1alpha1 # Indicates the schema used to decode the contents.\n",
|
||||
Error: "failed to load \"config.yaml\" from STATE: unknown keys found during decoding:\naaaversion: v1alpha1 # Indicates the schema used to decode the contents.\n",
|
||||
}, suite.eventPublisher.getEvents()[0])
|
||||
}
|
||||
|
||||
|
||||
@ -7,8 +7,6 @@ package config
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/controller"
|
||||
"github.com/cosi-project/runtime/pkg/resource"
|
||||
@ -17,11 +15,13 @@ import (
|
||||
"github.com/siderolabs/gen/optional"
|
||||
"go.uber.org/zap"
|
||||
|
||||
blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// PersistenceController ensures that the machine configuration is persisted in STATE partition.
|
||||
@ -112,7 +112,12 @@ func (ctrl *PersistenceController) Run(ctx context.Context, r controller.Runtime
|
||||
}
|
||||
|
||||
if ctrl.stateMachine == nil && ctrl.configToPersist != nil {
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(ctrl.Name(), constants.StatePartitionLabel, ctrl.persistMachineConfig)
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(
|
||||
ctrl.Name(),
|
||||
constants.StatePartitionLabel,
|
||||
ctrl.persistMachineConfig,
|
||||
blockautomaton.WithDetached(true),
|
||||
)
|
||||
}
|
||||
|
||||
if ctrl.stateMachine != nil {
|
||||
@ -143,26 +148,27 @@ func (ctrl *PersistenceController) Run(ctx context.Context, r controller.Runtime
|
||||
}
|
||||
|
||||
func (ctrl *PersistenceController) persistMachineConfig(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
|
||||
rootPath := mountStatus.TypedSpec().Target
|
||||
tempName := constants.ConfigFilename + "-tmp"
|
||||
return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error {
|
||||
tempName := constants.ConfigFilename + "-tmp"
|
||||
|
||||
configContents, err := ctrl.configToPersist.Provider().Bytes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting config bytes: %w", err)
|
||||
}
|
||||
configContents, err := ctrl.configToPersist.Provider().Bytes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting config bytes: %w", err)
|
||||
}
|
||||
|
||||
if err = os.WriteFile(filepath.Join(rootPath, tempName), configContents, 0o600); err != nil {
|
||||
return fmt.Errorf("error writing config to file: %w", err)
|
||||
}
|
||||
if err = xfs.WriteFile(root, tempName, configContents, 0o600); err != nil {
|
||||
return fmt.Errorf("error writing config to file: %w", err)
|
||||
}
|
||||
|
||||
if err = os.Rename(filepath.Join(rootPath, tempName), filepath.Join(rootPath, constants.ConfigFilename)); err != nil {
|
||||
return fmt.Errorf("error renaming config file: %w", err)
|
||||
}
|
||||
if err = xfs.Rename(root, tempName, constants.ConfigFilename); err != nil {
|
||||
return fmt.Errorf("error renaming config file: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("machine configuration persisted to STATE")
|
||||
logger.Info("machine configuration persisted to STATE")
|
||||
|
||||
ctrl.lastPersistedVersion = ctrl.configToPersist.Metadata().Version()
|
||||
ctrl.configToPersist = nil
|
||||
ctrl.lastPersistedVersion = ctrl.configToPersist.Metadata().Version()
|
||||
ctrl.configToPersist = nil
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@ -19,10 +19,10 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/containers/cri/containerd"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/cri"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/files"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// CRIRegistryConfigController generates parts of the CRI config for registry configuration.
|
||||
|
||||
@ -20,8 +20,8 @@ import (
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/mount/v3"
|
||||
"github.com/siderolabs/talos/internal/pkg/selinux"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/files"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// EtcFileController watches EtcFileSpecs, creates/updates files.
|
||||
|
||||
@ -25,9 +25,9 @@ import (
|
||||
|
||||
filesctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/files"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/opentree"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/files"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/opentree"
|
||||
)
|
||||
|
||||
type EtcFileSuite struct {
|
||||
|
||||
@ -6,7 +6,9 @@ package hardware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -154,7 +156,7 @@ func (c *PCIDriverRebindController) handlePCIDriverReBind(pciID, targetDriver st
|
||||
|
||||
// Unbind device from the host driver.
|
||||
// in some cases, the device may not be bound to any driver, so we ignore the error.
|
||||
if err := os.WriteFile(fmt.Sprintf(driverUnbindPath, pciID), []byte(pciID), 0o200); err != nil && !os.IsNotExist(err) {
|
||||
if err := os.WriteFile(fmt.Sprintf(driverUnbindPath, pciID), []byte(pciID), 0o200); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("error unbinding device with id: %s, %w", pciID, err)
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,9 @@ package hardware
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -100,7 +102,7 @@ func (ctrl *PCIDevicesController) Run(ctx context.Context, r controller.Runtime,
|
||||
for _, deviceID := range deviceIDs {
|
||||
class, err := readHexPCIInfo(deviceID.Name(), "class")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -109,7 +111,7 @@ func (ctrl *PCIDevicesController) Run(ctx context.Context, r controller.Runtime,
|
||||
|
||||
vendor, err := readHexPCIInfo(deviceID.Name(), "vendor")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -118,7 +120,7 @@ func (ctrl *PCIDevicesController) Run(ctx context.Context, r controller.Runtime,
|
||||
|
||||
product, err := readHexPCIInfo(deviceID.Name(), "device")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -176,7 +178,7 @@ func readDriverInfo(deviceID string) (string, error) {
|
||||
if err != nil {
|
||||
// ignore if the driver doesn't exist
|
||||
// this can happen if the device is not bound to a driver or a pci root port
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
||||
@ -15,18 +15,18 @@ import (
|
||||
"github.com/siderolabs/gen/optional"
|
||||
"go.uber.org/zap"
|
||||
|
||||
blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block"
|
||||
kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/opentree"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/fipsmode"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/kubespan"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// IdentityController watches KubeSpan configuration, updates KubeSpan Identity.
|
||||
@ -111,7 +111,12 @@ func (ctrl *IdentityController) Run(ctx context.Context, r controller.Runtime, l
|
||||
}
|
||||
|
||||
if ctrl.stateMachine == nil && !alreadyHasIdentity {
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(ctrl.Name(), constants.StatePartitionLabel, ctrl.establishIdentity(cfg, firstMAC))
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(
|
||||
ctrl.Name(),
|
||||
constants.StatePartitionLabel,
|
||||
ctrl.establishIdentity(cfg, firstMAC),
|
||||
blockautomaton.WithDetached(true),
|
||||
)
|
||||
}
|
||||
} else if alreadyHasIdentity {
|
||||
if err = r.Destroy(ctx, kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity).Metadata()); err != nil {
|
||||
@ -141,36 +146,27 @@ func (ctrl *IdentityController) establishIdentity(
|
||||
ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus,
|
||||
) error {
|
||||
return func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
|
||||
root := &xfs.UnixRoot{FS: opentree.NewFromPath(mountStatus.TypedSpec().Target)}
|
||||
if err := root.OpenFS(); err != nil {
|
||||
return fmt.Errorf("error opening filesystem: %w", err)
|
||||
}
|
||||
return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error {
|
||||
var localIdentity kubespan.IdentitySpec
|
||||
|
||||
defer func() {
|
||||
if err := root.Close(); err != nil {
|
||||
logger.Error("error closing filesystem", zap.Error(err))
|
||||
if err := controllers.LoadOrNewFromFile(root, constants.KubeSpanIdentityFilename, &localIdentity, func(v *kubespan.IdentitySpec) error {
|
||||
return kubespanadapter.IdentitySpec(v).GenerateKey()
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error caching kubespan identity: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var localIdentity kubespan.IdentitySpec
|
||||
kubespanCfg := cfg.TypedSpec()
|
||||
mac := firstMAC.TypedSpec()
|
||||
|
||||
if err := controllers.LoadOrNewFromFile(root, constants.KubeSpanIdentityFilename, &localIdentity, func(v *kubespan.IdentitySpec) error {
|
||||
return kubespanadapter.IdentitySpec(v).GenerateKey()
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error caching kubespan identity: %w", err)
|
||||
}
|
||||
if err := kubespanadapter.IdentitySpec(&localIdentity).UpdateAddress(kubespanCfg.ClusterID, net.HardwareAddr(mac.HardwareAddr)); err != nil {
|
||||
return fmt.Errorf("error updating KubeSpan address: %w", err)
|
||||
}
|
||||
|
||||
kubespanCfg := cfg.TypedSpec()
|
||||
mac := firstMAC.TypedSpec()
|
||||
return safe.WriterModify(ctx, r, kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity), func(res *kubespan.Identity) error {
|
||||
*res.TypedSpec() = localIdentity
|
||||
|
||||
if err := kubespanadapter.IdentitySpec(&localIdentity).UpdateAddress(kubespanCfg.ClusterID, net.HardwareAddr(mac.HardwareAddr)); err != nil {
|
||||
return fmt.Errorf("error updating KubeSpan address: %w", err)
|
||||
}
|
||||
|
||||
return safe.WriterModify(ctx, r, kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity), func(res *kubespan.Identity) error {
|
||||
*res.TypedSpec() = localIdentity
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,12 +28,12 @@ import (
|
||||
efiles "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/files"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/internal/pkg/mount/v3"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
talosconfig "github.com/siderolabs/talos/pkg/machinery/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/files"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// EtcFileController creates /etc/hostname and /etc/resolv.conf files based on finalized network configuration.
|
||||
|
||||
@ -29,13 +29,13 @@ import (
|
||||
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
|
||||
v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/internal/pkg/mount/v3"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/opentree"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/container"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/files"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/opentree"
|
||||
)
|
||||
|
||||
type EtcFileConfigSuite struct {
|
||||
|
||||
@ -6,20 +6,22 @@ package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"io/fs"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/controller"
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// PlatformConfigLoadController loads cached platform network config from STATE.
|
||||
@ -75,9 +77,11 @@ func (ctrl *PlatformConfigLoadController) Run(ctx context.Context, r controller.
|
||||
|
||||
if ctrl.stateMachine == nil {
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(
|
||||
ctrl.Name(), constants.StatePartitionLabel,
|
||||
ctrl.Name(),
|
||||
constants.StatePartitionLabel,
|
||||
ctrl.load(),
|
||||
blockautomaton.WithReadOnly(true),
|
||||
blockautomaton.WithDetached(true),
|
||||
)
|
||||
}
|
||||
|
||||
@ -104,36 +108,36 @@ func (ctrl *PlatformConfigLoadController) load() func(
|
||||
ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus,
|
||||
) error {
|
||||
return func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
|
||||
rootPath := mountStatus.TypedSpec().Target
|
||||
|
||||
cachedNetworkConfig, err := ctrl.loadConfig(filepath.Join(rootPath, constants.PlatformNetworkConfigFilename))
|
||||
if err != nil {
|
||||
logger.Warn("ignored failure loading cached platform network config", zap.Error(err))
|
||||
} else if cachedNetworkConfig != nil {
|
||||
logger.Debug("loaded cached platform network config")
|
||||
}
|
||||
|
||||
if cachedNetworkConfig != nil {
|
||||
if err := safe.WriterModify(ctx, r,
|
||||
network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigCachedID),
|
||||
func(out *network.PlatformConfig) error {
|
||||
*out.TypedSpec() = *cachedNetworkConfig
|
||||
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return fmt.Errorf("error modifying cached platform network config: %w", err)
|
||||
return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error {
|
||||
cachedNetworkConfig, err := ctrl.loadConfig(root, constants.PlatformNetworkConfigFilename)
|
||||
if err != nil {
|
||||
logger.Warn("ignored failure loading cached platform network config", zap.Error(err))
|
||||
} else if cachedNetworkConfig != nil {
|
||||
logger.Debug("loaded cached platform network config")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
if cachedNetworkConfig != nil {
|
||||
if err := safe.WriterModify(ctx, r,
|
||||
network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigCachedID),
|
||||
func(out *network.PlatformConfig) error {
|
||||
*out.TypedSpec() = *cachedNetworkConfig
|
||||
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return fmt.Errorf("error modifying cached platform network config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *PlatformConfigLoadController) loadConfig(path string) (*network.PlatformConfigSpec, error) {
|
||||
marshaled, err := os.ReadFile(path)
|
||||
func (ctrl *PlatformConfigLoadController) loadConfig(root xfs.Root, path string) (*network.PlatformConfigSpec, error) {
|
||||
marshaled, err := xfs.ReadFile(root, path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@ -8,8 +8,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/controller"
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
@ -18,11 +16,13 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/network"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// PlatformConfigStoreController stores (caches) active platform network config in STATE.
|
||||
@ -96,8 +96,10 @@ func (ctrl *PlatformConfigStoreController) Run(ctx context.Context, r controller
|
||||
|
||||
if ctrl.stateMachine == nil && ctrl.configToStore != nil {
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(
|
||||
ctrl.Name(), constants.StatePartitionLabel,
|
||||
ctrl.Name(),
|
||||
constants.StatePartitionLabel,
|
||||
ctrl.store(),
|
||||
blockautomaton.WithDetached(true),
|
||||
)
|
||||
}
|
||||
|
||||
@ -121,34 +123,34 @@ func (ctrl *PlatformConfigStoreController) store() func(
|
||||
ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus,
|
||||
) error {
|
||||
return func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
|
||||
rootPath := mountStatus.TypedSpec().Target
|
||||
return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error {
|
||||
if err := ctrl.storeConfig(root, constants.PlatformNetworkConfigFilename, ctrl.configToStore); err != nil {
|
||||
return fmt.Errorf("error saving platform network config: %w", err)
|
||||
}
|
||||
|
||||
if err := ctrl.storeConfig(filepath.Join(rootPath, constants.PlatformNetworkConfigFilename), ctrl.configToStore); err != nil {
|
||||
return fmt.Errorf("error saving platform network config: %w", err)
|
||||
}
|
||||
// remember last stored config
|
||||
ctrl.lastStoredConfig, ctrl.configToStore = ctrl.configToStore, nil
|
||||
|
||||
// remember last stored config
|
||||
ctrl.lastStoredConfig, ctrl.configToStore = ctrl.configToStore, nil
|
||||
logger.Debug("stored active platform network config")
|
||||
|
||||
logger.Debug("stored active platform network config")
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *PlatformConfigStoreController) storeConfig(path string, networkConfig *network.PlatformConfig) error {
|
||||
func (ctrl *PlatformConfigStoreController) storeConfig(root xfs.Root, path string, networkConfig *network.PlatformConfig) error {
|
||||
marshaled, err := yaml.Marshal(networkConfig.TypedSpec())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshaling network config: %w", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
existing, err := os.ReadFile(path)
|
||||
if _, err := xfs.Stat(root, path); err == nil {
|
||||
existing, err := xfs.ReadFile(root, path)
|
||||
if err == nil && bytes.Equal(marshaled, existing) {
|
||||
// existing contents are identical, skip writing to avoid no-op writes
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return os.WriteFile(path, marshaled, 0o400)
|
||||
return xfs.WriteFile(root, path, marshaled, 0o400)
|
||||
}
|
||||
|
||||
@ -6,7 +6,9 @@ package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -72,7 +74,7 @@ func (ctrl *ExtensionServiceController) Run(ctx context.Context, r controller.Ru
|
||||
// extensions loading only needs to run once, as services are static
|
||||
serviceFiles, err := os.ReadDir(ctrl.ConfigPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
// directory not present, skip completely
|
||||
logger.Debug("extension service directory is not found")
|
||||
|
||||
|
||||
@ -12,14 +12,14 @@ import (
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"go.uber.org/zap"
|
||||
|
||||
blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block"
|
||||
secretsadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/secrets"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/opentree"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// EncryptionSaltController manages secrets.EncryptionSalt in STATE.
|
||||
@ -74,7 +74,12 @@ func (ctrl *EncryptionSaltController) Run(ctx context.Context, r controller.Runt
|
||||
}
|
||||
|
||||
if ctrl.stateMachine == nil {
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(ctrl.Name(), constants.StatePartitionLabel, ctrl.establishEncryptionSalt)
|
||||
ctrl.stateMachine = blockautomaton.NewVolumeMounter(
|
||||
ctrl.Name(),
|
||||
constants.StatePartitionLabel,
|
||||
ctrl.establishEncryptionSalt,
|
||||
blockautomaton.WithDetached(true),
|
||||
)
|
||||
}
|
||||
|
||||
if err := ctrl.stateMachine.Run(ctx, r, logger); err != nil {
|
||||
@ -86,34 +91,25 @@ func (ctrl *EncryptionSaltController) Run(ctx context.Context, r controller.Runt
|
||||
}
|
||||
|
||||
func (ctrl *EncryptionSaltController) establishEncryptionSalt(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
|
||||
root := &xfs.UnixRoot{FS: opentree.NewFromPath(mountStatus.TypedSpec().Target)}
|
||||
if err := root.OpenFS(); err != nil {
|
||||
return fmt.Errorf("error opening filesystem: %w", err)
|
||||
}
|
||||
return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error {
|
||||
var salt secrets.EncryptionSaltSpec
|
||||
|
||||
defer func() {
|
||||
if err := root.Close(); err != nil {
|
||||
logger.Error("error closing filesystem", zap.Error(err))
|
||||
if err := controllers.LoadOrNewFromFile(root, constants.EncryptionSaltFilename, &salt, func(v *secrets.EncryptionSaltSpec) error {
|
||||
return secretsadapter.EncryptionSalt(v).Generate()
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error caching node identity: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var salt secrets.EncryptionSaltSpec
|
||||
if err := safe.WriterModify(ctx, r, secrets.NewEncryptionSalt(), func(r *secrets.EncryptionSalt) error {
|
||||
*r.TypedSpec() = salt
|
||||
|
||||
if err := controllers.LoadOrNewFromFile(root, constants.EncryptionSaltFilename, &salt, func(v *secrets.EncryptionSaltSpec) error {
|
||||
return secretsadapter.EncryptionSalt(v).Generate()
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error caching node identity: %w", err)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error modifying resource: %w", err)
|
||||
}
|
||||
|
||||
if err := safe.WriterModify(ctx, r, secrets.NewEncryptionSalt(), func(r *secrets.EncryptionSalt) error {
|
||||
*r.TypedSpec() = salt
|
||||
logger.Info("encryption salt established")
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("error modifying resource: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("encryption salt established")
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,37 +8,40 @@ package controllers
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// LoadOrNewFromFile either loads value from file.yaml or generates new values and saves as file.yaml.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func LoadOrNewFromFile[T any](root xfs.Root, path string, empty T, generate func(T) error) error {
|
||||
f, err := xfs.OpenFile(root, path, os.O_RDONLY, 0)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("error reading state file: %w", err)
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("error reading state file %q: %w", path, err)
|
||||
}
|
||||
|
||||
// file doesn't exist yet, generate new value and save it
|
||||
if f == nil {
|
||||
if f == nil || errors.Is(err, fs.ErrNotExist) {
|
||||
if err = generate(empty); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err = xfs.OpenFile(root, path, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating state file: %w", err)
|
||||
return fmt.Errorf("error creating state file %q: %w", path, err)
|
||||
}
|
||||
|
||||
defer f.Close() //nolint:errcheck
|
||||
|
||||
encoder := yaml.NewEncoder(f)
|
||||
if err = encoder.Encode(empty); err != nil {
|
||||
return fmt.Errorf("error marshaling: %w", err)
|
||||
return fmt.Errorf("error marshaling %q: %w", path, err)
|
||||
}
|
||||
|
||||
if err = encoder.Close(); err != nil {
|
||||
@ -52,11 +55,11 @@ func LoadOrNewFromFile[T any](root xfs.Root, path string, empty T, generate func
|
||||
defer f.Close() //nolint:errcheck
|
||||
|
||||
if err = yaml.NewDecoder(f).Decode(empty); err != nil {
|
||||
return fmt.Errorf("error unmarshaling: %w", err)
|
||||
return fmt.Errorf("error unmarshaling %q: %w", path, err)
|
||||
}
|
||||
|
||||
if reflect.ValueOf(empty).Elem().IsZero() {
|
||||
return errors.New("value is still zero after unmarshaling")
|
||||
return fmt.Errorf("value of %q is still zero after unmarshaling", path)
|
||||
}
|
||||
|
||||
return f.Close()
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/opentree"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/xfs/opentree"
|
||||
)
|
||||
|
||||
// KernelCap represents kernel capabilities that we can check at runtime.
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
"github.com/siderolabs/go-pointer"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/mount/v3"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
// Spec specifies what has to be mounted.
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -340,7 +341,7 @@ func sdBootFilePath(arch string) (string, error) {
|
||||
//nolint:gocyclo,cyclop
|
||||
func (c *Config) install(opts options.InstallOptions) (*options.InstallResult, error) {
|
||||
if _, err := os.Stat(filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "loader", "loader.conf")); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"encoding/xml"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/netip"
|
||||
"os"
|
||||
@ -272,7 +273,7 @@ func (a *Azure) configFromCD() ([]byte, error) {
|
||||
ovfEnvFile, err := os.ReadFile(filepath.Join(mnt, "ovf-env.xml"))
|
||||
if err != nil {
|
||||
// Device mount worked, but it wasn't the "CD" that contains the xml file
|
||||
if os.IsNotExist(err) {
|
||||
if stderrors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,9 @@ package metal
|
||||
|
||||
import (
|
||||
"context"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -28,7 +30,6 @@ import (
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/oauth2"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url"
|
||||
"github.com/siderolabs/talos/internal/pkg/mount/v3"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/download"
|
||||
"github.com/siderolabs/talos/pkg/machinery/cel"
|
||||
"github.com/siderolabs/talos/pkg/machinery/cel/celenv"
|
||||
@ -38,6 +39,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
|
||||
runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -89,7 +91,7 @@ func (m *Metal) Configuration(ctx context.Context, r state.State) ([]byte, error
|
||||
}
|
||||
|
||||
oauth2Cfg, err := oauth2.NewConfig(procfs.ProcCmdline(), *option)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && !stderrors.Is(err, fs.ErrNotExist) {
|
||||
return nil, fmt.Errorf("failed to parse OAuth2 config: %w", err)
|
||||
}
|
||||
|
||||
@ -180,7 +182,6 @@ func readConfigFromISO(ctx context.Context, r state.State) ([]byte, error) {
|
||||
mount.WithFsopen(
|
||||
volumeStatus.TypedSpec().Filesystem.String(),
|
||||
fsopen.WithSource(volumeStatus.TypedSpec().MountLocation),
|
||||
fsopen.WithPrinter(log.Printf),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@ -6,9 +6,10 @@ package oauth2_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -70,7 +71,7 @@ func TestNewConfig(t *testing.T) { //nolint:tparallel
|
||||
cfg, err := oauth2.NewConfig(procfs.NewCmdline(test.cmdline), "https://example.com/my/config")
|
||||
if test.expected == nil {
|
||||
require.Error(t, err)
|
||||
assert.True(t, os.IsNotExist(err))
|
||||
assert.True(t, errors.Is(err, fs.ErrNotExist))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -84,7 +85,7 @@ func WaitForUSB(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
|
||||
|
||||
_, err := os.Stat(file)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -716,7 +717,7 @@ func existsAndIsFile(p string) (err error) {
|
||||
|
||||
info, err = os.Stat(p)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -829,7 +830,7 @@ func CordonAndDrainNode(runtime.Sequence, any) (runtime.TaskExecutionFunc, strin
|
||||
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
|
||||
// skip not exist error as it means that the node hasn't fully joined yet
|
||||
if _, err = os.Stat("/var/lib/kubelet/pki/kubelet-client-current.pem"); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -893,7 +894,7 @@ func LeaveEtcd(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
|
||||
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
|
||||
_, err = os.Stat(filepath.Join(constants.EtcdDataPath, "/member"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1002,7 +1003,7 @@ func stopAndRemoveAllPods(stopAction cri.StopAction) runtime.TaskExecutionFunc {
|
||||
}
|
||||
|
||||
// check that the CRI is running and the socket is available, if not, skip the rest
|
||||
if _, err = os.Stat(constants.CRIContainerdAddress); os.IsNotExist(err) {
|
||||
if _, err = os.Stat(constants.CRIContainerdAddress); errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1717,12 +1718,12 @@ func ForceCleanup(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
|
||||
func ReloadMeta(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
|
||||
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error {
|
||||
err := r.State().Machine().Meta().Reload(ctx)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
// attempt to populate meta from the environment if Talos is not installed (yet)
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
env := environment.Get(r.Config())
|
||||
|
||||
prefix := constants.MetaValuesEnvVar + "="
|
||||
|
||||
@ -40,12 +40,12 @@ import (
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
runtimelogging "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/system"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/logging"
|
||||
talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
configresource "github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
// Controller implements runtime.V1alpha2Controller.
|
||||
|
||||
@ -7,10 +7,11 @@ package containerd
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@ -180,7 +181,7 @@ func (c *containerdRunner) Run(eventSink events.Recorder) error {
|
||||
// if one still exists
|
||||
defer func() {
|
||||
err := cg.Delete()
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
eventSink(events.StateStopping, "Failed to remove cgroup for %s, %s", c, err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/errdefs"
|
||||
@ -95,7 +94,7 @@ func (r *readSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||
func openReaderAt(p string, statFS fs.StatFS) (content.ReaderAt, error) {
|
||||
fi, err := statFS.Stat(p)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -104,7 +103,7 @@ func openReaderAt(p string, statFS fs.StatFS) (content.ReaderAt, error) {
|
||||
|
||||
fp, err := statFS.Open(p)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ func (s *singleFileStore) Info(_ context.Context, dgst digest.Digest) (content.I
|
||||
|
||||
fi, err := s.root.Stat(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || errors.Is(err, errdefs.ErrNotFound) {
|
||||
if errors.Is(err, fs.ErrNotExist) || errors.Is(err, errdefs.ErrNotFound) {
|
||||
return content.Info{}, xerrors.NewTaggedf[notFoundTag]("content '%s': %w", dgst, errdefs.ErrNotFound)
|
||||
}
|
||||
|
||||
|
||||
@ -6,9 +6,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
|
||||
@ -52,7 +53,7 @@ func revertBootloadInternal(ctx context.Context, resourceState state.State) erro
|
||||
|
||||
metaState, err := meta.New(ctx, resourceState)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
// no META, no way to revert
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -28,7 +29,7 @@ import (
|
||||
|
||||
// ProvidesKernelModules returns true if the extension provides kernel modules.
|
||||
func (ext *Extension) ProvidesKernelModules(quirks quirks.Quirks) bool {
|
||||
if _, err := os.Stat(ext.KernelModuleDirectory(quirks)); os.IsNotExist(err) {
|
||||
if _, err := os.Stat(ext.KernelModuleDirectory(quirks)); errors.Is(err, fs.ErrNotExist) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,9 @@ package extensions
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@ -18,7 +20,7 @@ import (
|
||||
func List(rootPath string) ([]*Extension, error) {
|
||||
items, err := os.ReadDir(rootPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@ -15,8 +15,8 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/selinux"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
//
|
||||
@ -40,9 +40,7 @@ func NewCgroup2() *Manager {
|
||||
|
||||
// NewReadOnlyOverlay creates a new read-only overlay filesystem.
|
||||
func NewReadOnlyOverlay(sources []string, target string, printer func(string, ...any), options ...ManagerOption) *Manager {
|
||||
fsOptions := []fsopen.Option{
|
||||
fsopen.WithPrinter(printer),
|
||||
}
|
||||
fsOptions := []fsopen.Option{}
|
||||
|
||||
if printer != nil {
|
||||
printer("mounting %d overlays: %v", len(sources), sources)
|
||||
@ -58,8 +56,7 @@ func NewReadOnlyOverlay(sources []string, target string, printer func(string, ..
|
||||
|
||||
options = append(options,
|
||||
WithTarget(target),
|
||||
WithPrinter(printer),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_RDONLY),
|
||||
WithReadOnly(),
|
||||
WithFsopen("overlay", fsOptions...),
|
||||
)
|
||||
|
||||
@ -75,7 +72,6 @@ func NewOverlayWithBasePath(sources []string, target, basePath string, printer f
|
||||
workdir := fmt.Sprintf(filepath.Join(basePath, "%s-workdir"), overlayPrefix)
|
||||
|
||||
fsOptions := []fsopen.Option{
|
||||
fsopen.WithPrinter(printer),
|
||||
fsopen.WithStringParameter("upperdir", diff),
|
||||
fsopen.WithStringParameter("workdir", workdir),
|
||||
}
|
||||
@ -127,7 +123,6 @@ func Squashfs(target, squashfsFile string, printer func(string, ...any)) (*Manag
|
||||
"squashfs",
|
||||
fsopen.WithSource(dev.Path()),
|
||||
fsopen.WithBoolParameter("ro"),
|
||||
fsopen.WithPrinter(printer),
|
||||
),
|
||||
), nil
|
||||
}
|
||||
@ -184,7 +179,6 @@ func Pseudo(printer func(string, ...any)) Managers {
|
||||
WithFsopen(
|
||||
"devtmpfs",
|
||||
fsopen.WithStringParameter("mode", "0755"),
|
||||
fsopen.WithPrinter(printer),
|
||||
),
|
||||
),
|
||||
newManager(
|
||||
@ -192,13 +186,13 @@ func Pseudo(printer func(string, ...any)) Managers {
|
||||
WithTarget("/proc"),
|
||||
WithKeepOpenAfterMount(),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV),
|
||||
WithFsopen("proc", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("proc"),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
WithTarget("/sys"),
|
||||
WithKeepOpenAfterMount(),
|
||||
WithFsopen("sysfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("sysfs"),
|
||||
),
|
||||
)
|
||||
}
|
||||
@ -213,7 +207,6 @@ func PseudoLate(printer func(string, ...any)) Managers {
|
||||
WithSelinuxLabel(constants.RunSelinuxLabel),
|
||||
WithFsopen(
|
||||
"tmpfs",
|
||||
fsopen.WithPrinter(printer),
|
||||
fsopen.WithStringParameter("mode", "0755"),
|
||||
),
|
||||
),
|
||||
@ -223,7 +216,6 @@ func PseudoLate(printer func(string, ...any)) Managers {
|
||||
WithSelinuxLabel(constants.SystemSelinuxLabel),
|
||||
WithFsopen(
|
||||
"tmpfs",
|
||||
fsopen.WithPrinter(printer),
|
||||
fsopen.WithStringParameter("mode", "0755"),
|
||||
),
|
||||
),
|
||||
@ -233,7 +225,6 @@ func PseudoLate(printer func(string, ...any)) Managers {
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV),
|
||||
WithFsopen(
|
||||
"tmpfs",
|
||||
fsopen.WithPrinter(printer),
|
||||
fsopen.WithStringParameter("mode", "0755"),
|
||||
fsopen.WithStringParameter("size", "64M"),
|
||||
),
|
||||
@ -248,7 +239,7 @@ func PseudoSub(printer func(string, ...any)) Managers {
|
||||
always,
|
||||
WithTarget("/dev/shm"),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME),
|
||||
WithFsopen("tmpfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("tmpfs"),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
@ -259,55 +250,54 @@ func PseudoSub(printer func(string, ...any)) Managers {
|
||||
fsopen.WithStringParameter("ptmxmode", "000"),
|
||||
fsopen.WithStringParameter("mode", "620"),
|
||||
fsopen.WithStringParameter("gid", "5"),
|
||||
fsopen.WithPrinter(printer),
|
||||
),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV),
|
||||
WithTarget("/dev/hugepages"),
|
||||
WithFsopen("hugetlbfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("hugetlbfs"),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
WithTarget("/sys/fs/bpf"),
|
||||
WithFsopen("bpf", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("bpf"),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
WithTarget("/sys/kernel/security"),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME),
|
||||
WithFsopen("securityfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("securityfs"),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
WithTarget("/sys/kernel/tracing"),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV),
|
||||
WithFsopen("tracefs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("tracefs"),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
WithTarget("/sys/kernel/config"),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME),
|
||||
WithFsopen("configfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("configfs"),
|
||||
),
|
||||
newManager(
|
||||
always,
|
||||
WithTarget("/sys/kernel/debug"),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME),
|
||||
WithFsopen("debugfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("debugfs"),
|
||||
),
|
||||
newManager(
|
||||
selinux.IsEnabled,
|
||||
WithTarget("/sys/fs/selinux"),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_RELATIME),
|
||||
WithFsopen("selinuxfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("selinuxfs"),
|
||||
),
|
||||
newManager(
|
||||
hasEFIVars,
|
||||
WithTarget(constants.EFIVarsMountPoint),
|
||||
WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME|unix.MOUNT_ATTR_RDONLY),
|
||||
WithFsopen("efivarfs", fsopen.WithPrinter(printer)),
|
||||
WithFsopen("efivarfs"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
// Manager is the filesystem manager for mounting and unmounting filesystems.
|
||||
@ -26,6 +26,7 @@ type Manager struct {
|
||||
shared bool
|
||||
skipIfMounted bool
|
||||
keepOpen bool
|
||||
detached bool
|
||||
mountattr int
|
||||
extraDirs []string
|
||||
extraUnmountCallbacks []func(m *Manager)
|
||||
@ -71,6 +72,7 @@ func (m *Manager) Mount() (*Point, error) {
|
||||
|
||||
m.point = &Point{
|
||||
root: root,
|
||||
detached: m.detached,
|
||||
keepOpen: m.keepOpen,
|
||||
target: m.target,
|
||||
selinuxLabel: m.selinuxLabel,
|
||||
@ -83,8 +85,6 @@ func (m *Manager) Mount() (*Point, error) {
|
||||
MountAttributes: m.mountattr,
|
||||
}
|
||||
|
||||
printer("mkdirAll %q", m.target)
|
||||
|
||||
if err := os.MkdirAll(m.target, 0o755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create mount target %s: %w", m.target, err)
|
||||
}
|
||||
@ -207,6 +207,16 @@ func WithReadOnly() ManagerOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDetached sets the mount as detached.
|
||||
func WithDetached() ManagerOption {
|
||||
return ManagerOption{
|
||||
set: func(m *Manager) {
|
||||
m.detached = true
|
||||
m.keepOpen = true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WithExtraDirs adds extra dirs to the manager that should be created prior to mounting the filesystem.
|
||||
func WithExtraDirs(dirs ...string) ManagerOption {
|
||||
return ManagerOption{
|
||||
|
||||
@ -11,19 +11,21 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/siderolabs/go-retry/retry"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/selinux"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// Point represents a mount point in the filesystem.
|
||||
type Point struct {
|
||||
root xfs.Root
|
||||
keepOpen bool
|
||||
detached bool
|
||||
fstype string
|
||||
source string
|
||||
target string
|
||||
@ -62,6 +64,10 @@ func (p *Point) Mount(opts Options) error {
|
||||
|
||||
defer p.Release(false) //nolint:errcheck
|
||||
|
||||
if p.detached {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.retry(func() error {
|
||||
if err := p.moveMount(p.target); err != nil {
|
||||
return fmt.Errorf("error mounting %q to %q: %w", p.Source(), p.target, err)
|
||||
@ -85,6 +91,10 @@ func (p *Point) Mount(opts Options) error {
|
||||
|
||||
// Share makes the mount point shared.
|
||||
func (p *Point) Share() error {
|
||||
if p.detached {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
|
||||
return p.setattr(&unix.MountAttr{
|
||||
Propagation: unix.MS_SHARED,
|
||||
}, unix.AT_RECURSIVE)
|
||||
@ -110,6 +120,10 @@ func (p *Point) Unmount(opts UnmountOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.detached {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.retry(func() error {
|
||||
return SafeUnmount(context.Background(), opts.Printer, p.target)
|
||||
}, true)
|
||||
@ -237,8 +251,17 @@ func (p *Point) FSType() string {
|
||||
return p.fstype
|
||||
}
|
||||
|
||||
// Root returns the underlying xfs.Root of the mount point.
|
||||
func (p *Point) Root() xfs.Root {
|
||||
return p.root
|
||||
}
|
||||
|
||||
// RemountReadOnly remounts the mount point as read-only.
|
||||
func (p *Point) RemountReadOnly() error {
|
||||
if p.detached {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
|
||||
return p.setattr(&unix.MountAttr{
|
||||
Attr_set: unix.MOUNT_ATTR_RDONLY,
|
||||
}, 0)
|
||||
@ -246,6 +269,10 @@ func (p *Point) RemountReadOnly() error {
|
||||
|
||||
// RemountReadWrite remounts the mount point as read-write.
|
||||
func (p *Point) RemountReadWrite() error {
|
||||
if p.detached {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.setattr(&unix.MountAttr{
|
||||
Attr_clr: unix.MOUNT_ATTR_RDONLY,
|
||||
}, 0)
|
||||
|
||||
@ -5,9 +5,10 @@
|
||||
package rng
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
@ -22,7 +23,7 @@ func TPMSeed() error {
|
||||
t, err := tpm.Open()
|
||||
if err != nil {
|
||||
// if the TPM is not available we can skip seeding random pool
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
log.Printf("TPM device is not available")
|
||||
|
||||
return nil
|
||||
|
||||
@ -8,9 +8,10 @@ package tpm2
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/google/go-tpm/tpm2/transport"
|
||||
@ -67,7 +68,7 @@ func PCRExtend(pcr int, data []byte) error {
|
||||
t, err := tpm.Open()
|
||||
if err != nil {
|
||||
// if the TPM is not available we can skip the PCR extension
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
log.Printf("TPM device is not available, skipping PCR extension")
|
||||
|
||||
return nil
|
||||
|
||||
@ -19,8 +19,8 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/containermode"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
//go:embed policy/policy.33
|
||||
|
||||
@ -6,7 +6,9 @@ package conditions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@ -25,7 +27,7 @@ func (filename file) Wait(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,9 @@ package conditions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@ -21,7 +23,7 @@ func (filename kubeconfig) Wait(ctx context.Context) error {
|
||||
|
||||
for {
|
||||
_, err := os.Stat(string(filename))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
package profile_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -79,7 +81,7 @@ func TestFillDefaults(t *testing.T) {
|
||||
require.NoError(t, p.Dump(&profileData))
|
||||
|
||||
expectedData, err := os.ReadFile("testdata/" + profile + "-" + arch + "-" + version + ".yaml")
|
||||
if os.IsNotExist(err) && recordMissing {
|
||||
if errors.Is(err, fs.ErrNotExist) && recordMissing {
|
||||
require.NoError(t, os.WriteFile("testdata/"+profile+"-"+arch+"-"+version+".yaml", []byte(profileData.String()), 0o644))
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -8,7 +8,9 @@ package kubelet
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
@ -49,7 +51,7 @@ func NewClient(nodename string, clientCert, clientKey, caPEM []byte) (*Client, e
|
||||
config.CAData = append(config.CAData, kubeletCert...)
|
||||
} else if err != nil {
|
||||
// ignore if file doesn't exist, assume cert isn't self-signed
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, fmt.Errorf("error reading kubelet certificate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -931,6 +931,7 @@ type MountRequestSpec struct {
|
||||
Requesters []string `protobuf:"bytes,3,rep,name=requesters,proto3" json:"requesters,omitempty"`
|
||||
RequesterIDs []string `protobuf:"bytes,4,rep,name=requester_i_ds,json=requesterIDs,proto3" json:"requester_i_ds,omitempty"`
|
||||
ReadOnly bool `protobuf:"varint,5,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
|
||||
Detached bool `protobuf:"varint,6,opt,name=detached,proto3" json:"detached,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -1000,6 +1001,13 @@ func (x *MountRequestSpec) GetReadOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *MountRequestSpec) GetDetached() bool {
|
||||
if x != nil {
|
||||
return x.Detached
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MountSpec is the spec for volume mount.
|
||||
type MountSpec struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@ -1111,6 +1119,7 @@ type MountStatusSpec struct {
|
||||
ReadOnly bool `protobuf:"varint,5,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
|
||||
ProjectQuotaSupport bool `protobuf:"varint,6,opt,name=project_quota_support,json=projectQuotaSupport,proto3" json:"project_quota_support,omitempty"`
|
||||
EncryptionProvider enums.BlockEncryptionProviderType `protobuf:"varint,7,opt,name=encryption_provider,json=encryptionProvider,proto3,enum=talos.resource.definitions.enums.BlockEncryptionProviderType" json:"encryption_provider,omitempty"`
|
||||
Detached bool `protobuf:"varint,8,opt,name=detached,proto3" json:"detached,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -1194,6 +1203,13 @@ func (x *MountStatusSpec) GetEncryptionProvider() enums.BlockEncryptionProviderT
|
||||
return enums.BlockEncryptionProviderType(0)
|
||||
}
|
||||
|
||||
func (x *MountStatusSpec) GetDetached() bool {
|
||||
if x != nil {
|
||||
return x.Detached
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PartitionSpec is the spec for volume partitioning.
|
||||
type PartitionSpec struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@ -1789,6 +1805,7 @@ type VolumeMountRequestSpec struct {
|
||||
VolumeId string `protobuf:"bytes,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"`
|
||||
Requester string `protobuf:"bytes,2,opt,name=requester,proto3" json:"requester,omitempty"`
|
||||
ReadOnly bool `protobuf:"varint,3,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
|
||||
Detached bool `protobuf:"varint,4,opt,name=detached,proto3" json:"detached,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -1844,6 +1861,13 @@ func (x *VolumeMountRequestSpec) GetReadOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *VolumeMountRequestSpec) GetDetached() bool {
|
||||
if x != nil {
|
||||
return x.Detached
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// VolumeMountStatusSpec is the spec for VolumeMountStatus.
|
||||
type VolumeMountStatusSpec struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@ -1851,6 +1875,7 @@ type VolumeMountStatusSpec struct {
|
||||
Requester string `protobuf:"bytes,2,opt,name=requester,proto3" json:"requester,omitempty"`
|
||||
Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"`
|
||||
ReadOnly bool `protobuf:"varint,4,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
|
||||
Detached bool `protobuf:"varint,5,opt,name=detached,proto3" json:"detached,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -1913,6 +1938,13 @@ func (x *VolumeMountStatusSpec) GetReadOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *VolumeMountStatusSpec) GetDetached() bool {
|
||||
if x != nil {
|
||||
return x.Detached
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// VolumeStatusSpec is the spec for VolumeStatus resource.
|
||||
type VolumeStatusSpec struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@ -2340,7 +2372,7 @@ const file_resource_definitions_block_block_proto_rawDesc = "" +
|
||||
"\x04type\x18\x01 \x01(\x0e25.talos.resource.definitions.enums.BlockFilesystemTypeR\x04type\x12\x14\n" +
|
||||
"\x05label\x18\x02 \x01(\tR\x05label\"J\n" +
|
||||
"\vLocatorSpec\x12;\n" +
|
||||
"\x05match\x18\x01 \x01(\v2%.google.api.expr.v1alpha1.CheckedExprR\x05match\"\xba\x01\n" +
|
||||
"\x05match\x18\x01 \x01(\v2%.google.api.expr.v1alpha1.CheckedExprR\x05match\"\xd6\x01\n" +
|
||||
"\x10MountRequestSpec\x12\x1b\n" +
|
||||
"\tvolume_id\x18\x01 \x01(\tR\bvolumeId\x12&\n" +
|
||||
"\x0fparent_mount_id\x18\x02 \x01(\tR\rparentMountId\x12\x1e\n" +
|
||||
@ -2348,7 +2380,8 @@ const file_resource_definitions_block_block_proto_rawDesc = "" +
|
||||
"requesters\x18\x03 \x03(\tR\n" +
|
||||
"requesters\x12$\n" +
|
||||
"\x0erequester_i_ds\x18\x04 \x03(\tR\frequesterIDs\x12\x1b\n" +
|
||||
"\tread_only\x18\x05 \x01(\bR\breadOnly\"\x90\x02\n" +
|
||||
"\tread_only\x18\x05 \x01(\bR\breadOnly\x12\x1a\n" +
|
||||
"\bdetached\x18\x06 \x01(\bR\bdetached\"\x90\x02\n" +
|
||||
"\tMountSpec\x12\x1f\n" +
|
||||
"\vtarget_path\x18\x01 \x01(\tR\n" +
|
||||
"targetPath\x12#\n" +
|
||||
@ -2358,7 +2391,7 @@ const file_resource_definitions_block_block_proto_rawDesc = "" +
|
||||
"\tfile_mode\x18\x05 \x01(\rR\bfileMode\x12\x10\n" +
|
||||
"\x03uid\x18\x06 \x01(\x03R\x03uid\x12\x10\n" +
|
||||
"\x03gid\x18\a \x01(\x03R\x03gid\x12+\n" +
|
||||
"\x11recursive_relabel\x18\b \x01(\bR\x10recursiveRelabel\"\xa1\x03\n" +
|
||||
"\x11recursive_relabel\x18\b \x01(\bR\x10recursiveRelabel\"\xbd\x03\n" +
|
||||
"\x0fMountStatusSpec\x12F\n" +
|
||||
"\x04spec\x18\x01 \x01(\v22.talos.resource.definitions.block.MountRequestSpecR\x04spec\x12\x16\n" +
|
||||
"\x06target\x18\x02 \x01(\tR\x06target\x12\x16\n" +
|
||||
@ -2368,7 +2401,8 @@ const file_resource_definitions_block_block_proto_rawDesc = "" +
|
||||
"filesystem\x12\x1b\n" +
|
||||
"\tread_only\x18\x05 \x01(\bR\breadOnly\x122\n" +
|
||||
"\x15project_quota_support\x18\x06 \x01(\bR\x13projectQuotaSupport\x12n\n" +
|
||||
"\x13encryption_provider\x18\a \x01(\x0e2=.talos.resource.definitions.enums.BlockEncryptionProviderTypeR\x12encryptionProvider\"\x8c\x01\n" +
|
||||
"\x13encryption_provider\x18\a \x01(\x0e2=.talos.resource.definitions.enums.BlockEncryptionProviderTypeR\x12encryptionProvider\x12\x1a\n" +
|
||||
"\bdetached\x18\b \x01(\bR\bdetached\"\x8c\x01\n" +
|
||||
"\rPartitionSpec\x12\x19\n" +
|
||||
"\bmin_size\x18\x01 \x01(\x04R\aminSize\x12\x19\n" +
|
||||
"\bmax_size\x18\x02 \x01(\x04R\amaxSize\x12\x12\n" +
|
||||
@ -2416,16 +2450,18 @@ const file_resource_definitions_block_block_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"encryption\x18\x06 \x01(\v20.talos.resource.definitions.block.EncryptionSpecR\n" +
|
||||
"encryption\x12S\n" +
|
||||
"\asymlink\x18\a \x01(\v29.talos.resource.definitions.block.SymlinkProvisioningSpecR\asymlink\"p\n" +
|
||||
"\asymlink\x18\a \x01(\v29.talos.resource.definitions.block.SymlinkProvisioningSpecR\asymlink\"\x8c\x01\n" +
|
||||
"\x16VolumeMountRequestSpec\x12\x1b\n" +
|
||||
"\tvolume_id\x18\x01 \x01(\tR\bvolumeId\x12\x1c\n" +
|
||||
"\trequester\x18\x02 \x01(\tR\trequester\x12\x1b\n" +
|
||||
"\tread_only\x18\x03 \x01(\bR\breadOnly\"\x87\x01\n" +
|
||||
"\tread_only\x18\x03 \x01(\bR\breadOnly\x12\x1a\n" +
|
||||
"\bdetached\x18\x04 \x01(\bR\bdetached\"\xa3\x01\n" +
|
||||
"\x15VolumeMountStatusSpec\x12\x1b\n" +
|
||||
"\tvolume_id\x18\x01 \x01(\tR\bvolumeId\x12\x1c\n" +
|
||||
"\trequester\x18\x02 \x01(\tR\trequester\x12\x16\n" +
|
||||
"\x06target\x18\x03 \x01(\tR\x06target\x12\x1b\n" +
|
||||
"\tread_only\x18\x04 \x01(\bR\breadOnly\"\x83\n" +
|
||||
"\tread_only\x18\x04 \x01(\bR\breadOnly\x12\x1a\n" +
|
||||
"\bdetached\x18\x05 \x01(\bR\bdetached\"\x83\n" +
|
||||
"\n" +
|
||||
"\x10VolumeStatusSpec\x12H\n" +
|
||||
"\x05phase\x18\x01 \x01(\x0e22.talos.resource.definitions.enums.BlockVolumePhaseR\x05phase\x12\x1a\n" +
|
||||
|
||||
@ -901,6 +901,16 @@ func (m *MountRequestSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.Detached {
|
||||
i--
|
||||
if m.Detached {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x30
|
||||
}
|
||||
if m.ReadOnly {
|
||||
i--
|
||||
if m.ReadOnly {
|
||||
@ -1065,6 +1075,16 @@ func (m *MountStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.Detached {
|
||||
i--
|
||||
if m.Detached {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x40
|
||||
}
|
||||
if m.EncryptionProvider != 0 {
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.EncryptionProvider))
|
||||
i--
|
||||
@ -1725,6 +1745,16 @@ func (m *VolumeMountRequestSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.Detached {
|
||||
i--
|
||||
if m.Detached {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if m.ReadOnly {
|
||||
i--
|
||||
if m.ReadOnly {
|
||||
@ -1782,6 +1812,16 @@ func (m *VolumeMountStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error)
|
||||
i -= len(m.unknownFields)
|
||||
copy(dAtA[i:], m.unknownFields)
|
||||
}
|
||||
if m.Detached {
|
||||
i--
|
||||
if m.Detached {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if m.ReadOnly {
|
||||
i--
|
||||
if m.ReadOnly {
|
||||
@ -2496,6 +2536,9 @@ func (m *MountRequestSpec) SizeVT() (n int) {
|
||||
if m.ReadOnly {
|
||||
n += 2
|
||||
}
|
||||
if m.Detached {
|
||||
n += 2
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
@ -2567,6 +2610,9 @@ func (m *MountStatusSpec) SizeVT() (n int) {
|
||||
if m.EncryptionProvider != 0 {
|
||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.EncryptionProvider))
|
||||
}
|
||||
if m.Detached {
|
||||
n += 2
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
@ -2803,6 +2849,9 @@ func (m *VolumeMountRequestSpec) SizeVT() (n int) {
|
||||
if m.ReadOnly {
|
||||
n += 2
|
||||
}
|
||||
if m.Detached {
|
||||
n += 2
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
@ -2828,6 +2877,9 @@ func (m *VolumeMountStatusSpec) SizeVT() (n int) {
|
||||
if m.ReadOnly {
|
||||
n += 2
|
||||
}
|
||||
if m.Detached {
|
||||
n += 2
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
@ -5535,6 +5587,26 @@ func (m *MountRequestSpec) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.ReadOnly = bool(v != 0)
|
||||
case 6:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Detached = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
@ -6008,6 +6080,26 @@ func (m *MountStatusSpec) UnmarshalVT(dAtA []byte) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 8:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Detached = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
@ -7607,6 +7699,26 @@ func (m *VolumeMountRequestSpec) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.ReadOnly = bool(v != 0)
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Detached = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
@ -7774,6 +7886,26 @@ func (m *VolumeMountStatusSpec) UnmarshalVT(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.ReadOnly = bool(v != 0)
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return protohelpers.ErrIntOverflow
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Detached = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
||||
|
||||
@ -7,8 +7,10 @@ package config
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -248,7 +250,7 @@ func (c *Config) Merge(cfg *Config) []Rename {
|
||||
}
|
||||
|
||||
func ensure(path string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
|
||||
config := &Config{
|
||||
Context: "",
|
||||
Contexts: map[string]*Context{},
|
||||
|
||||
@ -8,6 +8,7 @@ package bundle
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -53,7 +54,7 @@ func NewBundle(opts ...Option) (*Bundle, error) {
|
||||
for _, configType := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} {
|
||||
data, err := os.ReadFile(filepath.Join(options.ExistingConfigs, strings.ToLower(configType.String())+".yaml"))
|
||||
if err != nil {
|
||||
if configType == machine.TypeInit && os.IsNotExist(err) {
|
||||
if configType == machine.TypeInit && errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -85,7 +86,7 @@ func NewBundle(opts ...Option) (*Bundle, error) {
|
||||
|
||||
// Pull existing talosconfig
|
||||
talosConfig, err := os.Open(filepath.Join(options.ExistingConfigs, "talosconfig"))
|
||||
if os.IsNotExist(err) { // talosconfig is optional
|
||||
if errors.Is(err, fs.ErrNotExist) { // talosconfig is optional
|
||||
return bundle, nil
|
||||
}
|
||||
|
||||
|
||||
@ -59,6 +59,11 @@ func NewFromFile(filepath string) (config.Provider, error) {
|
||||
return newConfig(f)
|
||||
}
|
||||
|
||||
// NewFromReader will take a reader and attempt to parse a config file from it.
|
||||
func NewFromReader(f io.Reader) (config.Provider, error) {
|
||||
return newConfig(f)
|
||||
}
|
||||
|
||||
// NewFromStdin initializes a config provider by reading from stdin.
|
||||
func NewFromStdin() (config.Provider, error) {
|
||||
return newConfig(os.Stdin)
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@ -67,7 +68,7 @@ func (fd *FileDoc) Encode(root *Doc, frontmatter func(title, description string)
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (fd *FileDoc) Write(path string, frontmatter func(title, description string) string) error {
|
||||
if stat, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
if stat, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {
|
||||
if !stat.IsDir() {
|
||||
return errors.New("destination path should be a directory")
|
||||
}
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
package v1alpha1_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -120,7 +122,7 @@ func testConfigStability(t *testing.T, in *generate.Input, versionContract *conf
|
||||
expectedPath := fmt.Sprintf("testdata/stability/%s/%s-%s.yaml", versionContract, flavor, machineType)
|
||||
|
||||
expectedBytes, err := os.ReadFile(expectedPath)
|
||||
if os.IsNotExist(err) && generateMode {
|
||||
if errors.Is(err, fs.ErrNotExist) && generateMode {
|
||||
require.NoError(t, os.WriteFile(expectedPath, cfgBytes, 0o644))
|
||||
|
||||
t.Logf("generated %s", expectedPath)
|
||||
|
||||
@ -6,7 +6,9 @@ package nethelpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -41,7 +43,7 @@ func GetDeviceInfo(deviceName string) (*DeviceInfo, error) {
|
||||
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return &DeviceInfo{}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ type MountRequestSpec struct {
|
||||
|
||||
ParentMountID string `yaml:"parentID" protobuf:"2"`
|
||||
ReadOnly bool `yaml:"readOnly" protobuf:"5"`
|
||||
Detached bool `yaml:"detached" protobuf:"6"`
|
||||
|
||||
Requesters []string `yaml:"requesters" protobuf:"3"`
|
||||
RequesterIDs []string `yaml:"requesterIDs" protobuf:"4"`
|
||||
|
||||
@ -31,6 +31,20 @@ type MountStatusSpec struct {
|
||||
|
||||
ReadOnly bool `yaml:"readOnly" protobuf:"5"`
|
||||
ProjectQuotaSupport bool `yaml:"projectQuotaSupport" protobuf:"6"`
|
||||
Detached bool `yaml:"detached" protobuf:"8"`
|
||||
|
||||
root any
|
||||
}
|
||||
|
||||
// SetRoot sets the XFS root for the mount.
|
||||
func (m *MountStatusSpec) SetRoot(root any) {
|
||||
m.root = root
|
||||
}
|
||||
|
||||
// Root gets the XFS root for the mount.
|
||||
// It's not guaranteed to be set (may be nil).
|
||||
func (m *MountStatusSpec) Root() any {
|
||||
return m.root
|
||||
}
|
||||
|
||||
// NewMountStatus initializes a MountStatus resource.
|
||||
|
||||
@ -27,6 +27,8 @@ type VolumeMountRequestSpec struct {
|
||||
|
||||
ReadOnly bool `yaml:"readOnly" protobuf:"3"`
|
||||
|
||||
Detached bool `yaml:"detached" protobuf:"4"`
|
||||
|
||||
Requester string `yaml:"requester" protobuf:"2"`
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,20 @@ type VolumeMountStatusSpec struct {
|
||||
|
||||
Target string `yaml:"target" protobuf:"3"`
|
||||
ReadOnly bool `yaml:"readOnly" protobuf:"4"`
|
||||
Detached bool `yaml:"detached" protobuf:"5"`
|
||||
|
||||
root any
|
||||
}
|
||||
|
||||
// SetRoot sets the XFS root for the mount.
|
||||
func (m *VolumeMountStatusSpec) SetRoot(root any) {
|
||||
m.root = root
|
||||
}
|
||||
|
||||
// Root gets the XFS root for the mount.
|
||||
// It's not guaranteed to be set (may be nil).
|
||||
func (m *VolumeMountStatusSpec) Root() any {
|
||||
return m.root
|
||||
}
|
||||
|
||||
// NewVolumeMountStatus initializes a VolumeMountStatus resource.
|
||||
|
||||
@ -5,8 +5,10 @@
|
||||
package qemu
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/provision/providers/vm"
|
||||
@ -37,7 +39,7 @@ func (p *provisioner) createPFlashImages(state *vm.State, nodeName string, pflas
|
||||
|
||||
src, err = os.Open(sourcePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,9 @@ package qemu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -42,7 +44,7 @@ func (check *preflightCheckContext) cniDirectories(ctx context.Context) error {
|
||||
for _, cniDir := range cniDirs {
|
||||
st, err := os.Stat(cniDir)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("error checking CNI directory %q: %w", cniDir, err)
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,9 @@ package vm
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -56,7 +58,7 @@ func DumpIPAMRecord(statePath string, record IPAMRecord) error {
|
||||
func LoadIPAMRecords(statePath string) (IPAMDatabase, error) {
|
||||
f, err := os.Open(filepath.Join(statePath, dbFile))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
@ -17,7 +19,7 @@ import (
|
||||
func StopProcessByPidfile(pidPath string) error {
|
||||
pidFile, err := os.Open(pidPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,9 @@ package vm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -21,7 +23,7 @@ func (p *Provisioner) Reflect(ctx context.Context, clusterName, stateDirectory s
|
||||
|
||||
st, err := os.Stat(statePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, fmt.Errorf("cluster %q not found: %w", clusterName, err)
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ package vm
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -44,7 +45,7 @@ func NewState(statePath, provisionerName, clusterName string) (*State, error) {
|
||||
)
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, fmt.Errorf("error checking state directory: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
// 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/.
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Package fsopen provides a simple interface to create and manage a filesystem
|
||||
// using the Linux syscalls for filesystem operations.
|
||||
package fsopen
|
||||
@ -14,8 +16,8 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/makefs"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// ErrRepairUnsupported is reported when the filesystem does not support repairs.
|
||||
@ -28,8 +30,6 @@ type FS struct {
|
||||
fstype string
|
||||
source string
|
||||
|
||||
printer func(string, ...any)
|
||||
|
||||
boolParams map[string]struct{}
|
||||
stringParams map[string][]string
|
||||
binaryParams map[string][][]byte
|
||||
@ -84,19 +84,10 @@ func (fs *FS) Open() (int, error) {
|
||||
return fs.mntfd, err
|
||||
}
|
||||
|
||||
func discard(string, ...any) {}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (fs *FS) new() (err error) {
|
||||
var fsfd int
|
||||
|
||||
printer := discard
|
||||
if fs.printer != nil {
|
||||
printer = fs.printer
|
||||
}
|
||||
|
||||
printer("creating filesystem of type: %q", fs.fstype)
|
||||
|
||||
fsfd, err = unix.Fsopen(fs.fstype, unix.FSOPEN_CLOEXEC)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unix.Fsopen fstype=%q failed: %w", fs.fstype, err)
|
||||
@ -111,16 +102,12 @@ func (fs *FS) new() (err error) {
|
||||
}()
|
||||
|
||||
if fs.source != "" {
|
||||
printer("setting source: %q", fs.source)
|
||||
|
||||
if err := unix.FsconfigSetString(fsfd, "source", fs.source); err != nil {
|
||||
return fmt.Errorf("FSCONFIG_SET_STRING failed: %w: key=%q value=%q", err, "source", fs.source)
|
||||
}
|
||||
}
|
||||
|
||||
for key := range fs.boolParams {
|
||||
printer("setting boolean flag: %q", key)
|
||||
|
||||
if err := unix.FsconfigSetFlag(fsfd, key); err != nil {
|
||||
return fmt.Errorf("FSCONFIG_SET_FLAG failed: %w: key=%q", err, key)
|
||||
}
|
||||
@ -128,8 +115,6 @@ func (fs *FS) new() (err error) {
|
||||
|
||||
for key, binary := range fs.binaryParams {
|
||||
for _, bf := range binary {
|
||||
printer("setting binary param: %q", key)
|
||||
|
||||
if err := unix.FsconfigSetBinary(fsfd, key, bf); err != nil {
|
||||
return fmt.Errorf("FSCONFIG_SET_BINARY failed: %w: key=%q", err, key)
|
||||
}
|
||||
@ -138,8 +123,6 @@ func (fs *FS) new() (err error) {
|
||||
|
||||
for key, values := range fs.stringParams {
|
||||
for _, value := range values {
|
||||
printer("setting string param: %q=%q", key, value)
|
||||
|
||||
if err := unix.FsconfigSetString(fsfd, key, value); err != nil {
|
||||
return fmt.Errorf("FSCONFIG_SET_BINARY failed: %w: key=%q", err, key)
|
||||
}
|
||||
@ -2,6 +2,8 @@
|
||||
// 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/.
|
||||
|
||||
//go:build linux
|
||||
|
||||
package fsopen
|
||||
|
||||
// Option is a functional option for configuring a filesystem instance.
|
||||
@ -27,15 +29,6 @@ func WithMountFlags(flag int) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithPrinter adds a printer to the filesystem configuration.
|
||||
func WithPrinter(printer func(string, ...any)) Option {
|
||||
return Option{
|
||||
set: func(t *FS) {
|
||||
t.printer = printer
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WithProjectQuota sets the project quota flag.
|
||||
func WithProjectQuota(enabled bool) Option {
|
||||
return Option{
|
||||
@ -2,7 +2,7 @@
|
||||
// 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/.
|
||||
|
||||
//go:build unix
|
||||
//go:build linux
|
||||
|
||||
package xfs_test
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
)
|
||||
|
||||
func TestFsopen(t *testing.T) {
|
||||
@ -32,9 +32,7 @@ func TestFsopen(t *testing.T) {
|
||||
t.Run(tc.fstype, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fs := fsopen.New(tc.fstype, tc.opts...)
|
||||
|
||||
root := &xfs.UnixRoot{FS: fs}
|
||||
root := &xfs.UnixRoot{FS: fsopen.New(tc.fstype, tc.opts...)}
|
||||
|
||||
err := root.OpenFS()
|
||||
require.NoError(t, err)
|
||||
30
pkg/xfs/helpers_linux.go
Normal file
30
pkg/xfs/helpers_linux.go
Normal file
@ -0,0 +1,30 @@
|
||||
// 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/.
|
||||
|
||||
//go:build linux
|
||||
|
||||
package xfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// AsOSFile attempts to convert fs.File to *os.File.
|
||||
func AsOSFile(f fs.File, name string) (*os.File, error) {
|
||||
ff, ok := f.(File)
|
||||
if !ok {
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
|
||||
newFd, err := syscall.Dup(int(ff.Fd()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to duplicate file descriptor: %w", err)
|
||||
}
|
||||
|
||||
return os.NewFile(uintptr(newFd), name), nil
|
||||
}
|
||||
@ -2,6 +2,8 @@
|
||||
// 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/.
|
||||
|
||||
//go:build linux
|
||||
|
||||
// Package opentree provides a simple interface to create and manage a subfilesystem
|
||||
// using the `open_tree` syscall. It allows for creating a new subfilesystem
|
||||
// by cloning an existing filesystem tree and provides a method to close the filesystem
|
||||
@ -13,7 +15,7 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
xfs "github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
xfs "github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
// FS represents a subfilesystem that can be created and managed.
|
||||
@ -2,7 +2,7 @@
|
||||
// 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/.
|
||||
|
||||
//go:build unix
|
||||
//go:build linux
|
||||
|
||||
package xfs_test
|
||||
|
||||
@ -13,9 +13,9 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs/opentree"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs/fsopen"
|
||||
"github.com/siderolabs/talos/pkg/xfs/opentree"
|
||||
)
|
||||
|
||||
func TestOpentree(t *testing.T) {
|
||||
@ -30,9 +30,7 @@ func TestOpentree(t *testing.T) {
|
||||
|
||||
testRoot := t.TempDir()
|
||||
|
||||
fs := opentree.NewFromPath(testRoot)
|
||||
|
||||
root := &xfs.UnixRoot{FS: fs}
|
||||
root := &xfs.UnixRoot{FS: opentree.NewFromPath(testRoot)}
|
||||
|
||||
err := root.OpenFS()
|
||||
require.NoError(t, err)
|
||||
@ -86,9 +84,7 @@ func TestOpentree(t *testing.T) {
|
||||
t.Skip("OpenTree on Anonymous FS requires kernel 6.15.0+")
|
||||
}
|
||||
|
||||
fs := fsopen.New("tmpfs")
|
||||
|
||||
roRoot := &xfs.UnixRoot{FS: fs}
|
||||
roRoot := &xfs.UnixRoot{FS: fsopen.New("tmpfs")}
|
||||
|
||||
err = roRoot.OpenFS()
|
||||
require.NoError(t, err)
|
||||
@ -107,10 +103,6 @@ func TestOpentree(t *testing.T) {
|
||||
err = rwRoot.OpenFS()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, fs.Close())
|
||||
})
|
||||
|
||||
testFilesystem(t, rwRoot, roRoot)
|
||||
})
|
||||
}
|
||||
@ -60,6 +60,11 @@ func (root *OSRoot) Remove(name string) error {
|
||||
return os.Remove(filepath.Join(root.Shadow, name))
|
||||
}
|
||||
|
||||
// Rename renames a file or directory in the root filesystem from old to new.
|
||||
func (root *OSRoot) Rename(oldname, newname string) error {
|
||||
return os.Rename(filepath.Join(root.Shadow, oldname), filepath.Join(root.Shadow, newname))
|
||||
}
|
||||
|
||||
// Source returns the source of the underlying filesystem.
|
||||
func (root *OSRoot) Source() string {
|
||||
return root.Shadow
|
||||
@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
func TestOs(t *testing.T) {
|
||||
@ -18,7 +18,6 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// FS is an interface for creating file system handles.
|
||||
@ -32,6 +31,8 @@ type FS interface {
|
||||
}
|
||||
|
||||
// Root is an interface that extends the standard fs.FS interface with Write capabilities.
|
||||
//
|
||||
//nolint:interfacebloat
|
||||
type Root interface {
|
||||
fs.FS
|
||||
|
||||
@ -43,6 +44,7 @@ type Root interface {
|
||||
Mkdir(name string, perm os.FileMode) error
|
||||
OpenFile(name string, flags int, perm os.FileMode) (File, error)
|
||||
Remove(name string) error
|
||||
Rename(oldname, newname string) error
|
||||
|
||||
Source() string
|
||||
FSType() string
|
||||
@ -65,22 +67,22 @@ var _ File = (*os.File)(nil)
|
||||
|
||||
// ReadFile wraps fs.ReadFile to read a file from the specified FileSystem.
|
||||
func ReadFile(root Root, name string) ([]byte, error) {
|
||||
return fs.ReadFile(root, strings.TrimLeft(name, "/"))
|
||||
return fs.ReadFile(root, name)
|
||||
}
|
||||
|
||||
// ReadDir wraps fs.ReadDir to read a directory from the specified FileSystem.
|
||||
func ReadDir(root Root, name string) ([]fs.DirEntry, error) {
|
||||
return fs.ReadDir(root, strings.TrimLeft(name, "/"))
|
||||
return fs.ReadDir(root, name)
|
||||
}
|
||||
|
||||
// Stat wraps fs.Stat to get the file or directory information from the specified FileSystem.
|
||||
func Stat(root Root, name string) (fs.FileInfo, error) {
|
||||
return fs.Stat(root, strings.TrimLeft(name, "/"))
|
||||
return fs.Stat(root, name)
|
||||
}
|
||||
|
||||
// WriteFile is equivalent of os.WriteFile acting on specified FileSystem.
|
||||
func WriteFile(root Root, name string, data []byte, perm os.FileMode) error {
|
||||
f, err := root.OpenFile(strings.TrimLeft(name, "/"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||
f, err := root.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -93,39 +95,24 @@ func WriteFile(root Root, name string, data []byte, perm os.FileMode) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// AsOSFile attempts to convert fs.File to *os.File.
|
||||
func AsOSFile(f fs.File, name string) (*os.File, error) {
|
||||
ff, ok := f.(File)
|
||||
if !ok {
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
|
||||
newFd, err := syscall.Dup(int(ff.Fd()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to duplicate file descriptor: %w", err)
|
||||
}
|
||||
|
||||
return os.NewFile(uintptr(newFd), name), nil
|
||||
}
|
||||
|
||||
// Open wraps (FS).Open.
|
||||
func Open(root Root, name string) (fs.File, error) {
|
||||
return root.Open(strings.TrimLeft(name, "/"))
|
||||
return root.Open(name)
|
||||
}
|
||||
|
||||
// OpenFile wraps (FS).OpenFile.
|
||||
func OpenFile(root Root, name string, flags int, perm os.FileMode) (File, error) {
|
||||
return root.OpenFile(strings.TrimLeft(name, "/"), flags, perm)
|
||||
return root.OpenFile(name, flags, perm)
|
||||
}
|
||||
|
||||
// Mkdir wraps (FS).Mkdir.
|
||||
func Mkdir(root Root, name string, perm os.FileMode) error {
|
||||
return root.Mkdir(strings.TrimLeft(name, "/"), perm)
|
||||
return root.Mkdir(name, perm)
|
||||
}
|
||||
|
||||
// MkdirAll is equivalent of os.MkdirAll acting on specified FileSystem.
|
||||
func MkdirAll(root Root, name string, perm os.FileMode) error {
|
||||
components := SplitPath(strings.TrimLeft(name, "/"))
|
||||
components := SplitPath(name)
|
||||
|
||||
for i := range len(components) + 1 {
|
||||
dir := filepath.Join(components[:i]...)
|
||||
@ -149,8 +136,6 @@ func MkdirTemp(root Root, dir, pattern string) (string, error) {
|
||||
dir = os.TempDir()
|
||||
}
|
||||
|
||||
dir = strings.TrimLeft(dir, "/")
|
||||
|
||||
if pattern == "" {
|
||||
pattern = "tmp"
|
||||
}
|
||||
@ -188,14 +173,14 @@ func MkdirTemp(root Root, dir, pattern string) (string, error) {
|
||||
|
||||
// Remove wraps (FS).Remove.
|
||||
func Remove(root Root, name string) error {
|
||||
return root.Remove(strings.TrimLeft(name, "/"))
|
||||
return root.Remove(name)
|
||||
}
|
||||
|
||||
// RemoveAll is equivalent of os.RemoveAll acting on specified FileSystem.
|
||||
func RemoveAll(root Root, name string) (err error) {
|
||||
var f fs.File
|
||||
|
||||
f, err = root.Open(strings.TrimLeft(name, "/"))
|
||||
f, err = root.Open(name)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
@ -214,22 +199,27 @@ func RemoveAll(root Root, name string) (err error) {
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
return root.Remove(strings.TrimLeft(name, "/"))
|
||||
return root.Remove(name)
|
||||
}
|
||||
|
||||
entries, err := ReadDir(root, strings.TrimLeft(name, "/"))
|
||||
entries, err := ReadDir(root, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
childPath := filepath.Join(strings.TrimLeft(name, "/"), entry.Name())
|
||||
childPath := filepath.Join(name, entry.Name())
|
||||
if err := RemoveAll(root, childPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return root.Remove(strings.TrimLeft(name, "/"))
|
||||
return root.Remove(name)
|
||||
}
|
||||
|
||||
// Rename wraps (FS).Rename.
|
||||
func Rename(root Root, oldname, newname string) error {
|
||||
return root.Rename(oldname, newname)
|
||||
}
|
||||
|
||||
// SplitPath splits a path into its components, similar to filepath.Split but returns all parts.
|
||||
@ -2,12 +2,15 @@
|
||||
// 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/.
|
||||
|
||||
//go:build linux
|
||||
|
||||
package xfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
@ -70,29 +73,29 @@ func (root *UnixRoot) Fd() (int, error) {
|
||||
|
||||
// Mkdir creates a new directory in the root filesystem with the specified name and permissions.
|
||||
func (root *UnixRoot) Mkdir(name string, perm os.FileMode) error {
|
||||
return unix.Mkdirat(root.mntfd, name, uint32(perm))
|
||||
return unix.Mkdirat(root.mntfd, strings.TrimLeft(name, "/"), uint32(perm))
|
||||
}
|
||||
|
||||
// Open opens a file in the root filesystem with the specified name in read-only mode.
|
||||
func (root *UnixRoot) Open(name string) (fs.File, error) {
|
||||
return root.OpenFile(name, unix.O_RDONLY, 0)
|
||||
return root.OpenFile(strings.TrimLeft(name, "/"), unix.O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// OpenFile opens a file in the root filesystem with the specified name, flags, and permissions.
|
||||
func (root *UnixRoot) OpenFile(name string, flags int, perm os.FileMode) (File, error) {
|
||||
fd, err := unix.Openat(root.mntfd, name, flags, uint32(perm))
|
||||
fd, err := unix.Openat(root.mntfd, strings.TrimLeft(name, "/"), flags, uint32(perm))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return os.NewFile(uintptr(fd), name), nil
|
||||
return os.NewFile(uintptr(fd), strings.TrimLeft(name, "/")), nil
|
||||
}
|
||||
|
||||
// Remove removes a file or directory from the root filesystem.
|
||||
func (root *UnixRoot) Remove(name string) error {
|
||||
flags := 0
|
||||
|
||||
info, err := root.stat(name)
|
||||
info, err := root.stat(strings.TrimLeft(name, "/"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -101,11 +104,16 @@ func (root *UnixRoot) Remove(name string) error {
|
||||
flags = unix.AT_REMOVEDIR
|
||||
}
|
||||
|
||||
return unix.Unlinkat(root.mntfd, name, flags)
|
||||
return unix.Unlinkat(root.mntfd, strings.TrimLeft(name, "/"), flags)
|
||||
}
|
||||
|
||||
// Rename renames a file or directory in the root filesystem from old to new.
|
||||
func (root *UnixRoot) Rename(oldname, newname string) error {
|
||||
return unix.Renameat(root.mntfd, strings.TrimLeft(oldname, "/"), root.mntfd, strings.TrimLeft(newname, "/"))
|
||||
}
|
||||
|
||||
func (root *UnixRoot) stat(name string) (os.FileInfo, error) {
|
||||
f, err := root.Open(name)
|
||||
f, err := root.Open(strings.TrimLeft(name, "/"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/xfs"
|
||||
"github.com/siderolabs/talos/pkg/xfs"
|
||||
)
|
||||
|
||||
const testDir = "testdir"
|
||||
@ -184,6 +184,50 @@ func testFilesystem(t *testing.T, rwRoot xfs.Root, roRoot xfs.Root) {
|
||||
require.NoError(t, err, "stat file %q failed", name)
|
||||
assert.False(t, actual.IsDir())
|
||||
})
|
||||
|
||||
t.Run("Rename", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Dir", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
oldName := filepath.Join(testDir, "rename.old.d", "test")
|
||||
newName := filepath.Join(testDir, "rename.new.d", "test")
|
||||
|
||||
touchTree(t, rwRoot, oldName)
|
||||
|
||||
err := xfs.Rename(rwRoot, filepath.Dir(oldName), filepath.Dir(newName))
|
||||
assert.NoError(t, err)
|
||||
|
||||
newDirStat, err := xfs.Stat(roRoot, filepath.Dir(newName))
|
||||
require.NoError(t, err, "stat dir %q failed", filepath.Dir(newName))
|
||||
assert.True(t, newDirStat.IsDir())
|
||||
|
||||
_, err = xfs.Stat(roRoot, newName)
|
||||
require.NoError(t, err, "stat file %q failed", newName)
|
||||
|
||||
_, err = xfs.Stat(roRoot, oldName)
|
||||
require.ErrorIs(t, err, os.ErrNotExist, "stat dir %q failed", filepath.Dir(oldName))
|
||||
})
|
||||
|
||||
t.Run("File", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
oldName := filepath.Join(testDir, "rename.old.test")
|
||||
newName := filepath.Join(testDir, "rename.new.test")
|
||||
|
||||
touchTree(t, rwRoot, oldName)
|
||||
|
||||
err := xfs.Rename(rwRoot, oldName, newName)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = xfs.Stat(roRoot, newName)
|
||||
require.NoError(t, err, "stat file %q failed", newName)
|
||||
|
||||
_, err = xfs.Stat(roRoot, oldName)
|
||||
require.ErrorIs(t, err, os.ErrNotExist, "stat file %q failed", oldName)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func writeFile(tb testing.TB, root xfs.Root, name string, content []byte) {
|
||||
@ -7,7 +7,9 @@ package main
|
||||
|
||||
//nolint:gci
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@ -148,7 +150,7 @@ func withFile(filename string, fn func(f *os.File) error) error {
|
||||
dir := filepath.Dir(filename)
|
||||
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
err = os.MkdirAll(dir, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -136,6 +136,10 @@ func ParseDeclsData(sortedPkgs slices.Sorted[*PkgDecl], taggedStructs ast.Tagged
|
||||
for j := 0; j < structType.NumFields(); j++ {
|
||||
field := structType.Field(j)
|
||||
|
||||
if !field.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
v := sliceutil.GetOrAdd(&result, &Type{
|
||||
Pkg: pkg.path,
|
||||
Name: structName,
|
||||
@ -184,6 +188,10 @@ func FindExternalTypes(pkgsTypes slices.Sorted[*Type], taggedStructs ast.TaggedS
|
||||
for j := 0; j < typ.fields.Len(); j++ {
|
||||
field := typ.fields.Get(j)
|
||||
|
||||
if !field.TypeData.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
typeData := TypeInfo(field.TypeData.Type())
|
||||
|
||||
if typePkg := typeData.typePkg(); typePkg != "" {
|
||||
|
||||
@ -5426,6 +5426,7 @@ MountRequestSpec is the spec for MountRequest.
|
||||
| requesters | [string](#string) | repeated | |
|
||||
| requester_i_ds | [string](#string) | repeated | |
|
||||
| read_only | [bool](#bool) | | |
|
||||
| detached | [bool](#bool) | | |
|
||||
|
||||
|
||||
|
||||
@ -5469,6 +5470,7 @@ MountStatusSpec is the spec for MountStatus.
|
||||
| read_only | [bool](#bool) | | |
|
||||
| project_quota_support | [bool](#bool) | | |
|
||||
| encryption_provider | [talos.resource.definitions.enums.BlockEncryptionProviderType](#talos.resource.definitions.enums.BlockEncryptionProviderType) | | |
|
||||
| detached | [bool](#bool) | | |
|
||||
|
||||
|
||||
|
||||
@ -5644,6 +5646,7 @@ VolumeMountRequestSpec is the spec for VolumeMountRequest.
|
||||
| volume_id | [string](#string) | | |
|
||||
| requester | [string](#string) | | |
|
||||
| read_only | [bool](#bool) | | |
|
||||
| detached | [bool](#bool) | | |
|
||||
|
||||
|
||||
|
||||
@ -5662,6 +5665,7 @@ VolumeMountStatusSpec is the spec for VolumeMountStatus.
|
||||
| requester | [string](#string) | | |
|
||||
| target | [string](#string) | | |
|
||||
| read_only | [bool](#bool) | | |
|
||||
| detached | [bool](#bool) | | |
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user