mirror of https://github.com/hashicorp/packer
parent
aaee600be0
commit
bd4ce90728
@ -0,0 +1,54 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CommandWrapper func(string) (string, error)
|
||||||
|
|
||||||
|
// ChrootCommunicator works as a wrapper on SSHCommunicator, modyfing paths in
|
||||||
|
// flight to be run in a chroot.
|
||||||
|
type ChrootCommunicator struct {
|
||||||
|
Chroot string
|
||||||
|
CmdWrapper CommandWrapper
|
||||||
|
Wrapped packer.Communicator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChrootCommunicator) Start(cmd *packer.RemoteCmd) error {
|
||||||
|
command := strconv.Quote(cmd.Command)
|
||||||
|
chrootCommand, err := c.CmdWrapper(
|
||||||
|
fmt.Sprintf("sudo chroot %s /bin/sh -c %s", c.Chroot, command))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Command = chrootCommand
|
||||||
|
|
||||||
|
return c.Wrapped.Start(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChrootCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error {
|
||||||
|
dst = filepath.Join(c.Chroot, dst)
|
||||||
|
return c.Wrapped.Upload(dst, r, fi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChrootCommunicator) UploadDir(dst string, src string, exclude []string) error {
|
||||||
|
dst = filepath.Join(c.Chroot, dst)
|
||||||
|
return c.Wrapped.UploadDir(dst, src, exclude)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChrootCommunicator) Download(src string, w io.Writer) error {
|
||||||
|
src = filepath.Join(c.Chroot, src)
|
||||||
|
return c.Wrapped.Download(src, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChrootCommunicator) DownloadDir(src string, dst string, exclude []string) error {
|
||||||
|
src = filepath.Join(c.Chroot, src)
|
||||||
|
return c.Wrapped.DownloadDir(src, dst, exclude)
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepChrootProvision struct{}
|
||||||
|
|
||||||
|
func (s *stepChrootProvision) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
||||||
|
sshCommunicator := state.Get("communicator").(packer.Communicator)
|
||||||
|
|
||||||
|
comm := &ChrootCommunicator{
|
||||||
|
Chroot: config.ChrootMountPath,
|
||||||
|
CmdWrapper: wrappedCommand,
|
||||||
|
Wrapped: sshCommunicator,
|
||||||
|
}
|
||||||
|
|
||||||
|
stepProvision := common.StepProvision{Comm: comm}
|
||||||
|
return stepProvision.Run(ctx, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepChrootProvision) Cleanup(multistep.StateBag) {}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepCopyFiles struct{}
|
||||||
|
|
||||||
|
func (s *stepCopyFiles) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
if len(config.ChrootCopyFiles) == 0 {
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Copying files from host to chroot...")
|
||||||
|
for _, path := range config.ChrootCopyFiles {
|
||||||
|
chrootPath := filepath.Join(config.ChrootMountPath, path)
|
||||||
|
log.Printf("Copying '%s' to '%s'", path, chrootPath)
|
||||||
|
|
||||||
|
command := fmt.Sprintf("cp --remove-destination %s %s", path, chrootPath)
|
||||||
|
err := runCommands([]string{command}, config.ctx, state)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepCopyFiles) Cleanup(state multistep.StateBag) {}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hyperonecom/h1-client-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepCreateVMFromDisk struct {
|
||||||
|
vmID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepCreateVMFromDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
client := state.Get("client").(*openapi.APIClient)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
sshKey := state.Get("ssh_public_key").(string)
|
||||||
|
chrootDiskID := state.Get("chroot_disk_id").(string)
|
||||||
|
|
||||||
|
ui.Say("Creating VM from disk...")
|
||||||
|
|
||||||
|
options := openapi.VmCreate{
|
||||||
|
Name: config.VmName,
|
||||||
|
Service: config.VmFlavour,
|
||||||
|
Disk: []openapi.VmCreateDisk{
|
||||||
|
{
|
||||||
|
Id: chrootDiskID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SshKeys: []string{sshKey},
|
||||||
|
Boot: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
vm, _, err := client.VmApi.VmCreate(ctx, options)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("error creating VM from disk: %s", formatOpenAPIError(err))
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.vmID = vm.Id
|
||||||
|
state.Put("vm_id", vm.Id)
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepCreateVMFromDisk) Cleanup(state multistep.StateBag) {
|
||||||
|
if s.vmID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := state.Get("client").(*openapi.APIClient)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
chrootDiskID := state.Get("chroot_disk_id").(string)
|
||||||
|
|
||||||
|
ui.Say("Deleting VM (from disk)...")
|
||||||
|
|
||||||
|
deleteOptions := openapi.VmDelete{
|
||||||
|
RemoveDisks: []string{chrootDiskID},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := client.VmApi.VmDelete(context.TODO(), s.vmID, deleteOptions)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error deleting server '%s' - please delete it manually: %s", s.vmID, formatOpenAPIError(err)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hyperonecom/h1-client-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepDetachDisk struct {
|
||||||
|
vmID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepDetachDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
client := state.Get("client").(*openapi.APIClient)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vmID := state.Get("vm_id").(string)
|
||||||
|
chrootDiskID := state.Get("chroot_disk_id").(string)
|
||||||
|
|
||||||
|
ui.Say("Detaching chroot disk...")
|
||||||
|
_, _, err := client.VmApi.VmDeleteHddDiskId(ctx, vmID, chrootDiskID)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("error detaching disk: %s", formatOpenAPIError(err))
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepDetachDisk) Cleanup(state multistep.StateBag) {}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepMountChroot struct{}
|
||||||
|
|
||||||
|
func (s *stepMountChroot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
device := state.Get("device").(string)
|
||||||
|
|
||||||
|
log.Printf("Mount path: %s", config.ChrootMountPath)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Creating mount directory: %s", config.ChrootMountPath))
|
||||||
|
|
||||||
|
opts := ""
|
||||||
|
if len(config.MountOptions) > 0 {
|
||||||
|
opts = "-o " + strings.Join(config.MountOptions, " -o ")
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceMount := device
|
||||||
|
if config.MountPartition != "" {
|
||||||
|
deviceMount = fmt.Sprintf("%s%s", device, config.MountPartition)
|
||||||
|
}
|
||||||
|
|
||||||
|
commands := []string{
|
||||||
|
fmt.Sprintf("mkdir -m 755 -p %s", config.ChrootMountPath),
|
||||||
|
fmt.Sprintf("mount %s %s %s", opts, deviceMount, config.ChrootMountPath),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := runCommands(commands, config.ctx, state)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Put("mount_path", config.ChrootMountPath)
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepMountChroot) Cleanup(state multistep.StateBag) {}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepMountExtra struct{}
|
||||||
|
|
||||||
|
func (s *stepMountExtra) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
mountPath := state.Get("mount_path").(string)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
ui.Say("Mounting additional paths within the chroot...")
|
||||||
|
for _, mountInfo := range config.ChrootMounts {
|
||||||
|
innerPath := mountPath + mountInfo[2]
|
||||||
|
|
||||||
|
flags := "-t " + mountInfo[0]
|
||||||
|
if mountInfo[0] == "bind" {
|
||||||
|
flags = "--bind"
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Message(fmt.Sprintf("Mounting: %s", mountInfo[2]))
|
||||||
|
|
||||||
|
commands := []string{
|
||||||
|
fmt.Sprintf("mkdir -m 755 -p %s", innerPath),
|
||||||
|
fmt.Sprintf("mount %s %s %s", flags, mountInfo[1], innerPath),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := runCommands(commands, config.ctx, state)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepMountExtra) Cleanup(state multistep.StateBag) {}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type postMountCommandsData struct {
|
||||||
|
Device string
|
||||||
|
MountPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type stepPostMountCommands struct{}
|
||||||
|
|
||||||
|
func (s *stepPostMountCommands) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
device := state.Get("device").(string)
|
||||||
|
|
||||||
|
ctx := config.ctx
|
||||||
|
ctx.Data = &postMountCommandsData{
|
||||||
|
Device: device,
|
||||||
|
MountPath: config.ChrootMountPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Running post-mount commands...")
|
||||||
|
if err := runCommands(config.PostMountCommands, ctx, state); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepPostMountCommands) Cleanup(state multistep.StateBag) {}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type preMountCommandsData struct {
|
||||||
|
Device string
|
||||||
|
MountPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type stepPreMountCommands struct{}
|
||||||
|
|
||||||
|
func (s *stepPreMountCommands) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
device := state.Get("device").(string)
|
||||||
|
|
||||||
|
ctx := config.ctx
|
||||||
|
ctx.Data = &preMountCommandsData{
|
||||||
|
Device: device,
|
||||||
|
MountPath: config.ChrootMountPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Running pre-mount commands...")
|
||||||
|
if err := runCommands(config.PreMountCommands, ctx, state); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepPreMountCommands) Cleanup(state multistep.StateBag) {}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package hyperone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stepPrepareDevice struct{}
|
||||||
|
|
||||||
|
func (s *stepPrepareDevice) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
chrootDiskID := state.Get("chroot_disk_id").(string)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
log.Println("Searching for available device...")
|
||||||
|
device, err := availableDevice(chrootDiskID)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("error finding available device: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(device); err == nil {
|
||||||
|
err := fmt.Errorf("device is in use: %s", device)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Found device: %s", device))
|
||||||
|
state.Put("device", device)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stepPrepareDevice) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
||||||
|
func availableDevice(scsiID string) (string, error) {
|
||||||
|
// TODO proper SCSI search
|
||||||
|
return "/dev/sdb", nil
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"token": "{{ env `HYPERONE_TOKEN` }}",
|
||||||
|
"project": "{{ env `HYPERONE_PROJECT` }}"
|
||||||
|
},
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"token": "{{ user `token` }}",
|
||||||
|
"project": "{{ user `project` }}",
|
||||||
|
"type": "hyperone",
|
||||||
|
"source_image": "ubuntu",
|
||||||
|
"disk_size": 10,
|
||||||
|
"vm_type": "a1.nano",
|
||||||
|
|
||||||
|
"chroot_disk": true,
|
||||||
|
"chroot_command_wrapper": "sudo {{.Command}}",
|
||||||
|
"pre_mount_commands": [
|
||||||
|
"parted {{.Device}} mklabel msdos mkpart primary 1M 100% set 1 boot on print",
|
||||||
|
"mkfs.ext4 {{.Device}}1"
|
||||||
|
],
|
||||||
|
"post_mount_commands": [
|
||||||
|
"apt-get update",
|
||||||
|
"apt-get install debootstrap",
|
||||||
|
"debootstrap --arch amd64 bionic {{.MountPath}}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"provisioners": []
|
||||||
|
}
|
||||||
Loading…
Reference in new issue