Skip to content

Commit

Permalink
Automatically detect the disk to resize (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
edigaryev authored Nov 1, 2022
1 parent bcdfa76 commit 470048a
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 43 deletions.
68 changes: 68 additions & 0 deletions builder/tart/diskutil_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package tart

import (
"fmt"
"howett.net/plist"
)

const expectedLastPartitionContent = "Apple_APFS"

// ParseDiskUtilPlistOutput parses "diskutil list -plist" output,
// makes sure there's only one disk on the system and returns
// its name and the name of the last partition, additionally
// validating that the last partition is not a recovery one
// (which we should've deleted for the disk expansion to work).
func ParseDiskUtilPlistOutput(input []byte) (string, string, error) {
unmarshalledInput := map[string]interface{}{}

_, err := plist.Unmarshal(input, &unmarshalledInput)
if err != nil {
return "", "", err
}

allDisksAndPartitions, ok := unmarshalledInput["AllDisksAndPartitions"].([]interface{})
if !ok {
return "", "", fmt.Errorf("\"AllDisksAndPartitions\" value doesn't seem to be a dictionary")
}

if len(allDisksAndPartitions) != 1 {
return "", "", fmt.Errorf("there are more than one physical disk present on the system")
}

disk, ok := allDisksAndPartitions[0].(map[string]interface{})
if !ok {
return "", "", fmt.Errorf("first disk entry doesn't seem to be a dictionary")
}

diskDeviceIdentifier, ok := disk["DeviceIdentifier"].(string)
if !ok {
return "", "", fmt.Errorf("first disk's \"DeviceIdentifier\" doesn't seem to be a string")
}

partitions, ok := disk["Partitions"].([]interface{})
if !ok {
return "", "", fmt.Errorf("first disk's \"Partitions\" doesn't seem to be a list")
}

lastPartitionRaw := partitions[len(partitions)-1]
lastPartition, ok := lastPartitionRaw.(map[string]interface{})
if !ok {
return "", "", fmt.Errorf("last partition entry doesn't seem to be a map")
}

lastPartitionContent, ok := lastPartition["Content"].(string)
if !ok {
return "", "", fmt.Errorf("last partition's \"Content\" doesn't seem to be a string")
}
if lastPartitionContent != expectedLastPartitionContent {
return "", "", fmt.Errorf("last partition's \"Content\" should be %q, got %q",
expectedLastPartitionContent, lastPartitionContent)
}

lastPartitionDeviceIdentifier, ok := lastPartition["DeviceIdentifier"].(string)
if !ok {
return "", "", fmt.Errorf("last partition's \"DeviceIdentifier\" doesn't seem to be a string")
}

return diskDeviceIdentifier, lastPartitionDeviceIdentifier, nil
}
70 changes: 70 additions & 0 deletions builder/tart/diskutil_parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package tart

import (
"github.com/stretchr/testify/require"
"testing"
)

func TestKek(t *testing.T) {
plistBytes := `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AllDisks</key>
<array>
<string>disk0</string>
<string>disk0s1</string>
<string>disk0s2</string>
<string>disk0s3</string>
</array>
<key>AllDisksAndPartitions</key>
<array>
<dict>
<key>Content</key>
<string>GUID_partition_scheme</string>
<key>DeviceIdentifier</key>
<string>disk0</string>
<key>OSInternal</key>
<false/>
<key>Partitions</key>
<array>
<dict>
<key>Content</key>
<string>Apple_APFS_ISC</string>
<key>DeviceIdentifier</key>
<string>disk0s1</string>
<key>DiskUUID</key>
<string>024D2AE5-891F-4244-81EC-182B88D1AA0B</string>
<key>Size</key>
<integer>524288000</integer>
</dict>
<dict>
<key>Content</key>
<string>Apple_APFS</string>
<key>DeviceIdentifier</key>
<string>disk0s2</string>
<key>DiskUUID</key>
<string>430F1409-D91A-47F4-8418-0876B14AA807</string>
<key>Size</key>
<integer>494384795648</integer>
</dict>
</array>
<key>Size</key>
<integer>500277792768</integer>
</dict>
</array>
<key>VolumesFromDisks</key>
<array/>
<key>WholeDisks</key>
<array>
<string>disk0</string>
</array>
</dict>
</plist>
`

diskName, partitionName, err := ParseDiskUtilPlistOutput([]byte(plistBytes))
require.NoError(t, err)
require.Equal(t, "disk0", diskName)
require.Equal(t, "disk0s2", partitionName)
}
34 changes: 34 additions & 0 deletions builder/tart/quiet_ui.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package tart

import (
"github.com/hashicorp/packer-plugin-sdk/packer"
"io"
)

type QuietUi struct {
BaseUi packer.Ui
}

func (ui QuietUi) Ask(s string) (string, error) {
return ui.BaseUi.Ask(s)
}

func (ui QuietUi) Say(s string) {
// do nothing
}

func (ui QuietUi) Message(s string) {
// do nothing
}

func (ui QuietUi) Error(s string) {
ui.BaseUi.Error(s)
}

func (ui QuietUi) Machine(s string, s2 ...string) {
ui.BaseUi.Machine(s, s2...)
}

func (ui QuietUi) TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser) {
return ui.BaseUi.TrackProgress(src, currentSize, totalSize, stream)
}
30 changes: 27 additions & 3 deletions builder/tart/step_disk_resize.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package tart

import (
"bytes"
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
Expand All @@ -26,20 +28,42 @@ func (s *stepResize) Run(ctx context.Context, state multistep.StateBag) multiste

ui.Say("Let's SSH in and claim the new space for the disk...")

// Determine the disk and a partition to act on
listCmd := packersdk.RemoteCmd{
Command: "diskutil list -plist physical",
}

buf := bytes.NewBufferString("")
listCmd.Stdout = buf

err := listCmd.RunWithUi(ctx, communicator, &QuietUi{BaseUi: ui})
if err != nil {
ui.Error(err.Error())

return multistep.ActionHalt
}

diskName, partitionName, err := ParseDiskUtilPlistOutput(buf.Bytes())
if err != nil {
ui.Error(fmt.Sprintf("failed to parse \"diskutil list -plist physical\" output: %v", err))

return multistep.ActionHalt
}

ui.Say("Freeing space...")
repairCmd := packersdk.RemoteCmd{
Command: "yes | diskutil repairDisk disk0",
Command: fmt.Sprintf("yes | diskutil repairDisk %s", diskName),
}

err := repairCmd.RunWithUi(ctx, communicator, ui)
err = repairCmd.RunWithUi(ctx, communicator, ui)

if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}

resizeCmd := packersdk.RemoteCmd{
Command: "diskutil apfs resizeContainer disk0s2 0",
Command: fmt.Sprintf("diskutil apfs resizeContainer %s 0", partitionName),
}

ui.Say("Resizing the partition...")
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ require (
github.com/hashicorp/hcl/v2 v2.14.0
github.com/hashicorp/packer-plugin-sdk v0.3.2
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
github.com/stretchr/testify v1.7.0
github.com/zclconf/go-cty v1.10.0
howett.net/plist v1.0.0
)

require (
Expand All @@ -21,6 +23,7 @@ require (
github.com/aws/aws-sdk-go v1.40.34 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dylanmei/iso8601 v0.1.0 // indirect
github.com/fatih/color v1.12.0 // indirect
github.com/gofrs/flock v0.8.1 // indirect
Expand Down Expand Up @@ -70,6 +73,7 @@ require (
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pkg/sftp v1.13.2 // indirect
github.com/pkg/xattr v0.4.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
Expand All @@ -91,4 +95,5 @@ require (
gopkg.in/djherbis/times.v1 v1.2.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
Loading

0 comments on commit 470048a

Please sign in to comment.