Add FreeBSD support.

This commit is contained in:
Brad Ackerman 2020-08-01 21:09:43 -07:00
parent a28edbaa0b
commit 364c415294
No known key found for this signature in database
GPG Key ID: 5C5E7A6B14E3D827
6 changed files with 164 additions and 85 deletions

View File

@ -391,8 +391,11 @@ func checkHyperVGeneration(s string) interface{} {
} }
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
if runtime.GOOS != "linux" { switch runtime.GOOS {
return nil, errors.New("the azure-chroot builder only works on Linux environments") case "linux", "freebsd":
break
default:
return nil, errors.New("the azure-chroot builder only works on Linux and FreeBSD environments")
} }
err := b.config.ClientConfig.FillParameters() err := b.config.ClientConfig.FillParameters()

View File

@ -3,24 +3,18 @@ package chroot
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"log" "log"
"os"
"path/filepath"
"strings" "strings"
"syscall"
"time" "time"
"github.com/hashicorp/packer/builder/azure/common/client" "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to" "github.com/Azure/go-autorest/autorest/to"
"github.com/hashicorp/packer/builder/azure/common/client"
) )
type DiskAttacher interface { type DiskAttacher interface {
AttachDisk(ctx context.Context, disk string) (lun int32, err error) AttachDisk(ctx context.Context, disk string) (lun int32, err error)
DiskPathForLun(lun int32) string
WaitForDevice(ctx context.Context, i int32) (device string, err error) WaitForDevice(ctx context.Context, i int32) (device string, err error)
DetachDisk(ctx context.Context, disk string) (err error) DetachDisk(ctx context.Context, disk string) (err error)
WaitForDetach(ctx context.Context, diskID string) error WaitForDetach(ctx context.Context, diskID string) error
@ -38,79 +32,6 @@ type diskAttacher struct {
vm *client.ComputeInfo // store info about this VM so that we don't have to ask metadata service on every call vm *client.ComputeInfo // store info about this VM so that we don't have to ask metadata service on every call
} }
func (diskAttacher) DiskPathForLun(lun int32) string {
return fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lun)
}
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
path := da.DiskPathForLun(lun)
for {
link, err := os.Readlink(path)
if err == nil {
return filepath.Abs("/dev/disk/azure/scsi1/" + link)
} else if err != os.ErrNotExist {
if pe, ok := err.(*os.PathError); ok && pe.Err != syscall.ENOENT {
return "", err
}
}
select {
case <-time.After(100 * time.Millisecond):
// continue
case <-ctx.Done():
return "", ctx.Err()
}
}
}
func (da *diskAttacher) DetachDisk(ctx context.Context, diskID string) error {
log.Println("Fetching list of disks currently attached to VM")
currentDisks, err := da.getDisks(ctx)
if err != nil {
return err
}
log.Printf("Removing %q from list of disks currently attached to VM", diskID)
newDisks := []compute.DataDisk{}
for _, disk := range currentDisks {
if disk.ManagedDisk != nil &&
!strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
newDisks = append(newDisks, disk)
}
}
if len(currentDisks) == len(newDisks) {
return DiskNotFoundError
}
log.Println("Updating new list of disks attached to VM")
err = da.setDisks(ctx, newDisks)
if err != nil {
return err
}
return nil
}
func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error {
for { // loop until disk is not attached, timeout or error
list, err := da.getDisks(ctx)
if err != nil {
return err
}
if findDiskInList(list, diskID) == nil {
log.Println("Disk is no longer in VM model, assuming detached")
return nil
}
select {
case <-time.After(time.Second): //continue
case <-ctx.Done():
return ctx.Err()
}
}
}
var DiskNotFoundError = errors.New("Disk not found") var DiskNotFoundError = errors.New("Disk not found")
func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int32, error) { func (da *diskAttacher) AttachDisk(ctx context.Context, diskID string) (int32, error) {
@ -160,6 +81,53 @@ findFreeLun:
return lun, nil return lun, nil
} }
func (da *diskAttacher) DetachDisk(ctx context.Context, diskID string) error {
log.Println("Fetching list of disks currently attached to VM")
currentDisks, err := da.getDisks(ctx)
if err != nil {
return err
}
log.Printf("Removing %q from list of disks currently attached to VM", diskID)
newDisks := []compute.DataDisk{}
for _, disk := range currentDisks {
if disk.ManagedDisk != nil &&
!strings.EqualFold(to.String(disk.ManagedDisk.ID), diskID) {
newDisks = append(newDisks, disk)
}
}
if len(currentDisks) == len(newDisks) {
return DiskNotFoundError
}
log.Println("Updating new list of disks attached to VM")
err = da.setDisks(ctx, newDisks)
if err != nil {
return err
}
return nil
}
func (da *diskAttacher) WaitForDetach(ctx context.Context, diskID string) error {
for { // loop until disk is not attached, timeout or error
list, err := da.getDisks(ctx)
if err != nil {
return err
}
if findDiskInList(list, diskID) == nil {
log.Println("Disk is no longer in VM model, assuming detached")
return nil
}
select {
case <-time.After(time.Second): //continue
case <-ctx.Done():
return ctx.Err()
}
}
}
func (da *diskAttacher) getThisVM(ctx context.Context) (compute.VirtualMachine, error) { func (da *diskAttacher) getThisVM(ctx context.Context) (compute.VirtualMachine, error) {
// getting resource info for this VM // getting resource info for this VM
if da.vm == nil { if da.vm == nil {

View File

@ -0,0 +1,54 @@
package chroot
import (
"bufio"
"bytes"
"context"
"fmt"
"os/exec"
"regexp"
"strings"
"time"
)
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
// This builder will always be running in Azure, where data disks show up
// on scbus5 target 0. The camcontrol command always outputs LUNs in
// unpadded hexadecimal format.
regexStr := fmt.Sprintf(`at scbus5 target 0 lun %x \(.*?da([\d]+)`, lun)
devRegex, err := regexp.Compile(regexStr)
if err != nil {
return "", err
}
for {
cmd := exec.Command("camcontrol", "devlist")
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
return "", err
}
outString := out.String()
scanner := bufio.NewScanner(strings.NewReader(outString))
for scanner.Scan() {
line := scanner.Text()
// Check if this is the correct bus, target, and LUN.
if matches := devRegex.FindStringSubmatch(line); matches != nil {
// If this function immediately returns, devfs won't have
// created the device yet.
time.Sleep(100 * time.Millisecond)
return fmt.Sprintf("/dev/da%s", matches[1]), nil
}
}
if err = scanner.Err(); err != nil {
return "", err
}
select {
case <-time.After(100 * time.Millisecond):
// continue
case <-ctx.Done():
return "", ctx.Err()
}
}
}

View File

@ -0,0 +1,36 @@
package chroot
import (
"context"
"fmt"
"os"
"path/filepath"
"syscall"
"time"
)
func diskPathForLun(lun int32) string {
return fmt.Sprintf("/dev/disk/azure/scsi1/lun%d", lun)
}
func (da diskAttacher) WaitForDevice(ctx context.Context, lun int32) (device string, err error) {
path := diskPathForLun(lun)
for {
link, err := os.Readlink(path)
if err == nil {
return filepath.Abs("/dev/disk/azure/scsi1/" + link)
} else if err != os.ErrNotExist {
if pe, ok := err.(*os.PathError); ok && pe.Err != syscall.ENOENT {
return "", err
}
}
select {
case <-time.After(100 * time.Millisecond):
// continue
case <-ctx.Done():
return "", ctx.Err()
}
}
}

View File

@ -9,6 +9,7 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
@ -62,7 +63,13 @@ func (s *StepMountDevice) Run(ctx context.Context, state multistep.StateBag) mul
return multistep.ActionHalt return multistep.ActionHalt
} }
deviceMount := fmt.Sprintf("%s%s", device, s.MountPartition) var deviceMount string
switch runtime.GOOS {
case "freebsd":
deviceMount = fmt.Sprintf("%sp%s", device, s.MountPartition)
default:
deviceMount = fmt.Sprintf("%s%s", device, s.MountPartition)
}
state.Put("deviceMount", deviceMount) state.Put("deviceMount", deviceMount)

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"log" "log"
"path/filepath" "path/filepath"
"runtime"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
@ -31,12 +32,22 @@ func (s *StepCopyFiles) Run(ctx context.Context, state multistep.StateBag) multi
s.files = make([]string, 0, len(s.Files)) s.files = make([]string, 0, len(s.Files))
if len(s.Files) > 0 { if len(s.Files) > 0 {
ui.Say("Copying files from host to chroot...") ui.Say("Copying files from host to chroot...")
var removeDestinationOption string
switch runtime.GOOS {
case "freebsd":
// The -f option here is closer to GNU --remove-destination than
// what POSIX says -f should do.
removeDestinationOption = "-f"
default:
// This is the GNU binutils version.
removeDestinationOption = "--remove-destination"
}
for _, path := range s.Files { for _, path := range s.Files {
ui.Message(path) ui.Message(path)
chrootPath := filepath.Join(mountPath, path) chrootPath := filepath.Join(mountPath, path)
log.Printf("Copying '%s' to '%s'", path, chrootPath) log.Printf("Copying '%s' to '%s'", path, chrootPath)
cmdText, err := wrappedCommand(fmt.Sprintf("cp --remove-destination %s %s", path, chrootPath)) cmdText, err := wrappedCommand(fmt.Sprintf("cp %s %s %s", removeDestinationOption, path, chrootPath))
if err != nil { if err != nil {
err := fmt.Errorf("Error building copy command: %s", err) err := fmt.Errorf("Error building copy command: %s", err)
state.Put("error", err) state.Put("error", err)