Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial disk wipe support to vogelkop #84

Merged
merged 4 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions cmd/disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cmd

import (
"github.com/spf13/cobra"
)

var diskCommand = &cobra.Command{
Use: "disk",
Short: "Modifies disks",
}

func init() {
rootCmd.AddCommand(diskCommand)
}
23 changes: 15 additions & 8 deletions cmd/partition_disk.go → cmd/disk_partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import (
"github.com/spf13/cobra"
)

var partitionDiskCmd = &cobra.Command{
Use: "partition-disk",
Short: "Partitions a block device",
Long: "Partitions a block device with a GPT table",
var diskPartitionCommand = &cobra.Command{
Use: "partition",
Short: "Partitions a disk with a GPT table",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, _ []string) {
ctx := context.Background()
Expand All @@ -37,8 +36,16 @@ var partitionDiskCmd = &cobra.Command{
}

func init() {
partitionDiskCmd.PersistentFlags().String("device", "/dev/sda", "Device to be partitioned")
markFlagAsRequired(partitionDiskCmd, "device")
partitionDiskCmd.PersistentFlags().StringSlice("partitions", []string{}, "Partition Definitions Name:Position:Size:Type")
rootCmd.AddCommand(partitionDiskCmd)
diskPartitionCommand.PersistentFlags().String("device", "/dev/sda", "Device to be partitioned")
markFlagAsRequired(diskPartitionCommand, "device")

diskPartitionCommand.PersistentFlags().StringSlice("partitions", []string{}, "Partition Definitions Name:Position:Size:Type")

diskCommand.AddCommand(diskPartitionCommand)

rootCmd.AddCommand(&cobra.Command{
Use: "partition-disk",
Deprecated: "use \"disk partition\"",
Run: diskPartitionCommand.Run,
})
}
94 changes: 94 additions & 0 deletions cmd/disk_wipe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package cmd

import (
"cmp"
"context"
"errors"
"os"

"github.com/bmc-toolbox/common"
"github.com/metal-toolbox/ironlib"
"github.com/metal-toolbox/ironlib/actions"
"github.com/metal-toolbox/ironlib/utils"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

func init() {
cmd := &cobra.Command{
Use: "wipe /dev/disk",
Short: "Wipes all data from a disk",
Args: func(_ *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires at least one arg") // nolint:goerr113
}

_, err := os.Open(args[0])
return err
},
Run: func(cmd *cobra.Command, args []string) {
timeout, err := cmd.Flags().GetDuration("timeout")
if err != nil {
logger.With("error", err).Fatal("--timeout argument is invalid")
}

verbose, err := cmd.Flags().GetBool("verbose")
if err != nil {
logger.With("error", err).Fatal("--verbose argument is invalid")
}

driveName := args[0]

logger := logrus.New()
logger.Formatter = new(logrus.TextFormatter)
if verbose {
logger.SetLevel(logrus.TraceLevel)
}
l := logger.WithField("drive", driveName)

ctx := cmp.Or(cmd.Context(), context.Background())
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()

collector, err := ironlib.New(logger)
if err != nil {
l.WithError(err).Fatal("exiting")
}

inventory, err := collector.GetInventory(ctx, actions.WithDynamicCollection())
if err != nil {
l.WithError(err).Fatal("exiting")
}

var drive *common.Drive
for _, d := range inventory.Drives {
if d.LogicalName == driveName {
drive = d
break
}
}
if drive == nil {
l.Fatal("unable to find disk")
}

// Pick the most appropriate wipe based on the disk type and/or features supported
var wiper actions.DriveWiper
// nolint:gocritic // will have more cases soon, remove nolint then
switch drive.Protocol {
case "nvme":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine the intention here is to add more cases in the future?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, shouldn't this 'figure out what tool to use based upon protocol' be moved into ironlib?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine the intention here is to add more cases in the future?

Yep after this is out I’ll be adding more things in like in the ironlib example (and also hdparm based SED once I add it to ironlib)

Copy link
Member Author

@mmlb mmlb Jun 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, shouldn't this 'figure out what tool to use based upon protocol' be moved into ironlib?

Yep but I’d want the Drives to support that themselves instead of having a function that needs to grow more and more complex as features are needed. For example maybe we want to add a flag to vogelkop that says "never use fill_zeros" or maybe we only want to allow that for HDDs. Maybe we want secure erase methods only. Adding flags to the hypothetical ironlib function for this means lots of params or lots of individual functions. Either way I don't think this belongs in ironlib. I think other tools should take ironlib's raw features and smooth them out for their specific use case instead of throwing everything into ironlib.

I do want ironlib to grow some more features/smarts but leave it as functionality only and leave policy decisions to its users/callers. I want the Drives to be able to keep a ref to the tool(s) able to wipe it and offer those up to the ironlib users so that it can make decisions regarding which tool/method it wants to use. I started in on that in metal-toolbox/ironlib#160 but that can be seen as a breaking change (even though ironlib is still v0... really it should have been v1 by now) so will have to wait until work starts on v2.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like scattering this logic everywhere that needs it is wrong... Maybe there should be another lib in the middle that contains the logic you'll otherwise implement in vogelkop. I imagine reusing this logic elsewhere... but maybe ultimately nothing else will ever use it...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is what I think vogelkop should be. Right now its also very cli driven but there's no reason it can't be a library for disk stuff that also comes with a cli. Similar to libcurl & curl.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you're both talking about here is the distinction between implementation and behaviour. Implementation, for instance, might vary from one disk to the next: what we do to a disk is dependent on the disk type, and given the disk type is an interface we would want a call that reflects the behaviour which is diskX.destroy_all_data_with_prejudice() or diskX.lightly_mangle_data() or diskX.do_what_you_can_in_5_minutes().

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point about hiding the implementation for a disk type is that I should be able to test that the implementation for a disk type produces a result without having to know how it does that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you're both talking about here is the distinction between implementation and behaviour. Implementation, for instance, might vary from one disk to the next: what we do to a disk is dependent on the disk type, and given the disk type is an interface we would want a call that reflects the behaviour which is diskX.destroy_all_data_with_prejudice() or diskX.lightly_mangle_data() or diskX.do_what_you_can_in_5_minutes().

Not quite because the disk type is not an interface and has none, its just a POD struct. So we either provided a function to take the drive and return back some behavior or we make the drive implement an interface so that we can leave that to the disk or we let caller deal with it. I vote to let the caller deal with it instead of providing the function and later implement interfacification in the drive :D

wiper = utils.NewNvmeCmd(verbose)
}

if wiper == nil {
l.Fatal("failed find appropriate wiper drive")
}

err = wiper.WipeDrive(ctx, logger, drive)
if err != nil {
l.Fatal("failed to wipe drive")
}
},
}

diskCommand.AddCommand(cmd)
}
14 changes: 14 additions & 0 deletions cmd/partition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cmd

import (
"github.com/spf13/cobra"
)

var partitionCommand = &cobra.Command{
Use: "partition",
Short: "Modifies partitions",
}

func init() {
rootCmd.AddCommand(partitionCommand)
}
26 changes: 16 additions & 10 deletions cmd/format_partition.go → cmd/partition_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"github.com/spf13/cobra"
)

var formatPartitionCmd = &cobra.Command{
Use: "format-partition",
var partitionFormatPartitionCommand = &cobra.Command{
Use: "format",
Short: "Formats a partition",
Long: "Formats a partition with your choice of filesystem",
Run: func(cmd *cobra.Command, _ []string) {
Expand Down Expand Up @@ -49,15 +49,21 @@ var formatPartitionCmd = &cobra.Command{
}

func init() {
formatPartitionCmd.PersistentFlags().String("device", "", "Block device")
formatPartitionCmd.PersistentFlags().String("filesystem-device", "", "Filesystem Block device")
partitionFormatPartitionCommand.PersistentFlags().String("device", "", "Block device")
partitionFormatPartitionCommand.PersistentFlags().String("filesystem-device", "", "Filesystem Block device")

formatPartitionCmd.PersistentFlags().Uint("partition", 0, "Partition number")
partitionFormatPartitionCommand.PersistentFlags().Uint("partition", 0, "Partition number")

formatPartitionCmd.PersistentFlags().String("format", "ext4", "Filesystem to be applied to the partition")
markFlagAsRequired(formatPartitionCmd, "format")
partitionFormatPartitionCommand.PersistentFlags().String("format", "ext4", "Filesystem to be applied to the partition")
markFlagAsRequired(partitionFormatPartitionCommand, "format")

formatPartitionCmd.PersistentFlags().String("mount-point", "/", "Filesystem mount point")
formatPartitionCmd.PersistentFlags().StringSlice("options", []string{}, "Filesystem creation options")
rootCmd.AddCommand(formatPartitionCmd)
partitionFormatPartitionCommand.PersistentFlags().String("mount-point", "/", "Filesystem mount point")
partitionFormatPartitionCommand.PersistentFlags().StringSlice("options", []string{}, "Filesystem creation options")
partitionCommand.AddCommand(partitionFormatPartitionCommand)

rootCmd.AddCommand(&cobra.Command{
Use: "format-partition",
Deprecated: "use \"partition format\"",
Run: partitionFormatPartitionCommand.Run,
})
}
26 changes: 10 additions & 16 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,38 @@ go 1.22

require (
github.com/Sytten/logrus-zap-hook v0.1.0
github.com/bmc-toolbox/common v0.0.0-20230717121556-5eb9915a8a5a
github.com/bmc-toolbox/common v0.0.0-20240510143200-3db7cecbb5a6
github.com/freddierice/go-losetup/v2 v2.0.1
github.com/metal-toolbox/ironlib v0.2.17
github.com/metal-toolbox/ironlib v0.2.18-0.20240611133518-3514176030a4
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
go.uber.org/zap v1.26.0
)

require (
github.com/beevik/etree v1.1.0 // indirect
github.com/beevik/etree v1.4.0 // indirect
github.com/dselans/dmidecode v0.0.0-20180814053009-65c3f9d81910 // indirect
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab // indirect
github.com/frankban/quicktest v1.14.3 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/xattr v0.4.9 // indirect
github.com/r3labs/diff/v2 v2.15.1 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/r3labs/diff/v3 v3.0.1 // indirect
github.com/tidwall/gjson v1.17.1 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.30.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
)

require (
github.com/diskfs/go-diskfs v1.4.0
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
)
Loading