feat: implement block device wipe

Fixes #9731

The wipe doesn't require a reboot, but it requires the blockdevice not
to be used as a volume.

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
Andrey Smirnov 2024-11-18 12:58:52 +04:00
parent 6fb518ae57
commit cc768037f8
No known key found for this signature in database
GPG Key ID: FE042E3D4085A811
20 changed files with 1752 additions and 225 deletions

View File

@ -11,6 +11,12 @@ import "google/protobuf/empty.proto";
// StorageService represents the storage service.
service StorageService {
rpc Disks(google.protobuf.Empty) returns (DisksResponse);
// BlockDeviceWipe performs a wipe of the blockdevice (partition or disk).
//
// The method doesn't require a reboot, and it can only wipe blockdevices which are not
// being used as volumes at the moment.
// Wiping of volumes requires a different API.
rpc BlockDeviceWipe(BlockDeviceWipeRequest) returns (BlockDeviceWipeResponse);
}
// Disk represents a disk.
@ -60,3 +66,39 @@ message Disks {
message DisksResponse {
repeated Disks messages = 1;
}
// rpc BlockDeviceWipe
message BlockDeviceWipeRequest {
repeated BlockDeviceWipeDescriptor devices = 1;
}
// BlockDeviceWipeDescriptor represents a single block device to be wiped.
//
// The device can be either a full disk (e.g. vda) or a partition (vda5).
// The device should not be used in any of active volumes.
// The device should not be used as a secondary (e.g. part of LVM).
message BlockDeviceWipeDescriptor {
enum Method {
// Fast wipe - wipe only filesystem signatures.
FAST = 0;
// Zeroes wipe - wipe by overwriting with zeroes (might be slow depending on the disk size and available hardware features).
ZEROES = 1;
}
// Device name to wipe (e.g. sda or sda5).
//
// The name should be submitted without `/dev/` prefix.
string device = 1;
// Wipe method to use.
Method method = 2;
// Skip the volume in use check.
bool skip_volume_check = 3;
}
message BlockDeviceWipeResponse {
repeated BlockDeviceWipe messages = 1;
}
message BlockDeviceWipe {
common.Metadata metadata = 1;
}

View File

@ -5,138 +5,21 @@
package talos
import (
"context"
"fmt"
"os"
"strings"
"text/tabwriter"
"errors"
humanize "github.com/dustin/go-humanize"
"github.com/spf13/cobra"
"github.com/siderolabs/talos/pkg/cli"
"github.com/siderolabs/talos/pkg/machinery/client"
)
var disksCmdFlags struct {
insecure bool
}
var disksCmd = &cobra.Command{
Use: "disks",
Short: "Get the list of disks from /sys/block on the machine",
Long: ``,
Use: "disks",
Short: "Get the list of disks from /sys/block on the machine",
Long: ``,
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
if disksCmdFlags.insecure {
return WithClientMaintenance(nil, printDisks)
}
return WithClient(printDisks)
return errors.New("`talosctl disks` is deprecated, please use `talosctl get disks`, `talosctl get systemdisk`, `talosctl get discoveredvolumes` instead")
},
}
//nolint:gocyclo
func printDisks(ctx context.Context, c *client.Client) error {
response, err := c.Disks(ctx)
if err != nil {
if response == nil {
return fmt.Errorf("error getting disks: %w", err)
}
cli.Warning("%s", err)
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
node := ""
labels := strings.Join(
[]string{
"DEV",
"MODEL",
"SERIAL",
"TYPE",
"UUID",
"WWID",
"MODALIAS",
"NAME",
"SIZE",
"BUS_PATH",
"SUBSYSTEM",
"READ_ONLY",
"SYSTEM_DISK",
}, "\t")
getWithPlaceholder := func(in string) string {
if in == "" {
return "-"
}
return in
}
for i, message := range response.Messages {
if message.Metadata != nil && message.Metadata.Hostname != "" {
node = message.Metadata.Hostname
}
if len(message.Disks) == 0 {
continue
}
for j, disk := range message.Disks {
if i == 0 && j == 0 {
if node != "" {
fmt.Fprintln(w, "NODE\t"+labels)
} else {
fmt.Fprintln(w, labels)
}
}
var args []any
if node != "" {
args = append(args, node)
}
isReadonly := ""
if disk.Readonly {
isReadonly = "*"
}
isSystemDisk := ""
if disk.SystemDisk {
isSystemDisk = "*"
}
args = append(args, []any{
getWithPlaceholder(disk.DeviceName),
getWithPlaceholder(disk.Model),
getWithPlaceholder(disk.Serial),
disk.Type.String(),
getWithPlaceholder(disk.Uuid),
getWithPlaceholder(disk.Wwid),
getWithPlaceholder(disk.Modalias),
getWithPlaceholder(disk.Name),
humanize.Bytes(disk.Size),
getWithPlaceholder(disk.BusPath),
getWithPlaceholder(disk.Subsystem),
isReadonly,
isSystemDisk,
}...)
pattern := strings.Repeat("%s\t", len(args))
pattern = strings.TrimSpace(pattern) + "\n"
fmt.Fprintf(w, pattern, args...)
}
}
return w.Flush()
}
func init() {
disksCmd.Flags().BoolVarP(&disksCmdFlags.insecure, "insecure", "i", false, "get disks using the insecure (encrypted with no auth) maintenance service")
addCommand(disksCmd)
}

View File

@ -0,0 +1,78 @@
// 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 talos
import (
"context"
"fmt"
"github.com/siderolabs/gen/xslices"
"github.com/spf13/cobra"
"github.com/siderolabs/talos/pkg/machinery/api/storage"
"github.com/siderolabs/talos/pkg/machinery/client"
)
// wipeCmd represents the wipe command.
var wipeCmd = &cobra.Command{
Use: "wipe",
Short: "Wipe block device or volumes",
Args: cobra.NoArgs,
}
var wipeDiskCmdFlags struct {
wipeMethod string
skipVolumeCheck bool
}
// wipeDiskCmd represents the wipe disk command.
var wipeDiskCmd = &cobra.Command{
Use: "disk <device names>...",
Short: "Wipe a block device (disk or partition) which is not used as a volume",
Long: `Wipe a block device (disk or partition) which is not used as a volume.
Use device names as arguments, for example: vda or sda5.`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return WithClient(func(ctx context.Context, c *client.Client) error {
method, ok := storage.BlockDeviceWipeDescriptor_Method_value[wipeDiskCmdFlags.wipeMethod]
if !ok {
return fmt.Errorf("invalid wipe method %q", wipeDiskCmdFlags.wipeMethod)
}
return c.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{
Devices: xslices.Map(args, func(devName string) *storage.BlockDeviceWipeDescriptor {
return &storage.BlockDeviceWipeDescriptor{
Device: devName,
Method: storage.BlockDeviceWipeDescriptor_Method(method),
SkipVolumeCheck: wipeDiskCmdFlags.skipVolumeCheck,
}
}),
})
})
},
}
func wipeMethodValues() []string {
var method storage.BlockDeviceWipeDescriptor_Method
values := make([]string, method.Descriptor().Values().Len())
for idx := range method.Descriptor().Values().Len() {
values[idx] = storage.BlockDeviceWipeDescriptor_Method_name[int32(idx)]
}
return values
}
func init() {
addCommand(wipeCmd)
wipeDiskCmd.Flags().StringVar(&wipeDiskCmdFlags.wipeMethod, "method", wipeMethodValues()[0], fmt.Sprintf("wipe method to use %s", wipeMethodValues()))
wipeDiskCmd.Flags().BoolVar(&wipeDiskCmdFlags.skipVolumeCheck, "skip-volume-check", false, "skip volume check")
wipeDiskCmd.Flags().MarkHidden("skip-volume-check") //nolint:errcheck
wipeCmd.AddCommand(wipeDiskCmd)
}

2
go.mod
View File

@ -140,7 +140,7 @@ require (
github.com/siderolabs/gen v0.7.0
github.com/siderolabs/go-api-signature v0.3.6
github.com/siderolabs/go-blockdevice v0.4.8
github.com/siderolabs/go-blockdevice/v2 v2.0.4
github.com/siderolabs/go-blockdevice/v2 v2.0.5
github.com/siderolabs/go-circular v0.2.1
github.com/siderolabs/go-cmd v0.1.3
github.com/siderolabs/go-copy v0.1.0

4
go.sum
View File

@ -648,8 +648,8 @@ github.com/siderolabs/go-api-signature v0.3.6 h1:wDIsXbpl7Oa/FXvxB6uz4VL9INA9fmr
github.com/siderolabs/go-api-signature v0.3.6/go.mod h1:hoH13AfunHflxbXfh+NoploqV13ZTDfQ1mQJWNVSW9U=
github.com/siderolabs/go-blockdevice v0.4.8 h1:KfdWvIx0Jft5YVuCsFIJFwjWEF1oqtzkgX9PeU9cX4c=
github.com/siderolabs/go-blockdevice v0.4.8/go.mod h1:4PeOuk71pReJj1JQEXDE7kIIQJPVe8a+HZQa+qjxSEA=
github.com/siderolabs/go-blockdevice/v2 v2.0.4 h1:+5umLlCtwJL0zkjExVr5liwDQtmK2vM2hALDfQMGSTk=
github.com/siderolabs/go-blockdevice/v2 v2.0.4/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w=
github.com/siderolabs/go-blockdevice/v2 v2.0.5 h1:VLmIdDB/1P30Inrpe94FQAz4WUpByGwun5ZeTekxIQc=
github.com/siderolabs/go-blockdevice/v2 v2.0.5/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w=
github.com/siderolabs/go-circular v0.2.1 h1:a++iVCn9jyhICX3POQZZX8n72p2h5JGdGU6w1ulmpcA=
github.com/siderolabs/go-circular v0.2.1/go.mod h1:ZDItzVyXK+B/XuqTBV5MtQtSv06VI+oCmWGRnNCATo8=
github.com/siderolabs/go-cmd v0.1.3 h1:JrgZwqhJQeoec3QRON0LK+fv+0y7d0DyY7zsfkO6ciw=

View File

@ -96,6 +96,18 @@ configuration option `.skipFallback` can be used to disable this behavior both f
description = """\
Talos now supports matching on permanent hardware (MAC) address of the network interfaces.
This is specifically useful to match bond members, as they change their hardware addresses when they become part of the bond.
"""
[notes.talosctl-disk]
title = "talosctl disks"
description = """\
The command `talosctl disks` was removed, please use `talosctl get disks`, `talosctl get systemdisk`, and `talosctl get blockdevices` instead.
"""
[notes.talosctl-wipe]
title = "talosctl wipe"
description = """\
The new command `talosctl wipe disk` allows to wipe a disk or a partition which is not used as a volume.
"""
[make_deps]

View File

@ -107,6 +107,11 @@ func HandleEncryptionWithHandler(ctx context.Context, logger *zap.Logger, volume
return xerrors.NewTaggedf[Retryable]("error opening encrypted volume: %w", err)
}
encryptedPath, err = filepath.EvalSymlinks(encryptedPath)
if err != nil {
return fmt.Errorf("error resolving symlink: %w", err)
}
volumeContext.Status.Phase = block.VolumePhasePrepared
volumeContext.Status.MountLocation = encryptedPath
volumeContext.Status.EncryptionProvider = volumeContext.Cfg.TypedSpec().Encryption.Provider

View File

@ -99,7 +99,8 @@ var rules = map[string]role.Set{
"/cosi.resource.State/Update": role.MakeSet(role.Admin),
"/cosi.resource.State/Watch": role.MakeSet(role.Admin, role.Operator, role.Reader),
"/storage.StorageService/Disks": role.MakeSet(role.Admin, role.Operator, role.Reader),
"/storage.StorageService/Disks": role.MakeSet(role.Admin, role.Operator, role.Reader),
"/storage.StorageService/BlockDeviceWipe": role.MakeSet(role.Admin),
"/time.TimeService/Time": role.MakeSet(role.Admin, role.Operator, role.Reader),
"/time.TimeService/TimeCheck": role.MakeSet(role.Admin, role.Operator, role.Reader),

View File

@ -70,7 +70,12 @@ func (s *Server) Register(obj *grpc.Server) {
resourceState := s.controller.Runtime().State().V1Alpha2().Resources()
resourceState = state.WrapCore(state.Filter(resourceState, resources.AccessPolicy(resourceState)))
storage.RegisterStorageServiceServer(obj, &storaged.Server{Controller: s.controller})
storage.RegisterStorageServiceServer(obj,
&storaged.Server{
Controller: s.controller,
MaintenanceMode: true,
},
)
machine.RegisterMachineServiceServer(obj, s)
cosiv1alpha1.RegisterStateServer(obj, server.NewState(resourceState))
}

View File

@ -7,15 +7,26 @@ package internal
import (
"context"
"fmt"
"log"
"path/filepath"
"slices"
"strings"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/go-blockdevice/v2/blkid"
blockdev "github.com/siderolabs/go-blockdevice/v2/block"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
"github.com/siderolabs/talos/pkg/grpc/middleware/authz"
"github.com/siderolabs/talos/pkg/machinery/api/storage"
"github.com/siderolabs/talos/pkg/machinery/resources/block"
"github.com/siderolabs/talos/pkg/machinery/role"
)
// Server implements storage.StorageService.
@ -23,7 +34,8 @@ import (
// It is only kept here for compatibility purposes, proper API is to query `block.Disk` resources.
type Server struct {
storage.UnimplementedStorageServiceServer
Controller runtime.Controller
Controller runtime.Controller
MaintenanceMode bool
}
// Disks implements storage.StorageService.
@ -81,3 +93,204 @@ func (s *Server) Disks(ctx context.Context, in *emptypb.Empty) (reply *storage.D
return reply, nil
}
// BlockDeviceWipe implements storage.StorageService.
//
// It allows to wipe unused block devices, for blockdevices in use (volumes), use a different method.
func (s *Server) BlockDeviceWipe(ctx context.Context, req *storage.BlockDeviceWipeRequest) (*storage.BlockDeviceWipeResponse, error) {
// the storage server is included both into machined and maintenance service
// in apid/machined mode, the normal authz checks are used before reaching this method
// in maintenance mode, do the role check, which maps today to SideroLink API connection
if s.MaintenanceMode && !authz.HasRole(ctx, role.Admin) {
return nil, status.Error(codes.Unimplemented, "API is not implemented in maintenance mode")
}
// validate the list of devices
for _, deviceRequest := range req.GetDevices() {
if err := s.validateDeviceForWipe(ctx, deviceRequest.GetDevice(), deviceRequest.GetSkipVolumeCheck()); err != nil {
return nil, err
}
}
// perform the actual wipe
for _, deviceRequest := range req.GetDevices() {
if err := s.wipeDevice(deviceRequest.GetDevice(), deviceRequest.GetMethod()); err != nil {
return nil, err
}
}
return &storage.BlockDeviceWipeResponse{
Messages: []*storage.BlockDeviceWipe{
{},
},
}, nil
}
//nolint:gocyclo,cyclop
func (s *Server) validateDeviceForWipe(ctx context.Context, deviceName string, skipVolumeCheck bool) error {
// first, resolve the blockdevice and figure out what type it is
st := s.Controller.Runtime().State().V1Alpha2().Resources()
blockdevice, err := safe.StateGetByID[*block.Device](ctx, st, deviceName)
if err != nil {
if state.IsNotFoundError(err) {
return status.Errorf(codes.NotFound, "blockdevice %q not found", deviceName)
}
return err
}
var parent string
deviceType := blockdevice.TypedSpec().Type
switch deviceType {
case "disk": // supported
case "partition": // supported
parent = blockdevice.TypedSpec().Parent
default:
return status.Errorf(codes.InvalidArgument, "blockdevice %q is of unsupported type %q", deviceName, deviceType)
}
// check the disk (or parent)
var disk *block.Disk
if parent != "" {
disk, err = safe.StateGetByID[*block.Disk](ctx, st, parent)
} else {
disk, err = safe.StateGetByID[*block.Disk](ctx, st, deviceName)
}
if err != nil {
return fmt.Errorf("failed to get disk (or parent) for %q: %w", deviceName, err)
}
if disk.TypedSpec().Readonly {
return status.Errorf(codes.FailedPrecondition, "blockdevice %q is read-only", deviceName)
}
if disk.TypedSpec().CDROM {
return status.Errorf(codes.FailedPrecondition, "blockdevice %q is a CD-ROM", deviceName)
}
// secondaries check
switch deviceType {
case "disk": // for disks, check secondaries even if the partition is used as secondary (track via Disk resource)
disks, err := safe.StateListAll[*block.Disk](ctx, st)
if err != nil {
return err
}
for disk := range disks.All() {
if slices.Index(disk.TypedSpec().SecondaryDisks, deviceName) != -1 {
return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by disk %q", deviceName, disk.Metadata().ID())
}
}
case "partition": // for partitions, check secondaries only if the partition is used as a secondary
blockdevices, err := safe.StateListAll[*block.Device](ctx, st)
if err != nil {
return err
}
for blockdevice := range blockdevices.All() {
if slices.Index(blockdevice.TypedSpec().Secondaries, deviceName) != -1 {
return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by blockdevice %q", deviceName, blockdevice.Metadata().ID())
}
}
}
if skipVolumeCheck {
return nil
}
// volume in use checks
volumeStatuses, err := safe.StateListAll[*block.VolumeStatus](ctx, st)
if err != nil {
return err
}
for volumeStatus := range volumeStatuses.All() {
for _, location := range []string{
filepath.Base(volumeStatus.TypedSpec().Location),
filepath.Base(volumeStatus.TypedSpec().MountLocation),
} {
for _, dev := range []string{deviceName, parent} {
if dev == "" || location == "" {
continue
}
if location == dev {
return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by volume %q", dev, volumeStatus.Metadata().ID())
}
}
}
if filepath.Base(volumeStatus.TypedSpec().ParentLocation) == deviceName {
return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by volume %q", deviceName, volumeStatus.Metadata().ID())
}
}
return nil
}
// wipeDevice wipes the block device with the given method.
//
//nolint:gocyclo
func (s *Server) wipeDevice(deviceName string, method storage.BlockDeviceWipeDescriptor_Method) error {
bd, err := blockdev.NewFromPath(filepath.Join("/dev", deviceName), blockdev.OpenForWrite())
if err != nil {
return status.Errorf(codes.Internal, "failed to open block device %q: %v", deviceName, err)
}
defer bd.Close() //nolint:errcheck
if err = bd.Lock(true); err != nil {
return status.Errorf(codes.Internal, "failed to lock block device %q: %v", deviceName, err)
}
defer bd.Unlock() //nolint:errcheck
switch method {
case storage.BlockDeviceWipeDescriptor_ZEROES:
log.Printf("wiping block device %q with zeroes", deviceName)
if method, err := bd.Wipe(); err != nil {
return status.Errorf(codes.Internal, "failed to wipe block device %q: %v", deviceName, err)
} else {
log.Printf("block device %q wiped with method %q", deviceName, method)
}
case storage.BlockDeviceWipeDescriptor_FAST:
log.Printf("wiping block device %q with fast method", deviceName)
info, err := blkid.Probe(bd.File(), blkid.WithSkipLocking(true))
if err == nil && info != nil && len(info.SignatureRanges) > 0 { // probe successful, wipe by signatures
if err = bd.FastWipe(xslices.Map(info.SignatureRanges, func(r blkid.SignatureRange) blockdev.Range {
return blockdev.Range(r)
})...); err != nil {
return status.Errorf(codes.Internal, "failed to wipe block device %q: %v", deviceName, err)
}
log.Printf("block device %q wiped by ranges: %s",
deviceName,
strings.Join(
xslices.Map(info.SignatureRanges,
func(r blkid.SignatureRange) string {
return fmt.Sprintf("%d-%d", r.Offset, r.Offset+r.Size)
},
),
", ",
),
)
} else { // probe failed, use default fast wipe
if err = bd.FastWipe(); err != nil {
return status.Errorf(codes.Internal, "failed to wipe block device %q: %v", deviceName, err)
}
log.Printf("block device %q wiped with fast method", deviceName)
}
default:
return status.Errorf(codes.InvalidArgument, "unsupported wipe method %s", method)
}
return nil
}

View File

@ -0,0 +1,190 @@
// 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 integration_api
package api
import (
"context"
"fmt"
"path/filepath"
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/safe"
"github.com/cosi-project/runtime/pkg/state"
"google.golang.org/grpc/codes"
"github.com/siderolabs/talos/internal/integration/base"
"github.com/siderolabs/talos/pkg/machinery/api/storage"
"github.com/siderolabs/talos/pkg/machinery/client"
"github.com/siderolabs/talos/pkg/machinery/config/machine"
"github.com/siderolabs/talos/pkg/machinery/resources/block"
)
// WipeSuite ...
type WipeSuite struct {
base.K8sSuite
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
}
// SuiteName ...
func (suite *WipeSuite) SuiteName() string {
return "api.WipeSuite"
}
// SetupTest ...
func (suite *WipeSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute)
if !suite.Capabilities().SupportsVolumes {
suite.T().Skip("cluster doesn't support volumes")
}
}
// TearDownTest ...
func (suite *WipeSuite) TearDownTest() {
if suite.ctxCancel != nil {
suite.ctxCancel()
}
}
// TestWipeBlockDeviceInvalid verifies that invalid wipe requests are rejected.
func (suite *WipeSuite) TestWipeBlockDeviceInvalid() {
node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker)
nodeCtx := client.WithNode(suite.ctx, node)
disks, err := safe.StateListAll[*block.Disk](nodeCtx, suite.Client.COSI)
suite.Require().NoError(err)
for disk := range disks.All() {
if disk.TypedSpec().Readonly || disk.TypedSpec().CDROM {
suite.T().Logf("invalid wipe request for %s at %s", disk.Metadata().ID(), node)
err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{
Devices: []*storage.BlockDeviceWipeDescriptor{
{
Device: disk.Metadata().ID(),
},
},
})
suite.Require().Error(err)
suite.Assert().Equal(codes.FailedPrecondition, client.StatusCode(err))
}
}
err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{
Devices: []*storage.BlockDeviceWipeDescriptor{
{
Device: "nosuchdevice",
},
},
})
suite.Require().Error(err)
suite.Assert().Equal(codes.NotFound, client.StatusCode(err))
// try to wipe a system disk
systemDisk, err := safe.StateGetByID[*block.SystemDisk](nodeCtx, suite.Client.COSI, block.SystemDiskID)
suite.Require().NoError(err)
suite.T().Logf("invalid wipe request for %s at %s", systemDisk.TypedSpec().DiskID, node)
err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{
Devices: []*storage.BlockDeviceWipeDescriptor{
{
Device: systemDisk.TypedSpec().DiskID,
},
},
})
suite.Require().Error(err)
suite.Assert().Equal(codes.FailedPrecondition, client.StatusCode(err))
}
// TestWipeFilesystem verifies that the filesystem can be wiped.
func (suite *WipeSuite) TestWipeFilesystem() {
if testing.Short() {
suite.T().Skip("skipping test in short mode.")
}
if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU {
suite.T().Skip("skipping test for non-qemu provisioner")
}
node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker)
k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node)
suite.Require().NoError(err)
nodeName := k8sNode.Name
suite.T().Logf("creating filesystem on %s/%s", node, nodeName)
userDisks, err := suite.UserDisks(suite.ctx, node)
suite.Require().NoError(err)
if len(userDisks) < 1 {
suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks)
}
userDisk := userDisks[0]
podDef, err := suite.NewPrivilegedPod("fs-format")
suite.Require().NoError(err)
podDef = podDef.WithNodeName(nodeName)
suite.Require().NoError(podDef.Create(suite.ctx, 5*time.Minute))
defer podDef.Delete(suite.ctx) //nolint:errcheck
_, _, err = podDef.Exec(
suite.ctx,
fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- mkfs.xfs %s", userDisk),
)
suite.Require().NoError(err)
// now Talos should report the disk as xfs formatted
deviceName := filepath.Base(userDisk)
nodeCtx := client.WithNode(suite.ctx, node)
// wait for Talos to discover xfs
_, err = suite.Client.COSI.WatchFor(nodeCtx,
block.NewDiscoveredVolume(block.NamespaceName, deviceName).Metadata(),
state.WithEventTypes(state.Created, state.Updated),
state.WithCondition(func(r resource.Resource) (bool, error) {
return r.(*block.DiscoveredVolume).TypedSpec().Name == "xfs", nil
}),
)
suite.Require().NoError(err)
suite.T().Logf("xfs filesystem created on %s/%s", node, nodeName)
// wipe the filesystem
err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{
Devices: []*storage.BlockDeviceWipeDescriptor{
{
Device: deviceName,
},
},
})
suite.Require().NoError(err)
// wait for Talos to discover that the disk is wiped
_, err = suite.Client.COSI.WatchFor(nodeCtx,
block.NewDiscoveredVolume(block.NamespaceName, deviceName).Metadata(),
state.WithEventTypes(state.Created, state.Updated),
state.WithCondition(func(r resource.Resource) (bool, error) {
return r.(*block.DiscoveredVolume).TypedSpec().Name == "", nil
}),
)
suite.Require().NoError(err)
}
func init() {
allSuites = append(allSuites, new(WipeSuite))
}

View File

@ -1,34 +0,0 @@
// 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 integration_cli
package cli
import (
"github.com/siderolabs/talos/internal/integration/base"
)
// DisksSuite verifies dmesg command.
type DisksSuite struct {
base.CLISuite
}
// SuiteName ...
func (suite *DisksSuite) SuiteName() string {
return "cli.DisksSuite"
}
// TestSuccess runs comand with success.
func (suite *DisksSuite) TestSuccess() {
if suite.Cluster != nil && suite.Cluster.Provisioner() == "docker" {
suite.T().Skip("docker provisioner doesn't support disks command")
}
suite.RunCLI([]string{"disks", "--nodes", suite.RandomDiscoveredNodeInternalIP()})
}
func init() {
allSuites = append(allSuites, new(DisksSuite))
}

View File

@ -82,6 +82,54 @@ func (Disk_DiskType) EnumDescriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{0, 0}
}
type BlockDeviceWipeDescriptor_Method int32
const (
// Fast wipe - wipe only filesystem signatures.
BlockDeviceWipeDescriptor_FAST BlockDeviceWipeDescriptor_Method = 0
// Zeroes wipe - wipe by overwriting with zeroes (might be slow depending on the disk size and available hardware features).
BlockDeviceWipeDescriptor_ZEROES BlockDeviceWipeDescriptor_Method = 1
)
// Enum value maps for BlockDeviceWipeDescriptor_Method.
var (
BlockDeviceWipeDescriptor_Method_name = map[int32]string{
0: "FAST",
1: "ZEROES",
}
BlockDeviceWipeDescriptor_Method_value = map[string]int32{
"FAST": 0,
"ZEROES": 1,
}
)
func (x BlockDeviceWipeDescriptor_Method) Enum() *BlockDeviceWipeDescriptor_Method {
p := new(BlockDeviceWipeDescriptor_Method)
*p = x
return p
}
func (x BlockDeviceWipeDescriptor_Method) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (BlockDeviceWipeDescriptor_Method) Descriptor() protoreflect.EnumDescriptor {
return file_storage_storage_proto_enumTypes[1].Descriptor()
}
func (BlockDeviceWipeDescriptor_Method) Type() protoreflect.EnumType {
return &file_storage_storage_proto_enumTypes[1]
}
func (x BlockDeviceWipeDescriptor_Method) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use BlockDeviceWipeDescriptor_Method.Descriptor instead.
func (BlockDeviceWipeDescriptor_Method) EnumDescriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{4, 0}
}
// Disk represents a disk.
type Disk struct {
state protoimpl.MessageState
@ -336,6 +384,212 @@ func (x *DisksResponse) GetMessages() []*Disks {
return nil
}
type BlockDeviceWipeRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Devices []*BlockDeviceWipeDescriptor `protobuf:"bytes,1,rep,name=devices,proto3" json:"devices,omitempty"`
}
func (x *BlockDeviceWipeRequest) Reset() {
*x = BlockDeviceWipeRequest{}
mi := &file_storage_storage_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BlockDeviceWipeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BlockDeviceWipeRequest) ProtoMessage() {}
func (x *BlockDeviceWipeRequest) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BlockDeviceWipeRequest.ProtoReflect.Descriptor instead.
func (*BlockDeviceWipeRequest) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{3}
}
func (x *BlockDeviceWipeRequest) GetDevices() []*BlockDeviceWipeDescriptor {
if x != nil {
return x.Devices
}
return nil
}
// BlockDeviceWipeDescriptor represents a single block device to be wiped.
//
// The device can be either a full disk (e.g. vda) or a partition (vda5).
// The device should not be used in any of active volumes.
// The device should not be used as a secondary (e.g. part of LVM).
type BlockDeviceWipeDescriptor struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Device name to wipe (e.g. sda or sda5).
//
// The name should be submitted without `/dev/` prefix.
Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"`
// Wipe method to use.
Method BlockDeviceWipeDescriptor_Method `protobuf:"varint,2,opt,name=method,proto3,enum=storage.BlockDeviceWipeDescriptor_Method" json:"method,omitempty"`
// Skip the volume in use check.
SkipVolumeCheck bool `protobuf:"varint,3,opt,name=skip_volume_check,json=skipVolumeCheck,proto3" json:"skip_volume_check,omitempty"`
}
func (x *BlockDeviceWipeDescriptor) Reset() {
*x = BlockDeviceWipeDescriptor{}
mi := &file_storage_storage_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BlockDeviceWipeDescriptor) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BlockDeviceWipeDescriptor) ProtoMessage() {}
func (x *BlockDeviceWipeDescriptor) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BlockDeviceWipeDescriptor.ProtoReflect.Descriptor instead.
func (*BlockDeviceWipeDescriptor) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{4}
}
func (x *BlockDeviceWipeDescriptor) GetDevice() string {
if x != nil {
return x.Device
}
return ""
}
func (x *BlockDeviceWipeDescriptor) GetMethod() BlockDeviceWipeDescriptor_Method {
if x != nil {
return x.Method
}
return BlockDeviceWipeDescriptor_FAST
}
func (x *BlockDeviceWipeDescriptor) GetSkipVolumeCheck() bool {
if x != nil {
return x.SkipVolumeCheck
}
return false
}
type BlockDeviceWipeResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Messages []*BlockDeviceWipe `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
}
func (x *BlockDeviceWipeResponse) Reset() {
*x = BlockDeviceWipeResponse{}
mi := &file_storage_storage_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BlockDeviceWipeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BlockDeviceWipeResponse) ProtoMessage() {}
func (x *BlockDeviceWipeResponse) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BlockDeviceWipeResponse.ProtoReflect.Descriptor instead.
func (*BlockDeviceWipeResponse) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{5}
}
func (x *BlockDeviceWipeResponse) GetMessages() []*BlockDeviceWipe {
if x != nil {
return x.Messages
}
return nil
}
type BlockDeviceWipe struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"`
}
func (x *BlockDeviceWipe) Reset() {
*x = BlockDeviceWipe{}
mi := &file_storage_storage_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *BlockDeviceWipe) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BlockDeviceWipe) ProtoMessage() {}
func (x *BlockDeviceWipe) ProtoReflect() protoreflect.Message {
mi := &file_storage_storage_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BlockDeviceWipe.ProtoReflect.Descriptor instead.
func (*BlockDeviceWipe) Descriptor() ([]byte, []int) {
return file_storage_storage_proto_rawDescGZIP(), []int{6}
}
func (x *BlockDeviceWipe) GetMetadata() *common.Metadata {
if x != nil {
return x.Metadata
}
return nil
}
var File_storage_storage_proto protoreflect.FileDescriptor
var file_storage_storage_proto_rawDesc = []byte{
@ -380,17 +634,49 @@ var file_storage_storage_proto_rawDesc = []byte{
0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73,
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x52, 0x08, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x32, 0x49, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x56, 0x0a, 0x16, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44,
0x65, 0x76, 0x69, 0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x3c, 0x0a, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x22, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63,
0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x07, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0xc2,
0x01, 0x0a, 0x19, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x57, 0x69,
0x70, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06,
0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65,
0x76, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x44, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52,
0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x5f,
0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x43, 0x68,
0x65, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x08, 0x0a,
0x04, 0x46, 0x41, 0x53, 0x54, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x5a, 0x45, 0x52, 0x4f, 0x45,
0x53, 0x10, 0x01, 0x22, 0x4f, 0x0a, 0x17, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34,
0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x18, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x73, 0x22, 0x3f, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x76,
0x69, 0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74,
0x61, 0x64, 0x61, 0x74, 0x61, 0x32, 0x9f, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x44, 0x69, 0x73, 0x6b,
0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x73, 0x74, 0x6f, 0x72,
0x61, 0x67, 0x65, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x42, 0x4e, 0x0a, 0x15, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x64, 0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62,
0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68,
0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x12, 0x54, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
0x57, 0x69, 0x70, 0x65, 0x12, 0x1f, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x42,
0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e,
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x57, 0x69, 0x70, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, 0x0a, 0x15, 0x64, 0x65, 0x76, 0x2e, 0x74,
0x61, 0x6c, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x64,
0x65, 0x72, 0x6f, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x6c, 0x6f, 0x73, 0x2f, 0x70, 0x6b,
0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f,
0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -405,28 +691,39 @@ func file_storage_storage_proto_rawDescGZIP() []byte {
return file_storage_storage_proto_rawDescData
}
var file_storage_storage_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_storage_storage_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_storage_storage_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_storage_storage_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_storage_storage_proto_goTypes = []any{
(Disk_DiskType)(0), // 0: storage.Disk.DiskType
(*Disk)(nil), // 1: storage.Disk
(*Disks)(nil), // 2: storage.Disks
(*DisksResponse)(nil), // 3: storage.DisksResponse
(*common.Metadata)(nil), // 4: common.Metadata
(*emptypb.Empty)(nil), // 5: google.protobuf.Empty
(Disk_DiskType)(0), // 0: storage.Disk.DiskType
(BlockDeviceWipeDescriptor_Method)(0), // 1: storage.BlockDeviceWipeDescriptor.Method
(*Disk)(nil), // 2: storage.Disk
(*Disks)(nil), // 3: storage.Disks
(*DisksResponse)(nil), // 4: storage.DisksResponse
(*BlockDeviceWipeRequest)(nil), // 5: storage.BlockDeviceWipeRequest
(*BlockDeviceWipeDescriptor)(nil), // 6: storage.BlockDeviceWipeDescriptor
(*BlockDeviceWipeResponse)(nil), // 7: storage.BlockDeviceWipeResponse
(*BlockDeviceWipe)(nil), // 8: storage.BlockDeviceWipe
(*common.Metadata)(nil), // 9: common.Metadata
(*emptypb.Empty)(nil), // 10: google.protobuf.Empty
}
var file_storage_storage_proto_depIdxs = []int32{
0, // 0: storage.Disk.type:type_name -> storage.Disk.DiskType
4, // 1: storage.Disks.metadata:type_name -> common.Metadata
1, // 2: storage.Disks.disks:type_name -> storage.Disk
2, // 3: storage.DisksResponse.messages:type_name -> storage.Disks
5, // 4: storage.StorageService.Disks:input_type -> google.protobuf.Empty
3, // 5: storage.StorageService.Disks:output_type -> storage.DisksResponse
5, // [5:6] is the sub-list for method output_type
4, // [4:5] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
0, // 0: storage.Disk.type:type_name -> storage.Disk.DiskType
9, // 1: storage.Disks.metadata:type_name -> common.Metadata
2, // 2: storage.Disks.disks:type_name -> storage.Disk
3, // 3: storage.DisksResponse.messages:type_name -> storage.Disks
6, // 4: storage.BlockDeviceWipeRequest.devices:type_name -> storage.BlockDeviceWipeDescriptor
1, // 5: storage.BlockDeviceWipeDescriptor.method:type_name -> storage.BlockDeviceWipeDescriptor.Method
8, // 6: storage.BlockDeviceWipeResponse.messages:type_name -> storage.BlockDeviceWipe
9, // 7: storage.BlockDeviceWipe.metadata:type_name -> common.Metadata
10, // 8: storage.StorageService.Disks:input_type -> google.protobuf.Empty
5, // 9: storage.StorageService.BlockDeviceWipe:input_type -> storage.BlockDeviceWipeRequest
4, // 10: storage.StorageService.Disks:output_type -> storage.DisksResponse
7, // 11: storage.StorageService.BlockDeviceWipe:output_type -> storage.BlockDeviceWipeResponse
10, // [10:12] is the sub-list for method output_type
8, // [8:10] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_storage_storage_proto_init() }
@ -439,8 +736,8 @@ func file_storage_storage_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_storage_storage_proto_rawDesc,
NumEnums: 1,
NumMessages: 3,
NumEnums: 2,
NumMessages: 7,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -21,7 +21,8 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
StorageService_Disks_FullMethodName = "/storage.StorageService/Disks"
StorageService_Disks_FullMethodName = "/storage.StorageService/Disks"
StorageService_BlockDeviceWipe_FullMethodName = "/storage.StorageService/BlockDeviceWipe"
)
// StorageServiceClient is the client API for StorageService service.
@ -31,6 +32,12 @@ const (
// StorageService represents the storage service.
type StorageServiceClient interface {
Disks(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DisksResponse, error)
// BlockDeviceWipe performs a wipe of the blockdevice (partition or disk).
//
// The method doesn't require a reboot, and it can only wipe blockdevices which are not
// being used as volumes at the moment.
// Wiping of volumes requires a different API.
BlockDeviceWipe(ctx context.Context, in *BlockDeviceWipeRequest, opts ...grpc.CallOption) (*BlockDeviceWipeResponse, error)
}
type storageServiceClient struct {
@ -51,6 +58,16 @@ func (c *storageServiceClient) Disks(ctx context.Context, in *emptypb.Empty, opt
return out, nil
}
func (c *storageServiceClient) BlockDeviceWipe(ctx context.Context, in *BlockDeviceWipeRequest, opts ...grpc.CallOption) (*BlockDeviceWipeResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BlockDeviceWipeResponse)
err := c.cc.Invoke(ctx, StorageService_BlockDeviceWipe_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// StorageServiceServer is the server API for StorageService service.
// All implementations must embed UnimplementedStorageServiceServer
// for forward compatibility.
@ -58,6 +75,12 @@ func (c *storageServiceClient) Disks(ctx context.Context, in *emptypb.Empty, opt
// StorageService represents the storage service.
type StorageServiceServer interface {
Disks(context.Context, *emptypb.Empty) (*DisksResponse, error)
// BlockDeviceWipe performs a wipe of the blockdevice (partition or disk).
//
// The method doesn't require a reboot, and it can only wipe blockdevices which are not
// being used as volumes at the moment.
// Wiping of volumes requires a different API.
BlockDeviceWipe(context.Context, *BlockDeviceWipeRequest) (*BlockDeviceWipeResponse, error)
mustEmbedUnimplementedStorageServiceServer()
}
@ -71,6 +94,9 @@ type UnimplementedStorageServiceServer struct{}
func (UnimplementedStorageServiceServer) Disks(context.Context, *emptypb.Empty) (*DisksResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Disks not implemented")
}
func (UnimplementedStorageServiceServer) BlockDeviceWipe(context.Context, *BlockDeviceWipeRequest) (*BlockDeviceWipeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method BlockDeviceWipe not implemented")
}
func (UnimplementedStorageServiceServer) mustEmbedUnimplementedStorageServiceServer() {}
func (UnimplementedStorageServiceServer) testEmbeddedByValue() {}
@ -110,6 +136,24 @@ func _StorageService_Disks_Handler(srv interface{}, ctx context.Context, dec fun
return interceptor(ctx, in, info, handler)
}
func _StorageService_BlockDeviceWipe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BlockDeviceWipeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(StorageServiceServer).BlockDeviceWipe(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: StorageService_BlockDeviceWipe_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(StorageServiceServer).BlockDeviceWipe(ctx, req.(*BlockDeviceWipeRequest))
}
return interceptor(ctx, in, info, handler)
}
// StorageService_ServiceDesc is the grpc.ServiceDesc for StorageService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -121,6 +165,10 @@ var StorageService_ServiceDesc = grpc.ServiceDesc{
MethodName: "Disks",
Handler: _StorageService_Disks_Handler,
},
{
MethodName: "BlockDeviceWipe",
Handler: _StorageService_BlockDeviceWipe_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "storage/storage.proto",

View File

@ -260,6 +260,206 @@ func (m *DisksResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *BlockDeviceWipeRequest) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BlockDeviceWipeRequest) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *BlockDeviceWipeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.Devices) > 0 {
for iNdEx := len(m.Devices) - 1; iNdEx >= 0; iNdEx-- {
size, err := m.Devices[iNdEx].MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *BlockDeviceWipeDescriptor) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BlockDeviceWipeDescriptor) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *BlockDeviceWipeDescriptor) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.SkipVolumeCheck {
i--
if m.SkipVolumeCheck {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x18
}
if m.Method != 0 {
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Method))
i--
dAtA[i] = 0x10
}
if len(m.Device) > 0 {
i -= len(m.Device)
copy(dAtA[i:], m.Device)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Device)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *BlockDeviceWipeResponse) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BlockDeviceWipeResponse) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *BlockDeviceWipeResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.Messages) > 0 {
for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- {
size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *BlockDeviceWipe) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BlockDeviceWipe) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *BlockDeviceWipe) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.Metadata != nil {
if vtmsg, ok := interface{}(m.Metadata).(interface {
MarshalToSizedBufferVT([]byte) (int, error)
}); ok {
size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
} else {
encoded, err := proto.Marshal(m.Metadata)
if err != nil {
return 0, err
}
i -= len(encoded)
copy(dAtA[i:], encoded)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded)))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Disk) SizeVT() (n int) {
if m == nil {
return 0
@ -360,6 +560,78 @@ func (m *DisksResponse) SizeVT() (n int) {
return n
}
func (m *BlockDeviceWipeRequest) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Devices) > 0 {
for _, e := range m.Devices {
l = e.SizeVT()
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
}
n += len(m.unknownFields)
return n
}
func (m *BlockDeviceWipeDescriptor) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Device)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
if m.Method != 0 {
n += 1 + protohelpers.SizeOfVarint(uint64(m.Method))
}
if m.SkipVolumeCheck {
n += 2
}
n += len(m.unknownFields)
return n
}
func (m *BlockDeviceWipeResponse) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Messages) > 0 {
for _, e := range m.Messages {
l = e.SizeVT()
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
}
n += len(m.unknownFields)
return n
}
func (m *BlockDeviceWipe) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Metadata != nil {
if size, ok := interface{}(m.Metadata).(interface {
SizeVT() int
}); ok {
l = size.SizeVT()
} else {
l = proto.Size(m.Metadata)
}
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
func (m *Disk) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@ -991,3 +1263,390 @@ func (m *DisksResponse) UnmarshalVT(dAtA []byte) error {
}
return nil
}
func (m *BlockDeviceWipeRequest) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BlockDeviceWipeRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BlockDeviceWipeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Devices = append(m.Devices, &BlockDeviceWipeDescriptor{})
if err := m.Devices[len(m.Devices)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *BlockDeviceWipeDescriptor) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BlockDeviceWipeDescriptor: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BlockDeviceWipeDescriptor: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Device = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType)
}
m.Method = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Method |= BlockDeviceWipeDescriptor_Method(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field SkipVolumeCheck", 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.SkipVolumeCheck = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *BlockDeviceWipeResponse) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BlockDeviceWipeResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BlockDeviceWipeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Messages = append(m.Messages, &BlockDeviceWipe{})
if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *BlockDeviceWipe) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BlockDeviceWipe: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BlockDeviceWipe: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Metadata == nil {
m.Metadata = &common.Metadata{}
}
if unmarshal, ok := interface{}(m.Metadata).(interface {
UnmarshalVT([]byte) error
}); ok {
if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
} else {
if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil {
return err
}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}

View File

@ -1002,3 +1002,12 @@ func (c *Client) ImagePull(ctx context.Context, namespace common.ContainerdNames
return err
}
// BlockDeviceWipe wipes a block device which is not used as a volume.
func (c *Client) BlockDeviceWipe(ctx context.Context, req *storageapi.BlockDeviceWipeRequest, callOptions ...grpc.CallOption) error {
resp, err := c.StorageClient.BlockDeviceWipe(ctx, req, callOptions...)
_, err = FilterMessages(resp, err)
return err
}

View File

@ -27,7 +27,7 @@ require (
github.com/siderolabs/crypto v0.5.0
github.com/siderolabs/gen v0.7.0
github.com/siderolabs/go-api-signature v0.3.6
github.com/siderolabs/go-blockdevice/v2 v2.0.4
github.com/siderolabs/go-blockdevice/v2 v2.0.5
github.com/siderolabs/go-pointer v1.0.0
github.com/siderolabs/net v0.4.0
github.com/siderolabs/protoenc v0.2.1

View File

@ -113,8 +113,8 @@ github.com/siderolabs/gen v0.7.0 h1:uHAt3WD0dof28NHFuguWBbDokaXQraR/HyVxCLw2QCU=
github.com/siderolabs/gen v0.7.0/go.mod h1:an3a2Y53O7kUjnnK8Bfu3gewtvnIOu5RTU6HalFtXQQ=
github.com/siderolabs/go-api-signature v0.3.6 h1:wDIsXbpl7Oa/FXvxB6uz4VL9INA9fmr3EbmjEZYFJrU=
github.com/siderolabs/go-api-signature v0.3.6/go.mod h1:hoH13AfunHflxbXfh+NoploqV13ZTDfQ1mQJWNVSW9U=
github.com/siderolabs/go-blockdevice/v2 v2.0.4 h1:+5umLlCtwJL0zkjExVr5liwDQtmK2vM2hALDfQMGSTk=
github.com/siderolabs/go-blockdevice/v2 v2.0.4/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w=
github.com/siderolabs/go-blockdevice/v2 v2.0.5 h1:VLmIdDB/1P30Inrpe94FQAz4WUpByGwun5ZeTekxIQc=
github.com/siderolabs/go-blockdevice/v2 v2.0.5/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w=
github.com/siderolabs/go-pointer v1.0.0 h1:6TshPKep2doDQJAAtHUuHWXbca8ZfyRySjSBT/4GsMU=
github.com/siderolabs/go-pointer v1.0.0/go.mod h1:HTRFUNYa3R+k0FFKNv11zgkaCLzEkWVzoYZ433P3kHc=
github.com/siderolabs/go-retry v0.3.3 h1:zKV+S1vumtO72E6sYsLlmIdV/G/GcYSBLiEx/c9oCEg=

View File

@ -493,10 +493,15 @@ description: Talos gRPC API reference.
- [SecurityService](#securityapi.SecurityService)
- [storage/storage.proto](#storage/storage.proto)
- [BlockDeviceWipe](#storage.BlockDeviceWipe)
- [BlockDeviceWipeDescriptor](#storage.BlockDeviceWipeDescriptor)
- [BlockDeviceWipeRequest](#storage.BlockDeviceWipeRequest)
- [BlockDeviceWipeResponse](#storage.BlockDeviceWipeResponse)
- [Disk](#storage.Disk)
- [Disks](#storage.Disks)
- [DisksResponse](#storage.DisksResponse)
- [BlockDeviceWipeDescriptor.Method](#storage.BlockDeviceWipeDescriptor.Method)
- [Disk.DiskType](#storage.Disk.DiskType)
- [StorageService](#storage.StorageService)
@ -8488,6 +8493,74 @@ The security service definition.
<a name="storage.BlockDeviceWipe"></a>
### BlockDeviceWipe
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| metadata | [common.Metadata](#common.Metadata) | | |
<a name="storage.BlockDeviceWipeDescriptor"></a>
### BlockDeviceWipeDescriptor
BlockDeviceWipeDescriptor represents a single block device to be wiped.
The device can be either a full disk (e.g. vda) or a partition (vda5).
The device should not be used in any of active volumes.
The device should not be used as a secondary (e.g. part of LVM).
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| device | [string](#string) | | Device name to wipe (e.g. sda or sda5).
The name should be submitted without `/dev/` prefix. |
| method | [BlockDeviceWipeDescriptor.Method](#storage.BlockDeviceWipeDescriptor.Method) | | Wipe method to use. |
| skip_volume_check | [bool](#bool) | | Skip the volume in use check. |
<a name="storage.BlockDeviceWipeRequest"></a>
### BlockDeviceWipeRequest
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| devices | [BlockDeviceWipeDescriptor](#storage.BlockDeviceWipeDescriptor) | repeated | |
<a name="storage.BlockDeviceWipeResponse"></a>
### BlockDeviceWipeResponse
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| messages | [BlockDeviceWipe](#storage.BlockDeviceWipe) | repeated | |
<a name="storage.Disk"></a>
### Disk
@ -8548,6 +8621,18 @@ DisksResponse represents the response of the `Disks` RPC.
<!-- end messages -->
<a name="storage.BlockDeviceWipeDescriptor.Method"></a>
### BlockDeviceWipeDescriptor.Method
| Name | Number | Description |
| ---- | ------ | ----------- |
| FAST | 0 | Fast wipe - wipe only filesystem signatures. |
| ZEROES | 1 | Zeroes wipe - wipe by overwriting with zeroes (might be slow depending on the disk size and available hardware features). |
<a name="storage.Disk.DiskType"></a>
### Disk.DiskType
@ -8576,6 +8661,9 @@ StorageService represents the storage service.
| Method Name | Request Type | Response Type | Description |
| ----------- | ------------ | ------------- | ------------|
| Disks | [.google.protobuf.Empty](#google.protobuf.Empty) | [DisksResponse](#storage.DisksResponse) | |
| BlockDeviceWipe | [BlockDeviceWipeRequest](#storage.BlockDeviceWipeRequest) | [BlockDeviceWipeResponse](#storage.BlockDeviceWipeResponse) | BlockDeviceWipe performs a wipe of the blockdevice (partition or disk).
The method doesn't require a reboot, and it can only wipe blockdevices which are not being used as volumes at the moment. Wiping of volumes requires a different API. |
<!-- end services -->

View File

@ -872,35 +872,6 @@ talosctl dashboard [flags]
* [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos
## talosctl disks
Get the list of disks from /sys/block on the machine
```
talosctl disks [flags]
```
### Options
```
-h, --help help for disks
-i, --insecure get disks using the insecure (encrypted with no auth) maintenance service
```
### Options inherited from parent commands
```
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
### SEE ALSO
* [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos
## talosctl dmesg
Retrieve kernel logs
@ -3162,6 +3133,66 @@ talosctl version [flags]
* [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos
## talosctl wipe disk
Wipe a block device (disk or partition) which is not used as a volume
### Synopsis
Wipe a block device (disk or partition) which is not used as a volume.
Use device names as arguments, for example: vda or sda5.
```
talosctl wipe disk <device names>... [flags]
```
### Options
```
-h, --help help for disk
--method string wipe method to use [FAST ZEROES] (default "FAST")
```
### Options inherited from parent commands
```
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
### SEE ALSO
* [talosctl wipe](#talosctl-wipe) - Wipe block device or volumes
## talosctl wipe
Wipe block device or volumes
### Options
```
-h, --help help for wipe
```
### Options inherited from parent commands
```
--cluster string Cluster to connect to if a proxy endpoint is used.
--context string Context to be used in command
-e, --endpoints strings override default endpoints in Talos configuration
-n, --nodes strings target the specified nodes
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
```
### SEE ALSO
* [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos
* [talosctl wipe disk](#talosctl-wipe-disk) - Wipe a block device (disk or partition) which is not used as a volume
## talosctl
A CLI for out-of-band management of Kubernetes nodes created by Talos
@ -3189,7 +3220,6 @@ A CLI for out-of-band management of Kubernetes nodes created by Talos
* [talosctl containers](#talosctl-containers) - List containers
* [talosctl copy](#talosctl-copy) - Copy data out from the node
* [talosctl dashboard](#talosctl-dashboard) - Cluster dashboard with node overview, logs and real-time metrics
* [talosctl disks](#talosctl-disks) - Get the list of disks from /sys/block on the machine
* [talosctl dmesg](#talosctl-dmesg) - Retrieve kernel logs
* [talosctl edit](#talosctl-edit) - Edit a resource from the default editor.
* [talosctl etcd](#talosctl-etcd) - Manage etcd
@ -3227,4 +3257,5 @@ A CLI for out-of-band management of Kubernetes nodes created by Talos
* [talosctl usage](#talosctl-usage) - Retrieve a disk usage
* [talosctl validate](#talosctl-validate) - Validate config
* [talosctl version](#talosctl-version) - Prints the version
* [talosctl wipe](#talosctl-wipe) - Wipe block device or volumes