Extract Hyperone (#10949)

This commit is contained in:
Sylvia Moss 2021-04-21 15:08:38 +02:00 committed by GitHub
parent 0f6a081724
commit 2db338e322
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 21 additions and 2441 deletions

View File

@ -52,12 +52,6 @@
/builder/hcloud/ @LKaemmerling
/website/pages/docs/builders/hcloud* @LKaemmerling
/examples/hyperone/ @m110 @gregorybrzeski @ad-m
/builder/hyperone/ @m110 @gregorybrzeski @ad-m
/website/pages/docs/builders/hyperone* @m110 @gregorybrzeski @ad-m
/test/builder_hyperone* @m110 @gregorybrzeski @ad-m
/test/fixtures/builder-hyperone/ @m110 @gregorybrzeski @ad-m
/examples/ucloud/ @shawnmssu
/builder/ucloud/ @shawnmssu
/website/pages/docs/builders/ucloud* @shawnmssu

View File

@ -1,52 +0,0 @@
package hyperone
import (
"context"
"fmt"
openapi "github.com/hyperonecom/h1-client-go"
)
type Artifact struct {
imageName string
imageID string
client *openapi.APIClient
// StateData should store data such as GeneratedData
// to be shared with post-processors
StateData map[string]interface{}
}
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 a.StateData[name]
}
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

@ -1,115 +0,0 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
openapi "github.com/hyperonecom/h1-client-go"
)
const BuilderID = "hyperone.builder"
type Builder struct {
config Config
runner multistep.Runner
client *openapi.APIClient
}
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
warnings, errs := b.config.Prepare(raws...)
if errs != nil {
return nil, warnings, errs
}
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, nil
}
type wrappedCommandTemplate struct {
Command string
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
wrappedCommand := func(command string) (string, error) {
ictx := b.config.ctx
ictx.Data = &wrappedCommandTemplate{Command: command}
return interpolate.Render(b.config.ChrootCommandWrapper, &ictx)
}
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,
&commonsteps.StepProvision{},
&stepStopVM{},
&stepCreateImage{},
)
}
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(ctx, 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,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
}
return artifact, nil
}

View File

@ -1,69 +0,0 @@
package hyperone
import (
"os"
"testing"
builderT "github.com/hashicorp/packer/acctest"
)
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,
"image_tags": {
"key":"value"
},
"vm_tags": {
"key_vm":"value_vm"
}
}]
}
`
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}}"
]
}]
}
`

View File

@ -1,55 +0,0 @@
package hyperone
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
packersdk "github.com/hashicorp/packer-plugin-sdk/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 packersdk.Communicator
}
func (c *ChrootCommunicator) Start(ctx context.Context, cmd *packersdk.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(ctx, 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)
}

View File

@ -1,352 +0,0 @@
//go:generate packer-sdc struct-markdown
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package hyperone
import (
"errors"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/json"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-sdk/uuid"
"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"`
// Custom API endpoint URL, compatible with HyperOne.
// It can also be specified via environment variable HYPERONE_API_URL.
APIURL string `mapstructure:"api_url" required:"false"`
// 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:
Token string `mapstructure:"token" required:"true"`
// 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.
Project string `mapstructure:"project" required:"true"`
// Login (an e-mail) on HyperOne platform. Set this
// if you want to fetch the token by SSH authentication.
TokenLogin string `mapstructure:"token_login" required:"false"`
// Timeout for waiting on the API to complete
// a request. Defaults to 5m.
StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"`
// ID or name of the image to launch server from.
SourceImage string `mapstructure:"source_image" required:"true"`
// The name of the resulting image. Defaults to
// `packer-{{timestamp}}`
// (see configuration templates for more info).
ImageName string `mapstructure:"image_name" required:"false"`
// The description of the resulting image.
ImageDescription string `mapstructure:"image_description" required:"false"`
// Key/value pair tags to add to the created image.
ImageTags map[string]string `mapstructure:"image_tags" required:"false"`
// Same as [`image_tags`](#image_tags) but defined as a singular repeatable
// block containing a `key` and a `value` field. In HCL2 mode the
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
// will allow you to create those programatically.
ImageTag config.KeyValues `mapstructure:"image_tag" required:"false"`
// The service of the resulting image.
ImageService string `mapstructure:"image_service" required:"false"`
// ID or name of the type this server should be created with.
VmType string `mapstructure:"vm_type" required:"true"`
// The name of the created server.
VmName string `mapstructure:"vm_name" required:"false"`
// Key/value pair tags to add to the created server.
VmTags map[string]string `mapstructure:"vm_tags" required:"false"`
// Same as [`vm_tags`](#vm_tags) but defined as a singular repeatable block
// containing a `key` and a `value` field. In HCL2 mode the
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
// will allow you to create those programatically.
VmTag config.NameValues `mapstructure:"vm_tag" required:"false"`
// The name of the created disk.
DiskName string `mapstructure:"disk_name" required:"false"`
// The type of the created disk. Defaults to ssd.
DiskType string `mapstructure:"disk_type" required:"false"`
// Size of the created disk, in GiB.
DiskSize float32 `mapstructure:"disk_size" required:"true"`
// The ID of the network to attach to the created server.
Network string `mapstructure:"network" required:"false"`
// The ID of the private IP within chosen network
// that should be assigned to the created server.
PrivateIP string `mapstructure:"private_ip" required:"false"`
// 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.
PublicIP string `mapstructure:"public_ip" required:"false"`
// Custom service of public network adapter.
// Can be useful when using custom api_url. Defaults to public.
PublicNetAdpService string `mapstructure:"public_netadp_service" required:"false"`
ChrootDevice string `mapstructure:"chroot_device"`
ChrootDisk bool `mapstructure:"chroot_disk"`
ChrootDiskSize float32 `mapstructure:"chroot_disk_size"`
ChrootDiskType string `mapstructure:"chroot_disk_type"`
ChrootMountPath string `mapstructure:"chroot_mount_path"`
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
ChrootCopyFiles []string `mapstructure:"chroot_copy_files"`
// How to run shell commands. This defaults to `{{.Command}}`. This may be
// useful to set if you want to set environmental variables or perhaps run
// it with sudo or so on. This is a configuration template where the
// .Command variable is replaced with the command to be run. Defaults to
// `{{.Command}}`.
ChrootCommandWrapper string `mapstructure:"chroot_command_wrapper"`
MountOptions []string `mapstructure:"mount_options"`
MountPartition string `mapstructure:"mount_partition"`
// A series of commands to execute after attaching the root volume and
// before mounting the chroot. This is not required unless using
// from_scratch. If so, this should include any partitioning and filesystem
// creation commands. The path to the device is provided by `{{.Device}}`.
PreMountCommands []string `mapstructure:"pre_mount_commands"`
// As pre_mount_commands, but the commands are executed after mounting the
// root device and before the extra mount and copy steps. The device and
// mount path are provided by `{{.Device}}` and `{{.MountPath}}`.
PostMountCommands []string `mapstructure:"post_mount_commands"`
// List of SSH keys by name or id to be added
// to the server on launch.
SSHKeys []string `mapstructure:"ssh_keys" required:"false"`
// 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.
UserData string `mapstructure:"user_data" required:"false"`
ctx interpolate.Context
}
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
var md mapstructure.Metadata
err := config.Decode(c, &config.DecodeOpts{
Metadata: &md,
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
"chroot_command_wrapper",
"post_mount_commands",
"pre_mount_commands",
"mount_path",
},
},
}, raws...)
if err != nil {
return nil, err
}
cliConfig, err := loadCLIConfig()
if err != nil {
return nil, err
}
// Defaults
if c.Comm.SSHUsername == "" {
c.Comm.SSHUsername = defaultUserName
}
if c.Comm.SSHTimeout == 0 {
c.Comm.SSHTimeout = 10 * time.Minute
}
if c.APIURL == "" {
c.APIURL = os.Getenv("HYPERONE_API_URL")
}
if c.Token == "" {
c.Token = os.Getenv(tokenEnv)
if c.Token == "" {
c.Token = cliConfig.Profile.APIKey
}
// Fetching token by SSH is available only for the default API endpoint
if c.TokenLogin != "" && c.APIURL == "" {
c.Token, err = fetchTokenBySSH(c.TokenLogin)
if err != nil {
return 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, 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
}
if c.PublicNetAdpService == "" {
c.PublicNetAdpService = "public"
}
if c.ChrootCommandWrapper == "" {
c.ChrootCommandWrapper = "{{.Command}}"
}
if c.ChrootDiskSize == 0 {
c.ChrootDiskSize = c.DiskSize
}
if c.ChrootDiskType == "" {
c.ChrootDiskType = c.DiskType
}
if c.ChrootMountPath == "" {
path, err := interpolate.Render("/mnt/packer-hyperone-volumes/{{timestamp}}", nil)
if err != nil {
return nil, err
}
c.ChrootMountPath = path
}
if c.ChrootMounts == nil {
c.ChrootMounts = make([][]string, 0)
}
if len(c.ChrootMounts) == 0 {
c.ChrootMounts = [][]string{
{"proc", "proc", "/proc"},
{"sysfs", "sysfs", "/sys"},
{"bind", "/dev", "/dev"},
{"devpts", "devpts", "/dev/pts"},
{"binfmt_misc", "binfmt_misc", "/proc/sys/fs/binfmt_misc"},
}
}
if c.ChrootCopyFiles == nil {
c.ChrootCopyFiles = []string{"/etc/resolv.conf"}
}
if c.MountPartition == "" {
c.MountPartition = "1"
}
// Validation
var errs *packersdk.MultiError
errs = packersdk.MultiErrorAppend(errs, c.ImageTag.CopyOn(&c.ImageTags)...)
errs = packersdk.MultiErrorAppend(errs, c.VmTag.CopyOn(&c.VmTags)...)
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packersdk.MultiErrorAppend(errs, es...)
}
if c.Token == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("token is required"))
}
if c.VmType == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("vm type is required"))
}
if c.DiskSize == 0 {
errs = packersdk.MultiErrorAppend(errs, errors.New("disk size is required"))
}
if c.SourceImage == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("source image is required"))
}
if c.ChrootDisk {
if len(c.PreMountCommands) == 0 {
errs = packersdk.MultiErrorAppend(errs, errors.New("pre-mount commands are required for chroot disk"))
}
}
for _, mounts := range c.ChrootMounts {
if len(mounts) != 3 {
errs = packersdk.MultiErrorAppend(
errs, errors.New("each chroot_mounts entry should have three elements"))
break
}
}
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
packersdk.LogSecretFilter.Set(c.Token)
return 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

@ -1,216 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package hyperone
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
APIURL *string `mapstructure:"api_url" required:"false" cty:"api_url" hcl:"api_url"`
Token *string `mapstructure:"token" required:"true" cty:"token" hcl:"token"`
Project *string `mapstructure:"project" required:"true" cty:"project" hcl:"project"`
TokenLogin *string `mapstructure:"token_login" required:"false" cty:"token_login" hcl:"token_login"`
StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"`
SourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image" hcl:"source_image"`
ImageName *string `mapstructure:"image_name" required:"false" cty:"image_name" hcl:"image_name"`
ImageDescription *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"`
ImageTags map[string]string `mapstructure:"image_tags" required:"false" cty:"image_tags" hcl:"image_tags"`
ImageTag []config.FlatKeyValue `mapstructure:"image_tag" required:"false" cty:"image_tag" hcl:"image_tag"`
ImageService *string `mapstructure:"image_service" required:"false" cty:"image_service" hcl:"image_service"`
VmType *string `mapstructure:"vm_type" required:"true" cty:"vm_type" hcl:"vm_type"`
VmName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"`
VmTags map[string]string `mapstructure:"vm_tags" required:"false" cty:"vm_tags" hcl:"vm_tags"`
VmTag []config.FlatNameValue `mapstructure:"vm_tag" required:"false" cty:"vm_tag" hcl:"vm_tag"`
DiskName *string `mapstructure:"disk_name" required:"false" cty:"disk_name" hcl:"disk_name"`
DiskType *string `mapstructure:"disk_type" required:"false" cty:"disk_type" hcl:"disk_type"`
DiskSize *float32 `mapstructure:"disk_size" required:"true" cty:"disk_size" hcl:"disk_size"`
Network *string `mapstructure:"network" required:"false" cty:"network" hcl:"network"`
PrivateIP *string `mapstructure:"private_ip" required:"false" cty:"private_ip" hcl:"private_ip"`
PublicIP *string `mapstructure:"public_ip" required:"false" cty:"public_ip" hcl:"public_ip"`
PublicNetAdpService *string `mapstructure:"public_netadp_service" required:"false" cty:"public_netadp_service" hcl:"public_netadp_service"`
ChrootDevice *string `mapstructure:"chroot_device" cty:"chroot_device" hcl:"chroot_device"`
ChrootDisk *bool `mapstructure:"chroot_disk" cty:"chroot_disk" hcl:"chroot_disk"`
ChrootDiskSize *float32 `mapstructure:"chroot_disk_size" cty:"chroot_disk_size" hcl:"chroot_disk_size"`
ChrootDiskType *string `mapstructure:"chroot_disk_type" cty:"chroot_disk_type" hcl:"chroot_disk_type"`
ChrootMountPath *string `mapstructure:"chroot_mount_path" cty:"chroot_mount_path" hcl:"chroot_mount_path"`
ChrootMounts [][]string `mapstructure:"chroot_mounts" cty:"chroot_mounts" hcl:"chroot_mounts"`
ChrootCopyFiles []string `mapstructure:"chroot_copy_files" cty:"chroot_copy_files" hcl:"chroot_copy_files"`
ChrootCommandWrapper *string `mapstructure:"chroot_command_wrapper" cty:"chroot_command_wrapper" hcl:"chroot_command_wrapper"`
MountOptions []string `mapstructure:"mount_options" cty:"mount_options" hcl:"mount_options"`
MountPartition *string `mapstructure:"mount_partition" cty:"mount_partition" hcl:"mount_partition"`
PreMountCommands []string `mapstructure:"pre_mount_commands" cty:"pre_mount_commands" hcl:"pre_mount_commands"`
PostMountCommands []string `mapstructure:"post_mount_commands" cty:"post_mount_commands" hcl:"post_mount_commands"`
SSHKeys []string `mapstructure:"ssh_keys" required:"false" cty:"ssh_keys" hcl:"ssh_keys"`
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"api_url": &hcldec.AttrSpec{Name: "api_url", Type: cty.String, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"project": &hcldec.AttrSpec{Name: "project", Type: cty.String, Required: false},
"token_login": &hcldec.AttrSpec{Name: "token_login", Type: cty.String, Required: false},
"state_timeout": &hcldec.AttrSpec{Name: "state_timeout", Type: cty.String, Required: false},
"source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false},
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
"image_tags": &hcldec.AttrSpec{Name: "image_tags", Type: cty.Map(cty.String), Required: false},
"image_tag": &hcldec.BlockListSpec{TypeName: "image_tag", Nested: hcldec.ObjectSpec((*config.FlatKeyValue)(nil).HCL2Spec())},
"image_service": &hcldec.AttrSpec{Name: "image_service", Type: cty.String, Required: false},
"vm_type": &hcldec.AttrSpec{Name: "vm_type", Type: cty.String, Required: false},
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
"vm_tags": &hcldec.AttrSpec{Name: "vm_tags", Type: cty.Map(cty.String), Required: false},
"vm_tag": &hcldec.BlockListSpec{TypeName: "vm_tag", Nested: hcldec.ObjectSpec((*config.FlatNameValue)(nil).HCL2Spec())},
"disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false},
"disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
"private_ip": &hcldec.AttrSpec{Name: "private_ip", Type: cty.String, Required: false},
"public_ip": &hcldec.AttrSpec{Name: "public_ip", Type: cty.String, Required: false},
"public_netadp_service": &hcldec.AttrSpec{Name: "public_netadp_service", Type: cty.String, Required: false},
"chroot_device": &hcldec.AttrSpec{Name: "chroot_device", Type: cty.String, Required: false},
"chroot_disk": &hcldec.AttrSpec{Name: "chroot_disk", Type: cty.Bool, Required: false},
"chroot_disk_size": &hcldec.AttrSpec{Name: "chroot_disk_size", Type: cty.Number, Required: false},
"chroot_disk_type": &hcldec.AttrSpec{Name: "chroot_disk_type", Type: cty.String, Required: false},
"chroot_mount_path": &hcldec.AttrSpec{Name: "chroot_mount_path", Type: cty.String, Required: false},
"chroot_mounts": &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.List(cty.String)), Required: false},
"chroot_copy_files": &hcldec.AttrSpec{Name: "chroot_copy_files", Type: cty.List(cty.String), Required: false},
"chroot_command_wrapper": &hcldec.AttrSpec{Name: "chroot_command_wrapper", Type: cty.String, Required: false},
"mount_options": &hcldec.AttrSpec{Name: "mount_options", Type: cty.List(cty.String), Required: false},
"mount_partition": &hcldec.AttrSpec{Name: "mount_partition", Type: cty.String, Required: false},
"pre_mount_commands": &hcldec.AttrSpec{Name: "pre_mount_commands", Type: cty.List(cty.String), Required: false},
"post_mount_commands": &hcldec.AttrSpec{Name: "post_mount_commands", Type: cty.List(cty.String), Required: false},
"ssh_keys": &hcldec.AttrSpec{Name: "ssh_keys", Type: cty.List(cty.String), Required: false},
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,25 +0,0 @@
{
"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"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"apt-get update",
"apt-get upgrade -y"
]
}
]
}

View File

@ -1,29 +0,0 @@
{
"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": []
}

View File

@ -1,32 +0,0 @@
variable "token" {
type = string
}
variable "project" {
type = string
}
source "hyperone" "new-syntax" {
token = var.token
project = var.project
source_image = "debian"
disk_size = 10
vm_type = "a1.nano"
image_name = "packerbats-hcl-{{timestamp}}"
image_tags = {
key="value"
}
}
build {
sources = [
"source.hyperone.new-syntax"
]
provisioner "shell" {
inline = [
"apt-get update",
"apt-get upgrade -y"
]
}
}

View File

@ -1,28 +0,0 @@
package hyperone
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/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").(packersdk.Communicator)
comm := &ChrootCommunicator{
Chroot: config.ChrootMountPath,
CmdWrapper: wrappedCommand,
Wrapped: sshCommunicator,
}
stepProvision := commonsteps.StepProvision{Comm: comm}
return stepProvision.Run(ctx, state)
}
func (s *stepChrootProvision) Cleanup(multistep.StateBag) {}

View File

@ -1,40 +0,0 @@
package hyperone
import (
"context"
"fmt"
"log"
"path/filepath"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type stepCopyFiles struct{}
func (s *stepCopyFiles) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.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) {}

View File

@ -1,65 +0,0 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
openapi "github.com/hyperonecom/h1-client-go"
)
type stepCreateImage struct {
imageID string
}
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*openapi.APIClient)
ui := state.Get("ui").(packersdk.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
}
s.imageID = image.Id
state.Put("image_id", image.Id)
state.Put("image_name", image.Name)
return multistep.ActionContinue
}
func (s *stepCreateImage) Cleanup(state multistep.StateBag) {
if s.imageID == "" {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
client := state.Get("client").(*openapi.APIClient)
ui := state.Get("ui").(packersdk.Ui)
_, err := client.ImageApi.ImageDelete(context.TODO(), s.imageID)
if err != nil {
ui.Error(fmt.Sprintf("error deleting image '%s' - consider deleting it manually: %s",
s.imageID, formatOpenAPIError(err)))
}
}

View File

@ -1,82 +0,0 @@
package hyperone
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"runtime"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"golang.org/x/crypto/ssh"
)
type stepCreateSSHKey struct {
Debug bool
DebugKeyPath string
}
func (s *stepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.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

@ -1,203 +0,0 @@
package hyperone
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
openapi "github.com/hyperonecom/h1-client-go"
)
type stepCreateVM struct {
vmID string
}
const (
chrootDiskName = "packer-chroot-disk"
)
func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*openapi.APIClient)
ui := state.Get("ui").(packersdk.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...)
disks := []openapi.VmCreateDisk{
{
Service: config.DiskType,
Size: config.DiskSize,
},
}
if config.ChrootDisk {
disks = append(disks, openapi.VmCreateDisk{
Service: config.ChrootDiskType,
Size: config.ChrootDiskSize,
Name: chrootDiskName,
})
}
options := openapi.VmCreate{
Name: config.VmName,
Image: config.SourceImage,
Service: config.VmType,
SshKeys: sshKeys,
Disk: disks,
Netadp: []openapi.VmCreateNetadp{netAdapter},
UserMetadata: config.UserData,
Tag: config.VmTags,
Username: config.Comm.SSHUsername,
}
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)
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state.Put("instance_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
}
for _, hdd := range hdds {
if hdd.Disk.Name == chrootDiskName {
state.Put("chroot_disk_id", hdd.Disk.Id)
controllerNumber := strings.ToLower(strings.Trim(hdd.ControllerNumber, "{}"))
state.Put("chroot_controller_number", controllerNumber)
state.Put("chroot_controller_location", int(hdd.ControllerLocation))
break
}
}
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: config.PublicNetAdpService,
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: config.PublicNetAdpService,
}
}
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
}
ui := state.Get("ui").(packersdk.Ui)
ui.Say(fmt.Sprintf("Deleting VM %s...", s.vmID))
err := deleteVMWithDisks(s.vmID, state)
if err != nil {
ui.Error(err.Error())
}
}
func deleteVMWithDisks(vmID string, state multistep.StateBag) error {
client := state.Get("client").(*openapi.APIClient)
hdds, _, err := client.VmApi.VmListHdd(context.TODO(), vmID)
if err != nil {
return fmt.Errorf("error listing hdd: %s", formatOpenAPIError(err))
}
deleteOptions := openapi.VmDelete{}
for _, hdd := range hdds {
deleteOptions.RemoveDisks = append(deleteOptions.RemoveDisks, hdd.Disk.Id)
}
_, err = client.VmApi.VmDelete(context.TODO(), vmID, deleteOptions)
if err != nil {
return fmt.Errorf("Error deleting server '%s' - please delete it manually: %s", vmID, formatOpenAPIError(err))
}
return nil
}

View File

@ -1,63 +0,0 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
openapi "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").(packersdk.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.VmType,
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
}
ui := state.Get("ui").(packersdk.Ui)
ui.Say(fmt.Sprintf("Deleting VM %s (from chroot disk)...", s.vmID))
err := deleteVMWithDisks(s.vmID, state)
if err != nil {
ui.Error(err.Error())
}
}

View File

@ -1,116 +0,0 @@
package hyperone
import (
"testing"
openapi "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{
PublicNetAdpService: "public",
},
Expected: openapi.VmCreateNetadp{
Service: "public",
},
},
{
Name: "no_network_public_ip",
Config: Config{
PublicIP: "some-public-ip",
PublicNetAdpService: "public",
},
Expected: openapi.VmCreateNetadp{
Service: "public",
Ip: []string{"some-public-ip"},
},
},
{
Name: "no_network_private_ip",
Config: Config{
PrivateIP: "some-private-ip",
PublicNetAdpService: "public",
},
Expected: openapi.VmCreateNetadp{
Service: "public",
},
},
{
Name: "no_network_both_ip",
Config: Config{
PublicIP: "some-public-ip",
PrivateIP: "some-private-ip",
PublicNetAdpService: "public",
},
Expected: openapi.VmCreateNetadp{
Service: "public",
Ip: []string{"some-public-ip"},
},
},
{
Name: "network_no_ip",
Config: Config{
Network: "some-network",
PublicNetAdpService: "public",
},
Expected: openapi.VmCreateNetadp{
Service: "private",
Network: "some-network",
},
},
{
Name: "network_public_ip",
Config: Config{
Network: "some-network",
PublicIP: "some-public-ip",
PublicNetAdpService: "public",
},
Expected: openapi.VmCreateNetadp{
Service: "private",
Network: "some-network",
},
},
{
Name: "network_private_ip",
Config: Config{
Network: "some-network",
PrivateIP: "some-private-ip",
PublicNetAdpService: "public",
},
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",
PublicNetAdpService: "public",
},
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

@ -1,34 +0,0 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
openapi "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").(packersdk.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) {}

View File

@ -1,51 +0,0 @@
package hyperone
import (
"context"
"fmt"
"log"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/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").(packersdk.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) {}

View File

@ -1,45 +0,0 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type stepMountExtra struct{}
func (s *stepMountExtra) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packersdk.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) {}

View File

@ -1,37 +0,0 @@
package hyperone
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type postMountCommandsData struct {
Device string
MountPath string
}
type stepPostMountCommands struct{}
func (s *stepPostMountCommands) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)
device := state.Get("device").(string)
ictx := config.ctx
ictx.Data = &postMountCommandsData{
Device: device,
MountPath: config.ChrootMountPath,
}
ui.Say("Running post-mount commands...")
if err := runCommands(config.PostMountCommands, ictx, state); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepPostMountCommands) Cleanup(state multistep.StateBag) {}

View File

@ -1,37 +0,0 @@
package hyperone
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
type preMountCommandsData struct {
Device string
MountPath string
}
type stepPreMountCommands struct{}
func (s *stepPreMountCommands) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)
device := state.Get("device").(string)
ictx := config.ctx
ictx.Data = &preMountCommandsData{
Device: device,
MountPath: config.ChrootMountPath,
}
ui.Say("Running pre-mount commands...")
if err := runCommands(config.PreMountCommands, ictx, state); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepPreMountCommands) Cleanup(state multistep.StateBag) {}

View File

@ -1,57 +0,0 @@
package hyperone
import (
"context"
"fmt"
"log"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const (
vmBusPath = "/sys/bus/vmbus/devices"
)
type stepPrepareDevice struct{}
func (s *stepPrepareDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
config := state.Get("config").(*Config)
if config.ChrootDevice != "" {
state.Put("device", config.ChrootDevice)
return multistep.ActionContinue
}
controllerNumber := state.Get("chroot_controller_number").(string)
controllerLocation := state.Get("chroot_controller_location").(int)
log.Println("Searching for available device...")
cmd := fmt.Sprintf("find %s/%s/ -path *:%d/block -exec ls {} \\;",
vmBusPath, controllerNumber, controllerLocation)
block, err := captureOutput(cmd, state)
if err != nil {
err := fmt.Errorf("error finding available device: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if block == "" {
err := fmt.Errorf("device not found")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
device := fmt.Sprintf("/dev/%s", block)
ui.Say(fmt.Sprintf("Found device: %s", device))
state.Put("device", device)
return multistep.ActionContinue
}
func (s *stepPrepareDevice) Cleanup(state multistep.StateBag) {}

View File

@ -1,32 +0,0 @@
package hyperone
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
openapi "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").(packersdk.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

@ -1,89 +0,0 @@
package hyperone
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"os"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"github.com/hashicorp/packer-plugin-sdk/json"
)
const (
sshAddress = "api.hyperone.com:22"
sshSubsystem = "rbx-auth"
hostKeyHash = "3e2aa423d42d7e8b14d50625512c8ac19db767ed"
)
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: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
hash := sha1Sum(key)
if hash != hostKeyHash {
return fmt.Errorf("invalid host key hash: %s", hash)
}
return nil
},
}
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
}
func sha1Sum(pubKey ssh.PublicKey) string {
sum := sha1.Sum(pubKey.Marshal())
return hex.EncodeToString(sum[:])
}

View File

@ -1,89 +0,0 @@
package hyperone
import (
"bytes"
"context"
"fmt"
"log"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
openapi "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())
}
func runCommands(commands []string, ictx interpolate.Context, state multistep.StateBag) error {
ctx := context.TODO()
ui := state.Get("ui").(packersdk.Ui)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
comm := state.Get("communicator").(packersdk.Communicator)
for _, rawCmd := range commands {
intCmd, err := interpolate.Render(rawCmd, &ictx)
if err != nil {
return fmt.Errorf("error interpolating: %s", err)
}
command, err := wrappedCommand(intCmd)
if err != nil {
return fmt.Errorf("error wrapping command: %s", err)
}
remoteCmd := &packersdk.RemoteCmd{
Command: command,
}
ui.Say(fmt.Sprintf("Executing command: %s", command))
err = remoteCmd.RunWithUi(ctx, comm, ui)
if err != nil {
return fmt.Errorf("error running remote cmd: %s", err)
}
if remoteCmd.ExitStatus() != 0 {
return fmt.Errorf(
"received non-zero exit code %d from command: %s",
remoteCmd.ExitStatus(),
command)
}
}
return nil
}
func captureOutput(command string, state multistep.StateBag) (string, error) {
ctx := context.TODO()
comm := state.Get("communicator").(packersdk.Communicator)
var stdout bytes.Buffer
remoteCmd := &packersdk.RemoteCmd{
Command: command,
Stdout: &stdout,
}
log.Println(fmt.Sprintf("Executing command: %s", command))
err := comm.Start(ctx, remoteCmd)
if err != nil {
return "", fmt.Errorf("error running remote cmd: %s", err)
}
remoteCmd.Wait()
if remoteCmd.ExitStatus() != 0 {
return "", fmt.Errorf(
"received non-zero exit code %d from command: %s",
remoteCmd.ExitStatus(),
command)
}
return strings.TrimSpace(stdout.String()), nil
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var HyperonePluginVersion *version.PluginVersion
func init() {
HyperonePluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -19,7 +19,6 @@ import (
digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean"
filebuilder "github.com/hashicorp/packer/builder/file"
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"
jdcloudbuilder "github.com/hashicorp/packer/builder/jdcloud"
@ -72,7 +71,6 @@ var Builders = map[string]packersdk.Builder{
"digitalocean": new(digitaloceanbuilder.Builder),
"file": new(filebuilder.Builder),
"hcloud": new(hcloudbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
"hyperv-iso": new(hypervisobuilder.Builder),
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"jdcloud": new(jdcloudbuilder.Builder),

View File

@ -30,6 +30,7 @@ import (
googlecomputebuilder "github.com/hashicorp/packer-plugin-googlecompute/builder/googlecompute"
googlecomputeexportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-export"
googlecomputeimportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-import"
hyperonebuilder "github.com/hashicorp/packer-plugin-hyperone/builder/hyperone"
ncloudbuilder "github.com/hashicorp/packer-plugin-ncloud/builder/ncloud"
openstackbuilder "github.com/hashicorp/packer-plugin-openstack/builder/openstack"
oscbsubuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsu"
@ -94,6 +95,7 @@ var VendoredBuilders = map[string]packersdk.Builder{
"osc-bsusurrogate": new(oscbsusurrogatebuilder.Builder),
"osc-bsuvolume": new(oscbsuvolumebuilder.Builder),
"osc-chroot": new(oscchrootbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
}
// VendoredProvisioners are provisioner components that were once bundled with the

4
go.mod
View File

@ -44,6 +44,7 @@ require (
github.com/hashicorp/packer-plugin-cloudstack v0.0.1
github.com/hashicorp/packer-plugin-docker v0.0.7
github.com/hashicorp/packer-plugin-googlecompute v0.0.1
github.com/hashicorp/packer-plugin-hyperone v0.0.1
github.com/hashicorp/packer-plugin-ncloud v0.0.2
github.com/hashicorp/packer-plugin-openstack v0.0.1
github.com/hashicorp/packer-plugin-outscale v0.0.1
@ -57,7 +58,6 @@ require (
github.com/hashicorp/packer-plugin-vmware v0.0.1
github.com/hashicorp/packer-plugin-vsphere v0.0.1
github.com/hetznercloud/hcloud-go v1.15.1
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961
github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
@ -66,7 +66,7 @@ require (
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08
github.com/mitchellh/cli v1.1.0
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.4.0
github.com/mitchellh/mapstructure v1.4.1
github.com/mitchellh/panicwrap v1.0.0
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
github.com/mitchellh/reflectwalk v1.0.0

5
go.sum
View File

@ -476,6 +476,8 @@ github.com/hashicorp/packer-plugin-docker v0.0.7 h1:hMTrH7vrkFIjphtbbtpuzffTzSjM
github.com/hashicorp/packer-plugin-docker v0.0.7/go.mod h1:IpeKlwOSy2kdgQcysqd3gCsoqjME9jtmpFoKxn7RRNI=
github.com/hashicorp/packer-plugin-googlecompute v0.0.1 h1:Shjio88MraB+ocj0VI5+M65r4UBKbYI4eCqLNyPXKEo=
github.com/hashicorp/packer-plugin-googlecompute v0.0.1/go.mod h1:MfV898IrEMpKH6wVnvOI5Tkhxm2snf3QxwVqV4k3bNI=
github.com/hashicorp/packer-plugin-hyperone v0.0.1 h1:Owp1B5cI0VgFgR3pCyeeQdyKPTWls36mVedv+WxZMOM=
github.com/hashicorp/packer-plugin-hyperone v0.0.1/go.mod h1:9DglrxEBIig85Hr8r11YE+uMn3G0u+pt0AZHVP+wnAY=
github.com/hashicorp/packer-plugin-ncloud v0.0.2 h1:MGvGkOVfzeosqOSs5dteghLwv9VRcRxTuLoLX1ssUag=
github.com/hashicorp/packer-plugin-ncloud v0.0.2/go.mod h1:Hud2R1pkky96TQy3TPTTrr9Kej4b/4dqC/v+uEE0VDY=
github.com/hashicorp/packer-plugin-openstack v0.0.1 h1:FUaNjKguAipPZZXQ4UiJK6c5+2nS89CRxJHjAsfVyIQ=
@ -647,8 +649,9 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/panicwrap v1.0.0 h1:67zIyVakCIvcs69A0FGfZjBdPleaonSgGlXRSRlb6fE=
github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 h1:+DAetXqxv/mSyCkE9KBIYOZs9b68y7SUaDCxQMRjA68=

View File

@ -1,284 +0,0 @@
---
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.
page_title: HyperOne - Builders
---
# HyperOne Builder
Type: `hyperone`
Artifact BuilderId: `hyperone.builder`
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.
## Authentication
HyperOne supports several authentication methods, which are all supported by
this builder.
### User session
If using user session, set the `token` field to your authentication token.
The `project` field is required when using this method.
```json
{
"token": "YOUR TOKEN",
"project": "YOUR_PROJECT"
}
```
### User session by SSH key
If you've added an SSH key as a credential to your user account and the
private key is added to the ssh-agent on your local machine, you can
authenticate by setting just the platform login (your e-mail address):
```json
{
"token_login": "your.user@example.com"
}
```
### h1 CLI
If you're using [h1-cli](https://github.com/hyperonecom/h1-cli) on your local
machine, HyperOne builder can use your credentials saved in a config file.
All you have to do is login within the tool:
```shell-session
$ h1 login --username your.user@example.com
```
You don't have to set `token` or `project` fields at all using this method.
### Service account
Using `h1`, you can create a new token associated with chosen project.
```shell-session
$ h1 project token add --name packer-builder --project PROJECT_ID
```
Set the `token` field to the generated token or save it in the `HYPERONE_TOKEN`
environment variable. You don't have to set the `project` option using this
method.
```json
{
"token": "YOUR TOKEN"
}
```
## 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/legacy_json_templates/communicator) 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_type` (string) - ID or name of the type this server should be created with.
### Optional:
- `api_url` (string) - Custom API endpoint URL, compatible with HyperOne.
It can also be specified via environment variable `HYPERONE_API_URL`.
- `disk_name` (string) - The name of the created disk.
- `disk_type` (string) - The type of the created disk. Defaults to `ssd`.
- `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/legacy_json_templates/engine) 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.
- `public_netadp_service` (string) - Custom service of public network adapter.
Can be useful when using custom `api_url`. Defaults to `public`.
- `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.
## Chroot disk
### Required:
- `chroot_disk` (bool) - Set to `true` to enable chroot disk build.
- `pre_mount_commands` (array of strings) - A series of commands to execute
before mounting the chroot. This should include any partitioning and
filesystem creation commands. The path to the device is provided by
`{{.Device}}`.
### Optional:
- `chroot_command_wrapper` (string) - How to run shell commands. This defaults
to `{{.Command}}`. This may be useful to set if you want to set
environment variables or run commands with `sudo`.
- `chroot_copy_files` (array of strings) - Paths to files on the running VM
that will be copied into the chroot environment before provisioning.
Defaults to `/etc/resolv.conf` so that DNS lookups work.
- `chroot_device` (string) - The path of chroot device. Defaults an attempt is
made to identify it based on the attach location.
- `chroot_disk_size` (float) - The size of the chroot disk in GiB. Defaults
to `disk_size`.
- `chroot_disk_type` (string) - The type of the chroot disk. Defaults to
`disk_type`.
- `chroot_mount_path` (string) - The path on which the device will be mounted.
- `chroot_mounts` (array of strings) - A list of devices to mount into the
chroot environment. This is a list of 3-element tuples, in order:
- The filesystem type. If this is "bind", then Packer will properly bind the
filesystem to another mount point.
- The source device.
- The mount directory.
- `mount_options` (array of tuples) - Options to supply the `mount` command
when mounting devices. Each option will be prefixed with `-o` and supplied
to the `mount` command.
- `mount_partition` (string) - The partition number containing the / partition.
By default this is the first partition of the volume (for example, sdb1).
- `post_mount_commands` (array of strings) - As `pre_mount_commands`, but the
commands are executed after mounting the root device and before the extra
mount and copy steps. The device and mount path are provided by
`{{.Device}}` and `{{.MountPath}}`.
## 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_type": "a1.nano",
"disk_size": 10
}
```
## Chroot Example
```json
{
"type": "hyperone",
"token": "YOUR_AUTH_TOKEN",
"source_image": "ubuntu-18.04",
"vm_type": "a1.nano",
"disk_size": 10,
"chroot_disk": true,
"pre_mount_commands": [
"apt-get update",
"apt-get install debootstrap",
"debootstrap --arch amd64 bionic {{.MountPath}}"
]
}
```
## HCL Example
```hcl
variable "token" {
type = string
}
variable "project" {
type = string
}
source "hyperone" "new-syntax" {
token = var.token
project = var.project
source_image = "debian"
disk_size = 10
vm_type = "a1.nano"
image_name = "packerbats-hcl-{{timestamp}}"
image_tags = {
key="value"
}
}
build {
sources = [
"source.hyperone.new-syntax"
]
provisioner "shell" {
inline = [
"apt-get update",
"apt-get upgrade -y"
]
}
}
```

View File

@ -1,92 +0,0 @@
<!-- Code generated from the comments of the Config struct in builder/hyperone/config.go; DO NOT EDIT MANUALLY -->
- `api_url` (string) - Custom API endpoint URL, compatible with HyperOne.
It can also be specified via environment variable HYPERONE_API_URL.
- `token_login` (string) - Login (an e-mail) on HyperOne platform. Set this
if you want to fetch the token by SSH authentication.
- `state_timeout` (duration string | ex: "1h5m2s") - Timeout for waiting on the API to complete
a request. Defaults to 5m.
- `image_name` (string) - The name of the resulting image. Defaults to
`packer-{{timestamp}}`
(see configuration templates for more info).
- `image_description` (string) - The description of the resulting image.
- `image_tags` (map[string]string) - Key/value pair tags to add to the created image.
- `image_tag` ([]{key string, value string}) - Same as [`image_tags`](#image_tags) but defined as a singular repeatable
block containing a `key` and a `value` field. In HCL2 mode the
[`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
will allow you to create those programatically.
- `image_service` (string) - The service of the resulting image.
- `vm_name` (string) - The name of the created server.
- `vm_tags` (map[string]string) - Key/value pair tags to add to the created server.
- `vm_tag` ([]{name string, value string}) - Same as [`vm_tags`](#vm_tags) but defined as a singular repeatable block
containing a `key` and a `value` field. In HCL2 mode the
[`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
will allow you to create those programatically.
- `disk_name` (string) - The name of the created disk.
- `disk_type` (string) - The type of the created disk. Defaults to ssd.
- `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.
- `public_netadp_service` (string) - Custom service of public network adapter.
Can be useful when using custom api_url. Defaults to public.
- `chroot_device` (string) - Chroot Device
- `chroot_disk` (bool) - Chroot Disk
- `chroot_disk_size` (float32) - Chroot Disk Size
- `chroot_disk_type` (string) - Chroot Disk Type
- `chroot_mount_path` (string) - Chroot Mount Path
- `chroot_mounts` ([][]string) - Chroot Mounts
- `chroot_copy_files` ([]string) - Chroot Copy Files
- `chroot_command_wrapper` (string) - How to run shell commands. This defaults to `{{.Command}}`. This may be
useful to set if you want to set environmental variables or perhaps run
it with sudo or so on. This is a configuration template where the
.Command variable is replaced with the command to be run. Defaults to
`{{.Command}}`.
- `mount_options` ([]string) - Mount Options
- `mount_partition` (string) - Mount Partition
- `pre_mount_commands` ([]string) - A series of commands to execute after attaching the root volume and
before mounting the chroot. This is not required unless using
from_scratch. If so, this should include any partitioning and filesystem
creation commands. The path to the device is provided by `{{.Device}}`.
- `post_mount_commands` ([]string) - As pre_mount_commands, but the commands are executed after mounting the
root device and before the extra mount and copy steps. The device and
mount path are provided by `{{.Device}}` and `{{.MountPath}}`.
- `ssh_keys` ([]string) - List of SSH keys by name or id to be added
to the server on launch.
- `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.
<!-- End of code generated from the comments of the Config struct in builder/hyperone/config.go; -->

View File

@ -1,17 +0,0 @@
<!-- Code generated from the comments of the Config struct in builder/hyperone/config.go; DO NOT EDIT MANUALLY -->
- `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:
- `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.
- `vm_type` (string) - ID or name of the type this server should be created with.
- `disk_size` (float32) - Size of the created disk, in GiB.
<!-- End of code generated from the comments of the Config struct in builder/hyperone/config.go; -->

View File

@ -708,10 +708,6 @@
"title": "Hetzner Cloud",
"path": "builders/hetzner-cloud"
},
{
"title": "HyperOne",
"path": "builders/hyperone"
},
{
"title": "Hyper-V",
"routes": [

View File

@ -37,7 +37,6 @@
"title": "Docker",
"path": "docker",
"repo": "hashicorp/packer-plugin-docker",
"version": "latest"
},
{
@ -46,6 +45,13 @@
"repo": "hashicorp/packer-plugin-googlecompute",
"version": "latest"
},
{
"title": "HyperOne",
"path": "hyperone",
"repo": "hashicorp/packer-plugin-hyperone",
"version": "latest",
"pluginTier": "community"
},
{
"title": "Naver Cloud",
"path": "ncloud",
@ -87,6 +93,12 @@
"version": "latest",
"pluginTier": "community"
},
{
"title": "QEMU",
"path": "qemu",
"repo": "hashicorp/packer-plugin-qemu",
"version": "latest"
},
{
"title": "Scaleway",
"path": "scaleway",
@ -111,11 +123,5 @@
"path": "vmware",
"repo": "hashicorp/packer-plugin-vmware",
"version": "latest"
},
{
"title": "QEMU",
"path": "qemu",
"repo": "hashicorp/packer-plugin-qemu",
"version": "latest"
}
]