Extract ProfitBricks Plugin (#11084)

* Extract ProfitBricks Plugin

* Update CHANGELOG
pull/11096/head
Wilken Rivera 5 years ago committed by GitHub
parent 927093e5c0
commit e22346f0fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -29,6 +29,7 @@ Major refactor: Extracted a majority of HashiCorp-maintained and community plugi
* "github.com/hashicorp/packer-plugin-oracle" [GH-10962]
* "github.com/hashicorp/packer-plugin-outscale" [GH-10941]
* "github.com/hashicorp/packer-plugin-parallels" [GH-10936]
* "github.com/hashicorp/packer-plugin-profitbricks" [GH-11084]
* "github.com/hashicorp/packer-plugin-proxmox" [GH-10930]
* "github.com/hashicorp/packer-plugin-puppet" [GH-10943]
* "github.com/hashicorp/packer-plugin-qemu" [GH-10929]

@ -1,37 +0,0 @@
package profitbricks
import (
"fmt"
)
type Artifact struct {
snapshotData string
// StateData should store data such as GeneratedData
// to be shared with post-processors
StateData map[string]interface{}
}
func (*Artifact) BuilderId() string {
return BuilderId
}
func (a *Artifact) Files() []string {
return []string{}
}
func (*Artifact) Id() string {
return "Null"
}
func (a *Artifact) String() string {
return fmt.Sprintf("A snapshot was created: '%v'", a.snapshotData)
}
func (a *Artifact) State(name string) interface{} {
return a.StateData[name]
}
func (a *Artifact) Destroy() error {
return nil
}

@ -1,51 +0,0 @@
package profitbricks
import (
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func TestArtifact_Impl(t *testing.T) {
var raw interface{}
raw = &Artifact{}
if _, ok := raw.(packersdk.Artifact); !ok {
t.Fatalf("Artifact should be artifact")
}
}
func TestArtifactString(t *testing.T) {
generatedData := make(map[string]interface{})
a := &Artifact{"packer-foobar", generatedData}
expected := "A snapshot was created: 'packer-foobar'"
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,70 +0,0 @@
package profitbricks
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"
)
const BuilderId = "packer.profitbricks"
type Builder struct {
config Config
runner multistep.Runner
}
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, warnings, nil
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("hook", hook)
state.Put("ui", ui)
steps := []multistep.Step{
&StepCreateSSHKey{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("pb_%s", b.config.SnapshotName),
},
new(stepCreateServer),
&communicator.StepConnect{
Config: &b.config.Comm,
Host: communicator.CommHost(b.config.Comm.Host(), "server_ip"),
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
&commonsteps.StepProvision{},
&commonsteps.StepCleanupTempKeys{
Comm: &b.config.Comm,
},
new(stepTakeSnapshot),
}
config := state.Get("config").(*Config)
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{
snapshotData: config.SnapshotName,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
}
return artifact, nil
}

@ -1,38 +0,0 @@
package profitbricks
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("PROFITBRICKS_USERNAME"); v == "" {
t.Fatal("PROFITBRICKS_USERNAME must be set for acceptance tests")
}
if v := os.Getenv("PROFITBRICKS_PASSWORD"); v == "" {
t.Fatal("PROFITBRICKS_PASSWORD must be set for acceptance tests")
}
}
const testBuilderAccBasic = `
{
"builders": [{
"image": "Ubuntu-16.04",
"password": "password",
"username": "username",
"snapshot_name": "packer",
"type": "profitbricks"
}]
}
`

@ -1,57 +0,0 @@
package profitbricks
import (
"fmt"
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"image": "Ubuntu-16.04",
"password": "password",
"username": "username",
"snapshot_name": "packer",
"type": "profitbricks",
}
}
func TestImplementsBuilder(t *testing.T) {
var raw interface{}
raw = &Builder{}
if _, ok := raw.(packersdk.Builder); !ok {
t.Fatalf("Builder should be a builder")
}
}
func TestBuilder_Prepare_BadType(t *testing.T) {
b := &Builder{}
c := map[string]interface{}{
"api_key": []string{},
}
_, warns, err := b.Prepare(c)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
fmt.Println(err)
fmt.Println(warns)
t.Fatalf("prepare should fail")
}
}
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
config["i_should_not_be_valid"] = true
_, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}

@ -1,128 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package profitbricks
import (
"errors"
"os"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
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/mitchellh/mapstructure"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
PBUsername string `mapstructure:"username"`
PBPassword string `mapstructure:"password"`
PBUrl string `mapstructure:"url"`
Region string `mapstructure:"location"`
Image string `mapstructure:"image"`
SSHKey string
SnapshotName string `mapstructure:"snapshot_name"`
DiskSize int `mapstructure:"disk_size"`
DiskType string `mapstructure:"disk_type"`
Cores int `mapstructure:"cores"`
Ram int `mapstructure:"ram"`
Retries int `mapstructure:"retries"`
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",
},
},
}, raws...)
if err != nil {
return nil, err
}
var errs *packersdk.MultiError
if c.Comm.SSHPassword == "" && c.Comm.SSHPrivateKeyFile == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("Either ssh private key path or ssh password must be set."))
}
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.PBUsername == "" {
c.PBUsername = os.Getenv("PROFITBRICKS_USERNAME")
}
if c.PBPassword == "" {
c.PBPassword = os.Getenv("PROFITBRICKS_PASSWORD")
}
if c.PBUrl == "" {
c.PBUrl = "https://api.profitbricks.com/cloudapi/v4"
}
if c.Cores == 0 {
c.Cores = 4
}
if c.Ram == 0 {
c.Ram = 2048
}
if c.DiskSize == 0 {
c.DiskSize = 50
}
if c.Region == "" {
c.Region = "us/las"
}
if c.DiskType == "" {
c.DiskType = "HDD"
}
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packersdk.MultiErrorAppend(errs, es...)
}
if c.Image == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("ProfitBricks 'image' is required"))
}
if c.PBUsername == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("ProfitBricks username is required"))
}
if c.PBPassword == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("ProfitBricks password is required"))
}
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
packersdk.LogSecretFilter.Set(c.PBUsername)
return nil, nil
}

@ -1,167 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package profitbricks
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"`
PBUsername *string `mapstructure:"username" cty:"username" hcl:"username"`
PBPassword *string `mapstructure:"password" cty:"password" hcl:"password"`
PBUrl *string `mapstructure:"url" cty:"url" hcl:"url"`
Region *string `mapstructure:"location" cty:"location" hcl:"location"`
Image *string `mapstructure:"image" cty:"image" hcl:"image"`
SSHKey *string `cty:"ssh_key" hcl:"ssh_key"`
SnapshotName *string `mapstructure:"snapshot_name" cty:"snapshot_name" hcl:"snapshot_name"`
DiskSize *int `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
DiskType *string `mapstructure:"disk_type" cty:"disk_type" hcl:"disk_type"`
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
Ram *int `mapstructure:"ram" cty:"ram" hcl:"ram"`
Retries *int `mapstructure:"retries" cty:"retries" hcl:"retries"`
}
// 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},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"url": &hcldec.AttrSpec{Name: "url", Type: cty.String, Required: false},
"location": &hcldec.AttrSpec{Name: "location", Type: cty.String, Required: false},
"image": &hcldec.AttrSpec{Name: "image", Type: cty.String, Required: false},
"ssh_key": &hcldec.AttrSpec{Name: "ssh_key", Type: cty.String, Required: false},
"snapshot_name": &hcldec.AttrSpec{Name: "snapshot_name", Type: cty.String, Required: false},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false},
"cores": &hcldec.AttrSpec{Name: "cores", Type: cty.Number, Required: false},
"ram": &hcldec.AttrSpec{Name: "ram", Type: cty.Number, Required: false},
"retries": &hcldec.AttrSpec{Name: "retries", Type: cty.Number, Required: false},
}
return s
}

@ -1,279 +0,0 @@
package profitbricks
import (
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/profitbricks/profitbricks-sdk-go"
)
type stepCreateServer struct{}
func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
c := state.Get("config").(*Config)
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
profitbricks.SetDepth("5")
if c.Comm.SSHPublicKey != nil {
c.SSHKey = string(c.Comm.SSHPublicKey)
} else {
ui.Error("No ssh private key set; ssh authentication won't be possible. Please specify your private key in the ssh_private_key_file configuration key.")
return multistep.ActionHalt
}
ui.Say("Creating Virtual Data Center...")
img := s.getImageId(c.Image, c)
alias := ""
if img == "" {
alias = s.getImageAlias(c.Image, c.Region, ui)
}
datacenter := profitbricks.Datacenter{
Properties: profitbricks.DatacenterProperties{
Name: c.SnapshotName,
Location: c.Region,
},
}
server := profitbricks.Server{
Properties: profitbricks.ServerProperties{
Name: c.SnapshotName,
Ram: c.Ram,
Cores: c.Cores,
},
Entities: &profitbricks.ServerEntities{
Volumes: &profitbricks.Volumes{
Items: []profitbricks.Volume{
{
Properties: profitbricks.VolumeProperties{
Type: c.DiskType,
Size: c.DiskSize,
Name: c.SnapshotName,
ImageAlias: alias,
Image: img,
},
},
},
},
},
}
if c.SSHKey != "" {
server.Entities.Volumes.Items[0].Properties.SshKeys = []string{c.SSHKey}
}
if c.Comm.SSHPassword != "" {
server.Entities.Volumes.Items[0].Properties.ImagePassword = c.Comm.SSHPassword
}
datacenter = profitbricks.CompositeCreateDatacenter(datacenter)
if datacenter.StatusCode > 299 {
if datacenter.StatusCode > 299 {
var restError RestError
err := json.Unmarshal([]byte(datacenter.Response), &restError)
if err != nil {
ui.Error(fmt.Sprintf("Error decoding json response: %s", err.Error()))
return multistep.ActionHalt
}
if len(restError.Messages) > 0 {
ui.Error(restError.Messages[0].Message)
} else {
ui.Error(datacenter.Response)
}
return multistep.ActionHalt
}
}
err := s.waitTillProvisioned(datacenter.Headers.Get("Location"), *c)
if err != nil {
ui.Error(fmt.Sprintf("Error occurred while creating a datacenter %s", err.Error()))
return multistep.ActionHalt
}
state.Put("datacenter_id", datacenter.Id)
server = profitbricks.CreateServer(datacenter.Id, server)
if server.StatusCode > 299 {
ui.Error(fmt.Sprintf("Error occurred %s", parseErrorMessage(server.Response)))
return multistep.ActionHalt
}
err = s.waitTillProvisioned(server.Headers.Get("Location"), *c)
if err != nil {
ui.Error(fmt.Sprintf("Error occurred while creating a server %s", err.Error()))
return multistep.ActionHalt
}
lan := profitbricks.CreateLan(datacenter.Id, profitbricks.CreateLanRequest{
Properties: profitbricks.CreateLanProperties{
Public: true,
Name: c.SnapshotName,
},
})
if lan.StatusCode > 299 {
ui.Error(fmt.Sprintf("Error occurred %s", parseErrorMessage(lan.Response)))
return multistep.ActionHalt
}
err = s.waitTillProvisioned(lan.Headers.Get("Location"), *c)
if err != nil {
ui.Error(fmt.Sprintf("Error occurred while creating a LAN %s", err.Error()))
return multistep.ActionHalt
}
lanId, _ := strconv.Atoi(lan.Id)
nic := profitbricks.CreateNic(datacenter.Id, server.Id, profitbricks.Nic{
Properties: &profitbricks.NicProperties{
Name: c.SnapshotName,
Lan: lanId,
Dhcp: true,
},
})
if lan.StatusCode > 299 {
ui.Error(fmt.Sprintf("Error occurred %s", parseErrorMessage(nic.Response)))
return multistep.ActionHalt
}
err = s.waitTillProvisioned(nic.Headers.Get("Location"), *c)
if err != nil {
ui.Error(fmt.Sprintf("Error occurred while creating a NIC %s", err.Error()))
return multistep.ActionHalt
}
state.Put("volume_id", server.Entities.Volumes.Items[0].Id)
server = profitbricks.GetServer(datacenter.Id, 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", server.Id)
state.Put("server_ip", server.Entities.Nics.Items[0].Properties.Ips[0])
return multistep.ActionContinue
}
func (s *stepCreateServer) Cleanup(state multistep.StateBag) {
c := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Removing Virtual Data Center...")
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
if dcId, ok := state.GetOk("datacenter_id"); ok {
resp := profitbricks.DeleteDatacenter(dcId.(string))
if err := s.checkForErrors(resp); err != nil {
ui.Error(fmt.Sprintf(
"Error deleting Virtual Data Center. Please destroy it manually: %s", err))
}
if err := s.waitTillProvisioned(resp.Headers.Get("Location"), *c); err != nil {
ui.Error(fmt.Sprintf(
"Error deleting Virtual Data Center. Please destroy it manually: %s", err))
}
}
}
func (d *stepCreateServer) waitTillProvisioned(path string, config Config) error {
d.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
waitCount := 120
if config.Retries > 0 {
waitCount = config.Retries
}
for i := 0; i < waitCount; i++ {
request := profitbricks.GetRequestStatus(path)
if request.Metadata.Status == "DONE" {
return nil
}
if request.Metadata.Status == "FAILED" {
return errors.New(request.Metadata.Message)
}
time.Sleep(1 * time.Second)
i++
}
return nil
}
func (d *stepCreateServer) setPB(username string, password string, url string) {
profitbricks.SetAuth(username, password)
profitbricks.SetEndpoint(url)
}
func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error {
if instance.StatusCode > 299 {
return fmt.Errorf("Error occurred %s", string(instance.Body))
}
return nil
}
type RestError struct {
HttpStatus int `json:"httpStatus,omitempty"`
Messages []Message `json:"messages,omitempty"`
}
type Message struct {
ErrorCode string `json:"errorCode,omitempty"`
Message string `json:"message,omitempty"`
}
func (d *stepCreateServer) getImageId(imageName string, c *Config) string {
d.setPB(c.PBUsername, c.PBPassword, c.PBUrl)
images := profitbricks.ListImages()
for i := 0; i < len(images.Items); i++ {
imgName := ""
if images.Items[i].Properties.Name != "" {
imgName = images.Items[i].Properties.Name
}
diskType := c.DiskType
if c.DiskType == "SSD" {
diskType = "HDD"
}
if imgName != "" && strings.Contains(strings.ToLower(imgName), strings.ToLower(imageName)) && images.Items[i].Properties.ImageType == diskType && images.Items[i].Properties.Location == c.Region && images.Items[i].Properties.Public == true {
return images.Items[i].Id
}
}
return ""
}
func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui packersdk.Ui) string {
if imageAlias == "" {
return ""
}
locations := profitbricks.GetLocation(location)
if len(locations.Properties.ImageAliases) > 0 {
for _, i := range locations.Properties.ImageAliases {
alias := ""
if i != "" {
alias = i
}
if alias != "" && strings.EqualFold(alias, imageAlias) {
return alias
}
}
}
return ""
}
func parseErrorMessage(raw string) (toreturn string) {
var tmp map[string]interface{}
if json.Unmarshal([]byte(raw), &tmp) != nil {
return ""
}
for _, v := range tmp["messages"].([]interface{}) {
for index, i := range v.(map[string]interface{}) {
if index == "message" {
toreturn = toreturn + i.(string) + "\n"
}
}
}
return toreturn
}

@ -1,61 +0,0 @@
package profitbricks
import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"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)
if c.Comm.SSHPrivateKeyFile != "" {
pemBytes, err := c.Comm.ReadSSHPrivateKeyFile()
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
block, _ := pem.Decode(pemBytes)
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
state.Put("error", err.Error())
ui.Error(err.Error())
return multistep.ActionHalt
}
priv_blk := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: x509.MarshalPKCS1PrivateKey(priv),
}
pub, err := ssh.NewPublicKey(&priv.PublicKey)
if err != nil {
err := fmt.Errorf("Error creating temporary ssh key: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
c.Comm.SSHPrivateKey = pem.EncodeToMemory(&priv_blk)
c.Comm.SSHPublicKey = ssh.MarshalAuthorizedKey(pub)
}
return multistep.ActionContinue
}
func (s *StepCreateSSHKey) Cleanup(state multistep.StateBag) {}

@ -1,190 +0,0 @@
package profitbricks
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/profitbricks/profitbricks-sdk-go"
)
type stepTakeSnapshot struct{}
func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
c := state.Get("config").(*Config)
ui.Say("Creating ProfitBricks snapshot...")
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
dcId := state.Get("datacenter_id").(string)
volumeId := state.Get("volume_id").(string)
serverId := state.Get("instance_id").(string)
comm, _ := state.Get("communicator").(packersdk.Communicator)
if comm == nil {
ui.Error("no communicator found")
return multistep.ActionHalt
}
/* sync fs changes from the provisioning step */
os, err := s.getOs(dcId, serverId)
if err != nil {
ui.Error(fmt.Sprintf("an error occurred while getting the server os: %s", err.Error()))
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Server OS is %s", os))
switch strings.ToLower(os) {
case "linux":
ui.Say("syncing file system changes")
if err := s.syncFs(ctx, comm); err != nil {
ui.Error(fmt.Sprintf("error syncing fs changes: %s", err.Error()))
return multistep.ActionHalt
}
}
snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "")
state.Put("snapshotname", c.SnapshotName)
if snapshot.StatusCode > 299 {
var restError RestError
if err := json.Unmarshal([]byte(snapshot.Response), &restError); err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(restError.Messages) > 0 {
ui.Error(restError.Messages[0].Message)
} else {
ui.Error(snapshot.Response)
}
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Creating a snapshot for %s/volumes/%s", dcId, volumeId))
err = s.waitForRequest(snapshot.Headers.Get("Location"), *c, ui)
if err != nil {
ui.Error(fmt.Sprintf("An error occurred while waiting for the request to be done: %s", err.Error()))
return multistep.ActionHalt
}
err = s.waitTillSnapshotAvailable(snapshot.Id, *c, ui)
if err != nil {
ui.Error(fmt.Sprintf("An error occurred while waiting for the snapshot to be created: %s", err.Error()))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepTakeSnapshot) Cleanup(_ multistep.StateBag) {
}
func (s *stepTakeSnapshot) waitForRequest(path string, config Config, ui packersdk.Ui) error {
ui.Say(fmt.Sprintf("Watching request %s", path))
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
waitCount := 50
var waitInterval = 10 * time.Second
if config.Retries > 0 {
waitCount = config.Retries
}
done := false
for i := 0; i < waitCount; i++ {
request := profitbricks.GetRequestStatus(path)
ui.Say(fmt.Sprintf("request status = %s", request.Metadata.Status))
if request.Metadata.Status == "DONE" {
done = true
break
}
if request.Metadata.Status == "FAILED" {
return fmt.Errorf("Request failed: %s", request.Response)
}
time.Sleep(waitInterval)
i++
}
if done == false {
return fmt.Errorf("request not fulfilled after waiting %d seconds",
int64(waitCount)*int64(waitInterval)/int64(time.Second))
}
return nil
}
func (s *stepTakeSnapshot) waitTillSnapshotAvailable(id string, config Config, ui packersdk.Ui) error {
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
waitCount := 50
var waitInterval = 10 * time.Second
if config.Retries > 0 {
waitCount = config.Retries
}
done := false
ui.Say(fmt.Sprintf("waiting for snapshot %s to become available", id))
for i := 0; i < waitCount; i++ {
snap := profitbricks.GetSnapshot(id)
ui.Say(fmt.Sprintf("snapshot status = %s", snap.Metadata.State))
if snap.StatusCode != 200 {
return fmt.Errorf("%s", snap.Response)
}
if snap.Metadata.State == "AVAILABLE" {
done = true
break
}
time.Sleep(waitInterval)
i++
ui.Say(fmt.Sprintf("... still waiting, %d seconds have passed", int64(waitInterval)*int64(i)))
}
if done == false {
return fmt.Errorf("snapshot not created after waiting %d seconds",
int64(waitCount)*int64(waitInterval)/int64(time.Second))
}
ui.Say("snapshot created")
return nil
}
func (s *stepTakeSnapshot) syncFs(ctx context.Context, comm packersdk.Communicator) error {
cmd := &packersdk.RemoteCmd{
Command: "sync",
}
if err := comm.Start(ctx, cmd); err != nil {
return err
}
if cmd.Wait() != 0 {
return fmt.Errorf("sync command exited with code %d", cmd.ExitStatus())
}
return nil
}
func (s *stepTakeSnapshot) getOs(dcId string, serverId string) (string, error) {
server := profitbricks.GetServer(dcId, serverId)
if server.StatusCode != 200 {
return "", errors.New(server.Response)
}
if server.Properties.BootVolume == nil {
return "", errors.New("no boot volume found on server")
}
volumeId := server.Properties.BootVolume.Id
volume := profitbricks.GetVolume(dcId, volumeId)
if volume.StatusCode != 200 {
return "", errors.New(volume.Response)
}
return volume.Properties.LicenceType, nil
}
func (s *stepTakeSnapshot) setPB(username string, password string, url string) {
profitbricks.SetAuth(username, password)
profitbricks.SetEndpoint(url)
}

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

@ -15,7 +15,6 @@ import (
filebuilder "github.com/hashicorp/packer/builder/file"
nullbuilder "github.com/hashicorp/packer/builder/null"
profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks"
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum"
compresspostprocessor "github.com/hashicorp/packer/post-processor/compress"
@ -38,9 +37,8 @@ type PluginCommand struct {
}
var Builders = map[string]packersdk.Builder{
"file": new(filebuilder.Builder),
"null": new(nullbuilder.Builder),
"profitbricks": new(profitbricksbuilder.Builder),
"file": new(filebuilder.Builder),
"null": new(nullbuilder.Builder),
}
var Provisioners = map[string]packersdk.Provisioner{

@ -56,6 +56,7 @@ import (
oscchrootbuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/chroot"
parallelsisobuilder "github.com/hashicorp/packer-plugin-parallels/builder/parallels/iso"
parallelspvmbuilder "github.com/hashicorp/packer-plugin-parallels/builder/parallels/pvm"
profitbricksbuilder "github.com/hashicorp/packer-plugin-profitbricks/builder/profitbricks"
proxmoxclone "github.com/hashicorp/packer-plugin-proxmox/builder/proxmox/clone"
proxmoxiso "github.com/hashicorp/packer-plugin-proxmox/builder/proxmox/iso"
puppetmasterlessprovisioner "github.com/hashicorp/packer-plugin-puppet/provisioner/puppet-masterless"
@ -119,6 +120,7 @@ var VendoredBuilders = map[string]packersdk.Builder{
"openstack": new(openstackbuilder.Builder),
"oracle-classic": new(oracleclassicbuilder.Builder),
"oracle-oci": new(oracleocibuilder.Builder),
"profitbricks": new(profitbricksbuilder.Builder),
"proxmox": new(proxmoxiso.Builder),
"proxmox-iso": new(proxmoxiso.Builder),
"proxmox-clone": new(proxmoxclone.Builder),

@ -40,6 +40,7 @@ require (
github.com/hashicorp/packer-plugin-oracle v0.0.3
github.com/hashicorp/packer-plugin-outscale v0.0.1
github.com/hashicorp/packer-plugin-parallels v0.0.1
github.com/hashicorp/packer-plugin-profitbricks v1.0.1
github.com/hashicorp/packer-plugin-proxmox v0.0.2
github.com/hashicorp/packer-plugin-puppet v0.0.2
github.com/hashicorp/packer-plugin-qemu v0.0.1
@ -63,7 +64,6 @@ require (
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
github.com/pierrec/lz4 v2.0.5+incompatible
github.com/posener/complete v1.2.3
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
github.com/shirou/gopsutil v3.21.1+incompatible
github.com/stretchr/testify v1.7.0
github.com/ulikunitz/xz v0.5.8

@ -587,6 +587,8 @@ github.com/hashicorp/packer-plugin-outscale v0.0.1 h1:BrL8hKypNYrvP3NR+d+xX03SZK
github.com/hashicorp/packer-plugin-outscale v0.0.1/go.mod h1:6jEWfJO7TgAbaL3e+St1bN5PoIC/MmDIsYqNUzAHF1w=
github.com/hashicorp/packer-plugin-parallels v0.0.1 h1:fcaaiGWdU1+X4IGadXdUhJ2si1ZA3apXS9tMNJXln2A=
github.com/hashicorp/packer-plugin-parallels v0.0.1/go.mod h1:FGNtZ7XFBr3IYuj7uvJtSaNnyhAwe457zP464m06+20=
github.com/hashicorp/packer-plugin-profitbricks v1.0.1 h1:yFLUoDvm/zwzqj3J4a4pe49JpeDDGx08U9B38FXEyNk=
github.com/hashicorp/packer-plugin-profitbricks v1.0.1/go.mod h1:qtDQ7sbc0hMXWuxt0gweUwsVyVIazRLyNCHPO19CPU0=
github.com/hashicorp/packer-plugin-proxmox v0.0.2 h1:x6QW7PeKh+IJymPEt3QdpBhSRi5vqXb8qTWv7rMLuns=
github.com/hashicorp/packer-plugin-proxmox v0.0.2/go.mod h1:3URutEWX1yy10qcHNJncS4OMpZknA1FyvlrfL+5usYk=
github.com/hashicorp/packer-plugin-puppet v0.0.2 h1:ER0znnTwRAlWqnTg3JLo//Cgj3wy+LjTVzrzcYFbT7k=

@ -28,6 +28,7 @@ declare -a plugins=(
"oracle"
"outscale"
"parallels"
"profitbricks"
"proxmox"
"puppet"
"qemu"

@ -1,86 +0,0 @@
---
description: The ProfitBricks builder is able to create images for ProfitBricks cloud.
page_title: ProfitBricks - Builders
---
# ProfitBricks Builder
Type: `profitbricks`
Artifact BuilderId: `packer.profitbricks`
The ProfitBricks Builder is able to create virtual machines for
[ProfitBricks](https://www.profitbricks.com).
## 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. In addition to the options defined there, a private key file
can also be supplied to override the typical auto-generated key:
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
### Required
- `image` (string) - ProfitBricks volume image. Only Linux public images are
supported. To obtain full list of available images you can use
[ProfitBricks CLI](https://github.com/profitbricks/profitbricks-cli#image).
- `password` (string) - ProfitBricks password. This can be specified via
environment variable \`PROFITBRICKS_PASSWORD', if provided. The value
defined in the config has precedence over environemnt variable.
- `username` (string) - ProfitBricks username. This can be specified via
environment variable \`PROFITBRICKS_USERNAME', if provided. The value
defined in the config has precedence over environemnt variable.
### Optional
- `cores` (number) - Amount of CPU cores to use for this build. Defaults to
"4".
- `disk_size` (string) - Amount of disk space for this image in GB. Defaults
to "50"
- `disk_type` (string) - Type of disk to use for this image. Defaults to
"HDD".
- `location` (string) - Defaults to "us/las".
- `ram` (number) - Amount of RAM to use for this image. Defaults to "2048".
- `retries` (string) - Number of retries Packer will make status requests
while waiting for the build to complete. Default value 120 seconds.
- `snapshot_name` (string) - If snapshot name is not provided Packer will
generate it
- `snapshot_password` (string) - Password for the snapshot.
<!-- markdown-link-check-disable -->
- `url` (string) - Endpoint for the ProfitBricks REST API. Default URL
"<https://api.profitbricks.com/rest/v2>"
<!-- markdown-link-check-enable -->
## Example
Here is a basic example:
```json
{
"builders": [
{
"image": "Ubuntu-16.04",
"type": "profitbricks",
"disk_size": "5",
"snapshot_name": "double",
"snapshot_password": "test1234",
"ssh_username": "root",
"timeout": 100
}
]
}
```

@ -687,10 +687,6 @@
"title": "Null",
"path": "builders/null"
},
{
"title": "ProfitBricks",
"path": "builders/profitbricks"
},
{
"title": "Custom",
"path": "builders/custom"

@ -162,6 +162,13 @@
"repo": "hashicorp/packer-plugin-parallels",
"version": "latest"
},
{
"title": "Profitbricks",
"path": "profitbricks",
"repo": "hashicorp/packer-plugin-profitbricks",
"pluginTier": "community",
"version": "latest"
},
{
"title": "Proxmox",
"path": "proxmox",

Loading…
Cancel
Save