Skip to content

Commit

Permalink
Merge pull request hashicorp#7294 from hyperonecom/hyperone
Browse files Browse the repository at this point in the history
Add HyperOne builder
  • Loading branch information
SwampDragons authored Feb 25, 2019
2 parents f7ba933 + 94a7a07 commit 05897c8
Show file tree
Hide file tree
Showing 269 changed files with 49,854 additions and 8 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
/builder/ncloud/ @YuSungDuk
/builder/scaleway/ @sieben @mvaude @jqueuniet @fflorens @brmzkw
/builder/hcloud/ @LKaemmerling
/builder/hyperone @m110 @gregorybrzeski @ad-m

# provisioners

Expand Down
48 changes: 48 additions & 0 deletions builder/hyperone/artifact.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package hyperone

import (
"context"
"fmt"

"github.com/hyperonecom/h1-client-go"
)

type Artifact struct {
imageName string
imageID string
client *openapi.APIClient
}

func (a *Artifact) BuilderId() string {
return BuilderID
}

func (a *Artifact) Files() []string {
return nil
}

func (a *Artifact) Id() string {
return a.imageID
}

func (a *Artifact) String() string {
return fmt.Sprintf("Image '%s' created, ID: %s", a.imageName, a.imageID)
}

func (a *Artifact) State(name string) interface{} {
return nil
}

func (a *Artifact) Destroy() error {
if a.imageID == "" {
// No image to destroy
return nil
}

_, err := a.client.ImageApi.ImageDelete(context.TODO(), a.imageID)
if err != nil {
return err
}

return nil
}
120 changes: 120 additions & 0 deletions builder/hyperone/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package hyperone

import (
"fmt"
"log"

"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/hyperonecom/h1-client-go"
)

const BuilderID = "hyperone.builder"

type Builder struct {
config Config
runner multistep.Runner
client *openapi.APIClient
}

func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
config, warnings, errs := NewConfig(raws...)
if errs != nil {
return warnings, errs
}

b.config = *config

cfg := openapi.NewConfiguration()
cfg.AddDefaultHeader("x-auth-token", b.config.Token)
if b.config.Project != "" {
cfg.AddDefaultHeader("x-project", b.config.Project)
}

if b.config.APIURL != "" {
cfg.BasePath = b.config.APIURL
}

prefer := fmt.Sprintf("respond-async,wait=%d", int(b.config.StateTimeout.Seconds()))
cfg.AddDefaultHeader("Prefer", prefer)

b.client = openapi.NewAPIClient(cfg)

return nil, nil
}

type wrappedCommandTemplate struct {
Command string
}

func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
wrappedCommand := func(command string) (string, error) {
ctx := b.config.ctx
ctx.Data = &wrappedCommandTemplate{Command: command}
return interpolate.Render(b.config.ChrootCommandWrapper, &ctx)
}

state := &multistep.BasicStateBag{}
state.Put("config", &b.config)
state.Put("client", b.client)
state.Put("hook", hook)
state.Put("ui", ui)
state.Put("wrappedCommand", CommandWrapper(wrappedCommand))

steps := []multistep.Step{
&stepCreateSSHKey{},
&stepCreateVM{},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: getPublicIP,
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
}

if b.config.ChrootDisk {
steps = append(steps,
&stepPrepareDevice{},
&stepPreMountCommands{},
&stepMountChroot{},
&stepPostMountCommands{},
&stepMountExtra{},
&stepCopyFiles{},
&stepChrootProvision{},
&stepStopVM{},
&stepDetachDisk{},
&stepCreateVMFromDisk{},
&stepCreateImage{},
)
} else {
steps = append(steps,
&common.StepProvision{},
&stepStopVM{},
&stepCreateImage{},
)
}

b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(state)

if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}

artifact := &Artifact{
imageID: state.Get("image_id").(string),
imageName: state.Get("image_name").(string),
client: b.client,
}

return artifact, nil
}

func (b *Builder) Cancel() {
if b.runner != nil {
log.Println("Cancelling the runner...")
b.runner.Cancel()
}
}
63 changes: 63 additions & 0 deletions builder/hyperone/builder_acc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package hyperone

import (
"os"
"testing"

builderT "github.com/hashicorp/packer/helper/builder/testing"
)

func TestBuilderAcc_basic(t *testing.T) {
builderT.Test(t, builderT.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Builder: &Builder{},
Template: testBuilderAccBasic,
})
}

func TestBuilderAcc_chroot(t *testing.T) {
builderT.Test(t, builderT.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Builder: &Builder{},
Template: testBuilderAccChroot,
})
}

func testAccPreCheck(t *testing.T) {
if v := os.Getenv("HYPERONE_TOKEN"); v == "" {
t.Fatal("HYPERONE_TOKEN must be set for acceptance tests")
}
}

const testBuilderAccBasic = `
{
"builders": [{
"type": "test",
"vm_type": "a1.nano",
"source_image": "ubuntu",
"disk_size": 10
}]
}
`

const testBuilderAccChroot = `
{
"builders": [{
"type": "test",
"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}}"
]
}]
}
`
54 changes: 54 additions & 0 deletions builder/hyperone/chroot_communicator.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading

0 comments on commit 05897c8

Please sign in to comment.