Breakout hcloud (#10966)
* Delete hetzner-cloud.mdx * delete hcloud builder * use hcloud plugin * up mods * use github.com/hashicorp/packer-plugin-hcloud v0.0.1
This commit is contained in:
parent
972497589e
commit
ef612c0eb1
|
@ -1,51 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Artifact struct {
|
|
||||||
// The name of the snapshot
|
|
||||||
snapshotName string
|
|
||||||
|
|
||||||
// The ID of the image
|
|
||||||
snapshotId int
|
|
||||||
|
|
||||||
// The hcloudClient for making API calls
|
|
||||||
hcloudClient *hcloud.Client
|
|
||||||
|
|
||||||
// StateData should store data such as GeneratedData
|
|
||||||
// to be shared with post-processors
|
|
||||||
StateData map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Artifact) BuilderId() string {
|
|
||||||
return BuilderId
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Artifact) Files() []string {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) Id() string {
|
|
||||||
return strconv.Itoa(a.snapshotId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) String() string {
|
|
||||||
return fmt.Sprintf("A snapshot was created: '%v' (ID: %v)", a.snapshotName, a.snapshotId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) State(name string) interface{} {
|
|
||||||
return a.StateData[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Artifact) Destroy() error {
|
|
||||||
log.Printf("Destroying image: %d (%s)", a.snapshotId, a.snapshotName)
|
|
||||||
_, err := a.hcloudClient.Image.Delete(context.TODO(), &hcloud.Image{ID: a.snapshotId})
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestArtifact_Impl(t *testing.T) {
|
|
||||||
var _ packersdk.Artifact = (*Artifact)(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactId(t *testing.T) {
|
|
||||||
generatedData := make(map[string]interface{})
|
|
||||||
a := &Artifact{"packer-foobar", 42, nil, generatedData}
|
|
||||||
expected := "42"
|
|
||||||
|
|
||||||
if a.Id() != expected {
|
|
||||||
t.Fatalf("artifact ID should match: %v", expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactString(t *testing.T) {
|
|
||||||
generatedData := make(map[string]interface{})
|
|
||||||
a := &Artifact{"packer-foobar", 42, nil, generatedData}
|
|
||||||
expected := "A snapshot was created: 'packer-foobar' (ID: 42)"
|
|
||||||
|
|
||||||
if a.String() != expected {
|
|
||||||
t.Fatalf("artifact string should match: %v", expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactState_StateData(t *testing.T) {
|
|
||||||
expectedData := "this is the data"
|
|
||||||
artifact := &Artifact{
|
|
||||||
StateData: map[string]interface{}{"state_data": expectedData},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid state
|
|
||||||
result := artifact.State("state_data")
|
|
||||||
if result != expectedData {
|
|
||||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid state
|
|
||||||
result = artifact.State("invalid_key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil StateData should not fail and should return nil
|
|
||||||
artifact = &Artifact{}
|
|
||||||
result = artifact.State("key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
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/hetznercloud/hcloud-go/hcloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The unique id for the builder
|
|
||||||
const BuilderId = "hcloud.builder"
|
|
||||||
|
|
||||||
type Builder struct {
|
|
||||||
config Config
|
|
||||||
runner multistep.Runner
|
|
||||||
hcloudClient *hcloud.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
var pluginVersion = "1.0.0"
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
|
|
||||||
opts := []hcloud.ClientOption{
|
|
||||||
hcloud.WithToken(b.config.HCloudToken),
|
|
||||||
hcloud.WithEndpoint(b.config.Endpoint),
|
|
||||||
hcloud.WithPollInterval(b.config.PollInterval),
|
|
||||||
hcloud.WithApplication("hcloud-packer", pluginVersion),
|
|
||||||
}
|
|
||||||
b.hcloudClient = hcloud.NewClient(opts...)
|
|
||||||
// Set up the state
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("config", &b.config)
|
|
||||||
state.Put("hcloudClient", b.hcloudClient)
|
|
||||||
state.Put("hook", hook)
|
|
||||||
state.Put("ui", ui)
|
|
||||||
|
|
||||||
// Build the steps
|
|
||||||
steps := []multistep.Step{
|
|
||||||
&stepCreateSSHKey{
|
|
||||||
Debug: b.config.PackerDebug,
|
|
||||||
DebugKeyPath: fmt.Sprintf("ssh_key_%s.pem", b.config.PackerBuildName),
|
|
||||||
},
|
|
||||||
&stepCreateServer{},
|
|
||||||
&communicator.StepConnect{
|
|
||||||
Config: &b.config.Comm,
|
|
||||||
Host: getServerIP,
|
|
||||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
|
||||||
},
|
|
||||||
&commonsteps.StepProvision{},
|
|
||||||
&commonsteps.StepCleanupTempKeys{
|
|
||||||
Comm: &b.config.Comm,
|
|
||||||
},
|
|
||||||
&stepShutdownServer{},
|
|
||||||
&stepCreateSnapshot{},
|
|
||||||
}
|
|
||||||
// Run the steps
|
|
||||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
|
||||||
b.runner.Run(ctx, state)
|
|
||||||
// If there was an error, return that
|
|
||||||
if rawErr, ok := state.GetOk("error"); ok {
|
|
||||||
return nil, rawErr.(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := state.GetOk("snapshot_name"); !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
artifact := &Artifact{
|
|
||||||
snapshotName: state.Get("snapshot_name").(string),
|
|
||||||
snapshotId: state.Get("snapshot_id").(int),
|
|
||||||
hcloudClient: b.hcloudClient,
|
|
||||||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
|
|
||||||
}
|
|
||||||
|
|
||||||
return artifact, nil
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
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 testAccPreCheck(t *testing.T) {
|
|
||||||
if v := os.Getenv("HCLOUD_TOKEN"); v == "" {
|
|
||||||
t.Fatal("HCLOUD_TOKEN must be set for acceptance tests")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const testBuilderAccBasic = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"location": "nbg1",
|
|
||||||
"server_type": "cx11",
|
|
||||||
"image": "ubuntu-18.04",
|
|
||||||
"user_data": "",
|
|
||||||
"user_data_file": ""
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,152 +0,0 @@
|
||||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,imageFilter
|
|
||||||
|
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
|
||||||
"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/hetznercloud/hcloud-go/hcloud"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
|
||||||
Comm communicator.Config `mapstructure:",squash"`
|
|
||||||
|
|
||||||
HCloudToken string `mapstructure:"token"`
|
|
||||||
Endpoint string `mapstructure:"endpoint"`
|
|
||||||
|
|
||||||
PollInterval time.Duration `mapstructure:"poll_interval"`
|
|
||||||
|
|
||||||
ServerName string `mapstructure:"server_name"`
|
|
||||||
Location string `mapstructure:"location"`
|
|
||||||
ServerType string `mapstructure:"server_type"`
|
|
||||||
Image string `mapstructure:"image"`
|
|
||||||
ImageFilter *imageFilter `mapstructure:"image_filter"`
|
|
||||||
|
|
||||||
SnapshotName string `mapstructure:"snapshot_name"`
|
|
||||||
SnapshotLabels map[string]string `mapstructure:"snapshot_labels"`
|
|
||||||
UserData string `mapstructure:"user_data"`
|
|
||||||
UserDataFile string `mapstructure:"user_data_file"`
|
|
||||||
SSHKeys []string `mapstructure:"ssh_keys"`
|
|
||||||
|
|
||||||
RescueMode string `mapstructure:"rescue"`
|
|
||||||
|
|
||||||
ctx interpolate.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
type imageFilter struct {
|
|
||||||
WithSelector []string `mapstructure:"with_selector"`
|
|
||||||
MostRecent bool `mapstructure:"most_recent"`
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, raws...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defaults
|
|
||||||
if c.HCloudToken == "" {
|
|
||||||
c.HCloudToken = os.Getenv("HCLOUD_TOKEN")
|
|
||||||
}
|
|
||||||
if c.Endpoint == "" {
|
|
||||||
if os.Getenv("HCLOUD_ENDPOINT") != "" {
|
|
||||||
c.Endpoint = os.Getenv("HCLOUD_ENDPOINT")
|
|
||||||
} else {
|
|
||||||
c.Endpoint = hcloud.Endpoint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.PollInterval == 0 {
|
|
||||||
c.PollInterval = 500 * time.Millisecond
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.SnapshotName == "" {
|
|
||||||
def, err := interpolate.Render("packer-{{timestamp}}", nil)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Default to packer-{{ unix timestamp (utc) }}
|
|
||||||
c.SnapshotName = def
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.ServerName == "" {
|
|
||||||
// Default to packer-[time-ordered-uuid]
|
|
||||||
c.ServerName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
|
||||||
}
|
|
||||||
|
|
||||||
var errs *packersdk.MultiError
|
|
||||||
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
|
||||||
errs = packersdk.MultiErrorAppend(errs, es...)
|
|
||||||
}
|
|
||||||
if c.HCloudToken == "" {
|
|
||||||
// Required configurations that will display errors if not set
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("token for auth must be specified"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Location == "" {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("location is required"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.ServerType == "" {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("server type is required"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Image == "" && c.ImageFilter == nil {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("image or image_filter is required"))
|
|
||||||
}
|
|
||||||
if c.ImageFilter != nil {
|
|
||||||
if len(c.ImageFilter.WithSelector) == 0 {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("image_filter.with_selector is required when specifying filter"))
|
|
||||||
} else if c.Image != "" {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("only one of image or image_filter can be specified"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.UserData != "" && c.UserDataFile != "" {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New("only one of user_data or user_data_file can be specified"))
|
|
||||||
} else if c.UserDataFile != "" {
|
|
||||||
if _, err := os.Stat(c.UserDataFile); err != nil {
|
|
||||||
errs = packersdk.MultiErrorAppend(
|
|
||||||
errs, errors.New(fmt.Sprintf("user_data_file not found: %s", c.UserDataFile)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if errs != nil && len(errs.Errors) > 0 {
|
|
||||||
return nil, errs
|
|
||||||
}
|
|
||||||
|
|
||||||
packersdk.LogSecretFilter.Set(c.HCloudToken)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServerIP(state multistep.StateBag) (string, error) {
|
|
||||||
return state.Get("server_ip").(string), nil
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
|
||||||
"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"`
|
|
||||||
HCloudToken *string `mapstructure:"token" cty:"token" hcl:"token"`
|
|
||||||
Endpoint *string `mapstructure:"endpoint" cty:"endpoint" hcl:"endpoint"`
|
|
||||||
PollInterval *string `mapstructure:"poll_interval" cty:"poll_interval" hcl:"poll_interval"`
|
|
||||||
ServerName *string `mapstructure:"server_name" cty:"server_name" hcl:"server_name"`
|
|
||||||
Location *string `mapstructure:"location" cty:"location" hcl:"location"`
|
|
||||||
ServerType *string `mapstructure:"server_type" cty:"server_type" hcl:"server_type"`
|
|
||||||
Image *string `mapstructure:"image" cty:"image" hcl:"image"`
|
|
||||||
ImageFilter *FlatimageFilter `mapstructure:"image_filter" cty:"image_filter" hcl:"image_filter"`
|
|
||||||
SnapshotName *string `mapstructure:"snapshot_name" cty:"snapshot_name" hcl:"snapshot_name"`
|
|
||||||
SnapshotLabels map[string]string `mapstructure:"snapshot_labels" cty:"snapshot_labels" hcl:"snapshot_labels"`
|
|
||||||
UserData *string `mapstructure:"user_data" cty:"user_data" hcl:"user_data"`
|
|
||||||
UserDataFile *string `mapstructure:"user_data_file" cty:"user_data_file" hcl:"user_data_file"`
|
|
||||||
SSHKeys []string `mapstructure:"ssh_keys" cty:"ssh_keys" hcl:"ssh_keys"`
|
|
||||||
RescueMode *string `mapstructure:"rescue" cty:"rescue" hcl:"rescue"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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},
|
|
||||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
|
||||||
"endpoint": &hcldec.AttrSpec{Name: "endpoint", Type: cty.String, Required: false},
|
|
||||||
"poll_interval": &hcldec.AttrSpec{Name: "poll_interval", Type: cty.String, Required: false},
|
|
||||||
"server_name": &hcldec.AttrSpec{Name: "server_name", Type: cty.String, Required: false},
|
|
||||||
"location": &hcldec.AttrSpec{Name: "location", Type: cty.String, Required: false},
|
|
||||||
"server_type": &hcldec.AttrSpec{Name: "server_type", Type: cty.String, Required: false},
|
|
||||||
"image": &hcldec.AttrSpec{Name: "image", Type: cty.String, Required: false},
|
|
||||||
"image_filter": &hcldec.BlockSpec{TypeName: "image_filter", Nested: hcldec.ObjectSpec((*FlatimageFilter)(nil).HCL2Spec())},
|
|
||||||
"snapshot_name": &hcldec.AttrSpec{Name: "snapshot_name", Type: cty.String, Required: false},
|
|
||||||
"snapshot_labels": &hcldec.AttrSpec{Name: "snapshot_labels", Type: cty.Map(cty.String), Required: false},
|
|
||||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
|
||||||
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
|
|
||||||
"ssh_keys": &hcldec.AttrSpec{Name: "ssh_keys", Type: cty.List(cty.String), Required: false},
|
|
||||||
"rescue": &hcldec.AttrSpec{Name: "rescue", Type: cty.String, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatimageFilter is an auto-generated flat version of imageFilter.
|
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
type FlatimageFilter struct {
|
|
||||||
WithSelector []string `mapstructure:"with_selector" cty:"with_selector" hcl:"with_selector"`
|
|
||||||
MostRecent *bool `mapstructure:"most_recent" cty:"most_recent" hcl:"most_recent"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatimageFilter.
|
|
||||||
// FlatimageFilter is an auto-generated flat version of imageFilter.
|
|
||||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
|
||||||
func (*imageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
|
||||||
return new(FlatimageFilter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HCL2Spec returns the hcl spec of a imageFilter.
|
|
||||||
// This spec is used by HCL to read the fields of imageFilter.
|
|
||||||
// The decoded values from this spec will then be applied to a FlatimageFilter.
|
|
||||||
func (*FlatimageFilter) HCL2Spec() map[string]hcldec.Spec {
|
|
||||||
s := map[string]hcldec.Spec{
|
|
||||||
"with_selector": &hcldec.AttrSpec{Name: "with_selector", Type: cty.List(cty.String), Required: false},
|
|
||||||
"most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false},
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stepCreateServer struct {
|
|
||||||
serverId int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
client := state.Get("hcloudClient").(*hcloud.Client)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
c := state.Get("config").(*Config)
|
|
||||||
sshKeyId := state.Get("ssh_key_id").(int)
|
|
||||||
|
|
||||||
// Create the server based on configuration
|
|
||||||
ui.Say("Creating server...")
|
|
||||||
|
|
||||||
userData := c.UserData
|
|
||||||
if c.UserDataFile != "" {
|
|
||||||
contents, err := ioutil.ReadFile(c.UserDataFile)
|
|
||||||
if err != nil {
|
|
||||||
state.Put("error", fmt.Errorf("Problem reading user data file: %s", err))
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
userData = string(contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
sshKeys := []*hcloud.SSHKey{{ID: sshKeyId}}
|
|
||||||
for _, k := range c.SSHKeys {
|
|
||||||
sshKey, _, err := client.SSHKey.Get(ctx, k)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(err.Error())
|
|
||||||
state.Put("error", fmt.Errorf("Error fetching SSH key: %s", err))
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
if sshKey == nil {
|
|
||||||
state.Put("error", fmt.Errorf("Could not find key: %s", k))
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
sshKeys = append(sshKeys, sshKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
var image *hcloud.Image
|
|
||||||
if c.Image != "" {
|
|
||||||
image = &hcloud.Image{Name: c.Image}
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
image, err = getImageWithSelectors(ctx, client, c)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(err.Error())
|
|
||||||
state.Put("error", err)
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
ui.Message(fmt.Sprintf("Using image %s with ID %d", image.Description, image.ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
serverCreateResult, _, err := client.Server.Create(ctx, hcloud.ServerCreateOpts{
|
|
||||||
Name: c.ServerName,
|
|
||||||
ServerType: &hcloud.ServerType{Name: c.ServerType},
|
|
||||||
Image: image,
|
|
||||||
SSHKeys: sshKeys,
|
|
||||||
Location: &hcloud.Location{Name: c.Location},
|
|
||||||
UserData: userData,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error creating server: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
state.Put("server_ip", serverCreateResult.Server.PublicNet.IPv4.IP.String())
|
|
||||||
// We use this in cleanup
|
|
||||||
s.serverId = serverCreateResult.Server.ID
|
|
||||||
|
|
||||||
// Store the server id for later
|
|
||||||
state.Put("server_id", serverCreateResult.Server.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", serverCreateResult.Server.ID)
|
|
||||||
|
|
||||||
if err := waitForAction(ctx, client, serverCreateResult.Action); err != nil {
|
|
||||||
err := fmt.Errorf("Error creating server: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
for _, nextAction := range serverCreateResult.NextActions {
|
|
||||||
if err := waitForAction(ctx, client, nextAction); err != nil {
|
|
||||||
err := fmt.Errorf("Error creating server: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.RescueMode != "" {
|
|
||||||
ui.Say("Enabling Rescue Mode...")
|
|
||||||
rootPassword, err := setRescue(ctx, client, serverCreateResult.Server, c.RescueMode, sshKeys)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error enabling rescue mode: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
ui.Say("Reboot server...")
|
|
||||||
action, _, err := client.Server.Reset(ctx, serverCreateResult.Server)
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error rebooting server: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
if err := waitForAction(ctx, client, action); err != nil {
|
|
||||||
err := fmt.Errorf("Error rebooting server: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
if c.RescueMode == "freebsd64" {
|
|
||||||
// We will set this only on freebsd
|
|
||||||
ui.Say("Using Root Password instead of SSH Keys...")
|
|
||||||
c.Comm.SSHPassword = rootPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stepCreateServer) Cleanup(state multistep.StateBag) {
|
|
||||||
// If the serverID isn't there, we probably never created it
|
|
||||||
if s.serverId == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := state.Get("hcloudClient").(*hcloud.Client)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
|
|
||||||
// Destroy the server we just created
|
|
||||||
ui.Say("Destroying server...")
|
|
||||||
_, err := client.Server.Delete(context.TODO(), &hcloud.Server{ID: s.serverId})
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf(
|
|
||||||
"Error destroying server. Please destroy it manually: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setRescue(ctx context.Context, client *hcloud.Client, server *hcloud.Server, rescue string, sshKeys []*hcloud.SSHKey) (string, error) {
|
|
||||||
rescueChanged := false
|
|
||||||
if server.RescueEnabled {
|
|
||||||
rescueChanged = true
|
|
||||||
action, _, err := client.Server.DisableRescue(ctx, server)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := waitForAction(ctx, client, action); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rescue != "" {
|
|
||||||
rescueChanged = true
|
|
||||||
if rescue == "freebsd64" {
|
|
||||||
sshKeys = nil // freebsd64 doesn't allow ssh keys so we will remove them here
|
|
||||||
}
|
|
||||||
res, _, err := client.Server.EnableRescue(ctx, server, hcloud.ServerEnableRescueOpts{
|
|
||||||
Type: hcloud.ServerRescueType(rescue),
|
|
||||||
SSHKeys: sshKeys,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := waitForAction(ctx, client, res.Action); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return res.RootPassword, nil
|
|
||||||
}
|
|
||||||
if rescueChanged {
|
|
||||||
action, _, err := client.Server.Reset(ctx, server)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := waitForAction(ctx, client, action); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForAction(ctx context.Context, client *hcloud.Client, action *hcloud.Action) error {
|
|
||||||
_, errCh := client.Action.WatchProgress(ctx, action)
|
|
||||||
if err := <-errCh; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getImageWithSelectors(ctx context.Context, client *hcloud.Client, c *Config) (*hcloud.Image, error) {
|
|
||||||
var allImages []*hcloud.Image
|
|
||||||
|
|
||||||
var selector = strings.Join(c.ImageFilter.WithSelector, ",")
|
|
||||||
opts := hcloud.ImageListOpts{
|
|
||||||
ListOpts: hcloud.ListOpts{LabelSelector: selector},
|
|
||||||
Status: []hcloud.ImageStatus{hcloud.ImageStatusAvailable},
|
|
||||||
}
|
|
||||||
|
|
||||||
allImages, err := client.Image.AllWithOpts(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(allImages) == 0 {
|
|
||||||
return nil, fmt.Errorf("no image found for selector %q", selector)
|
|
||||||
}
|
|
||||||
if len(allImages) > 1 {
|
|
||||||
if !c.ImageFilter.MostRecent {
|
|
||||||
return nil, fmt.Errorf("more than one image found for selector %q", selector)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(allImages, func(i, j int) bool {
|
|
||||||
return allImages[i].Created.After(allImages[j].Created)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return allImages[0], nil
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stepCreateSnapshot struct{}
|
|
||||||
|
|
||||||
func (s *stepCreateSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
client := state.Get("hcloudClient").(*hcloud.Client)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
c := state.Get("config").(*Config)
|
|
||||||
serverID := state.Get("server_id").(int)
|
|
||||||
|
|
||||||
ui.Say("Creating snapshot ...")
|
|
||||||
ui.Say("This can take some time")
|
|
||||||
result, _, err := client.Server.CreateImage(ctx, &hcloud.Server{ID: serverID}, &hcloud.ServerCreateImageOpts{
|
|
||||||
Type: hcloud.ImageTypeSnapshot,
|
|
||||||
Labels: c.SnapshotLabels,
|
|
||||||
Description: hcloud.String(c.SnapshotName),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error creating snapshot: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
state.Put("snapshot_id", result.Image.ID)
|
|
||||||
state.Put("snapshot_name", c.SnapshotName)
|
|
||||||
_, errCh := client.Action.WatchProgress(ctx, result.Action)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case err1 := <-errCh:
|
|
||||||
if err1 == nil {
|
|
||||||
return multistep.ActionContinue
|
|
||||||
} else {
|
|
||||||
err := fmt.Errorf("Error creating snapshot: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stepCreateSnapshot) Cleanup(state multistep.StateBag) {
|
|
||||||
// no cleanup
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/uuid"
|
|
||||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stepCreateSSHKey struct {
|
|
||||||
Debug bool
|
|
||||||
DebugKeyPath string
|
|
||||||
|
|
||||||
keyId int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
client := state.Get("hcloudClient").(*hcloud.Client)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
c := state.Get("config").(*Config)
|
|
||||||
ui.Say("Creating temporary ssh key for server...")
|
|
||||||
|
|
||||||
priv, err := rsa.GenerateKey(rand.Reader, 2014)
|
|
||||||
if err != nil {
|
|
||||||
state.Put("error", fmt.Errorf("Error generating RSA key: %s", err))
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
// ASN.1 DER encoded form
|
|
||||||
privDER := x509.MarshalPKCS1PrivateKey(priv)
|
|
||||||
privBLK := pem.Block{
|
|
||||||
Type: "RSA PRIVATE KEY",
|
|
||||||
Headers: nil,
|
|
||||||
Bytes: privDER,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the private key in the config for later
|
|
||||||
c.Comm.SSHPrivateKey = pem.EncodeToMemory(&privBLK)
|
|
||||||
|
|
||||||
// Marshal the public key into SSH compatible format
|
|
||||||
pub, err := ssh.NewPublicKey(&priv.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
state.Put("error", fmt.Errorf("Error generating public key: %s", err))
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
pubSSHFormat := string(ssh.MarshalAuthorizedKey(pub))
|
|
||||||
|
|
||||||
// The name of the public key on the Hetzner Cloud
|
|
||||||
name := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
|
|
||||||
|
|
||||||
// Create the key!
|
|
||||||
key, _, err := client.SSHKey.Create(ctx, hcloud.SSHKeyCreateOpts{
|
|
||||||
Name: name,
|
|
||||||
PublicKey: pubSSHFormat,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error creating temporary SSH key: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use this to check cleanup
|
|
||||||
s.keyId = key.ID
|
|
||||||
|
|
||||||
log.Printf("temporary ssh key name: %s", name)
|
|
||||||
|
|
||||||
// Remember some state for the future
|
|
||||||
state.Put("ssh_key_id", key.ID)
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// If no key id is set, then we never created it, so just return
|
|
||||||
if s.keyId == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := state.Get("hcloudClient").(*hcloud.Client)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
|
|
||||||
ui.Say("Deleting temporary ssh key...")
|
|
||||||
_, err := client.SSHKey.Delete(context.TODO(), &hcloud.SSHKey{ID: s.keyId})
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error cleaning up ssh key: %s", err)
|
|
||||||
ui.Error(fmt.Sprintf(
|
|
||||||
"Error cleaning up ssh key. Please delete the key manually: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package hcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hetznercloud/hcloud-go/hcloud"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stepShutdownServer struct{}
|
|
||||||
|
|
||||||
func (s *stepShutdownServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
|
||||||
client := state.Get("hcloudClient").(*hcloud.Client)
|
|
||||||
ui := state.Get("ui").(packersdk.Ui)
|
|
||||||
serverID := state.Get("server_id").(int)
|
|
||||||
|
|
||||||
ui.Say("Shutting down server...")
|
|
||||||
|
|
||||||
action, _, err := client.Server.Shutdown(ctx, &hcloud.Server{ID: serverID})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error stopping server: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
_, errCh := client.Action.WatchProgress(ctx, action)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case err1 := <-errCh:
|
|
||||||
if err1 == nil {
|
|
||||||
return multistep.ActionContinue
|
|
||||||
} else {
|
|
||||||
err := fmt.Errorf("Error stopping server: %s", err)
|
|
||||||
state.Put("error", err)
|
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stepShutdownServer) Cleanup(state multistep.StateBag) {
|
|
||||||
// no cleanup
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package version
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
|
||||||
packerVersion "github.com/hashicorp/packer/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
var HcloudPluginVersion *version.PluginVersion
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
HcloudPluginVersion = version.InitializePluginVersion(
|
|
||||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
|
||||||
}
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
azurechrootbuilder "github.com/hashicorp/packer/builder/azure/chroot"
|
azurechrootbuilder "github.com/hashicorp/packer/builder/azure/chroot"
|
||||||
azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl"
|
azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl"
|
||||||
filebuilder "github.com/hashicorp/packer/builder/file"
|
filebuilder "github.com/hashicorp/packer/builder/file"
|
||||||
hcloudbuilder "github.com/hashicorp/packer/builder/hcloud"
|
|
||||||
nullbuilder "github.com/hashicorp/packer/builder/null"
|
nullbuilder "github.com/hashicorp/packer/builder/null"
|
||||||
oneandonebuilder "github.com/hashicorp/packer/builder/oneandone"
|
oneandonebuilder "github.com/hashicorp/packer/builder/oneandone"
|
||||||
profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks"
|
profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks"
|
||||||
|
@ -52,7 +51,6 @@ var Builders = map[string]packersdk.Builder{
|
||||||
"azure-chroot": new(azurechrootbuilder.Builder),
|
"azure-chroot": new(azurechrootbuilder.Builder),
|
||||||
"azure-dtl": new(azuredtlbuilder.Builder),
|
"azure-dtl": new(azuredtlbuilder.Builder),
|
||||||
"file": new(filebuilder.Builder),
|
"file": new(filebuilder.Builder),
|
||||||
"hcloud": new(hcloudbuilder.Builder),
|
|
||||||
"null": new(nullbuilder.Builder),
|
"null": new(nullbuilder.Builder),
|
||||||
"oneandone": new(oneandonebuilder.Builder),
|
"oneandone": new(oneandonebuilder.Builder),
|
||||||
"profitbricks": new(profitbricksbuilder.Builder),
|
"profitbricks": new(profitbricksbuilder.Builder),
|
||||||
|
|
|
@ -33,6 +33,7 @@ import (
|
||||||
googlecomputebuilder "github.com/hashicorp/packer-plugin-googlecompute/builder/googlecompute"
|
googlecomputebuilder "github.com/hashicorp/packer-plugin-googlecompute/builder/googlecompute"
|
||||||
googlecomputeexportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-export"
|
googlecomputeexportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-export"
|
||||||
googlecomputeimportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-import"
|
googlecomputeimportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-import"
|
||||||
|
hcloudbuilder "github.com/hashicorp/packer-plugin-hcloud/builder/hcloud"
|
||||||
hyperonebuilder "github.com/hashicorp/packer-plugin-hyperone/builder/hyperone"
|
hyperonebuilder "github.com/hashicorp/packer-plugin-hyperone/builder/hyperone"
|
||||||
hypervisobuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/iso"
|
hypervisobuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/iso"
|
||||||
hypervvmcxbuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/vmcx"
|
hypervvmcxbuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/vmcx"
|
||||||
|
@ -93,6 +94,7 @@ var VendoredBuilders = map[string]packersdk.Builder{
|
||||||
"digitalocean": new(digitaloceanbuilder.Builder),
|
"digitalocean": new(digitaloceanbuilder.Builder),
|
||||||
"docker": new(dockerbuilder.Builder),
|
"docker": new(dockerbuilder.Builder),
|
||||||
"googlecompute": new(googlecomputebuilder.Builder),
|
"googlecompute": new(googlecomputebuilder.Builder),
|
||||||
|
"hcloud": new(hcloudbuilder.Builder),
|
||||||
"hyperv-iso": new(hypervisobuilder.Builder),
|
"hyperv-iso": new(hypervisobuilder.Builder),
|
||||||
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
|
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
|
||||||
"hyperone": new(hyperonebuilder.Builder),
|
"hyperone": new(hyperonebuilder.Builder),
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -41,6 +41,7 @@ require (
|
||||||
github.com/hashicorp/packer-plugin-digitalocean v0.0.1
|
github.com/hashicorp/packer-plugin-digitalocean v0.0.1
|
||||||
github.com/hashicorp/packer-plugin-docker v0.0.7
|
github.com/hashicorp/packer-plugin-docker v0.0.7
|
||||||
github.com/hashicorp/packer-plugin-googlecompute v0.0.1
|
github.com/hashicorp/packer-plugin-googlecompute v0.0.1
|
||||||
|
github.com/hashicorp/packer-plugin-hcloud v0.0.1
|
||||||
github.com/hashicorp/packer-plugin-hyperone v0.0.1
|
github.com/hashicorp/packer-plugin-hyperone v0.0.1
|
||||||
github.com/hashicorp/packer-plugin-hyperv v0.0.1
|
github.com/hashicorp/packer-plugin-hyperv v0.0.1
|
||||||
github.com/hashicorp/packer-plugin-jdcloud v0.0.1
|
github.com/hashicorp/packer-plugin-jdcloud v0.0.1
|
||||||
|
@ -63,7 +64,6 @@ require (
|
||||||
github.com/hashicorp/packer-plugin-virtualbox v0.0.1
|
github.com/hashicorp/packer-plugin-virtualbox v0.0.1
|
||||||
github.com/hashicorp/packer-plugin-vmware v0.0.1
|
github.com/hashicorp/packer-plugin-vmware v0.0.1
|
||||||
github.com/hashicorp/packer-plugin-vsphere v0.0.1
|
github.com/hashicorp/packer-plugin-vsphere v0.0.1
|
||||||
github.com/hetznercloud/hcloud-go v1.15.1
|
|
||||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
|
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
|
||||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380
|
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380
|
||||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08
|
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -536,6 +536,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-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 h1:Shjio88MraB+ocj0VI5+M65r4UBKbYI4eCqLNyPXKEo=
|
||||||
github.com/hashicorp/packer-plugin-googlecompute v0.0.1/go.mod h1:MfV898IrEMpKH6wVnvOI5Tkhxm2snf3QxwVqV4k3bNI=
|
github.com/hashicorp/packer-plugin-googlecompute v0.0.1/go.mod h1:MfV898IrEMpKH6wVnvOI5Tkhxm2snf3QxwVqV4k3bNI=
|
||||||
|
github.com/hashicorp/packer-plugin-hcloud v0.0.1 h1:KM4fxnIpBBCe0SHoK9FHLFlGDWoW8uvMLUdtcNRzla0=
|
||||||
|
github.com/hashicorp/packer-plugin-hcloud v0.0.1/go.mod h1:H+LpMPP8V+VqFqBHBD2qVSYWE5woFWQRVdtB7S6LMT0=
|
||||||
github.com/hashicorp/packer-plugin-hyperone v0.0.1 h1:Owp1B5cI0VgFgR3pCyeeQdyKPTWls36mVedv+WxZMOM=
|
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-hyperone v0.0.1/go.mod h1:9DglrxEBIig85Hr8r11YE+uMn3G0u+pt0AZHVP+wnAY=
|
||||||
github.com/hashicorp/packer-plugin-hyperv v0.0.1 h1:ZdsJw4X+4zSgRYPzVQbJrx8Az73AkneSWLnmfpojl0k=
|
github.com/hashicorp/packer-plugin-hyperv v0.0.1 h1:ZdsJw4X+4zSgRYPzVQbJrx8Az73AkneSWLnmfpojl0k=
|
||||||
|
@ -604,8 +606,9 @@ github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvh
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hetznercloud/hcloud-go v1.15.1 h1:G8Q+xyAqQ5IUY7yq4HKZgkabFa0S/VXJXq3TGCeT8JM=
|
|
||||||
github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2wrVdERJgCtxsF3Q=
|
github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2wrVdERJgCtxsF3Q=
|
||||||
|
github.com/hetznercloud/hcloud-go v1.25.0 h1:QAaFKtGKWRxjwjKJWBGMxGYUxVEQmIkb35j/WXrsazY=
|
||||||
|
github.com/hetznercloud/hcloud-go v1.25.0/go.mod h1:2C5uMtBiMoFr3m7lBFPf7wXTdh33CevmZpQIIDPGYJI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4 h1:mSmyzhwBeQt2TlHbsXYLona9pwjWAvYGwQJ2Cq/k3VE=
|
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4 h1:mSmyzhwBeQt2TlHbsXYLona9pwjWAvYGwQJ2Cq/k3VE=
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
|
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
---
|
|
||||||
description: |
|
|
||||||
The Hetzner Cloud Packer builder is able to create new images for use with the
|
|
||||||
Hetzner Cloud. The builder takes a source image, runs any provisioning
|
|
||||||
necessary on the image after launching it, then snapshots it into a reusable
|
|
||||||
image. This reusable image can then be used as the foundation of new servers
|
|
||||||
that are launched within the Hetzner Cloud.
|
|
||||||
page_title: Hetzner Cloud - Builders
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hetzner Cloud Builder
|
|
||||||
|
|
||||||
Type: `hcloud`
|
|
||||||
Artifact BuilderId: `hcloud.builder`
|
|
||||||
|
|
||||||
The `hcloud` Packer builder is able to create new images for use with [Hetzner
|
|
||||||
Cloud](https://www.hetzner.cloud). The builder takes a source image, runs any
|
|
||||||
provisioning necessary on the image after launching it, then snapshots it into
|
|
||||||
a reusable image. This reusable image can then be used as the foundation of new
|
|
||||||
servers that are launched within the Hetzner Cloud.
|
|
||||||
|
|
||||||
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/legacy_json_templates/communicator) can be configured for this
|
|
||||||
builder.
|
|
||||||
|
|
||||||
### Required Builder Configuration options:
|
|
||||||
|
|
||||||
- `token` (string) - The client TOKEN to use to access your account. It can
|
|
||||||
also be specified via environment variable `HCLOUD_TOKEN`, if set.
|
|
||||||
|
|
||||||
- `image` (string) - ID or name of image to launch server from. Alternatively
|
|
||||||
you can use `image_filter`.
|
|
||||||
|
|
||||||
- `location` (string) - The name of the location to launch the server in.
|
|
||||||
|
|
||||||
- `server_type` (string) - ID or name of the server type this server should
|
|
||||||
be created with.
|
|
||||||
|
|
||||||
### Optional:
|
|
||||||
|
|
||||||
- `endpoint` (string) - Non standard api endpoint URL. Set this if you are
|
|
||||||
using a Hetzner Cloud API compatible service. It can also be specified via
|
|
||||||
environment variable `HCLOUD_ENDPOINT`.
|
|
||||||
|
|
||||||
- `image_filter` (object) - Filters used to populate the `filter`
|
|
||||||
field. Example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"image_filter": {
|
|
||||||
"with_selector": ["name==my-image"],
|
|
||||||
"most_recent": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This selects the most recent image with the label `name==my-image`. NOTE:
|
|
||||||
This will fail unless _exactly_ one AMI is returned. In the above example,
|
|
||||||
`most_recent` will cause this to succeed by selecting the newest image.
|
|
||||||
|
|
||||||
- `with_selector` (list of strings) - label selectors used to select an
|
|
||||||
`image`. NOTE: This will fail unless _exactly_ one image is returned.
|
|
||||||
Check the official hcloud docs on
|
|
||||||
[Label Selectors](https://docs.hetzner.cloud/#overview-label-selector)
|
|
||||||
for more info.
|
|
||||||
|
|
||||||
- `most_recent` (boolean) - Selects the newest created image when true.
|
|
||||||
This is most useful if you base your image on another Packer build image.
|
|
||||||
|
|
||||||
You may set this in place of `image`, but not both.
|
|
||||||
|
|
||||||
- `server_name` (string) - The name assigned to the server. The Hetzner Cloud
|
|
||||||
sets the hostname of the machine to this value.
|
|
||||||
|
|
||||||
- `snapshot_name` (string) - The name of the resulting snapshot that will
|
|
||||||
appear in your account as image description. Defaults to `packer-{{timestamp}}` (see
|
|
||||||
[configuration templates](/docs/templates/legacy_json_templates/engine) for more info). If you want to reference the image as a sample in your terraform configuration please use the image id or the `snapshot_labels`.
|
|
||||||
|
|
||||||
- `snapshot_labels` (map of key/value strings) - Key/value pair labels to
|
|
||||||
apply to the created image.
|
|
||||||
|
|
||||||
- `poll_interval` (string) - Configures the interval in which actions are
|
|
||||||
polled by the client. Default `500ms`. Increase this interval if you run
|
|
||||||
into rate limiting errors.
|
|
||||||
|
|
||||||
- `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.
|
|
||||||
|
|
||||||
- `user_data_file` (string) - Path to a file that will be used for the user
|
|
||||||
data when launching the server.
|
|
||||||
|
|
||||||
- `ssh_keys` (array of strings) - List of SSH keys by name or id to be added
|
|
||||||
to image on launch.
|
|
||||||
|
|
||||||
- `rescue` (string) - Enable and boot in to the specified rescue system. This
|
|
||||||
enables simple installation of custom operating systems. `linux64`
|
|
||||||
`linux32` or `freebsd64`
|
|
||||||
|
|
||||||
## Basic Example
|
|
||||||
|
|
||||||
Here is a basic example. It is completely valid as soon as you enter your own
|
|
||||||
access tokens:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"builders": [
|
|
||||||
{
|
|
||||||
"type": "hcloud",
|
|
||||||
"token": "YOUR API KEY",
|
|
||||||
"image": "ubuntu-18.04",
|
|
||||||
"location": "nbg1",
|
|
||||||
"server_type": "cx11",
|
|
||||||
"ssh_username": "root"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -700,10 +700,6 @@
|
||||||
"title": "File",
|
"title": "File",
|
||||||
"path": "builders/file"
|
"path": "builders/file"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "Hetzner Cloud",
|
|
||||||
"path": "builders/hetzner-cloud"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Null",
|
"title": "Null",
|
||||||
"path": "builders/null"
|
"path": "builders/null"
|
||||||
|
|
|
@ -58,6 +58,13 @@
|
||||||
"repo": "hashicorp/packer-plugin-googlecompute",
|
"repo": "hashicorp/packer-plugin-googlecompute",
|
||||||
"version": "latest"
|
"version": "latest"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "hcloud",
|
||||||
|
"path": "hetzner-cloud",
|
||||||
|
"repo": "hashicorp/packer-plugin-hcloud",
|
||||||
|
"version": "latest",
|
||||||
|
"pluginTier": "community"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "HyperOne",
|
"title": "HyperOne",
|
||||||
"path": "hyperone",
|
"path": "hyperone",
|
||||||
|
|
Loading…
Reference in New Issue