Skip to content

Commit

Permalink
Merge pull request #1179 from sohankunkerkar/wipe-fs
Browse files Browse the repository at this point in the history
Add a way to erase a block device without creating a filesystem
  • Loading branch information
sohankunkerkar authored May 5, 2021
2 parents 557b3bf + 0589505 commit 63a6789
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 11 deletions.
2 changes: 1 addition & 1 deletion config/v3_3_experimental/types/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (f Filesystem) validateFormat() error {
}
} else {
switch *f.Format {
case "ext4", "btrfs", "xfs", "swap", "vfat":
case "ext4", "btrfs", "xfs", "swap", "vfat", "none":
default:
return errors.ErrFilesystemInvalidFormat
}
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration-v3_3_experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The Ignition configuration is a JSON document conforming to the following specif
* **_options_** (list of strings): any additional options to be passed to mdadm.
* **_filesystems_** (list of objects): the list of filesystems to be configured. `device` and `format` need to be specified. Every filesystem must have a unique `device`.
* **device** (string): the absolute path to the device. Devices are typically referenced by the `/dev/disk/by-*` symlinks.
* **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, or swap).
* **format** (string): the filesystem format (ext4, btrfs, xfs, vfat, swap, or none).
* **_path_** (string): the mount-point of the filesystem while Ignition is running relative to where the root filesystem will be mounted. This is not necessarily the same as where it should be mounted in the real root, but it is encouraged to make it the same.
* **_wipeFilesystem_** (boolean): whether or not to wipe the device before filesystem creation, see [the documentation on filesystems](operator-notes.md#filesystem-reuse-semantics) for more information. Defaults to false.
* **_label_** (string): the label of the filesystem.
Expand Down
4 changes: 2 additions & 2 deletions docs/operator-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ In the first case, when there is no preexisting filesystem, Ignition will always

In the second two cases, where there is a preexisting filesystem, Ignition's behavior is controlled by the `wipeFilesystem` flag in the `filesystem` section.

If `wipeFilesystem` is set to true, Ignition will always wipe any preexisting filesystem and create the desired filesystem. Note this will result in any data on the old filesystem being lost.
If `wipeFilesystem` is set to true, Ignition will always wipe any preexisting filesystem and create the desired filesystem (or skip creation if the format is set to `none`). Note this will result in any data on the old filesystem being lost.

If `wipeFilesystem` is set to false, Ignition will then attempt to reuse the existing filesystem. If the filesystem is of the correct type, has a matching label, and has a matching UUID, then Ignition will reuse the filesystem. If the label or UUID is not set in the Ignition config, they don't need to match for Ignition to reuse the filesystem. Any preexisting data will be left on the device and will be available to the installation. If the preexisting filesystem is *not* of the correct type, then Ignition will fail, and the machine will fail to boot.
If `wipeFilesystem` is set to false, Ignition will then attempt to reuse the existing filesystem. If the filesystem is of the correct type, has a matching label, and has a matching UUID, then Ignition will reuse the filesystem. If the label or UUID is not set in the Ignition config, they don't need to match for Ignition to reuse the filesystem. Any preexisting data will be left on the device and will be available to the installation. If the preexisting filesystem is *not* of the correct type, then Ignition will fail, and the machine will fail to boot. Similarly, if the format is set to `none`, then any preexisting filesystem will cause Ignition to fail.

## Path Traversal and Following Symlinks

Expand Down
11 changes: 9 additions & 2 deletions internal/exec/stages/disks/filesystems.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,15 @@ func (s stage) createFilesystem(fs types.Filesystem) error {
s.Logger.Info("found %s filesystem at %q with uuid %q and label %q", info.Type, fs.Device, info.UUID, info.Label)

if !cutil.IsTrue(fs.WipeFilesystem) {
fileSystemFormat := *fs.Format
if fileSystemFormat == "none" {
fileSystemFormat = ""
}
// If the filesystem isn't forcefully being created, then we need
// to check if it is of the correct type or that no filesystem exists.
if info.Type == *fs.Format &&
if info.Type == fileSystemFormat &&
(fs.Label == nil || info.Label == *fs.Label) &&
(fs.UUID == nil || canonicalizeFilesystemUUID(info.Type, info.UUID) == canonicalizeFilesystemUUID(*fs.Format, *fs.UUID)) {
(fs.UUID == nil || canonicalizeFilesystemUUID(info.Type, info.UUID) == canonicalizeFilesystemUUID(fileSystemFormat, *fs.UUID)) {
s.Logger.Info("filesystem at %q is already correctly formatted. Skipping mkfs...", fs.Device)
return nil
} else if info.Type != "" {
Expand Down Expand Up @@ -190,6 +194,9 @@ func (s stage) createFilesystem(fs types.Filesystem) error {
if fs.Label != nil {
args = append(args, "-n", *fs.Label)
}
case "none":
// The user specifies this format to skip the creation of a filesystem on a block device.
return nil
default:
return fmt.Errorf("unsupported filesystem format: %q", *fs.Format)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/exec/stages/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ func checkForNonDirectories(path string) error {
return err
}
if !st.Mode().IsDir() {
return fmt.Errorf("Mount path %q contains non-directory component %q", path, p)
return fmt.Errorf("mount path %q contains non-directory component %q", path, p)
}
}
return nil
}

func (s stage) mountFs(fs types.Filesystem) error {
if fs.Format == nil || *fs.Format == "swap" || *fs.Format == "" {
if fs.Format == nil || *fs.Format == "swap" || *fs.Format == "" || *fs.Format == "none" {
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion internal/exec/stages/umount/umount.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (s stage) Run(config types.Config) error {
}

func (s stage) umountFs(fs types.Filesystem) error {
if fs.Format != nil && *fs.Format == "swap" {
if fs.Format == nil || *fs.Format == "swap" || *fs.Format == "" || *fs.Format == "none" {
return nil
}
path, err := s.JoinPath(*fs.Path)
Expand Down
5 changes: 3 additions & 2 deletions tests/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,11 @@ func formatPartition(ctx context.Context, partition *types.Partition) error {
return writePartitionData(partition.Device, partition.FilesystemImage)
default:
if partition.FilesystemType == "blank" ||
partition.FilesystemType == "" {
partition.FilesystemType == "" ||
partition.FilesystemType == "none" {
return nil
}
return fmt.Errorf("Unknown partition: %v", partition.FilesystemType)
return fmt.Errorf("unknown partition: %v", partition.FilesystemType)
}

if partition.FilesystemLabel != "" {
Expand Down
63 changes: 63 additions & 0 deletions tests/negative/filesystems/creation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2021 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package filesystems

import (
"github.com/coreos/ignition/v2/tests/register"
"github.com/coreos/ignition/v2/tests/types"
)

func init() {
register.Register(register.NegativeTest, EraseBlockDeviceWithInvalidoptions())
}

// EraseBlockDeviceWithInvalidoptions verifies that the Ignition
// fails to erase the block device with pre-existing filesystem
// on it if `wipeFileSystem is set to `false` and the format is
// set to `none`.
func EraseBlockDeviceWithInvalidoptions() types.Test {
name := "filesystem.erase.block.device.invalidoptions"
in := types.GetBaseDisk()
out := types.GetBaseDisk()
mntDevices := []types.MntDevice{
{
Label: "EFI-SYSTEM",
Substitution: "$DEVICE",
},
}
config := `{
"ignition": { "version": "$version" },
"storage": {
"filesystems": [{
"device": "$DEVICE",
"format": "none",
"path": "/tmp0",
"wipeFilesystem": false
}]
}
}`
configMinVersion := "3.3.0-experimental"

in[0].Partitions.GetPartition("EFI-SYSTEM").FilesystemType = "ext4"

return types.Test{
Name: name,
In: in,
Out: out,
MntDevices: mntDevices,
Config: config,
ConfigMinVersion: configMinVersion,
}
}
84 changes: 84 additions & 0 deletions tests/positive/filesystems/creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (

func init() {
register.Register(register.PositiveTest, WipeFilesystemWithSameType())
register.Register(register.PositiveTest, EraseBlockDeviceWithPreExistingFileSystem())
register.Register(register.PositiveTest, EraseBlockDeviceWithNoFileSystem())
register.Register(register.PositiveTest, FilesystemCreationOnMultipleDisks())
}

Expand Down Expand Up @@ -78,6 +80,88 @@ func WipeFilesystemWithSameType() types.Test {
}
}

// EraseBlockDeviceWithPreExistingFileSystem verifies that
// the Ignition erases the block device with pre-existing
// filesystem on it and doesn't create any filesystem on
// the given partition if `wipeFileSystem is set to `true`
// and the format is set to `none`.
func EraseBlockDeviceWithPreExistingFileSystem() types.Test {
name := "filesystem.erase.block.device.withpreexistingfilesystem"
in := types.GetBaseDisk()
out := types.GetBaseDisk()
mntDevices := []types.MntDevice{
{
Label: "OEM",
Substitution: "$DEVICE",
},
}
config := `{
"ignition": { "version": "$version" },
"storage": {
"filesystems": [{
"device": "$DEVICE",
"format": "none",
"path": "/tmp0",
"wipeFilesystem": true
}]
}
}`
configMinVersion := "3.3.0-experimental"

in[0].Partitions.GetPartition("OEM").FilesystemType = "ext4"
out[0].Partitions.GetPartition("OEM").FilesystemType = ""
out[0].Partitions.GetPartition("OEM").Files = []types.File{}

return types.Test{
Name: name,
In: in,
Out: out,
MntDevices: mntDevices,
Config: config,
ConfigMinVersion: configMinVersion,
}
}

// EraseBlockDeviceWithNoFileSystem verifies that the Ignition
// succeeds without any changes to the block device if `wipeFileSystem
// is set to `false` and the format is set to `none`.
func EraseBlockDeviceWithNoFileSystem() types.Test {
name := "filesystem.erase.block.device.withnofilesystem"
in := types.GetBaseDisk()
out := types.GetBaseDisk()
mntDevices := []types.MntDevice{
{
Label: "OEM",
Substitution: "$DEVICE",
},
}
config := `{
"ignition": { "version": "$version" },
"storage": {
"filesystems": [{
"device": "$DEVICE",
"format": "none",
"path": "/tmp0",
"wipeFilesystem": false
}]
}
}`
configMinVersion := "3.3.0-experimental"

in[0].Partitions.GetPartition("OEM").FilesystemType = ""
out[0].Partitions.GetPartition("OEM").FilesystemType = ""
out[0].Partitions.GetPartition("OEM").Files = []types.File{}

return types.Test{
Name: name,
In: in,
Out: out,
MntDevices: mntDevices,
Config: config,
ConfigMinVersion: configMinVersion,
}
}

func FilesystemCreationOnMultipleDisks() types.Test {
name := "filesystem.create.multipledisks"
in := types.GetBaseDisk()
Expand Down

0 comments on commit 63a6789

Please sign in to comment.