Add HyperOne builder

This commit is contained in:
Miłosz Smółka 2019-01-23 14:04:05 +01:00
parent 9cbeae2c5a
commit be30415395
15 changed files with 980 additions and 0 deletions

View File

@ -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
}

View File

@ -0,0 +1,85 @@
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/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)
}
prefer := fmt.Sprintf("respond-async,wait=%d", int(b.config.StateTimeout.Seconds()))
cfg.AddDefaultHeader("Prefer", prefer)
b.client = openapi.NewAPIClient(cfg)
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
state := &multistep.BasicStateBag{}
state.Put("config", &b.config)
state.Put("client", b.client)
state.Put("hook", hook)
state.Put("ui", ui)
steps := []multistep.Step{
&stepCreateSSHKey{},
&stepCreateVM{},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: getPublicIP,
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
&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()
}
}

208
builder/hyperone/config.go Normal file
View File

@ -0,0 +1,208 @@
package hyperone
import (
"errors"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/json"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/go-homedir"
"github.com/mitchellh/mapstructure"
)
const (
configPath = "~/.h1-cli/conf.json"
tokenEnv = "HYPERONE_TOKEN"
defaultDiskType = "ssd"
defaultImageService = "564639bc052c084e2f2e3266"
defaultStateTimeout = 5 * time.Minute
defaultUserName = "guru"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
Token string `mapstructure:"token"`
Project string `mapstructure:"project"`
TokenLogin string `mapstructure:"token_login"`
StateTimeout time.Duration `mapstructure:"state_timeout"`
SourceImage string `mapstructure:"source_image"`
ImageName string `mapstructure:"image_name"`
ImageDescription string `mapstructure:"image_description"`
ImageTags map[string]interface{} `mapstructure:"image_tags"`
ImageService string `mapstructure:"image_service"`
VmFlavour string `mapstructure:"vm_flavour"`
VmName string `mapstructure:"vm_name"`
VmTags map[string]interface{} `mapstructure:"vm_tags"`
DiskName string `mapstructure:"disk_name"`
DiskType string `mapstructure:"disk_type"`
DiskSize float32 `mapstructure:"disk_size"`
Network string `mapstructure:"network"`
PrivateIP string `mapstructure:"private_ip"`
PublicIP string `mapstructure:"public_ip"`
SSHKeys []string `mapstructure:"ssh_keys"`
UserData string `mapstructure:"user_data"`
ctx interpolate.Context
}
func NewConfig(raws ...interface{}) (*Config, []string, error) {
c := &Config{}
var md mapstructure.Metadata
err := config.Decode(c, &config.DecodeOpts{
Metadata: &md,
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
},
},
}, raws...)
if err != nil {
return nil, nil, err
}
cliConfig, err := loadCLIConfig()
if err != nil {
return nil, nil, err
}
// Defaults
if c.Comm.SSHUsername == "" {
c.Comm.SSHUsername = defaultUserName
}
if c.Comm.SSHTimeout == 0 {
c.Comm.SSHTimeout = 10 * time.Minute
}
if c.Token == "" {
c.Token = os.Getenv(tokenEnv)
if c.Token == "" {
c.Token = cliConfig.Profile.APIKey
}
if c.TokenLogin != "" {
c.Token, err = fetchTokenBySSH(c.TokenLogin)
if err != nil {
return nil, nil, err
}
}
}
if c.Project == "" {
c.Project = cliConfig.Profile.Project.ID
}
if c.StateTimeout == 0 {
c.StateTimeout = defaultStateTimeout
}
if c.ImageName == "" {
name, err := interpolate.Render("packer-{{timestamp}}", nil)
if err != nil {
return nil, nil, err
}
c.ImageName = name
}
if c.ImageService == "" {
c.ImageService = defaultImageService
}
if c.VmName == "" {
c.VmName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
}
if c.DiskType == "" {
c.DiskType = defaultDiskType
}
var errs *packer.MultiError
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
if c.Token == "" {
errs = packer.MultiErrorAppend(errs, errors.New("token is required"))
}
if c.VmFlavour == "" {
errs = packer.MultiErrorAppend(errs, errors.New("vm flavour is required"))
}
if c.DiskSize == 0 {
errs = packer.MultiErrorAppend(errs, errors.New("disk size is required"))
}
if c.SourceImage == "" {
errs = packer.MultiErrorAppend(errs, errors.New("source image is required"))
}
if errs != nil && len(errs.Errors) > 0 {
return nil, nil, errs
}
packer.LogSecretFilter.Set(c.Token)
return c, nil, nil
}
type cliConfig struct {
Profile struct {
APIKey string `json:"apiKey"`
Project struct {
ID string `json:"_id"`
} `json:"project"`
} `json:"profile"`
}
func loadCLIConfig() (cliConfig, error) {
path, err := homedir.Expand(configPath)
if err != nil {
return cliConfig{}, err
}
_, err = os.Stat(path)
if err != nil {
// Config not found
return cliConfig{}, nil
}
content, err := ioutil.ReadFile(path)
if err != nil {
return cliConfig{}, err
}
var c cliConfig
err = json.Unmarshal(content, &c)
if err != nil {
return cliConfig{}, err
}
return c, nil
}
func getPublicIP(state multistep.StateBag) (string, error) {
return state.Get("public_ip").(string), nil
}

View File

@ -0,0 +1,42 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hyperonecom/h1-client-go"
)
type stepCreateImage struct{}
func (s *stepCreateImage) 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)
vmID := state.Get("vm_id").(string)
ui.Say("Creating image...")
image, _, err := client.ImageApi.ImageCreate(ctx, openapi.ImageCreate{
Name: config.ImageName,
Vm: vmID,
Service: config.ImageService,
Description: config.ImageDescription,
Tag: config.ImageTags,
})
if err != nil {
err := fmt.Errorf("error creating image: %s", formatOpenAPIError(err))
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("image_id", image.Id)
state.Put("image_name", image.Name)
return multistep.ActionContinue
}
func (s *stepCreateImage) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,82 @@
package hyperone
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"runtime"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"golang.org/x/crypto/ssh"
)
type stepCreateSSHKey struct {
Debug bool
DebugKeyPath string
}
func (s *stepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(*Config)
ui.Say("Creating a temporary ssh key for the VM...")
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
state.Put("error", fmt.Errorf("error generating ssh key: %s", err))
return multistep.ActionHalt
}
privDER := x509.MarshalPKCS1PrivateKey(priv)
privBLK := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: privDER,
}
c.Comm.SSHPrivateKey = pem.EncodeToMemory(&privBLK)
pub, err := ssh.NewPublicKey(&priv.PublicKey)
if err != nil {
state.Put("error", fmt.Errorf("error getting public key: %s", err))
return multistep.ActionHalt
}
pubSSHFormat := string(ssh.MarshalAuthorizedKey(pub))
// Remember public SSH key for future connections
state.Put("ssh_public_key", pubSSHFormat)
// If we're in debug mode, output the private key to the working directory.
if s.Debug {
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
f, err := os.Create(s.DebugKeyPath)
if err != nil {
state.Put("error", fmt.Errorf("error saving debug key: %s", err))
return multistep.ActionHalt
}
defer f.Close()
// Write the key out
if _, err := f.Write(pem.EncodeToMemory(&privBLK)); err != nil {
state.Put("error", fmt.Errorf("error saving debug key: %s", err))
return multistep.ActionHalt
}
// Chmod it so that it is SSH ready
if runtime.GOOS != "windows" {
if err := f.Chmod(0600); err != nil {
state.Put("error", fmt.Errorf("error setting permissions of debug key: %s", err))
return multistep.ActionHalt
}
}
}
return multistep.ActionContinue
}
func (s *stepCreateSSHKey) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,169 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hyperonecom/h1-client-go"
)
type stepCreateVM struct {
vmID string
}
func (s *stepCreateVM) 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)
ui.Say("Creating VM...")
netAdapter := pickNetAdapter(config)
var sshKeys = []string{sshKey}
sshKeys = append(sshKeys, config.SSHKeys...)
options := openapi.VmCreate{
Name: config.VmName,
Image: config.SourceImage,
Service: config.VmFlavour,
SshKeys: sshKeys,
Disk: []openapi.VmCreateDisk{
{
Service: config.DiskType,
Size: config.DiskSize,
},
},
Netadp: []openapi.VmCreateNetadp{netAdapter},
UserMetadata: config.UserData,
Tag: config.VmTags,
}
vm, _, err := client.VmApi.VmCreate(ctx, options)
if err != nil {
err := fmt.Errorf("error creating VM: %s", formatOpenAPIError(err))
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.vmID = vm.Id
state.Put("vm_id", vm.Id)
hdds, _, err := client.VmApi.VmListHdd(ctx, vm.Id)
if err != nil {
err := fmt.Errorf("error listing hdd: %s", formatOpenAPIError(err))
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
var diskIDs []string
for _, hdd := range hdds {
diskIDs = append(diskIDs, hdd.Disk.Id)
}
state.Put("disk_ids", diskIDs)
netadp, _, err := client.VmApi.VmListNetadp(ctx, vm.Id)
if err != nil {
err := fmt.Errorf("error listing netadp: %s", formatOpenAPIError(err))
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(netadp) < 1 {
err := fmt.Errorf("no network adapters found")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
publicIP, err := associatePublicIP(ctx, config, client, netadp[0])
if err != nil {
err := fmt.Errorf("error associating IP: %s", formatOpenAPIError(err))
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("public_ip", publicIP)
return multistep.ActionContinue
}
func pickNetAdapter(config *Config) openapi.VmCreateNetadp {
if config.Network == "" {
if config.PublicIP != "" {
return openapi.VmCreateNetadp{
Service: "public",
Ip: []string{config.PublicIP},
}
}
} else {
var privateIPs []string
if config.PrivateIP == "" {
privateIPs = nil
} else {
privateIPs = []string{config.PrivateIP}
}
return openapi.VmCreateNetadp{
Service: "private",
Network: config.Network,
Ip: privateIPs,
}
}
return openapi.VmCreateNetadp{
Service: "public",
}
}
func associatePublicIP(ctx context.Context, config *Config, client *openapi.APIClient, netadp openapi.Netadp) (string, error) {
if config.Network == "" || config.PublicIP == "" {
// Public IP belongs to attached net adapter
return netadp.Ip[0].Address, nil
}
var privateIP string
if config.PrivateIP == "" {
privateIP = netadp.Ip[0].Id
} else {
privateIP = config.PrivateIP
}
ip, _, err := client.IpApi.IpActionAssociate(ctx, config.PublicIP, openapi.IpActionAssociate{Ip: privateIP})
if err != nil {
return "", err
}
return ip.Address, nil
}
func (s *stepCreateVM) Cleanup(state multistep.StateBag) {
if s.vmID == "" {
return
}
client := state.Get("client").(*openapi.APIClient)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting VM...")
deleteOptions := openapi.VmDelete{}
diskIDs, ok := state.Get("disk_ids").([]string)
if ok && len(diskIDs) > 0 {
deleteOptions.RemoveDisks = diskIDs
}
_, 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)))
}
}

View File

@ -0,0 +1,107 @@
package hyperone
import (
"testing"
"github.com/hyperonecom/h1-client-go"
"github.com/stretchr/testify/assert"
)
func TestPickNetAdapter(t *testing.T) {
cases := []struct {
Name string
Config Config
Expected openapi.VmCreateNetadp
}{
{
Name: "no_network",
Config: Config{},
Expected: openapi.VmCreateNetadp{
Service: "public",
},
},
{
Name: "no_network_public_ip",
Config: Config{
PublicIP: "some-public-ip",
},
Expected: openapi.VmCreateNetadp{
Service: "public",
Ip: []string{"some-public-ip"},
},
},
{
Name: "no_network_private_ip",
Config: Config{
PrivateIP: "some-private-ip",
},
Expected: openapi.VmCreateNetadp{
Service: "public",
},
},
{
Name: "no_network_both_ip",
Config: Config{
PublicIP: "some-public-ip",
PrivateIP: "some-private-ip",
},
Expected: openapi.VmCreateNetadp{
Service: "public",
Ip: []string{"some-public-ip"},
},
},
{
Name: "network_no_ip",
Config: Config{
Network: "some-network",
},
Expected: openapi.VmCreateNetadp{
Service: "private",
Network: "some-network",
},
},
{
Name: "network_public_ip",
Config: Config{
Network: "some-network",
PublicIP: "some-public-ip",
},
Expected: openapi.VmCreateNetadp{
Service: "private",
Network: "some-network",
},
},
{
Name: "network_private_ip",
Config: Config{
Network: "some-network",
PrivateIP: "some-private-ip",
},
Expected: openapi.VmCreateNetadp{
Service: "private",
Network: "some-network",
Ip: []string{"some-private-ip"},
},
},
{
Name: "network_both_ip",
Config: Config{
Network: "some-network",
PublicIP: "some-public-ip",
PrivateIP: "some-private-ip",
},
Expected: openapi.VmCreateNetadp{
Service: "private",
Network: "some-network",
Ip: []string{"some-private-ip"},
},
},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
result := pickNetAdapter(&c.Config)
assert.Equal(t, c.Expected, result)
})
}
}

View File

@ -0,0 +1,32 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hyperonecom/h1-client-go"
)
type stepStopVM struct{}
func (s *stepStopVM) 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)
ui.Say("Stopping VM...")
_, _, err := client.VmApi.VmActionStop(ctx, vmID)
if err != nil {
err := fmt.Errorf("error stopping VM: %s", formatOpenAPIError(err))
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepStopVM) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,73 @@
package hyperone
import (
"io/ioutil"
"net"
"os"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"github.com/hashicorp/packer/common/json"
)
const (
sshAddress = "api.hyperone.com:22"
sshSubsystem = "rbx-auth"
)
type sshData struct {
ID string `json:"_id"`
}
func sshAgent() ssh.AuthMethod {
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
}
return nil
}
func fetchTokenBySSH(user string) (string, error) {
sshConfig := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
sshAgent(),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial("tcp", sshAddress, sshConfig)
if err != nil {
return "", err
}
defer client.Close()
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
stdout, err := session.StdoutPipe()
if err != nil {
return "", err
}
err = session.RequestSubsystem(sshSubsystem)
if err != nil {
return "", err
}
out, err := ioutil.ReadAll(stdout)
if err != nil {
return "", err
}
var data sshData
err = json.Unmarshal(out, &data)
if err != nil {
return "", err
}
return data.ID, nil
}

16
builder/hyperone/utils.go Normal file
View File

@ -0,0 +1,16 @@
package hyperone
import (
"fmt"
"github.com/hyperonecom/h1-client-go"
)
func formatOpenAPIError(err error) string {
openAPIError, ok := err.(openapi.GenericOpenAPIError)
if !ok {
return err.Error()
}
return fmt.Sprintf("%s (body: %s)", openAPIError.Error(), openAPIError.Body())
}

View File

@ -26,6 +26,7 @@ import (
filebuilder "github.com/hashicorp/packer/builder/file"
googlecomputebuilder "github.com/hashicorp/packer/builder/googlecompute"
hcloudbuilder "github.com/hashicorp/packer/builder/hcloud"
hyperonebuilder "github.com/hashicorp/packer/builder/hyperone"
hypervisobuilder "github.com/hashicorp/packer/builder/hyperv/iso"
hypervvmcxbuilder "github.com/hashicorp/packer/builder/hyperv/vmcx"
lxcbuilder "github.com/hashicorp/packer/builder/lxc"
@ -99,6 +100,7 @@ var Builders = map[string]packer.Builder{
"file": new(filebuilder.Builder),
"googlecompute": new(googlecomputebuilder.Builder),
"hcloud": new(hcloudbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
"hyperv-iso": new(hypervisobuilder.Builder),
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"lxc": new(lxcbuilder.Builder),

1
go.mod
View File

@ -90,6 +90,7 @@ require (
github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20181106190520-2236f141171e // indirect
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb
github.com/hetznercloud/hcloud-go v1.12.0
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee // indirect
github.com/joyent/triton-go v0.0.0-20180116165742-545edbe0d564
github.com/jtolds/gls v4.2.1+incompatible // indirect

2
go.sum
View File

@ -197,6 +197,8 @@ github.com/hetznercloud/hcloud-go v1.12.0 h1:ugZO8a8ADekqSWi7xWlcs6pxr4QE0tw5Vny
github.com/hetznercloud/hcloud-go v1.12.0/go.mod h1:g5pff0YNAZywQaivY/CmhUYFVp7oP0nu3MiODC2W4Hw=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 h1:MIteIoIQ5nFoOmwEHPDsqng8d0dtKj3lCnQCwGvtxXc=
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775/go.mod h1:R9rU87RxxmcD3DkspW9JqGBXiJyg5MA+WNCtJrBtnXs=
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee h1:AQ/QmCk6x8ECPpf2pkPtA4lyncEEBbs8VFnVXPYKhIs=
github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=

View File

@ -0,0 +1,110 @@
---
description: |
HyperOne Packer builder creates new images on the HyperOne platform.
The builder takes a source image, runs any provisioning necessary on
the image after launching it, then creates a reusable image.
layout: docs
page_title: 'HyperOne - Builders'
sidebar_current: 'docs-builders-hyperone'
---
# HyperOne Builder
Type: `hyperone`
The `hyperone` Packer builder is able to create new images on the [HyperOne
platform](http://www.hyperone.com/). The builder takes a source image, runs
any provisioning necessary on the image after launching it, then creates a
reusable image.
The builder does *not* manage images. Once it creates an image, it is up to you
to use it or delete it.
## Configuration Reference
There are many configuration options available for the builder. They are
segmented below into two categories: required and optional parameters. Within
each category, the available configuration keys are alphabetized.
In addition to the options listed here, a
[communicator](/docs/templates/communicator.html) can be configured for this
builder.
### Required:
- `disk_size` (float) - Size of the created disk, in GiB.
- `project` (string) - The id or name of the project. This field is required
only if using session tokens. It should be skipped when using service
account authentication.
- `source_image` (string) - ID or name of the image to launch server from.
- `token` (string) - The authentication token used to access your account.
This can be either a session token or a service account token.
If not defined, the builder will attempt to find it in the following order:
- In `HYPERONE_TOKEN` environment variable.
- In `~/.h1-cli/conf.json` config file used by [h1-cli](https://github.com/hyperonecom/h1-cli).
- By using SSH authentication if `token_login` variable has been set.
- `vm_flavour` (string) - ID or name of the type this server should be created with.
### Optional:
- `disk_name` (string) - The name of the created disk.
- `disk_type` (string) - The type of the created disk.
- `image_description` (string) - The description of the resulting image.
- `image_name` (string) - The name of the resulting image. Defaults to
"packer-{{timestamp}}"
(see [configuration templates](/docs/templates/engine.html) for more info).
- `image_service` (string) - The service of the resulting image.
- `image_tags` (map of key/value strings) - Key/value pair tags to
add to the created image.
- `network` (string) - The ID of the network to attach to the created server.
- `private_ip` (string) - The ID of the private IP within chosen `network`
that should be assigned to the created server.
- `public_ip` (string) - The ID of the public IP that should be assigned to
the created server. If `network` is chosen, the public IP will be associated
with server's private IP.
- `ssh_keys` (array of strings) - List of SSH keys by name or id to be added
to the server on launch.
- `state_timeout` (string) - Timeout for waiting on the API to complete
a request. Defaults to 5m.
- `token_login` (string) - Login (an e-mail) on HyperOne platform. Set this
if you want to fetch the token by SSH authentication.
- `user_data` (string) - User data to launch with the server. Packer will not
automatically wait for a user script to finish before shutting down the
instance, this must be handled in a provisioner.
- `vm_name` (string) - The name of the created server.
- `vm_tags` (map of key/value strings) - Key/value pair tags to
add to the created server.
## Basic Example
Here is a basic example. It is completely valid as soon as you enter your own
token.
``` json
{
"type": "hyperone",
"token": "YOUR_AUTH_TOKEN",
"source_image": "ubuntu-18.04",
"vm_flavour": "a1.nano",
"disk_size": 10
}
```

View File

@ -102,6 +102,9 @@
<li<%= sidebar_current("docs-builders-hetzner-cloud") %>>
<a href="/docs/builders/hetzner-cloud.html">Hetzner Cloud</a>
</li>
<li<%= sidebar_current("docs-builders-hyperone") %>>
<a href="/docs/builders/hyperone.html">HyperOne</a>
</li>
<li<%= sidebar_current("docs-builders-hyperv") %>>
<a href="/docs/builders/hyperv.html">Hyper-V</a>
<ul class="nav">