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 repartition mode #329

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This plugin allows you to build or extend ARM system image. It operates in two m
* new - creates empty disk image and populates the rootfs on it
* reuse - uses already existing image as the base
* resize - uses already existing image but resize given partition (ie. root)
* repartition - uses already existing image but recreates partition table and optionally formats selected partitions

Plugin mimics standard image creation process, such as:
* building base empty image (dd)
Expand Down Expand Up @@ -226,6 +227,59 @@ Complete examples:
- [`boards/raspberry-pi/raspbian-resize.json`](./boards/raspberry-pi/raspbian-resize.json)
- [`boards/beaglebone-black/ubuntu.pkr.hcl`](./boards/beaglebone-black/ubuntu.pkr.hcl)

## Repartitioning image

The `repartition` method allows for rewriting the partition table of an image, formatting any partitions (opt-out with `skip_mkfs`), and resizing the filesystems on the partition to the size of the partition with `resize2fs`. This is similar in outcome to the `new` method, however may be used with raw image files (`.img` or `.iso`), rather than rootfs archives.

To repartition an image:

* Set `image_build_method` to `repartition`
* Ensure all partitions are fully configured in `image_partitions`
* To **prevent** a partition from being reformatted, set `skip_mkfs` to `true` (`false` by default)
* To invoke resize2fs on a partition after the partition table has been rewritten (this is necessary if you change the partition size), set `resize_fs` to `true` (`false` by default). **Note**: as resize2fs only operates on ext{2,3,4} partitions, non-ext partitions cannot be resized.

For example:

```json
"builders": [
{
"type": "arm",
"image_build_method": "resize",
"image_partitions": [
{
"name": "boot",
"start_sector": "2048",
"size": "524288", // 256MiB
"skip_mkfs": true,
...
},
{
"name": "root",
"size": "16777216", // 8GiB
"start_sector": "526336",
"skip_mkfs": true,
"resize_fs": true,
...
},
{
"name": "home",
"size": "0", // expand to end of disk
"start_sector": "17303552",
"resize_fs": true,
...
}
],
...
}
]
```

Assuming a source image with a boot partition of 256MiB and a root partition of 4GiB, the above configuration will:

* Keep the boot partition as-is
* Resize the root partition to 8GiB
* Create and format a new partition which fills the remainder of the disk

## Docker
With `artifice` plugin you can pass rootfs archive to docker plugins
```
Expand Down
12 changes: 12 additions & 0 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&StepMountImage{FromKey: "image_loop_device", ResultKey: "image_mountpoint", MountPath: b.config.ImageMountPath},
)

case "repartition":
steps = append(
steps,
&StepExtractAndCopyImage{FromKey: "rootfs_archive_path"},
&StepResizeQemuImage{},
&StepPartitionImage{},
&StepMapImage{ResultKey: "image_loop_device"},
&StepMkfsImage{FromKey: "image_loop_device"},
&StepResizePartitionFs{FromKey: "image_loop_device"},
&StepMountImage{FromKey: "image_loop_device", ResultKey: "image_mountpoint", MountPath: b.config.ImageMountPath},
)

default:
return nil, errors.New("invalid build method")
}
Expand Down
5 changes: 5 additions & 0 deletions builder/step_mkfs_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ func (s *StepMkfsImage) Run(_ context.Context, state multistep.StateBag) multist
loopDevice := state.Get(s.FromKey).(string)

for i, partition := range config.ImageConfig.ImagePartitions {
if partition.SkipMkfs {
ui.Message(fmt.Sprintf("skipping mkfs for partition #%d", i+1))
continue
}

cmd := fmt.Sprintf("mkfs.%s", partition.Filesystem)
args := append(partition.FilesystemMakeOptions, fmt.Sprintf("%sp%d", loopDevice, i+1))

Expand Down
48 changes: 38 additions & 10 deletions builder/step_resize_partition_fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/mkaczanowski/packer-builder-arm/config"
)

// StepResizePartitionFs expand already partitioned image
Expand All @@ -15,21 +16,48 @@ type StepResizePartitionFs struct {
SelectedPartitionKey string
}

// Find partitions marked with ResizeFs that we explicitly want to resize
func findPartitionsToResize(imagePartitions []config.Partition) []int {
var selectedPartitions []int

for i, partition := range imagePartitions {
if partition.ResizeFs {
selectedPartitions = append(selectedPartitions, i+1)
}
}

return selectedPartitions
}

// Run the step
func (s *StepResizePartitionFs) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
var (
ui = state.Get("ui").(packer.Ui)

loopDevice = state.Get(s.FromKey).(string)
selectedPartition = state.Get(s.SelectedPartitionKey).(int)
device = fmt.Sprintf("%sp%d", loopDevice, selectedPartition)
ui = state.Get("ui").(packer.Ui)
loopDevice = state.Get(s.FromKey).(string)
)

out, err := exec.Command("resize2fs", "-f", device).CombinedOutput()
ui.Message(fmt.Sprintf("running resize2fs on %s ", device))
if err != nil {
ui.Error(fmt.Sprintf("error while resizing partition %v: %s", err, out))
return multistep.ActionHalt
var selectedPartitions []int

// If we're running in resize mode, we'll have a single selected partition
// to resize from StepExpandPartition, and we can be sure we don't need to
// expand any other partitions. If we're in repartition mode, we manually
// choose which partitions to resize: only the user knows which ones
// have been expanded.
if s.SelectedPartitionKey != "" {
selectedPartitions = append(selectedPartitions, state.Get(s.SelectedPartitionKey).(int))
} else {
config := state.Get("config").(*Config)
selectedPartitions = findPartitionsToResize(config.ImagePartitions)
}

for _, partition := range selectedPartitions {
device := fmt.Sprintf("%sp%d", loopDevice, partition)
out, err := exec.Command("resize2fs", "-f", device).CombinedOutput()
ui.Message(fmt.Sprintf("running resize2fs on %s ", device))
if err != nil {
ui.Error(fmt.Sprintf("error while resizing partition %v: %s", err, out))
return multistep.ActionHalt
}
}

return multistep.ActionContinue
Expand Down
6 changes: 4 additions & 2 deletions config/image_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type Partition struct {
Filesystem string `mapstructure:"filesystem"`
FilesystemMakeOptions []string `mapstructure:"filesystem_make_options"`
Mountpoint string `mapstructure:"mountpoint"`
ResizeFs bool `mapstructure:"resize_fs"`
SkipMkfs bool `mapstructure:"skip_mkfs"`
}

// ChrootMount describes a mountpoint that is being setup
Expand Down Expand Up @@ -74,8 +76,8 @@ func (c *ImageConfig) Prepare(_ *interpolate.Context) (warnings []string, errs [
errs = append(errs, errors.New("image build method must be specified"))
}

if !(c.ImageBuildMethod == "new" || c.ImageBuildMethod == "reuse" || c.ImageBuildMethod == "resize") {
errs = append(errs, errors.New("invalid image build method specified (valid options: new, reuse)"))
if !(c.ImageBuildMethod == "new" || c.ImageBuildMethod == "reuse" || c.ImageBuildMethod == "resize" || c.ImageBuildMethod == "repartition") {
errs = append(errs, errors.New("invalid image build method specified (valid options: new, reuse, resize, repartition)"))
}

if len(c.ImagePartitions) == 0 {
Expand Down
4 changes: 4 additions & 0 deletions config/image_config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading