Extract jdcloud (#10946)

* delete jdcloud builder dir, revendor, regenerate, add to vendored_plugins

* change website pathing

* Extract linode (#10947)

* started extracting linode

* revendor linode

* clean up vendoring
This commit is contained in:
Megan Marsh 2021-04-21 07:55:41 -07:00 committed by GitHub
parent 469f033c36
commit b5666b84cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 104 additions and 3039 deletions

View File

@ -1,47 +0,0 @@
package jdcloud
import (
"fmt"
"sort"
"strings"
)
type Artifact struct {
ImageId string
RegionID string
// StateData should store data such as GeneratedData
// to be shared with post-processors
StateData map[string]interface{}
}
func (*Artifact) BuilderId() string {
return BUILDER_ID
}
func (*Artifact) Files() []string {
return nil
}
// Plan
// Though this part is supposed to be an array of Image Ids associated
// with its region, but currently only a single image is supported
func (a *Artifact) Id() string {
parts := []string{fmt.Sprintf("%s:%s", a.RegionID, a.ImageId)}
sort.Strings(parts)
return strings.Join(parts, ",")
}
func (a *Artifact) String() string {
return fmt.Sprintf("A VMImage was created: %s", a.ImageId)
}
// Plan
// State and destroy function is abandoned
func (a *Artifact) State(name string) interface{} {
return a.StateData[name]
}
func (a *Artifact) Destroy() error {
return nil
}

View File

@ -1,97 +0,0 @@
package jdcloud
import (
"context"
"fmt"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
PluginType: BUILDER_ID,
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"boot_command",
},
},
}, raws...)
if err != nil {
return nil, nil, fmt.Errorf("[ERROR] Failed in decoding JSON->mapstructure")
}
errs := &packersdk.MultiError{}
errs = packersdk.MultiErrorAppend(errs, b.config.JDCloudCredentialConfig.Prepare(&b.config.ctx)...)
errs = packersdk.MultiErrorAppend(errs, b.config.JDCloudInstanceSpecConfig.Prepare(&b.config.ctx)...)
if errs != nil && len(errs.Errors) != 0 {
return nil, nil, errs
}
packersdk.LogSecretFilter.Set(b.config.AccessKey, b.config.SecretKey)
return nil, nil, nil
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
state := new(multistep.BasicStateBag)
state.Put("hook", hook)
state.Put("ui", ui)
state.Put("config", &b.config)
steps := []multistep.Step{
&stepValidateParameters{
InstanceSpecConfig: &b.config.JDCloudInstanceSpecConfig,
},
&stepConfigCredentials{
InstanceSpecConfig: &b.config.JDCloudInstanceSpecConfig,
},
&stepCreateJDCloudInstance{
InstanceSpecConfig: &b.config.JDCloudInstanceSpecConfig,
CredentialConfig: &b.config.JDCloudCredentialConfig,
},
&communicator.StepConnect{
Config: &b.config.JDCloudInstanceSpecConfig.Comm,
SSHConfig: b.config.JDCloudInstanceSpecConfig.Comm.SSHConfigFunc(),
Host: instanceHost,
},
&commonsteps.StepProvision{},
&stepStopJDCloudInstance{
InstanceSpecConfig: &b.config.JDCloudInstanceSpecConfig,
},
&stepCreateJDCloudImage{
InstanceSpecConfig: &b.config.JDCloudInstanceSpecConfig,
},
}
b.runner = commonsteps.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
b.runner.Run(ctx, state)
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
artifact := &Artifact{
ImageId: b.config.ArtifactId,
RegionID: b.config.RegionId,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
}
return artifact, nil
}

View File

@ -1,435 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package jdcloud
import (
"fmt"
"log"
"strings"
"sync"
"time"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
vm "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/client"
vpc "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/client"
)
const (
FINE = 0
CONNECT_FAILED = "Client.Timeout exceeded"
VM_PENDING = "pending"
VM_RUNNING = "running"
VM_STARTING = "starting"
VM_STOPPING = "stopping"
VM_STOPPED = "stopped"
READY = "ready"
BUILDER_ID = "hashicorp.jdcloud"
)
var (
VmClient *vm.VmClient
VpcClient *vpc.VpcClient
Region string
)
type Config struct {
JDCloudCredentialConfig `mapstructure:",squash"`
JDCloudInstanceSpecConfig `mapstructure:",squash"`
common.PackerConfig `mapstructure:",squash"`
ctx interpolate.Context
}
type Builder struct {
config Config
runner multistep.Runner
}
func Retry(timeout time.Duration, f RetryFunc) error {
// These are used to pull the error out of the function; need a mutex to
// avoid a data race.
var resultErr error
var resultErrMu sync.Mutex
c := &StateChangeConf{
Pending: []string{"retryableerror"},
Target: []string{"success"},
Timeout: timeout,
MinTimeout: 500 * time.Millisecond,
Refresh: func() (interface{}, string, error) {
rerr := f()
resultErrMu.Lock()
defer resultErrMu.Unlock()
if rerr == nil {
resultErr = nil
return 42, "success", nil
}
resultErr = rerr.Err
if rerr.Retryable {
return 42, "retryableerror", nil
}
return nil, "quit", rerr.Err
},
}
_, waitErr := c.WaitForState()
// Need to acquire the lock here to be able to avoid race using resultErr as
// the return value
resultErrMu.Lock()
defer resultErrMu.Unlock()
// resultErr may be nil because the wait timed out and resultErr was never
// set; this is still an error
if resultErr == nil {
return waitErr
}
// resultErr takes precedence over waitErr if both are set because it is
// more likely to be useful
return resultErr
}
// RetryFunc is the function retried until it succeeds.
type RetryFunc func() *RetryError
// RetryError is the required return type of RetryFunc. It forces client code
// to choose whether or not a given error is retryable.
type RetryError struct {
Err error
Retryable bool
}
// RetryableError is a helper to create a RetryError that's retryable from a
// given error.
func RetryableError(err error) *RetryError {
if err == nil {
return nil
}
return &RetryError{Err: err, Retryable: true}
}
// NonRetryableError is a helper to create a RetryError that's _not_ retryable
// from a given error.
func NonRetryableError(err error) *RetryError {
if err == nil {
return nil
}
return &RetryError{Err: err, Retryable: false}
}
// WaitForState watches an object and waits for it to achieve the state
// specified in the configuration using the specified Refresh() func,
// waiting the number of seconds specified in the timeout configuration.
//
// If the Refresh function returns an error, exit immediately with that error.
//
// If the Refresh function returns a state other than the Target state or one
// listed in Pending, return immediately with an error.
//
// If the Timeout is exceeded before reaching the Target state, return an
// error.
//
// Otherwise, the result is the result of the first call to the Refresh function to
// reach the target state.
func (conf *StateChangeConf) WaitForState() (interface{}, error) {
log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target)
notfoundTick := 0
targetOccurence := 0
// Set a default for times to check for not found
if conf.NotFoundChecks == 0 {
conf.NotFoundChecks = 20
}
if conf.ContinuousTargetOccurence == 0 {
conf.ContinuousTargetOccurence = 1
}
type Result struct {
Result interface{}
State string
Error error
Done bool
}
// Read every result from the refresh loop, waiting for a positive result.Done.
resCh := make(chan Result, 1)
// cancellation channel for the refresh loop
cancelCh := make(chan struct{})
result := Result{}
go func() {
defer close(resCh)
time.Sleep(conf.Delay)
// start with 0 delay for the first loop
var wait time.Duration
for {
// store the last result
resCh <- result
// wait and watch for cancellation
select {
case <-cancelCh:
return
case <-time.After(wait):
// first round had no wait
if wait == 0 {
wait = 100 * time.Millisecond
}
}
res, currentState, err := conf.Refresh()
result = Result{
Result: res,
State: currentState,
Error: err,
}
if err != nil {
resCh <- result
return
}
// If we're waiting for the absence of a thing, then return
if res == nil && len(conf.Target) == 0 {
targetOccurence++
if conf.ContinuousTargetOccurence == targetOccurence {
result.Done = true
resCh <- result
return
}
continue
}
if res == nil {
// If we didn't find the resource, check if we have been
// not finding it for awhile, and if so, report an error.
notfoundTick++
if notfoundTick > conf.NotFoundChecks {
result.Error = &NotFoundError{
LastError: err,
Retries: notfoundTick,
}
resCh <- result
return
}
} else {
// Reset the counter for when a resource isn't found
notfoundTick = 0
found := false
for _, allowed := range conf.Target {
if currentState == allowed {
found = true
targetOccurence++
if conf.ContinuousTargetOccurence == targetOccurence {
result.Done = true
resCh <- result
return
}
continue
}
}
for _, allowed := range conf.Pending {
if currentState == allowed {
found = true
targetOccurence = 0
break
}
}
if !found && len(conf.Pending) > 0 {
result.Error = &UnexpectedStateError{
LastError: err,
State: result.State,
ExpectedState: conf.Target,
}
resCh <- result
return
}
}
// Wait between refreshes using exponential backoff, except when
// waiting for the target state to reoccur.
if targetOccurence == 0 {
wait *= 2
}
// If a poll interval has been specified, choose that interval.
// Otherwise bound the default value.
if conf.PollInterval > 0 && conf.PollInterval < 180*time.Second {
wait = conf.PollInterval
} else {
if wait < conf.MinTimeout {
wait = conf.MinTimeout
} else if wait > 10*time.Second {
wait = 10 * time.Second
}
}
log.Printf("[TRACE] Waiting %s before next try", wait)
}
}()
// store the last value result from the refresh loop
lastResult := Result{}
timeout := time.After(conf.Timeout)
for {
select {
case r, ok := <-resCh:
// channel closed, so return the last result
if !ok {
return lastResult.Result, lastResult.Error
}
// we reached the intended state
if r.Done {
return r.Result, r.Error
}
// still waiting, store the last result
lastResult = r
case <-timeout:
log.Printf("[WARN] WaitForState timeout after %s", conf.Timeout)
log.Printf("[WARN] WaitForState starting %s refresh grace period", 30*time.Second)
// cancel the goroutine and start our grace period timer
close(cancelCh)
timeout := time.After(30 * time.Second)
// we need a for loop and a label to break on, because we may have
// an extra response value to read, but still want to wait for the
// channel to close.
forSelect:
for {
select {
case r, ok := <-resCh:
if r.Done {
// the last refresh loop reached the desired state
return r.Result, r.Error
}
if !ok {
// the goroutine returned
break forSelect
}
// target state not reached, save the result for the
// TimeoutError and wait for the channel to close
lastResult = r
case <-timeout:
log.Println("[ERROR] WaitForState exceeded refresh grace period")
break forSelect
}
}
return nil, &TimeoutError{
LastError: lastResult.Error,
LastState: lastResult.State,
Timeout: conf.Timeout,
ExpectedState: conf.Target,
}
}
}
}
type StateChangeConf struct {
Delay time.Duration // Wait this time before starting checks
Pending []string // States that are "allowed" and will continue trying
Refresh StateRefreshFunc // Refreshes the current state
Target []string // Target state
Timeout time.Duration // The amount of time to wait before timeout
MinTimeout time.Duration // Smallest time to wait before refreshes
PollInterval time.Duration // Override MinTimeout/backoff and only poll this often
NotFoundChecks int // Number of times to allow not found
// This is to work around inconsistent APIs
ContinuousTargetOccurence int // Number of times the Target state has to occur continuously
}
type NotFoundError struct {
LastError error
LastRequest interface{}
LastResponse interface{}
Message string
Retries int
}
func (e *NotFoundError) Error() string {
if e.Message != "" {
return e.Message
}
if e.Retries > 0 {
return fmt.Sprintf("couldn't find resource (%d retries)", e.Retries)
}
return "couldn't find resource"
}
// UnexpectedStateError is returned when Refresh returns a state that's neither in Target nor Pending
type UnexpectedStateError struct {
LastError error
State string
ExpectedState []string
}
func (e *UnexpectedStateError) Error() string {
return fmt.Sprintf(
"unexpected state '%s', wanted target '%s'. last error: %s",
e.State,
strings.Join(e.ExpectedState, ", "),
e.LastError,
)
}
// TimeoutError is returned when WaitForState times out
type TimeoutError struct {
LastError error
LastState string
Timeout time.Duration
ExpectedState []string
}
func (e *TimeoutError) Error() string {
expectedState := "resource to be gone"
if len(e.ExpectedState) > 0 {
expectedState = fmt.Sprintf("state to become '%s'", strings.Join(e.ExpectedState, ", "))
}
extraInfo := make([]string, 0)
if e.LastState != "" {
extraInfo = append(extraInfo, fmt.Sprintf("last state: '%s'", e.LastState))
}
if e.Timeout > 0 {
extraInfo = append(extraInfo, fmt.Sprintf("timeout: %s", e.Timeout.String()))
}
suffix := ""
if len(extraInfo) > 0 {
suffix = fmt.Sprintf(" (%s)", strings.Join(extraInfo, ", "))
}
if e.LastError != nil {
return fmt.Sprintf("timeout while waiting for %s%s: %s",
expectedState, suffix, e.LastError)
}
return fmt.Sprintf("timeout while waiting for %s%s",
expectedState, suffix)
}
type StateRefreshFunc func() (result interface{}, state string, err error)

View File

@ -1,169 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package jdcloud
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 {
AccessKey *string `mapstructure:"access_key" cty:"access_key" hcl:"access_key"`
SecretKey *string `mapstructure:"secret_key" cty:"secret_key" hcl:"secret_key"`
RegionId *string `mapstructure:"region_id" cty:"region_id" hcl:"region_id"`
Az *string `mapstructure:"az" cty:"az" hcl:"az"`
ImageId *string `mapstructure:"image_id" cty:"image_id" hcl:"image_id"`
InstanceName *string `mapstructure:"instance_name" cty:"instance_name" hcl:"instance_name"`
InstanceType *string `mapstructure:"instance_type" cty:"instance_type" hcl:"instance_type"`
ImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
SubnetId *string `mapstructure:"subnet_id" cty:"subnet_id" hcl:"subnet_id"`
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"`
InstanceId *string `cty:"instance_id" hcl:"instance_id"`
ArtifactId *string `cty:"artifact_id" hcl:"artifact_id"`
PublicIpAddress *string `cty:"public_ip_address" hcl:"public_ip_address"`
PublicIpId *string `cty:"public_ip_id" hcl:"public_ip_id"`
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"`
}
// 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{
"access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false},
"secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false},
"region_id": &hcldec.AttrSpec{Name: "region_id", Type: cty.String, Required: false},
"az": &hcldec.AttrSpec{Name: "az", Type: cty.String, Required: false},
"image_id": &hcldec.AttrSpec{Name: "image_id", Type: cty.String, Required: false},
"instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false},
"instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false},
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: 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},
"instance_id": &hcldec.AttrSpec{Name: "instance_id", Type: cty.String, Required: false},
"artifact_id": &hcldec.AttrSpec{Name: "artifact_id", Type: cty.String, Required: false},
"public_ip_address": &hcldec.AttrSpec{Name: "public_ip_address", Type: cty.String, Required: false},
"public_ip_id": &hcldec.AttrSpec{Name: "public_ip_id", Type: cty.String, Required: false},
"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},
}
return s
}

View File

@ -1,87 +0,0 @@
package jdcloud
import (
"fmt"
"os"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/jdcloud-api/jdcloud-sdk-go/core"
vm "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/client"
vpc "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/client"
)
type JDCloudCredentialConfig struct {
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
RegionId string `mapstructure:"region_id"`
Az string `mapstructure:"az"`
}
func (jd *JDCloudCredentialConfig) Prepare(ctx *interpolate.Context) []error {
errorArray := []error{}
if jd == nil {
return append(errorArray, fmt.Errorf("[PRE-FLIGHT] Empty JDCloudCredentialConfig detected"))
}
if err := jd.ValidateKeyPair(); err != nil {
errorArray = append(errorArray, err)
}
if err := jd.validateRegion(); err != nil {
errorArray = append(errorArray, err)
}
if err := jd.validateAz(); err != nil {
errorArray = append(errorArray, err)
}
if len(errorArray) != 0 {
return errorArray
}
credential := core.NewCredentials(jd.AccessKey, jd.SecretKey)
VmClient = vm.NewVmClient(credential)
VpcClient = vpc.NewVpcClient(credential)
Region = jd.RegionId
return nil
}
func (jd *JDCloudCredentialConfig) ValidateKeyPair() error {
if jd.AccessKey == "" {
jd.AccessKey = os.Getenv("JDCLOUD_ACCESS_KEY")
}
if jd.SecretKey == "" {
jd.SecretKey = os.Getenv("JDCLOUD_SECRET_KEY")
}
if jd.AccessKey == "" || jd.SecretKey == "" {
return fmt.Errorf("[PRE-FLIGHT] We can't find your key pairs," +
"write them here {access_key=xxx , secret_key=xxx} " +
"or export them as env-variable, {export JDCLOUD_ACCESS_KEY=xxx, export JDCLOUD_SECRET_KEY=xxx} ")
}
return nil
}
func (jd *JDCloudCredentialConfig) validateRegion() error {
regionArray := []string{"cn-north-1", "cn-south-1", "cn-east-1", "cn-east-2"}
for _, item := range regionArray {
if item == jd.RegionId {
return nil
}
}
return fmt.Errorf("[PRE-FLIGHT] Invalid RegionId:%s. "+
"Legit RegionId are: {cn-north-1, cn-south-1, cn-east-1, cn-east-2}", jd.RegionId)
}
func (jd *JDCloudCredentialConfig) validateAz() error {
if len(jd.Az) == 0 {
return fmt.Errorf("[PRE-FLIGHT] az info missing")
}
return nil
}

View File

@ -1,34 +0,0 @@
package jdcloud
import (
"testing"
)
func TestJDCloudCredentialConfig_Prepare(t *testing.T) {
creds := &JDCloudCredentialConfig{}
if err := creds.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when there's nothing set")
}
creds.AccessKey = "abc"
if err := creds.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when theres no Secret key")
}
creds.SecretKey = "123"
if err := creds.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when theres no Az and region")
}
creds.RegionId = "cn-west-1"
creds.Az = "cn-north-1c"
if err := creds.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when region_id illegal")
}
creds.RegionId = "cn-north-1"
if err := creds.Prepare(nil); err != nil {
t.Fatalf("Test shouldn't fail...")
}
}

View File

@ -1,34 +0,0 @@
{
"builders": [
{
"type": "jdcloud",
"image_id": "<your-base-image-id>",
"access_key": "<your-access-key>",
"secret_key": "<your-secret-key>",
"region_id": "cn-north-1",
"az": "cn-north-1c",
"instance_name": "created_by_packer",
"instance_type": "g.n2.medium",
"ssh_private_key_file":"/Path/to/your/.ssh/id_rsa",
"ssh_keypair_name":"packer_keys",
"image_name": "created_by_packer",
"subnet_id": "<your-subnet>",
"communicator":"ssh",
"ssh_username": "root",
"ssh_timeout" : "60s"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 3",
"echo 123",
"pwd"
]
}
]
}

View File

@ -1,31 +0,0 @@
{
"builders": [
{
"type": "jdcloud",
"image_id": "<your-image-id>",
"access_key": "<your-access-key>",
"secret_key": "<your-secret-key>",
"region_id": "cn-north-1",
"az": "cn-north-1c",
"instance_name": "created_by_packer",
"instance_type": "g.n2.medium",
"image_name": "packerImage",
"temporary_key_pair_name": "whatever_new_key_name",
"subnet_id": "<your-subnet>",
"communicator":"ssh",
"ssh_username": "root",
"ssh_timeout" : "60s"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 3",
"echo 123",
"pwd"
]
}
]
}

View File

@ -1,32 +0,0 @@
{
"builders": [
{
"type": "jdcloud",
"image_id": "img-h8ly274zg9",
"access_key": "<Your access_key>",
"secret_key": "<Your secret_key>",
"region_id": "cn-north-1",
"az": "cn-north-1c",
"instance_name": "created_by_packer",
"instance_type": "g.n2.medium",
"ssh_password":"/Users/mac/.ssh/id_rsa",
"ssh_keypair_name":"created_by_xiaohan",
"image_name": "packerImage",
"subnet_id": "subnet-jo6e38sdli",
"communicator":"ssh",
"ssh_username": "root",
"ssh_timeout" : "60s"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 3",
"echo 123",
"pwd"
]
}
]
}

View File

@ -1,34 +0,0 @@
{
"builders": [
{
"type": "jdcloud",
"image_id": "img-h8ly274zg9",
"access_key": "<Your access_key>",
"secret_key": "<Your secret_key>",
"region_id": "cn-north-1",
"az": "cn-north-1c",
"instance_name": "created_by_packer",
"instance_type": "g.n2.medium",
"ssh_password":"/Users/mac/.ssh/id_rsa",
"ssh_keypair_name":"created_by_xiaohan",
"image_name": "packerImage",
"communicator":"ssh",
"ssh_username": "root",
"ssh_timeout" : "60s"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"sleep 3",
"echo 123",
"pwd"
]
}
]
}

View File

@ -1,54 +0,0 @@
package jdcloud
import (
"fmt"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type JDCloudInstanceSpecConfig struct {
ImageId string `mapstructure:"image_id"`
InstanceName string `mapstructure:"instance_name"`
InstanceType string `mapstructure:"instance_type"`
ImageName string `mapstructure:"image_name"`
SubnetId string `mapstructure:"subnet_id"`
Comm communicator.Config `mapstructure:",squash"`
InstanceId string
ArtifactId string
PublicIpAddress string
PublicIpId string
}
func (jd *JDCloudInstanceSpecConfig) Prepare(ctx *interpolate.Context) []error {
errs := jd.Comm.Prepare(ctx)
if jd == nil {
return append(errs, fmt.Errorf("[PRE-FLIGHT] Configuration appears to be empty"))
}
if len(jd.ImageId) == 0 {
errs = append(errs, fmt.Errorf("[PRE-FLIGHT] 'image_id' empty"))
}
if len(jd.InstanceName) == 0 {
errs = append(errs, fmt.Errorf("[PRE-FLIGHT] 'instance_name' empty"))
}
if len(jd.InstanceType) == 0 {
errs = append(errs, fmt.Errorf("[PRE-FLIGHT] 'instance-type' empty"))
}
noPassword := len(jd.Comm.SSHPassword) == 0
noKeys := len(jd.Comm.SSHKeyPairName) == 0 && len(jd.Comm.SSHPrivateKeyFile) == 0
noTempKey := len(jd.Comm.SSHTemporaryKeyPairName) == 0
if noPassword && noKeys && noTempKey {
errs = append(errs, fmt.Errorf("[PRE-FLIGHT] Didn't detect any credentials, you have to specify either "+
"{password} or "+
"{key_name+local_private_key_path} or "+
"{temporary_key_pair_name} cheers :)"))
}
return errs
}

View File

@ -1,54 +0,0 @@
package jdcloud
import (
"testing"
)
func TestJDCloudInstanceSpecConfig_Prepare(t *testing.T) {
specs := &JDCloudInstanceSpecConfig{}
if err := specs.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when there's nothing set")
}
specs.InstanceName = "packer_test_instance_name"
specs.InstanceType = "packer_test_instance_type"
if err := specs.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when base-image not given")
}
specs.ImageId = "img-packer-test"
if err := specs.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when credentials not set")
}
specs.Comm.SSHPassword = "abc123"
if err := specs.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when username = nil")
}
specs.Comm.SSHUsername = "root"
if err := specs.Prepare(nil); err != nil {
t.Fatalf("Test shouldn't fail when password set ")
}
specs.Comm.SSHPassword = ""
specs.Comm.SSHTemporaryKeyPairName = "abc"
if err := specs.Prepare(nil); err != nil {
t.Fatalf("Test shouldn't fail when temp password set")
}
specs.Comm.SSHTemporaryKeyPairName = ""
specs.Comm.SSHPrivateKeyFile = "abc"
specs.Comm.SSHKeyPairName = ""
if err := specs.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when SSHKeypairName missing")
}
specs.Comm.SSHPrivateKeyFile = "abc"
specs.Comm.SSHKeyPairName = "123"
if err := specs.Prepare(nil); err == nil {
t.Fatalf("Test shouldn't pass when private key pair path is wrong ")
}
}

View File

@ -1,74 +0,0 @@
package jdcloud
import (
"context"
"fmt"
"io/ioutil"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis"
)
type stepConfigCredentials struct {
InstanceSpecConfig *JDCloudInstanceSpecConfig
ui packersdk.Ui
}
func (s *stepConfigCredentials) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
s.ui = state.Get("ui").(packersdk.Ui)
password := s.InstanceSpecConfig.Comm.SSHPassword
privateKeyPath := s.InstanceSpecConfig.Comm.SSHPrivateKeyFile
privateKeyName := s.InstanceSpecConfig.Comm.SSHKeyPairName
newKeyName := s.InstanceSpecConfig.Comm.SSHTemporaryKeyPairName
if len(privateKeyPath) > 0 && len(privateKeyName) > 0 {
s.ui.Message("\t Private key detected, we are going to login with this private key :)")
return s.ReadExistingPair()
}
if len(newKeyName) > 0 {
s.ui.Message("\t We are going to create a new key pair with its name=" + newKeyName)
return s.CreateRandomKeyPair(newKeyName)
}
if len(password) > 0 {
s.ui.Message("\t Password detected, we are going to login with this password :)")
return multistep.ActionContinue
}
s.ui.Error("[ERROR] Didn't detect any credentials, you have to specify either " +
"{password} or " +
"{key_name+local_private_key_path} or " +
"{temporary_key_pair_name} cheers :)")
return multistep.ActionHalt
}
func (s *stepConfigCredentials) ReadExistingPair() multistep.StepAction {
privateKeyBytes, err := ioutil.ReadFile(s.InstanceSpecConfig.Comm.SSHPrivateKeyFile)
if err != nil {
s.ui.Error("Cannot read local private-key, were they correctly placed? Here's the error" + err.Error())
return multistep.ActionHalt
}
s.ui.Message("\t\t Keys read successfully :)")
s.InstanceSpecConfig.Comm.SSHPrivateKey = privateKeyBytes
return multistep.ActionContinue
}
func (s *stepConfigCredentials) CreateRandomKeyPair(keyName string) multistep.StepAction {
req := apis.NewCreateKeypairRequest(Region, keyName)
resp, err := VmClient.CreateKeypair(req)
if err != nil || resp.Error.Code != FINE {
s.ui.Error(fmt.Sprintf("[ERROR] Cannot create a new key pair for you, \n error=%v \n response=%v", err, resp))
return multistep.ActionHalt
}
s.ui.Message("\t\t Keys created successfully :)")
s.InstanceSpecConfig.Comm.SSHPrivateKey = []byte(resp.Result.PrivateKey)
s.InstanceSpecConfig.Comm.SSHKeyPairName = keyName
return multistep.ActionContinue
}
func (s *stepConfigCredentials) Cleanup(state multistep.StateBag) {
}

View File

@ -1,75 +0,0 @@
package jdcloud
import (
"context"
"fmt"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis"
)
type stepCreateJDCloudImage struct {
InstanceSpecConfig *JDCloudInstanceSpecConfig
}
func (s *stepCreateJDCloudImage) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Creating images")
req := apis.NewCreateImageRequest(Region, s.InstanceSpecConfig.InstanceId, s.InstanceSpecConfig.ImageName, "")
resp, err := VmClient.CreateImage(req)
if err != nil || resp.Error.Code != FINE {
ui.Error(fmt.Sprintf("[ERROR] Creating image: Error-%v ,Resp:%v", err, resp))
return multistep.ActionHalt
}
s.InstanceSpecConfig.ArtifactId = resp.Result.ImageId
if err := ImageStatusWaiter(s.InstanceSpecConfig.ArtifactId); err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func ImageStatusWaiter(imageId string) error {
req := apis.NewDescribeImageRequest(Region, imageId)
return Retry(5*time.Minute, func() *RetryError {
resp, err := VmClient.DescribeImage(req)
if err == nil && resp.Result.Image.Status == READY {
return nil
}
if connectionError(err) {
return RetryableError(err)
} else {
return NonRetryableError(err)
}
})
}
// Delete created instance image on error
func (s *stepCreateJDCloudImage) Cleanup(state multistep.StateBag) {
if s.InstanceSpecConfig.ArtifactId != "" {
req := apis.NewDeleteImageRequest(Region, s.InstanceSpecConfig.ArtifactId)
_ = Retry(time.Minute, func() *RetryError {
_, err := VmClient.DeleteImage(req)
if err == nil {
return nil
}
if connectionError(err) {
return RetryableError(err)
} else {
return NonRetryableError(err)
}
})
}
}

View File

@ -1,227 +0,0 @@
package jdcloud
import (
"context"
"fmt"
"regexp"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/jdcloud-api/jdcloud-sdk-go/core"
"github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis"
vm "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/models"
vpcApis "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/apis"
vpcClient "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/client"
vpc "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/models"
)
type stepCreateJDCloudInstance struct {
InstanceSpecConfig *JDCloudInstanceSpecConfig
CredentialConfig *JDCloudCredentialConfig
ui packersdk.Ui
}
func (s *stepCreateJDCloudInstance) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
privateKey := s.InstanceSpecConfig.Comm.SSHPrivateKey
keyName := s.InstanceSpecConfig.Comm.SSHKeyPairName
password := s.InstanceSpecConfig.Comm.SSHPassword
s.ui = state.Get("ui").(packersdk.Ui)
s.ui.Say("Creating instances")
instanceSpec := vm.InstanceSpec{
Az: &s.CredentialConfig.Az,
InstanceType: &s.InstanceSpecConfig.InstanceType,
ImageId: &s.InstanceSpecConfig.ImageId,
Name: s.InstanceSpecConfig.InstanceName,
PrimaryNetworkInterface: &vm.InstanceNetworkInterfaceAttachmentSpec{
NetworkInterface: &vpc.NetworkInterfaceSpec{
SubnetId: s.InstanceSpecConfig.SubnetId,
Az: &s.CredentialConfig.Az,
},
},
}
if len(password) > 0 {
instanceSpec.Password = &password
}
if len(keyName) > 0 && len(privateKey) > 0 {
instanceSpec.KeyNames = []string{keyName}
}
req := apis.NewCreateInstancesRequest(Region, &instanceSpec)
resp, err := VmClient.CreateInstances(req)
if err != nil || resp.Error.Code != FINE {
err := fmt.Errorf("Error creating instance, error-%v response:%v", err, resp)
s.ui.Error(err.Error())
return multistep.ActionHalt
}
s.InstanceSpecConfig.InstanceId = resp.Result.InstanceIds[0]
instanceInterface, err := InstanceStatusRefresher(s.InstanceSpecConfig.InstanceId, []string{VM_PENDING, VM_STARTING}, []string{VM_RUNNING})
if err != nil {
s.ui.Error(err.Error())
return multistep.ActionHalt
}
instance := instanceInterface.(vm.Instance)
privateIpAddress := instance.PrivateIpAddress
networkInterfaceId := instance.PrimaryNetworkInterface.NetworkInterface.NetworkInterfaceId
s.ui.Message("Creating public-ip")
s.InstanceSpecConfig.PublicIpId, err = createElasticIp(state)
if err != nil {
s.ui.Error(err.Error())
return multistep.ActionHalt
}
s.ui.Message("Associating public-ip with instance")
err = associatePublicIp(networkInterfaceId, s.InstanceSpecConfig.PublicIpId, privateIpAddress)
if err != nil {
s.ui.Error(err.Error())
return multistep.ActionHalt
}
req_ := vpcApis.NewDescribeElasticIpRequest(Region, s.InstanceSpecConfig.PublicIpId)
eip, err := VpcClient.DescribeElasticIp(req_)
if err != nil || eip.Error.Code != FINE {
s.ui.Error(fmt.Sprintf("[ERROR] Failed in getting eip,error:%v \n response:%v", err, eip))
return multistep.ActionHalt
}
s.InstanceSpecConfig.PublicIpAddress = eip.Result.ElasticIp.ElasticIpAddress
state.Put("eip", s.InstanceSpecConfig.PublicIpAddress)
s.ui.Message(fmt.Sprintf(
"Hi, we have created the instance, its name=%v , "+
"its id=%v, "+
"and its eip=%v :) ", instance.InstanceName, s.InstanceSpecConfig.InstanceId, eip.Result.ElasticIp.ElasticIpAddress))
// 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", s.InstanceSpecConfig.InstanceId)
return multistep.ActionContinue
}
// Delete created resources {instance,ip} on error
func (s *stepCreateJDCloudInstance) Cleanup(state multistep.StateBag) {
if s.InstanceSpecConfig.PublicIpId != "" {
req := vpcApis.NewDeleteElasticIpRequest(Region, s.InstanceSpecConfig.PublicIpId)
_ = Retry(time.Minute, func() *RetryError {
_, err := VpcClient.DeleteElasticIp(req)
if err == nil {
return nil
}
if connectionError(err) {
return RetryableError(err)
} else {
return NonRetryableError(err)
}
})
}
if s.InstanceSpecConfig.InstanceId != "" {
req := apis.NewDeleteInstanceRequest(Region, s.InstanceSpecConfig.InstanceId)
_ = Retry(time.Minute, func() *RetryError {
_, err := VmClient.DeleteInstance(req)
if err == nil {
return nil
}
if connectionError(err) {
return RetryableError(err)
} else {
return NonRetryableError(err)
}
})
}
}
func createElasticIp(state multistep.StateBag) (string, error) {
generalConfig := state.Get("config").(*Config)
regionId := generalConfig.RegionId
credential := core.NewCredentials(generalConfig.AccessKey, generalConfig.SecretKey)
vpcclient := vpcClient.NewVpcClient(credential)
req := vpcApis.NewCreateElasticIpsRequest(regionId, 1, &vpc.ElasticIpSpec{
BandwidthMbps: 1,
Provider: "bgp",
})
resp, err := vpcclient.CreateElasticIps(req)
if err != nil || resp.Error.Code != 0 {
return "", fmt.Errorf("[ERROR] Failed in creating new publicIp, Error-%v, Response:%v", err, resp)
}
return resp.Result.ElasticIpIds[0], nil
}
func associatePublicIp(networkInterfaceId string, eipId string, privateIpAddress string) error {
req := vpcApis.NewAssociateElasticIpRequest(Region, networkInterfaceId)
req.ElasticIpId = &eipId
req.PrivateIpAddress = &privateIpAddress
resp, err := VpcClient.AssociateElasticIp(req)
if err != nil || resp.Error.Code != FINE {
return fmt.Errorf("[ERROR] Failed in associating publicIp, Error-%v, Response:%v", err, resp)
}
return nil
}
func instanceHost(state multistep.StateBag) (string, error) {
return state.Get("eip").(string), nil
}
func InstanceStatusRefresher(id string, pending, target []string) (instance interface{}, err error) {
stateConf := &StateChangeConf{
Pending: pending,
Target: target,
Refresh: instanceStatusRefresher(id),
Delay: 3 * time.Second,
Timeout: 10 * time.Minute,
MinTimeout: 1 * time.Second,
}
if instance, err = stateConf.WaitForState(); err != nil {
return nil, fmt.Errorf("[ERROR] Failed in creating instance ,err message:%v", err)
}
return instance, nil
}
func instanceStatusRefresher(instanceId string) StateRefreshFunc {
return func() (instance interface{}, status string, err error) {
err = Retry(time.Minute, func() *RetryError {
req := apis.NewDescribeInstanceRequest(Region, instanceId)
resp, err := VmClient.DescribeInstance(req)
if err == nil && resp.Error.Code == FINE {
instance = resp.Result.Instance
status = resp.Result.Instance.Status
return nil
}
instance = nil
status = ""
if connectionError(err) {
return RetryableError(err)
} else {
return NonRetryableError(err)
}
})
return instance, status, err
}
}
func connectionError(e error) bool {
if e == nil {
return false
}
ok, _ := regexp.MatchString(CONNECT_FAILED, e.Error())
return ok
}

View File

@ -1,40 +0,0 @@
package jdcloud
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis"
)
type stepStopJDCloudInstance struct {
InstanceSpecConfig *JDCloudInstanceSpecConfig
}
func (s *stepStopJDCloudInstance) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Stopping this instance")
req := apis.NewStopInstanceRequest(Region, s.InstanceSpecConfig.InstanceId)
resp, err := VmClient.StopInstance(req)
if err != nil || resp.Error.Code != FINE {
ui.Error(fmt.Sprintf("[ERROR] Failed in trying to stop this vm: Error-%v ,Resp:%v", err, resp))
return multistep.ActionHalt
}
_, err = InstanceStatusRefresher(s.InstanceSpecConfig.InstanceId, []string{VM_RUNNING, VM_STOPPING}, []string{VM_STOPPED})
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Message("Instance has been stopped :)")
return multistep.ActionContinue
}
func (s *stepStopJDCloudInstance) Cleanup(multistep.StateBag) {
return
}

View File

@ -1,110 +0,0 @@
package jdcloud
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
vm "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis"
vpc "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/apis"
)
type stepValidateParameters struct {
InstanceSpecConfig *JDCloudInstanceSpecConfig
ui packersdk.Ui
state multistep.StateBag
}
func (s *stepValidateParameters) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
s.ui = state.Get("ui").(packersdk.Ui)
s.state = state
s.ui.Say("Validating parameters...")
if err := s.ValidateSubnetFunc(); err != nil {
s.ui.Error(err.Error())
return multistep.ActionHalt
}
if err := s.ValidateImageFunc(); err != nil {
s.ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepValidateParameters) ValidateSubnetFunc() error {
subnetId := s.InstanceSpecConfig.SubnetId
if len(subnetId) == 0 {
s.ui.Message("\t 'subnet' is not specified, we will create a new one for you :) ")
return s.CreateRandomSubnet()
}
s.ui.Message("\t validating your subnet:" + s.InstanceSpecConfig.SubnetId)
req := vpc.NewDescribeSubnetRequest(Region, subnetId)
resp, err := VpcClient.DescribeSubnet(req)
if err != nil {
return fmt.Errorf("[ERROR] Failed in validating subnet->%s, reasons:%v", subnetId, err)
}
if resp != nil && resp.Error.Code != FINE {
return fmt.Errorf("[ERROR] Something wrong with your subnet->%s, reasons:%v", subnetId, resp.Error)
}
s.ui.Message("\t subnet found:" + resp.Result.Subnet.SubnetName)
return nil
}
func (s *stepValidateParameters) ValidateImageFunc() error {
s.ui.Message("\t validating your base image:" + s.InstanceSpecConfig.ImageId)
imageId := s.InstanceSpecConfig.ImageId
req := vm.NewDescribeImageRequest(Region, imageId)
resp, err := VmClient.DescribeImage(req)
if err != nil {
return fmt.Errorf("[ERROR] Failed in validating your image->%s, reasons:%v", imageId, err)
}
if resp != nil && resp.Error.Code != FINE {
return fmt.Errorf("[ERROR] Something wrong with your image->%s, reasons:%v", imageId, resp.Error)
}
s.ui.Message("\t image found:" + resp.Result.Image.Name)
s.state.Put("source_image", &resp.Result.Image)
return nil
}
func (s *stepValidateParameters) CreateRandomSubnet() error {
newVpc, err := s.CreateRandomVpc()
if err != nil {
return err
}
req := vpc.NewCreateSubnetRequest(Region, newVpc, "created_by_packer", "192.168.0.0/20")
resp, err := VpcClient.CreateSubnet(req)
if err != nil || resp.Error.Code != FINE {
errorMessage := fmt.Sprintf("[ERROR] Failed in creating new subnet :( \n error:%v \n response:%v", err, resp)
s.ui.Error(errorMessage)
return fmt.Errorf(errorMessage)
}
s.InstanceSpecConfig.SubnetId = resp.Result.SubnetId
s.ui.Message("\t\t Hi, we have created a new subnet for you :) its name is 'created_by_packer' and its id=" + resp.Result.SubnetId)
return nil
}
func (s *stepValidateParameters) CreateRandomVpc() (string, error) {
req := vpc.NewCreateVpcRequest(Region, "created_by_packer")
resp, err := VpcClient.CreateVpc(req)
if err != nil || resp.Error.Code != FINE {
errorMessage := fmt.Sprintf("[ERROR] Failed in creating new vpc :( \n error :%v, \n response:%v", err, resp)
s.ui.Error(errorMessage)
return "", fmt.Errorf(errorMessage)
}
return resp.Result.VpcId, nil
}
func (s *stepValidateParameters) Cleanup(state multistep.StateBag) {}

View File

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

View File

@ -1,38 +0,0 @@
package linode
import (
"context"
"fmt"
"log"
"github.com/linode/linodego"
)
type Artifact struct {
ImageID string
ImageLabel string
Driver *linodego.Client
// StateData should store data such as GeneratedData
// to be shared with post-processors
StateData map[string]interface{}
}
func (a Artifact) BuilderId() string { return BuilderID }
func (a Artifact) Files() []string { return nil }
func (a Artifact) Id() string { return a.ImageID }
func (a Artifact) String() string {
return fmt.Sprintf("Linode image: %s (%s)", a.ImageLabel, a.ImageID)
}
func (a Artifact) State(name string) interface{} {
return a.StateData[name]
}
func (a Artifact) Destroy() error {
log.Printf("Destroying image: %s (%s)", a.ImageID, a.ImageLabel)
err := a.Driver.DeleteImage(context.TODO(), a.ImageID)
return err
}

View File

@ -1,61 +0,0 @@
package linode
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 TestArtifactId(t *testing.T) {
generatedData := make(map[string]interface{})
a := &Artifact{"private/42", "packer-foobar", nil, generatedData}
expected := "private/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{"private/42", "packer-foobar", nil, generatedData}
expected := "Linode image: packer-foobar (private/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")
}
}

View File

@ -1,117 +0,0 @@
// The linode package contains a packersdk.Builder implementation
// that builds Linode images.
package linode
import (
"context"
"errors"
"fmt"
"log"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/linode/linodego"
"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"
)
// The unique ID for this builder.
const BuilderID = "packer.linode"
// Builder represents a Packer Builder.
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, nil, nil
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (ret packersdk.Artifact, err error) {
ui.Say("Running builder ...")
client := newLinodeClient(b.config.PersonalAccessToken)
if err != nil {
ui.Error(err.Error())
return nil, err
}
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("linode_%s.pem", b.config.PackerBuildName),
},
&stepCreateLinode{client},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: commHost(b.config.Comm.Host()),
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
&commonsteps.StepProvision{},
&commonsteps.StepCleanupTempKeys{
Comm: &b.config.Comm,
},
&stepShutdownLinode{client},
&stepCreateImage{client},
}
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)
}
// If we were interrupted or cancelled, then just exit.
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("Build was cancelled.")
}
if _, ok := state.GetOk(multistep.StateHalted); ok {
return nil, errors.New("Build was halted.")
}
if _, ok := state.GetOk("image"); !ok {
return nil, errors.New("Cannot find image in state.")
}
image := state.Get("image").(*linodego.Image)
artifact := Artifact{
ImageLabel: image.Label,
ImageID: image.ID,
Driver: &client,
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
}
return artifact, nil
}
func commHost(host string) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
if host != "" {
log.Printf("Using host value: %s", host)
return host, nil
}
instance := state.Get("instance").(*linodego.Instance)
if len(instance.IPv4) == 0 {
return "", fmt.Errorf("Linode instance %d has no IPv4 addresses!", instance.ID)
}
return instance.IPv4[0].String(), nil
}
}

View File

@ -1,34 +0,0 @@
package linode
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("LINODE_TOKEN"); v == "" {
t.Fatal("LINODE_TOKEN must be set for acceptance tests")
}
}
const testBuilderAccBasic = `
{
"builders": [{
"type": "test",
"region": "us-east",
"instance_type": "g6-nanode-1",
"image": "linode/alpine3.9",
"ssh_username": "root"
}]
}
`

View File

@ -1,291 +0,0 @@
package linode
import (
"strconv"
"testing"
"time"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"linode_token": "bar",
"region": "us-east",
"instance_type": "g6-nanode-1",
"ssh_username": "root",
"image": "linode/alpine3.9",
}
}
func TestBuilder_ImplementsBuilder(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{}{
"linode_token": []string{},
}
_, warnings, err := b.Prepare(c)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatalf("prepare should fail")
}
}
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
// Add a random key
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")
}
}
func TestBuilderPrepare_Region(t *testing.T) {
var b Builder
config := testConfig()
// Test default
delete(config, "region")
_, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatalf("should error")
}
expected := "us-east"
// Test set
config["region"] = expected
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Region != expected {
t.Errorf("found %s, expected %s", b.config.Region, expected)
}
}
func TestBuilderPrepare_Size(t *testing.T) {
var b Builder
config := testConfig()
// Test default
delete(config, "instance_type")
_, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatalf("should error")
}
expected := "g6-nanode-1"
// Test set
config["instance_type"] = expected
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.InstanceType != expected {
t.Errorf("found %s, expected %s", b.config.InstanceType, expected)
}
}
func TestBuilderPrepare_Image(t *testing.T) {
var b Builder
config := testConfig()
// Test default
delete(config, "image")
_, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should error")
}
expected := "linode/alpine3.9"
// Test set
config["image"] = expected
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Image != expected {
t.Errorf("found %s, expected %s", b.config.Image, expected)
}
}
func TestBuilderPrepare_ImageLabel(t *testing.T) {
var b Builder
config := testConfig()
// Test default
_, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.ImageLabel == "" {
t.Errorf("invalid: %s", b.config.ImageLabel)
}
// Test set
config["image_label"] = "foobarbaz"
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test set with template
config["image_label"] = "{{timestamp}}"
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
_, err = strconv.ParseInt(b.config.ImageLabel, 0, 0)
if err != nil {
t.Fatalf("failed to parse int in template: %s", err)
}
}
func TestBuilderPrepare_Label(t *testing.T) {
var b Builder
config := testConfig()
// Test default
_, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Label == "" {
t.Errorf("invalid: %s", b.config.Label)
}
// Test normal set
config["instance_label"] = "foobar"
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test with template
config["instance_label"] = "foobar-{{timestamp}}"
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test with bad template
config["instance_label"] = "foobar-{{"
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_StateTimeout(t *testing.T) {
var b Builder
config := testConfig()
// Test default
_, warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.StateTimeout != 5*time.Minute {
t.Errorf("invalid: %s", b.config.StateTimeout)
}
// Test set
config["state_timeout"] = "5m"
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test bad
config["state_timeout"] = "tubes"
b = Builder{}
_, warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}

View File

@ -1,148 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
package linode
import (
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"os"
"regexp"
"time"
"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"
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
ctx interpolate.Context
Comm communicator.Config `mapstructure:",squash"`
PersonalAccessToken string `mapstructure:"linode_token"`
Region string `mapstructure:"region"`
InstanceType string `mapstructure:"instance_type"`
Label string `mapstructure:"instance_label"`
Tags []string `mapstructure:"instance_tags"`
Image string `mapstructure:"image"`
SwapSize int `mapstructure:"swap_size"`
RootPass string `mapstructure:"root_pass"`
RootSSHKey string `mapstructure:"root_ssh_key"`
ImageLabel string `mapstructure:"image_label"`
Description string `mapstructure:"image_description"`
StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"`
}
func createRandomRootPassword() (string, error) {
rawRootPass := make([]byte, 50)
_, err := rand.Read(rawRootPass)
if err != nil {
return "", fmt.Errorf("Failed to generate random password")
}
rootPass := base64.StdEncoding.EncodeToString(rawRootPass)
return rootPass, nil
}
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
if err := config.Decode(c, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
},
},
}, raws...); err != nil {
return nil, err
}
var errs *packersdk.MultiError
// Defaults
if c.PersonalAccessToken == "" {
// Default to environment variable for linode_token, if it exists
c.PersonalAccessToken = os.Getenv("LINODE_TOKEN")
}
if c.ImageLabel == "" {
if def, err := interpolate.Render("packer-{{timestamp}}", nil); err == nil {
c.ImageLabel = def
} else {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Unable to render image name: %s", err))
}
}
if c.Label == "" {
// Default to packer-[time-ordered-uuid]
if def, err := interpolate.Render("packer-{{timestamp}}", nil); err == nil {
c.Label = def
} else {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Unable to render Linode label: %s", err))
}
}
if c.RootPass == "" {
var err error
c.RootPass, err = createRandomRootPassword()
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Unable to generate root_pass: %s", err))
}
}
if c.StateTimeout == 0 {
// Default to 5 minute timeouts waiting for state change
c.StateTimeout = 5 * time.Minute
}
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
errs = packersdk.MultiErrorAppend(errs, es...)
}
c.Comm.SSHPassword = c.RootPass
if c.PersonalAccessToken == "" {
// Required configurations that will display errors if not set
errs = packersdk.MultiErrorAppend(
errs, errors.New("linode_token is required"))
}
if c.Region == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("region is required"))
}
if c.InstanceType == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("instance_type is required"))
}
if c.Image == "" {
errs = packersdk.MultiErrorAppend(
errs, errors.New("image is required"))
}
if c.Tags == nil {
c.Tags = make([]string, 0)
}
tagRe := regexp.MustCompile("^[[:alnum:]:_-]{1,255}$")
for _, t := range c.Tags {
if !tagRe.MatchString(t) {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("invalid tag: %s", t))
}
}
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
packersdk.LogSecretFilter.Set(c.PersonalAccessToken)
return nil, nil
}

View File

@ -1,167 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package linode
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"`
PersonalAccessToken *string `mapstructure:"linode_token" cty:"linode_token" hcl:"linode_token"`
Region *string `mapstructure:"region" cty:"region" hcl:"region"`
InstanceType *string `mapstructure:"instance_type" cty:"instance_type" hcl:"instance_type"`
Label *string `mapstructure:"instance_label" cty:"instance_label" hcl:"instance_label"`
Tags []string `mapstructure:"instance_tags" cty:"instance_tags" hcl:"instance_tags"`
Image *string `mapstructure:"image" cty:"image" hcl:"image"`
SwapSize *int `mapstructure:"swap_size" cty:"swap_size" hcl:"swap_size"`
RootPass *string `mapstructure:"root_pass" cty:"root_pass" hcl:"root_pass"`
RootSSHKey *string `mapstructure:"root_ssh_key" cty:"root_ssh_key" hcl:"root_ssh_key"`
ImageLabel *string `mapstructure:"image_label" cty:"image_label" hcl:"image_label"`
Description *string `mapstructure:"image_description" cty:"image_description" hcl:"image_description"`
StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"`
}
// 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},
"linode_token": &hcldec.AttrSpec{Name: "linode_token", Type: cty.String, Required: false},
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
"instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false},
"instance_label": &hcldec.AttrSpec{Name: "instance_label", Type: cty.String, Required: false},
"instance_tags": &hcldec.AttrSpec{Name: "instance_tags", Type: cty.List(cty.String), Required: false},
"image": &hcldec.AttrSpec{Name: "image", Type: cty.String, Required: false},
"swap_size": &hcldec.AttrSpec{Name: "swap_size", Type: cty.Number, Required: false},
"root_pass": &hcldec.AttrSpec{Name: "root_pass", Type: cty.String, Required: false},
"root_ssh_key": &hcldec.AttrSpec{Name: "root_ssh_key", Type: cty.String, Required: false},
"image_label": &hcldec.AttrSpec{Name: "image_label", Type: cty.String, Required: false},
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
"state_timeout": &hcldec.AttrSpec{Name: "state_timeout", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,30 +0,0 @@
package linode
import (
"fmt"
"net/http"
"github.com/hashicorp/packer/builder/linode/version"
"github.com/linode/linodego"
"golang.org/x/oauth2"
)
func newLinodeClient(pat string) linodego.Client {
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: pat})
oauthTransport := &oauth2.Transport{
Source: tokenSource,
}
oauth2Client := &http.Client{
Transport: oauthTransport,
}
client := linodego.NewClient(oauth2Client)
projectURL := "https://www.packer.io"
userAgent := fmt.Sprintf("Packer/%s (+%s) linodego/%s",
version.LinodePluginVersion.FormattedVersion(), projectURL, linodego.Version)
client.SetUserAgent(userAgent)
return client
}

View File

@ -1,48 +0,0 @@
package linode
import (
"context"
"errors"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/linode/linodego"
)
type stepCreateImage struct {
client linodego.Client
}
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
c := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)
disk := state.Get("disk").(*linodego.InstanceDisk)
instance := state.Get("instance").(*linodego.Instance)
ui.Say("Creating image...")
image, err := s.client.CreateImage(ctx, linodego.ImageCreateOptions{
DiskID: disk.ID,
Label: c.ImageLabel,
Description: c.Description,
})
if err == nil {
_, err = s.client.WaitForInstanceDiskStatus(ctx, instance.ID, disk.ID, linodego.DiskReady, 600)
}
if err == nil {
image, err = s.client.GetImage(ctx, image.ID)
}
if err != nil {
err = errors.New("Error creating image: " + err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("image", image)
return multistep.ActionContinue
}
func (s *stepCreateImage) Cleanup(state multistep.StateBag) {}

View File

@ -1,97 +0,0 @@
package linode
import (
"context"
"errors"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/linode/linodego"
)
type stepCreateLinode struct {
client linodego.Client
}
func (s *stepCreateLinode) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
c := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)
ui.Say("Creating Linode...")
createOpts := linodego.InstanceCreateOptions{
RootPass: c.Comm.Password(),
AuthorizedKeys: []string{string(c.Comm.SSHPublicKey)},
Region: c.Region,
Type: c.InstanceType,
Label: c.Label,
Image: c.Image,
SwapSize: &c.SwapSize,
}
instance, err := s.client.CreateInstance(ctx, createOpts)
if err != nil {
err = errors.New("Error creating Linode: " + err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("instance", instance)
// wait until instance is running
for instance.Status != linodego.InstanceRunning {
time.Sleep(2 * time.Second)
if instance, err = s.client.GetInstance(ctx, instance.ID); err != nil {
err = errors.New("Error creating Linode: " + err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("instance", instance)
// 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", instance.ID)
}
disk, err := s.findDisk(ctx, instance.ID)
if err != nil {
err = errors.New("Error creating Linode: " + err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
} else if disk == nil {
err := errors.New("Error creating Linode: no suitable disk was found")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("disk", disk)
return multistep.ActionContinue
}
func (s *stepCreateLinode) findDisk(ctx context.Context, instanceID int) (*linodego.InstanceDisk, error) {
disks, err := s.client.ListInstanceDisks(ctx, instanceID, nil)
if err != nil {
return nil, err
}
for _, disk := range disks {
if disk.Filesystem != linodego.FilesystemSwap {
return &disk, nil
}
}
return nil, nil
}
func (s *stepCreateLinode) Cleanup(state multistep.StateBag) {
instance, ok := state.GetOk("instance")
if !ok {
return
}
ui := state.Get("ui").(packersdk.Ui)
if err := s.client.DeleteInstance(context.Background(), instance.(*linodego.Instance).ID); err != nil {
ui.Error("Error cleaning up Linode: " + err.Error())
}
}

View File

@ -1,95 +0,0 @@
package linode
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"golang.org/x/crypto/ssh"
)
// StepCreateSSHKey represents a Packer build step that generates SSH key pairs.
type StepCreateSSHKey struct {
Debug bool
DebugKeyPath string
}
// Run executes the Packer build step that generates SSH key pairs.
// The key pairs are added to the ssh config
func (s *StepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
config := state.Get("config").(*Config)
if config.Comm.SSHPrivateKeyFile != "" {
ui.Say("Using existing SSH private key")
privateKeyBytes, err := ioutil.ReadFile(config.Comm.SSHPrivateKeyFile)
if err != nil {
state.Put("error", fmt.Errorf(
"Error loading configured private key file: %s", err))
return multistep.ActionHalt
}
config.Comm.SSHPrivateKey = privateKeyBytes
config.Comm.SSHPublicKey = nil
return multistep.ActionContinue
}
ui.Say("Creating temporary SSH key for instance...")
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
err := fmt.Errorf("Error creating temporary ssh key: %s", err)
state.Put("error", err)
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
}
config.Comm.SSHPrivateKey = pem.EncodeToMemory(&priv_blk)
config.Comm.SSHPublicKey = ssh.MarshalAuthorizedKey(pub)
// Linode has a serious issue with the newline that the ssh package appends to the end of the key.
if config.Comm.SSHPublicKey[len(config.Comm.SSHPublicKey)-1] == '\n' {
config.Comm.SSHPublicKey = config.Comm.SSHPublicKey[:len(config.Comm.SSHPublicKey)-1]
}
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
}
// Write out the key
err = pem.Encode(f, &priv_blk)
f.Close()
if err != nil {
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
// Nothing to clean up. SSH keys are associated with a single Linode instance.
func (s *StepCreateSSHKey) Cleanup(state multistep.StateBag) {}

View File

@ -1,40 +0,0 @@
package linode
import (
"context"
"errors"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/linode/linodego"
)
type stepShutdownLinode struct {
client linodego.Client
}
func (s *stepShutdownLinode) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
c := state.Get("config").(*Config)
ui := state.Get("ui").(packersdk.Ui)
instance := state.Get("instance").(*linodego.Instance)
ui.Say("Shutting down Linode...")
if err := s.client.ShutdownInstance(ctx, instance.ID); err != nil {
err = errors.New("Error shutting down Linode: " + err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
_, err := s.client.WaitForInstanceStatus(ctx, instance.ID, linodego.InstanceOffline, int(c.StateTimeout.Seconds()))
if err != nil {
err = errors.New("Error shutting down Linode: " + err.Error())
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepShutdownLinode) Cleanup(state multistep.StateBag) {}

View File

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

View File

@ -19,8 +19,6 @@ import (
digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean"
filebuilder "github.com/hashicorp/packer/builder/file"
hcloudbuilder "github.com/hashicorp/packer/builder/hcloud"
jdcloudbuilder "github.com/hashicorp/packer/builder/jdcloud"
linodebuilder "github.com/hashicorp/packer/builder/linode"
lxcbuilder "github.com/hashicorp/packer/builder/lxc"
lxdbuilder "github.com/hashicorp/packer/builder/lxd"
nullbuilder "github.com/hashicorp/packer/builder/null"
@ -69,8 +67,6 @@ var Builders = map[string]packersdk.Builder{
"digitalocean": new(digitaloceanbuilder.Builder),
"file": new(filebuilder.Builder),
"hcloud": new(hcloudbuilder.Builder),
"jdcloud": new(jdcloudbuilder.Builder),
"linode": new(linodebuilder.Builder),
"lxc": new(lxcbuilder.Builder),
"lxd": new(lxdbuilder.Builder),
"null": new(nullbuilder.Builder),

View File

@ -33,6 +33,8 @@ import (
hyperonebuilder "github.com/hashicorp/packer-plugin-hyperone/builder/hyperone"
hypervisobuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/iso"
hypervvmcxbuilder "github.com/hashicorp/packer-plugin-hyperv/builder/hyperv/vmcx"
jdcloudbuilder "github.com/hashicorp/packer-plugin-jdcloud/builder/jdcloud"
linodebuilder "github.com/hashicorp/packer-plugin-linode/builder/linode"
ncloudbuilder "github.com/hashicorp/packer-plugin-ncloud/builder/ncloud"
openstackbuilder "github.com/hashicorp/packer-plugin-openstack/builder/openstack"
oscbsubuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsu"
@ -80,6 +82,8 @@ var VendoredBuilders = map[string]packersdk.Builder{
"hyperv-iso": new(hypervisobuilder.Builder),
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
"jdcloud": new(jdcloudbuilder.Builder),
"linode": new(linodebuilder.Builder),
"ncloud": new(ncloudbuilder.Builder),
"openstack": new(openstackbuilder.Builder),
"proxmox": new(proxmoxiso.Builder),

4
go.mod
View File

@ -46,6 +46,8 @@ require (
github.com/hashicorp/packer-plugin-googlecompute 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-jdcloud v0.0.1
github.com/hashicorp/packer-plugin-linode v0.0.2
github.com/hashicorp/packer-plugin-ncloud v0.0.2
github.com/hashicorp/packer-plugin-openstack v0.0.2
github.com/hashicorp/packer-plugin-outscale v0.0.1
@ -59,10 +61,8 @@ require (
github.com/hashicorp/packer-plugin-vmware v0.0.1
github.com/hashicorp/packer-plugin-vsphere v0.0.1
github.com/hetznercloud/hcloud-go v1.15.1
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961
github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
github.com/linode/linodego v0.14.0
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08
github.com/mitchellh/cli v1.1.0

85
go.sum
View File

@ -83,9 +83,12 @@ github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nu
github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns=
github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.2.0 h1:c7GgSBfMt51UGM4SI1F7IFOokOVZO+uxNcJL3Xsmkp4=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.2.0/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/Telmate/proxmox-api-go v0.0.0-20200715182505-ec97c70ba887/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
@ -215,6 +218,7 @@ github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TR
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
@ -222,12 +226,15 @@ github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURU
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08/go.mod h1:VBVDFSBXCIW8JaHQpI8lldSKfYaLMzP9oyq6IJ4fhzY=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
github.com/exoscale/egoscale v0.43.1 h1:Lhr0UOfg3t3Y56yh1DsYCjQuUHqFvsC8iUVqvub8+0Q=
github.com/exoscale/egoscale v0.43.1/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc=
@ -241,8 +248,10 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
github.com/getkin/kin-openapi v0.37.0/go.mod h1:ZJSfy1PxJv2QQvH9EdBj3nupRTVvV42mkW6zKUlRBwk=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
@ -253,10 +262,15 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
@ -273,10 +287,13 @@ github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
@ -289,6 +306,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -331,6 +349,8 @@ github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69/go.mod h1:
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -359,6 +379,10 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/gophercloud v0.6.1-0.20191122030953-d8ac278c1c9d/go.mod h1:ozGNgr9KYOVATV5jsgHl/ceCDXGuguqOZAzoQ/2vcNM=
github.com/gophercloud/gophercloud v0.12.0 h1:mZrie07npp6ODiwHZolTicr5jV8Ogn43AvAsSMm6Ork=
github.com/gophercloud/gophercloud v0.12.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
@ -367,6 +391,7 @@ github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbL
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ=
@ -482,6 +507,10 @@ github.com/hashicorp/packer-plugin-hyperone v0.0.1 h1:Owp1B5cI0VgFgR3pCyeeQdyKPT
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/go.mod h1:sB9mEZCfaXVjTD6pS+Tt0xMtUD1Ocnl5mZ3i/PG6eB0=
github.com/hashicorp/packer-plugin-jdcloud v0.0.1 h1:MLvAroDOHWimBf6cBa0trlHJpB8nKtO9FPyg2l6Iafw=
github.com/hashicorp/packer-plugin-jdcloud v0.0.1/go.mod h1:bVGtjp3v98rpguEYxJAXQbg8CjllInh5WFqO9a0T4lc=
github.com/hashicorp/packer-plugin-linode v0.0.2 h1:obN0kQKlfCQuFmRwVx6ksQApQZ85gPAcSCtjDd0F30c=
github.com/hashicorp/packer-plugin-linode v0.0.2/go.mod h1:uF8FAE3+PG/lVI1TwjXSaS3AHv4+Wb3/3gsrg0h6IQ0=
github.com/hashicorp/packer-plugin-ncloud v0.0.2 h1:MGvGkOVfzeosqOSs5dteghLwv9VRcRxTuLoLX1ssUag=
github.com/hashicorp/packer-plugin-ncloud v0.0.2/go.mod h1:Hud2R1pkky96TQy3TPTTrr9Kej4b/4dqC/v+uEE0VDY=
github.com/hashicorp/packer-plugin-openstack v0.0.2 h1:wGNE8es3Bn9auuIoX+gqT9chXzYY9GlM55eSpM4uwtU=
@ -532,10 +561,13 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1
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/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/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
@ -554,6 +586,7 @@ github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbg
github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@ -563,6 +596,7 @@ github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVY
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@ -592,8 +626,10 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LE
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/linode/linodego v0.14.0 h1:0APKMjiVGyry2TTUVDiok72H6cWpFNMMrFWBFn14aFU=
github.com/linode/linodego v0.14.0/go.mod h1:2ce3S00NrDqJfp4i55ZuSlT0U3cKNELNYACWBPI8Tnw=
github.com/linode/linodego v1.0.0 h1:Kq/8oPPk3ui/aVlzEObll5oBewHyzo/eJPnVPYuONVE=
github.com/linode/linodego v1.0.0/go.mod h1:XOWXRHjqeU2uPS84tKLgfWIfTlv3TYzCS0io4GOQzEI=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
@ -668,12 +704,19 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/oracle/oci-go-sdk v18.0.0+incompatible h1:FLV4KixsVfF3rwyVTMI6Ryp/Q+OSb9sR5TawbfjFLN4=
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
@ -687,6 +730,7 @@ github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a/go.mod h1
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -736,7 +780,10 @@ github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:X
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -811,6 +858,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -822,6 +870,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -870,9 +919,11 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -886,6 +937,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -937,11 +989,14 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -960,6 +1015,7 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1000,6 +1056,7 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1016,6 +1073,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqG
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@ -1197,6 +1256,9 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -1204,6 +1266,7 @@ gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516 h1:H6trpavCIuipd
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516/go.mod h1:d3R+NllX3X5e0zlG1Rful3uLvsGC/Q3OHut5464DEQw=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -1222,6 +1285,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.18.3 h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0=
k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/client-go v0.18.3 h1:QaJzz92tsN67oorwzmoB0a9r9ZVHuD5ryjbCKP0U22k=
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@ -1,88 +0,0 @@
---
description: |
The `jdcloud` Packer builder helps you to build instance images
based on an existing image
page_title: JDCloud Image Builder
---
# JDCloud Image Builder
Type: `jdcloud`
Artifact BuilderId: `hashicorp.jdcloud`
The `jdcloud` Packer builder helps you to build instance images
based on an existing image
## Configuration Reference
In order to build a JDCloud instance image, full-fill your configuration file. Necessary attributes
are given below:
### Required-Parameters:
- `type` (string) - This parameter tells which cloud-service-provider you are using, in our case, use 'jdcloud'
- `image_id` (string) - New image is generated based on an old one, specify the base-image-id here.
- `access_key` (string) - Your JD Cloud access key. You may set this as an env-variable:`export JDCLOUD_ACCESS_KEY=xxx`
- `secret_key` (string) - Your JD Cloud secret key. You may also set this via env-variable:`export JDCLOUD_SECRET_KEY=xxx`
- `region_id` (string) - Region of your instance, candidates are {"cn-north-1","cn-south-1","cn-east-1","cn-east-2"}
- `az` (string) - Exact availability zone of instance, 'cn-north-1c' for example
- `instance_name` (string) - Name your instance
- `instance_type` (string) - Class of your expected instance
- `image_name` (string) - Name the image you would like to create
- `communicator` (string) - Currently only `ssh` is supported. `winrm` will be added if required
### Optional-Parameters
- `subnet_id` (string) - An instance is supposed to exists in an subnet, if not specified , we will create new one for you
In addition to the above configuration options, a communicator can be configured for this builder:
### Communicator Configuration
#### Optional:
@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSHTemporaryKeyPair-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Key-Pair-Name-not-required.mdx'
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
## Examples
Here is a basic example for JDCloud.
```json
{
"builders": [
{
"type": "jdcloud",
"image_id": "img-h8ly274zg9",
"access_key": "<your access key>",
"secret_key": "<your secret key>",
"region_id": "cn-north-1",
"az": "cn-north-1c",
"instance_name": "created_by_packer",
"instance_type": "g.n2.medium",
"ssh_password": "/Users/mac/.ssh/id_rsa",
"ssh_keypair_name": "created_by_xiaohan",
"image_name": "packerImage",
"subnet_id": "subnet-jo6e38sdli",
"communicator": "ssh",
"ssh_username": "root"
}
],
"provisioners": [
{
"type": "shell",
"inline": ["sleep 3", "echo 123", "pwd"]
}
]
}
```
[Find more examples](https://github.com/hashicorp/packer/tree/master/builder/jdcloud/examples)

View File

@ -1,110 +0,0 @@
---
description: >
The linode Packer builder is able to create new images for use with Linode.
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 all Linode regions.
page_title: Linode - Builders
---
# Linode Builder
Type: `linode`
Artifact BuilderId: `packer.linode`
The `linode` Packer builder is able to create [Linode
Images](https://www.linode.com/docs/platform/disk-images/linode-images/) for
use with [Linode](https://www.linode.com). 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 Linode.
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. 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'
<!--
Linode.com has DDOS protection that returns 403 for the markdown link checker
so the domain has been added to the ignorepatterns in mlc_config.json
See https://github.com/tcort/markdown-link-check/issues/109
-->
### Required
- `linode_token` (string) - The client TOKEN to use to access your account.
- `image` (string) - An Image ID to deploy the Disk from. Official Linode
Images start with `linode/`, while user Images start with `private/`. See
[images](https://api.linode.com/v4/images) for more information on the
Images available for use. Examples are `linode/debian9`, `linode/fedora28`,
`linode/ubuntu18.04`, `linode/arch`, and `private/12345`.
- `region` (string) - The id of the region to launch the Linode instance in.
Images are available in all regions, but there will be less delay when
deploying from the region where the image was taken. Examples are
`us-east`, `us-central`, `us-west`, `ap-south`, `ca-east`, `ap-northeast`,
`eu-central`, and `eu-west`.
- `instance_type` (string) - The Linode type defines the pricing, CPU, disk,
and RAM specs of the instance. Examples are `g6-nanode-1`, `g6-standard-2`,
`g6-highmem-16`, and `g6-dedicated-16`.
### Optional
- `instance_label` (string) - The name assigned to the Linode Instance.
- `instance_tags` (list) - Tags to apply to the instance when it is created.
- `swap_size` (int) - The disk size (MiB) allocated for swap space.
- `image_label` (string) - The name of the resulting image that will appear
in your account. Defaults to `packer-{{timestamp}}` (see [configuration
templates](/docs/templates/legacy_json_templates/engine) for more info).
- `image_description` (string) - The description of the resulting image that
will appear in your account. Defaults to "".
- `state_timeout` (string) - The time to wait, as a duration string, for the
Linode instance to enter a desired state (such as "running") before timing
out. The default state timeout is "5m".
## Basic Example
Here is a Linode builder example. The `linode_token` should be replaced with an
actual [Linode Personal Access
Token](https://www.linode.com/docs/platform/api/getting-started-with-the-linode-api/#get-an-access-token).
```json
{
"type": "linode",
"linode_token": "YOUR API TOKEN",
"image": "linode/debian9",
"region": "us-east",
"instance_type": "g6-nanode-1",
"instance_label": "temporary-linode-{{timestamp}}",
"image_label": "private-image-{{timestamp}}",
"image_description": "My Private Image",
"ssh_username": "root"
}
```

View File

@ -708,10 +708,6 @@
"title": "Hetzner Cloud",
"path": "builders/hetzner-cloud"
},
{
"title": "Linode",
"path": "builders/linode"
},
{
"title": "LXC",
"path": "builders/lxc"
@ -753,10 +749,6 @@
"title": "Tencent Cloud",
"path": "builders/tencentcloud-cvm"
},
{
"title": "JDCloud",
"path": "builders/jdcloud"
},
{
"title": "Triton",
"path": "builders/triton"

View File

@ -59,6 +59,20 @@
"version": "latest",
"pluginTier": "community"
},
{
"title": "JD Cloud",
"path": "jdcloud",
"repo": "hashicorp/packer-plugin-jdcloud",
"pluginTier": "community",
"version": "latest"
},
{
"title": "Linode",
"path": "linode",
"repo": "hashicorp/packer-plugin-linode",
"pluginTier": "community",
"version": "latest"
},
{
"title": "Naver Cloud",
"path": "ncloud",