extract oracle plugin (#10962)
This commit is contained in:
parent
6b59525408
commit
f161f2bed2
|
@ -1,49 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Artifact is an artifact implementation that contains Image List
|
||||
// and Machine Image info.
|
||||
type Artifact struct {
|
||||
MachineImageName string
|
||||
MachineImageFile string
|
||||
ImageListVersion int
|
||||
|
||||
// StateData should store data such as GeneratedData
|
||||
// to be shared with post-processors
|
||||
StateData map[string]interface{}
|
||||
}
|
||||
|
||||
// BuilderId uniquely identifies the builder.
|
||||
func (a *Artifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
// Files lists the files associated with an artifact. We don't have any files
|
||||
// as the custom image is stored server side.
|
||||
func (a *Artifact) Files() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Artifact) Id() string {
|
||||
return a.MachineImageName
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
return fmt.Sprintf("An image list entry was created: \n"+
|
||||
"Name: %s\n"+
|
||||
"File: %s\n"+
|
||||
"Version: %d",
|
||||
a.MachineImageName, a.MachineImageFile, a.ImageListVersion)
|
||||
}
|
||||
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
return a.StateData[name]
|
||||
}
|
||||
|
||||
// Destroy deletes the custom image associated with the artifact.
|
||||
func (a *Artifact) Destroy() error {
|
||||
return nil
|
||||
}
|
|
@ -1,206 +0,0 @@
|
|||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
|
||||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/go-oracle-terraform/opc"
|
||||
"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"
|
||||
ocommon "github.com/hashicorp/packer/builder/oracle/common"
|
||||
)
|
||||
|
||||
// BuilderId uniquely identifies the builder
|
||||
const BuilderId = "packer.oracle.classic"
|
||||
|
||||
// Builder is a builder implementation that creates Oracle OCI custom images.
|
||||
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) {
|
||||
err := b.config.Prepare(raws...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var errs *packersdk.MultiError
|
||||
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.PVConfig.Prepare(&b.config.ctx))
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, nil, errs
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
|
||||
loggingEnabled := os.Getenv("PACKER_OCI_CLASSIC_LOGGING") != ""
|
||||
httpClient := cleanhttp.DefaultClient()
|
||||
config := &opc.Config{
|
||||
Username: opc.String(b.config.Username),
|
||||
Password: opc.String(b.config.Password),
|
||||
IdentityDomain: opc.String(b.config.IdentityDomain),
|
||||
APIEndpoint: b.config.apiEndpointURL,
|
||||
LogLevel: opc.LogDebug,
|
||||
Logger: &Logger{loggingEnabled},
|
||||
// Logger: # Leave blank to use the default logger, or provide your own
|
||||
HTTPClient: httpClient,
|
||||
}
|
||||
// Create the Compute Client
|
||||
client, err := compute.NewComputeClient(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating OPC Compute Client: %s", err)
|
||||
}
|
||||
|
||||
runID := fmt.Sprintf("%s_%s", b.config.ImageName, os.Getenv("PACKER_RUN_UUID"))
|
||||
// Populate the state bag
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
state.Put("client", client)
|
||||
state.Put("run_id", runID)
|
||||
|
||||
var steps []multistep.Step
|
||||
if b.config.IsPV() {
|
||||
steps = []multistep.Step{
|
||||
&ocommon.StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
Comm: &b.config.Comm,
|
||||
DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName),
|
||||
},
|
||||
&stepCreateIPReservation{},
|
||||
&stepAddKeysToAPI{
|
||||
KeyName: fmt.Sprintf("packer-generated-key_%s", runID),
|
||||
},
|
||||
&stepSecurity{
|
||||
CommType: b.config.Comm.Type,
|
||||
SecurityListKey: "security_list_master",
|
||||
},
|
||||
&stepCreatePersistentVolume{
|
||||
VolumeSize: fmt.Sprintf("%d", b.config.PersistentVolumeSize),
|
||||
VolumeName: fmt.Sprintf("master-storage_%s", runID),
|
||||
ImageList: b.config.SourceImageList,
|
||||
ImageListEntry: b.config.SourceImageListEntry,
|
||||
Bootable: true,
|
||||
},
|
||||
&stepCreatePVMaster{
|
||||
Name: fmt.Sprintf("master-instance_%s", runID),
|
||||
VolumeName: fmt.Sprintf("master-storage_%s", runID),
|
||||
SecurityListKey: "security_list_master",
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.Comm,
|
||||
Host: communicator.CommHost(b.config.Comm.Host(), "instance_ip"),
|
||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&commonsteps.StepProvision{},
|
||||
&stepTerminatePVMaster{},
|
||||
&stepSecurity{
|
||||
SecurityListKey: "security_list_builder",
|
||||
CommType: "ssh",
|
||||
},
|
||||
&stepCreatePersistentVolume{
|
||||
// We double the master volume size because we need room to
|
||||
// tarball the disk image. We also need to chunk the tar ball,
|
||||
// but we can remove the original disk image first.
|
||||
VolumeSize: fmt.Sprintf("%d", b.config.PersistentVolumeSize*2),
|
||||
VolumeName: fmt.Sprintf("builder-storage_%s", runID),
|
||||
},
|
||||
&stepCreatePVBuilder{
|
||||
Name: fmt.Sprintf("builder-instance_%s", runID),
|
||||
BuilderVolumeName: fmt.Sprintf("builder-storage_%s", runID),
|
||||
SecurityListKey: "security_list_builder",
|
||||
},
|
||||
&stepAttachVolume{
|
||||
VolumeName: fmt.Sprintf("master-storage_%s", runID),
|
||||
Index: 2,
|
||||
InstanceInfoKey: "builder_instance_info",
|
||||
},
|
||||
&stepConnectBuilder{
|
||||
KeyName: fmt.Sprintf("packer-generated-key_%s", runID),
|
||||
StepConnectSSH: &communicator.StepConnectSSH{
|
||||
Config: &b.config.BuilderComm,
|
||||
Host: communicator.CommHost(b.config.Comm.Host(), "instance_ip"),
|
||||
SSHConfig: b.config.BuilderComm.SSHConfigFunc(),
|
||||
},
|
||||
},
|
||||
&stepUploadImage{
|
||||
UploadImageCommand: b.config.BuilderUploadImageCommand,
|
||||
},
|
||||
&stepCreateImage{},
|
||||
&stepListImages{},
|
||||
&commonsteps.StepCleanupTempKeys{
|
||||
Comm: &b.config.Comm,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// Build the steps
|
||||
steps = []multistep.Step{
|
||||
&ocommon.StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
Comm: &b.config.Comm,
|
||||
DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName),
|
||||
},
|
||||
&stepCreateIPReservation{},
|
||||
&stepAddKeysToAPI{
|
||||
Skip: b.config.Comm.Type != "ssh",
|
||||
KeyName: fmt.Sprintf("packer-generated-key_%s", runID),
|
||||
},
|
||||
&stepSecurity{
|
||||
SecurityListKey: "security_list",
|
||||
CommType: b.config.Comm.Type,
|
||||
},
|
||||
&stepCreateInstance{},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.Comm,
|
||||
Host: communicator.CommHost(b.config.Comm.Host(), "instance_ip"),
|
||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&commonsteps.StepProvision{},
|
||||
&commonsteps.StepCleanupTempKeys{
|
||||
Comm: &b.config.Comm,
|
||||
},
|
||||
&stepSnapshot{},
|
||||
&stepListImages{},
|
||||
}
|
||||
}
|
||||
|
||||
// Run the steps
|
||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(ctx, state)
|
||||
|
||||
// If there was an error, return that
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
// If there is no snapshot, then just return
|
||||
if _, ok := state.GetOk("machine_image_name"); !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Build the artifact and return it
|
||||
artifact := &Artifact{
|
||||
ImageListVersion: state.Get("image_list_version").(int),
|
||||
MachineImageName: state.Get("machine_image_name").(string),
|
||||
MachineImageFile: state.Get("machine_image_file").(string),
|
||||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
|
||||
}
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
// Cancel terminates a running build.
|
|
@ -1,184 +0,0 @@
|
|||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package classic
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"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"`
|
||||
PersistentVolumeSize *int `mapstructure:"persistent_volume_size" cty:"persistent_volume_size" hcl:"persistent_volume_size"`
|
||||
BuilderUploadImageCommand *string `mapstructure:"builder_upload_image_command" cty:"builder_upload_image_command" hcl:"builder_upload_image_command"`
|
||||
BuilderShape *string `mapstructure:"builder_shape" cty:"builder_shape" hcl:"builder_shape"`
|
||||
BuilderImageList *string `mapstructure:"builder_image_list" cty:"builder_image_list" hcl:"builder_image_list"`
|
||||
BuilderImageListEntry *int `mapstructure:"builder_image_list_entry" cty:"builder_image_list_entry" hcl:"builder_image_list_entry"`
|
||||
BuilderComm *communicator.FlatConfig `mapstructure:"builder_communicator" cty:"builder_communicator" hcl:"builder_communicator"`
|
||||
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"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
IdentityDomain *string `mapstructure:"identity_domain" cty:"identity_domain" hcl:"identity_domain"`
|
||||
APIEndpoint *string `mapstructure:"api_endpoint" cty:"api_endpoint" hcl:"api_endpoint"`
|
||||
ImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
|
||||
Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"`
|
||||
SourceImageList *string `mapstructure:"source_image_list" cty:"source_image_list" hcl:"source_image_list"`
|
||||
SourceImageListEntry *int `mapstructure:"source_image_list_entry" cty:"source_image_list_entry" hcl:"source_image_list_entry"`
|
||||
SnapshotTimeout *string `mapstructure:"snapshot_timeout" cty:"snapshot_timeout" hcl:"snapshot_timeout"`
|
||||
DestImageList *string `mapstructure:"dest_image_list" cty:"dest_image_list" hcl:"dest_image_list"`
|
||||
Attributes *string `mapstructure:"attributes" cty:"attributes" hcl:"attributes"`
|
||||
AttributesFile *string `mapstructure:"attributes_file" cty:"attributes_file" hcl:"attributes_file"`
|
||||
DestImageListDescription *string `mapstructure:"image_description" cty:"image_description" hcl:"image_description"`
|
||||
SSHSourceList *string `mapstructure:"ssh_source_list" cty:"ssh_source_list" hcl:"ssh_source_list"`
|
||||
}
|
||||
|
||||
// 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},
|
||||
"persistent_volume_size": &hcldec.AttrSpec{Name: "persistent_volume_size", Type: cty.Number, Required: false},
|
||||
"builder_upload_image_command": &hcldec.AttrSpec{Name: "builder_upload_image_command", Type: cty.String, Required: false},
|
||||
"builder_shape": &hcldec.AttrSpec{Name: "builder_shape", Type: cty.String, Required: false},
|
||||
"builder_image_list": &hcldec.AttrSpec{Name: "builder_image_list", Type: cty.String, Required: false},
|
||||
"builder_image_list_entry": &hcldec.AttrSpec{Name: "builder_image_list_entry", Type: cty.Number, Required: false},
|
||||
"builder_communicator": &hcldec.BlockSpec{TypeName: "builder_communicator", Nested: hcldec.ObjectSpec((*communicator.FlatConfig)(nil).HCL2Spec())},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
|
||||
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
|
||||
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
|
||||
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
|
||||
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
|
||||
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
|
||||
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
|
||||
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
|
||||
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
|
||||
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
|
||||
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
|
||||
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
|
||||
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
|
||||
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
|
||||
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
|
||||
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
|
||||
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
|
||||
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||
"identity_domain": &hcldec.AttrSpec{Name: "identity_domain", Type: cty.String, Required: false},
|
||||
"api_endpoint": &hcldec.AttrSpec{Name: "api_endpoint", Type: cty.String, Required: false},
|
||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||
"shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false},
|
||||
"source_image_list": &hcldec.AttrSpec{Name: "source_image_list", Type: cty.String, Required: false},
|
||||
"source_image_list_entry": &hcldec.AttrSpec{Name: "source_image_list_entry", Type: cty.Number, Required: false},
|
||||
"snapshot_timeout": &hcldec.AttrSpec{Name: "snapshot_timeout", Type: cty.String, Required: false},
|
||||
"dest_image_list": &hcldec.AttrSpec{Name: "dest_image_list", Type: cty.String, Required: false},
|
||||
"attributes": &hcldec.AttrSpec{Name: "attributes", Type: cty.String, Required: false},
|
||||
"attributes_file": &hcldec.AttrSpec{Name: "attributes_file", Type: cty.String, Required: false},
|
||||
"image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false},
|
||||
"ssh_source_list": &hcldec.AttrSpec{Name: "ssh_source_list", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"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"`
|
||||
PVConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
attribs map[string]interface{}
|
||||
|
||||
// Access config overrides
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
IdentityDomain string `mapstructure:"identity_domain"`
|
||||
APIEndpoint string `mapstructure:"api_endpoint"`
|
||||
apiEndpointURL *url.URL
|
||||
|
||||
// Image
|
||||
ImageName string `mapstructure:"image_name"`
|
||||
Shape string `mapstructure:"shape"`
|
||||
SourceImageList string `mapstructure:"source_image_list"`
|
||||
SourceImageListEntry int `mapstructure:"source_image_list_entry"`
|
||||
SnapshotTimeout time.Duration `mapstructure:"snapshot_timeout"`
|
||||
DestImageList string `mapstructure:"dest_image_list"`
|
||||
// Attributes and Attributes file are both optional and mutually exclusive.
|
||||
Attributes string `mapstructure:"attributes"`
|
||||
AttributesFile string `mapstructure:"attributes_file"`
|
||||
// Optional; if you don't enter anything, the image list description
|
||||
// will read "Packer-built image list"
|
||||
DestImageListDescription string `mapstructure:"image_description"`
|
||||
// Optional. Describes what computers are allowed to reach your instance
|
||||
// via SSH. This whitelist must contain the computer you're running Packer
|
||||
// from. It defaults to public-internet, meaning that you can SSH into your
|
||||
// instance from anywhere as long as you have the right keys
|
||||
SSHSourceList string `mapstructure:"ssh_source_list"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (c *Config) Identifier(s string) string {
|
||||
return fmt.Sprintf("/Compute-%s/%s/%s", c.IdentityDomain, c.Username, s)
|
||||
}
|
||||
|
||||
func (c *Config) Prepare(raws ...interface{}) error {
|
||||
|
||||
// Decode from template
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to mapstructure Config: %+v", err)
|
||||
}
|
||||
|
||||
c.apiEndpointURL, err = url.Parse(c.APIEndpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing API Endpoint: %s", err)
|
||||
}
|
||||
// Set default source list
|
||||
if c.SSHSourceList == "" {
|
||||
c.SSHSourceList = "seciplist:/oracle/public/public-internet"
|
||||
}
|
||||
|
||||
if c.SnapshotTimeout == 0 {
|
||||
c.SnapshotTimeout = 20 * time.Minute
|
||||
}
|
||||
|
||||
// Validate that all required fields are present
|
||||
var errs *packersdk.MultiError
|
||||
required := map[string]string{
|
||||
"username": c.Username,
|
||||
"password": c.Password,
|
||||
"api_endpoint": c.APIEndpoint,
|
||||
"identity_domain": c.IdentityDomain,
|
||||
"source_image_list": c.SourceImageList,
|
||||
"dest_image_list": c.DestImageList,
|
||||
"shape": c.Shape,
|
||||
}
|
||||
for k, v := range required {
|
||||
if v == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("You must specify a %s.", k))
|
||||
}
|
||||
}
|
||||
|
||||
// Object names can contain only alphanumeric characters, hyphens, underscores, and periods
|
||||
reValidObject := regexp.MustCompile("^[a-zA-Z0-9-._/]+$")
|
||||
var objectValidation = []struct {
|
||||
name string
|
||||
value string
|
||||
}{
|
||||
{"dest_image_list", c.DestImageList},
|
||||
{"image_name", c.ImageName},
|
||||
}
|
||||
for _, ov := range objectValidation {
|
||||
if !reValidObject.MatchString(ov.value) {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s can contain only alphanumeric characters, hyphens, underscores, and periods.", ov.name))
|
||||
}
|
||||
}
|
||||
|
||||
if c.Attributes != "" && c.AttributesFile != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
|
||||
} else if c.AttributesFile != "" {
|
||||
if _, err := os.Stat(c.AttributesFile); err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("attributes_file not found: %s", c.AttributesFile))
|
||||
}
|
||||
}
|
||||
|
||||
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
||||
errs = packersdk.MultiErrorAppend(errs, es...)
|
||||
}
|
||||
|
||||
// unpack attributes from json into config
|
||||
var data map[string]interface{}
|
||||
|
||||
if c.Attributes != "" {
|
||||
err := json.Unmarshal([]byte(c.Attributes), &data)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem parsing json from attributes: %s", err)
|
||||
errs = packersdk.MultiErrorAppend(errs, err)
|
||||
}
|
||||
c.attribs = data
|
||||
} else if c.AttributesFile != "" {
|
||||
fidata, err := ioutil.ReadFile(c.AttributesFile)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem reading attributes_file: %s", err)
|
||||
errs = packersdk.MultiErrorAppend(errs, err)
|
||||
}
|
||||
err = json.Unmarshal(fidata, &data)
|
||||
c.attribs = data
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem parsing json from attributes_file: %s", err)
|
||||
errs = packersdk.MultiErrorAppend(errs, err)
|
||||
}
|
||||
c.attribs = data
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"identity_domain": "abc12345",
|
||||
"username": "test@hashicorp.com",
|
||||
"password": "testpassword123",
|
||||
"api_endpoint": "https://api-test.compute.test.oraclecloud.com/",
|
||||
"dest_image_list": "/Config-thing/myuser/myimage",
|
||||
"source_image_list": "/oracle/public/whatever",
|
||||
"shape": "oc3",
|
||||
"image_name": "TestImageName",
|
||||
"ssh_username": "opc",
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigAutoFillsSourceList(t *testing.T) {
|
||||
tc := testConfig()
|
||||
var conf Config
|
||||
err := conf.Prepare(tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Should not have error: %s", err.Error())
|
||||
}
|
||||
if conf.SSHSourceList != "seciplist:/oracle/public/public-internet" {
|
||||
t.Fatalf("conf.SSHSourceList should have been "+
|
||||
"\"seciplist:/oracle/public/public-internet\" but is \"%s\"",
|
||||
conf.SSHSourceList)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidationCatchesMissing(t *testing.T) {
|
||||
required := []string{
|
||||
"username",
|
||||
"password",
|
||||
"api_endpoint",
|
||||
"identity_domain",
|
||||
"dest_image_list",
|
||||
"source_image_list",
|
||||
"shape",
|
||||
"ssh_username",
|
||||
}
|
||||
for _, key := range required {
|
||||
tc := testConfig()
|
||||
delete(tc, key)
|
||||
var c Config
|
||||
err := c.Prepare(tc)
|
||||
if err == nil {
|
||||
t.Fatalf("Test should have failed when config lacked %s!", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigValidatesObjects(t *testing.T) {
|
||||
var objectTests = []struct {
|
||||
object string
|
||||
valid bool
|
||||
}{
|
||||
{"foo-BAR.0_9", true},
|
||||
{"%", false},
|
||||
{"Matt...?", false},
|
||||
{"/Config-thing/myuser/myimage", true},
|
||||
}
|
||||
for _, s := range []string{"dest_image_list", "image_name"} {
|
||||
for _, tt := range objectTests {
|
||||
tc := testConfig()
|
||||
tc[s] = tt.object
|
||||
var c Config
|
||||
err := c.Prepare(tc)
|
||||
if tt.valid {
|
||||
assert.NoError(t, err, tt.object)
|
||||
} else {
|
||||
assert.Error(t, err, tt.object)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package classic
|
||||
|
||||
import "log"
|
||||
|
||||
type Logger struct {
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
func (l *Logger) Log(input ...interface{}) {
|
||||
if !l.Enabled {
|
||||
return
|
||||
}
|
||||
log.Println(input...)
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
const imageListDefault = "/oracle/public/OL_7.2_UEKR4_x86_64"
|
||||
const imageListEntryDefault = 5
|
||||
const usernameDefault = "opc"
|
||||
const shapeDefault = "oc3"
|
||||
const uploadImageCommandDefault = `
|
||||
# https://www.oracle.com/webfolder/technetwork/tutorials/obe/cloud/objectstorage/upload_files_gt_5GB_REST_API/upload_files_gt_5GB_REST_API.html
|
||||
|
||||
# Split diskimage in to 100mb chunks
|
||||
split -b 100m diskimage.tar.gz segment_
|
||||
printf "Split diskimage into %s segments\n" $(ls segment_* | wc -l)
|
||||
|
||||
# Download jq tool
|
||||
curl -OLs https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64
|
||||
mv jq-linux64 jq
|
||||
chmod u+x jq
|
||||
|
||||
# Create manifest file
|
||||
(
|
||||
for i in segment_*; do
|
||||
./jq -n --arg path "{{.SegmentPath}}/$i" \
|
||||
--arg etag $(md5sum $i | cut -f1 -d' ') \
|
||||
--arg size_bytes $(stat --printf "%s" $i) \
|
||||
'{path: $path, etag: $etag, size_bytes: $size_bytes}'
|
||||
done
|
||||
) | ./jq -s . > manifest.json
|
||||
|
||||
# Authenticate
|
||||
curl -D auth-headers -s -X GET \
|
||||
-H "X-Storage-User: Storage-{{.AccountID}}:{{.Username}}" \
|
||||
-H "X-Storage-Pass: {{.Password}}" \
|
||||
https://{{.AccountID}}.storage.oraclecloud.com/auth/v1.0
|
||||
|
||||
export AUTH_TOKEN=$(awk 'BEGIN {FS=": "; RS="\r\n"}/^X-Auth-Token/{print $2}' auth-headers)
|
||||
export STORAGE_URL=$(awk 'BEGIN {FS=": "; RS="\r\n"}/^X-Storage-Url/{print $2}' auth-headers)
|
||||
|
||||
# Upload segments
|
||||
for i in segment_*; do
|
||||
echo "Uploading segment $i"
|
||||
|
||||
curl -s -X PUT -T $i \
|
||||
-H "X-Auth-Token: $AUTH_TOKEN" ${STORAGE_URL}/{{.SegmentPath}}/$i;
|
||||
done
|
||||
|
||||
# Create machine image from manifest
|
||||
curl -s -X PUT \
|
||||
-H "X-Auth-Token: $AUTH_TOKEN" \
|
||||
"${STORAGE_URL}/compute_images/{{.ImageFile}}?multipart-manifest=put" \
|
||||
-T ./manifest.json
|
||||
|
||||
# Get uploaded image description
|
||||
echo "Details of ${STORAGE_URL}/compute_images/{{.ImageFile}}:"
|
||||
curl -I -X HEAD \
|
||||
-H "X-Auth-Token: $AUTH_TOKEN" \
|
||||
"${STORAGE_URL}/compute_images/{{.ImageFile}}"
|
||||
`
|
||||
|
||||
type PVConfig struct {
|
||||
// PersistentVolumeSize lets us control the volume size by using persistent boot storage
|
||||
PersistentVolumeSize int `mapstructure:"persistent_volume_size"`
|
||||
BuilderUploadImageCommand string `mapstructure:"builder_upload_image_command"`
|
||||
|
||||
// Builder Image
|
||||
BuilderShape string `mapstructure:"builder_shape"`
|
||||
BuilderImageList string `mapstructure:"builder_image_list"`
|
||||
BuilderImageListEntry *int `mapstructure:"builder_image_list_entry"`
|
||||
|
||||
BuilderComm communicator.Config `mapstructure:"builder_communicator"`
|
||||
}
|
||||
|
||||
// IsPV tells us if we're using a persistent volume for this build
|
||||
func (c *PVConfig) IsPV() bool {
|
||||
return c.PersistentVolumeSize > 0
|
||||
}
|
||||
|
||||
func (c *PVConfig) Prepare(ctx *interpolate.Context) (errs *packersdk.MultiError) {
|
||||
if !c.IsPV() {
|
||||
if c.BuilderShape != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs,
|
||||
fmt.Errorf("`builder_shape` has no meaning when `persistent_volume_size` is not set."))
|
||||
}
|
||||
if c.BuilderImageList != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs,
|
||||
fmt.Errorf("`builder_image_list` has no meaning when `persistent_volume_size` is not set."))
|
||||
}
|
||||
|
||||
if c.BuilderImageListEntry != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs,
|
||||
fmt.Errorf("`builder_image_list_entry` has no meaning when `persistent_volume_size` is not set."))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
c.BuilderComm.SSHPty = true
|
||||
if c.BuilderComm.Type == "winrm" {
|
||||
errs = packersdk.MultiErrorAppend(errs,
|
||||
fmt.Errorf("`ssh` is the only valid builder communicator type."))
|
||||
}
|
||||
|
||||
if c.BuilderShape == "" {
|
||||
c.BuilderShape = shapeDefault
|
||||
}
|
||||
|
||||
if c.BuilderComm.SSHUsername == "" {
|
||||
c.BuilderComm.SSHUsername = usernameDefault
|
||||
}
|
||||
|
||||
if c.BuilderImageList == "" {
|
||||
c.BuilderImageList = imageListDefault
|
||||
}
|
||||
|
||||
// Set to known working default if this is unset and we're using the
|
||||
// default image list
|
||||
if c.BuilderImageListEntry == nil {
|
||||
var entry int
|
||||
if c.BuilderImageList == imageListDefault {
|
||||
entry = imageListEntryDefault
|
||||
}
|
||||
c.BuilderImageListEntry = &entry
|
||||
}
|
||||
|
||||
if c.BuilderUploadImageCommand == "" {
|
||||
c.BuilderUploadImageCommand = uploadImageCommandDefault
|
||||
}
|
||||
|
||||
if es := c.BuilderComm.Prepare(ctx); len(es) > 0 {
|
||||
errs = packersdk.MultiErrorAppend(errs, es...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPVConfigEntry(t *testing.T) {
|
||||
entry := 1
|
||||
var entryTests = []struct {
|
||||
imageList string
|
||||
imageListEntry *int
|
||||
expected int
|
||||
}{
|
||||
{"x", nil, 0},
|
||||
{"x", &entry, 1},
|
||||
{imageListDefault, nil, 5},
|
||||
{imageListDefault, &entry, 1},
|
||||
}
|
||||
for _, tt := range entryTests {
|
||||
tc := &PVConfig{
|
||||
PersistentVolumeSize: 1,
|
||||
BuilderImageList: tt.imageList,
|
||||
BuilderImageListEntry: tt.imageListEntry,
|
||||
}
|
||||
errs := tc.Prepare(nil)
|
||||
assert.Nil(t, errs, "Didn't expect any errors")
|
||||
assert.Equal(t, tt.expected, *tc.BuilderImageListEntry)
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepAddKeysToAPI struct {
|
||||
Skip bool
|
||||
KeyName string
|
||||
}
|
||||
|
||||
func (s *stepAddKeysToAPI) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// get variables from state
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
|
||||
if s.Skip {
|
||||
ui.Say("Skipping generating SSH keys...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
// grab packer-generated key from statebag context.
|
||||
// Always check configured communicator for keys
|
||||
sshPublicKey := bytes.TrimSpace(config.Comm.SSHPublicKey)
|
||||
|
||||
// form API call to add key to compute cloud
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating temporary key: %s", s.KeyName))
|
||||
|
||||
sshKeysClient := client.SSHKeys()
|
||||
sshKeysInput := compute.CreateSSHKeyInput{
|
||||
Name: s.KeyName,
|
||||
Key: string(sshPublicKey),
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
// Load the packer-generated SSH key into the Oracle Compute cloud.
|
||||
keyInfo, err := sshKeysClient.CreateSSHKey(&sshKeysInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem adding Public SSH key through Oracle's API: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
config.Comm.SSHKeyPairName = keyInfo.Name
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepAddKeysToAPI) Cleanup(state multistep.StateBag) {
|
||||
if s.Skip {
|
||||
return
|
||||
}
|
||||
config := state.Get("config").(*Config)
|
||||
// Delete the keys we created during this run
|
||||
if len(config.Comm.SSHKeyPairName) == 0 {
|
||||
// No keys were generated; none need to be cleaned up.
|
||||
return
|
||||
}
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Deleting SSH keys...")
|
||||
deleteInput := compute.DeleteSSHKeyInput{Name: config.Comm.SSHKeyPairName}
|
||||
client := state.Get("client").(*compute.Client)
|
||||
deleteClient := client.SSHKeys()
|
||||
err := deleteClient.DeleteSSHKey(&deleteInput)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting SSH keys: %s", err.Error()))
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepAttachVolume struct {
|
||||
Index int
|
||||
VolumeName string
|
||||
InstanceInfoKey string
|
||||
}
|
||||
|
||||
func (s *stepAttachVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
client := state.Get("client").(*compute.Client)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
instanceInfo := state.Get(s.InstanceInfoKey).(*compute.InstanceInfo)
|
||||
|
||||
saClient := client.StorageAttachments()
|
||||
saInput := &compute.CreateStorageAttachmentInput{
|
||||
Index: s.Index,
|
||||
InstanceName: instanceInfo.Name + "/" + instanceInfo.ID,
|
||||
StorageVolumeName: s.VolumeName,
|
||||
}
|
||||
|
||||
sa, err := saClient.CreateStorageAttachment(saInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem attaching master volume: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put(s.InstanceInfoKey+"/attachment", sa)
|
||||
|
||||
ui.Message("Volume attached to instance.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepAttachVolume) Cleanup(state multistep.StateBag) {
|
||||
sa, ok := state.GetOk(s.InstanceInfoKey + "/attachment")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
client := state.Get("client").(*compute.Client)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
saClient := client.StorageAttachments()
|
||||
saI := &compute.DeleteStorageAttachmentInput{
|
||||
Name: sa.(*compute.StorageAttachmentInfo).Name,
|
||||
}
|
||||
|
||||
if err := saClient.DeleteStorageAttachment(saI); err != nil {
|
||||
err = fmt.Errorf("Problem detaching storage volume: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
type stepConnectBuilder struct {
|
||||
*communicator.StepConnectSSH
|
||||
KeyName string
|
||||
}
|
||||
|
||||
func (s *stepConnectBuilder) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
s.Config.SSHKeyPairName = s.KeyName
|
||||
return s.StepConnectSSH.Run(ctx, state)
|
||||
}
|
||||
|
||||
func (s *stepConnectBuilder) Cleanup(state multistep.StateBag) {
|
||||
s.StepConnectSSH.Cleanup(state)
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepCreateImage struct {
|
||||
}
|
||||
|
||||
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
config := state.Get("config").(*Config)
|
||||
imageFile := state.Get("image_file").(string)
|
||||
|
||||
// Image uploaded, let's register it
|
||||
machineImageClient := client.MachineImages()
|
||||
createMI := &compute.CreateMachineImageInput{
|
||||
// Two-part name of the account
|
||||
Account: fmt.Sprintf("/Compute-%s/cloud_storage", config.IdentityDomain),
|
||||
Description: "Packer generated Machine Image.",
|
||||
// The three-part name of the object
|
||||
Name: config.ImageName,
|
||||
// image_file.tar.gz, where image_file is the .tar.gz name of the
|
||||
// machine image file that you have uploaded to Oracle Cloud
|
||||
// Infrastructure Object Storage Classic.
|
||||
File: imageFile,
|
||||
}
|
||||
mi, err := machineImageClient.CreateMachineImage(createMI)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating machine image: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
log.Println("Registered machine image.")
|
||||
state.Put("machine_image", mi.Name)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreateImage) Cleanup(state multistep.StateBag) {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
if !cancelled && !halted {
|
||||
return
|
||||
}
|
||||
|
||||
client := state.Get("client").(*compute.Client)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Cleaning up Image...")
|
||||
|
||||
machineImageClient := client.MachineImages()
|
||||
deleteMI := &compute.DeleteMachineImageInput{
|
||||
Name: config.ImageName,
|
||||
}
|
||||
|
||||
if err := machineImageClient.DeleteMachineImage(deleteMI); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error cleaning up machine image: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Deleted Image: %s", config.ImageName))
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepCreateInstance struct{}
|
||||
|
||||
func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// get variables from state
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Creating Instance...")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
ipAddName := state.Get("ipres_name").(string)
|
||||
secListName := state.Get("security_list").(string)
|
||||
|
||||
netInfo := compute.NetworkingInfo{
|
||||
Nat: []string{ipAddName},
|
||||
SecLists: []string{secListName},
|
||||
}
|
||||
|
||||
// get instances client
|
||||
instanceClient := client.Instances()
|
||||
|
||||
// Instances Input
|
||||
input := &compute.CreateInstanceInput{
|
||||
Name: config.ImageName,
|
||||
Shape: config.Shape,
|
||||
ImageList: config.SourceImageList,
|
||||
Entry: config.SourceImageListEntry,
|
||||
Networking: map[string]compute.NetworkingInfo{"eth0": netInfo},
|
||||
Attributes: config.attribs,
|
||||
}
|
||||
if config.Comm.Type == "ssh" {
|
||||
input.SSHKeys = []string{config.Comm.SSHKeyPairName}
|
||||
}
|
||||
|
||||
instanceInfo, err := instanceClient.CreateInstance(input)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("instance_info", instanceInfo)
|
||||
state.Put("instance_id", instanceInfo.ID)
|
||||
ui.Message(fmt.Sprintf("Created instance: %s.", instanceInfo.ID))
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||
instanceID, ok := state.GetOk("instance_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// terminate instance
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
ui.Say("Terminating source instance...")
|
||||
|
||||
instanceClient := client.Instances()
|
||||
input := &compute.DeleteInstanceInput{
|
||||
Name: config.ImageName,
|
||||
ID: instanceID.(string),
|
||||
}
|
||||
|
||||
err := instanceClient.DeleteInstance(input)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem destroying instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return
|
||||
}
|
||||
ui.Say("Terminated instance.")
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/uuid"
|
||||
)
|
||||
|
||||
type stepCreateIPReservation struct{}
|
||||
|
||||
func (s *stepCreateIPReservation) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
iprClient := client.IPReservations()
|
||||
// TODO: add optional Name and Tags
|
||||
|
||||
ipresName := fmt.Sprintf("ipres_%s_%s", config.ImageName, uuid.TimeOrderedUUID())
|
||||
ui.Message(fmt.Sprintf("Creating temporary IP reservation: %s", ipresName))
|
||||
|
||||
IPInput := &compute.CreateIPReservationInput{
|
||||
ParentPool: compute.PublicReservationPool,
|
||||
Permanent: true,
|
||||
Name: ipresName,
|
||||
}
|
||||
ipRes, err := iprClient.CreateIPReservation(IPInput)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating IP Reservation: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("instance_ip", ipRes.IP)
|
||||
state.Put("ipres_name", ipresName)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreateIPReservation) Cleanup(state multistep.StateBag) {
|
||||
ipResName, ok := state.GetOk("ipres_name")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Cleaning up IP reservations...")
|
||||
client := state.Get("client").(*compute.Client)
|
||||
|
||||
input := compute.DeleteIPReservationInput{Name: ipResName.(string)}
|
||||
ipClient := client.IPReservations()
|
||||
err := ipClient.DeleteIPReservation(&input)
|
||||
if err != nil {
|
||||
fmt.Printf("error deleting IP reservation: %s", err.Error())
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepCreatePersistentVolume struct {
|
||||
VolumeSize string
|
||||
VolumeName string
|
||||
Bootable bool
|
||||
ImageList string
|
||||
ImageListEntry int
|
||||
}
|
||||
|
||||
func (s *stepCreatePersistentVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
client := state.Get("client").(*compute.Client)
|
||||
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Creating Volume...")
|
||||
|
||||
c := &compute.CreateStorageVolumeInput{
|
||||
Name: s.VolumeName,
|
||||
Size: s.VolumeSize,
|
||||
ImageList: s.ImageList,
|
||||
ImageListEntry: s.ImageListEntry,
|
||||
Properties: []string{"/oracle/public/storage/default"},
|
||||
Bootable: s.Bootable,
|
||||
}
|
||||
|
||||
sc := client.StorageVolumes()
|
||||
cc, err := sc.CreateStorageVolume(c)
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating persistent storage volume: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Created volume: %s", cc.Name))
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreatePersistentVolume) Cleanup(state multistep.StateBag) {
|
||||
client := state.Get("client").(*compute.Client)
|
||||
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Cleaning up Volume...")
|
||||
|
||||
c := &compute.DeleteStorageVolumeInput{
|
||||
Name: s.VolumeName,
|
||||
}
|
||||
|
||||
sc := client.StorageVolumes()
|
||||
|
||||
if err := sc.DeleteStorageVolume(c); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error cleaning up persistent storage volume: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Deleted volume: %s", s.VolumeName))
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepCreatePVBuilder struct {
|
||||
Name string
|
||||
BuilderVolumeName string
|
||||
SecurityListKey string
|
||||
}
|
||||
|
||||
func (s *stepCreatePVBuilder) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// get variables from state
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Creating builder instance...")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
ipAddName := state.Get("ipres_name").(string)
|
||||
secListName := state.Get(s.SecurityListKey).(string)
|
||||
|
||||
// get instances client
|
||||
instanceClient := client.Instances()
|
||||
|
||||
// Instances Input
|
||||
input := &compute.CreateInstanceInput{
|
||||
Name: s.Name,
|
||||
Shape: config.BuilderShape,
|
||||
Networking: map[string]compute.NetworkingInfo{
|
||||
"eth0": {
|
||||
Nat: []string{ipAddName},
|
||||
SecLists: []string{secListName},
|
||||
},
|
||||
},
|
||||
Storage: []compute.StorageAttachmentInput{
|
||||
{
|
||||
Volume: s.BuilderVolumeName,
|
||||
Index: 1,
|
||||
},
|
||||
},
|
||||
ImageList: config.BuilderImageList,
|
||||
SSHKeys: []string{config.Comm.SSHKeyPairName},
|
||||
Entry: *config.BuilderImageListEntry,
|
||||
}
|
||||
|
||||
instanceInfo, err := instanceClient.CreateInstance(input)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
log.Printf("Created instance %s", instanceInfo.Name)
|
||||
|
||||
state.Put("builder_instance_info", instanceInfo)
|
||||
state.Put("builder_instance_id", instanceInfo.ID)
|
||||
|
||||
ui.Message(fmt.Sprintf("Created builder instance: %s.", instanceInfo.Name))
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreatePVBuilder) Cleanup(state multistep.StateBag) {
|
||||
instanceID, ok := state.GetOk("builder_instance_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// terminate instance
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
|
||||
ui.Say("Terminating builder instance...")
|
||||
|
||||
instanceClient := client.Instances()
|
||||
input := &compute.DeleteInstanceInput{
|
||||
Name: s.Name,
|
||||
ID: instanceID.(string),
|
||||
}
|
||||
|
||||
err := instanceClient.DeleteInstance(input)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem destroying instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return
|
||||
}
|
||||
ui.Say("Terminated builder instance.")
|
||||
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepCreatePVMaster struct {
|
||||
Name string
|
||||
VolumeName string
|
||||
SecurityListKey string
|
||||
}
|
||||
|
||||
func (s *stepCreatePVMaster) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Creating master instance...")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
ipAddName := state.Get("ipres_name").(string)
|
||||
secListName := state.Get(s.SecurityListKey).(string)
|
||||
|
||||
// get instances client
|
||||
instanceClient := client.Instances()
|
||||
|
||||
// Instances Input
|
||||
input := &compute.CreateInstanceInput{
|
||||
Name: s.Name,
|
||||
Shape: config.Shape,
|
||||
Networking: map[string]compute.NetworkingInfo{
|
||||
"eth0": {
|
||||
Nat: []string{ipAddName},
|
||||
SecLists: []string{secListName},
|
||||
},
|
||||
},
|
||||
Storage: []compute.StorageAttachmentInput{
|
||||
{
|
||||
Volume: s.VolumeName,
|
||||
Index: 1,
|
||||
},
|
||||
},
|
||||
BootOrder: []int{1},
|
||||
Attributes: config.attribs,
|
||||
}
|
||||
if config.Comm.Type == "ssh" {
|
||||
input.SSHKeys = []string{config.Comm.SSHKeyPairName}
|
||||
}
|
||||
|
||||
instanceInfo, err := instanceClient.CreateInstance(input)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("master_instance_info", instanceInfo)
|
||||
state.Put("master_instance_id", instanceInfo.ID)
|
||||
ui.Message(fmt.Sprintf("Created master instance: %s.", instanceInfo.Name))
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreatePVMaster) Cleanup(state multistep.StateBag) {
|
||||
if _, deleted := state.GetOk("master_instance_deleted"); deleted {
|
||||
return
|
||||
}
|
||||
|
||||
instanceID, ok := state.GetOk("master_instance_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// terminate instance
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
|
||||
ui.Say("Terminating builder instance...")
|
||||
|
||||
instanceClient := client.Instances()
|
||||
input := &compute.DeleteInstanceInput{
|
||||
Name: s.Name,
|
||||
ID: instanceID.(string),
|
||||
}
|
||||
|
||||
err := instanceClient.DeleteInstance(input)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem destroying instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return
|
||||
}
|
||||
ui.Say("Terminated master instance.")
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepListImages struct{}
|
||||
|
||||
func (s *stepListImages) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// get variables from state
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
ui.Say("Adding image to image list...")
|
||||
|
||||
imageListClient := client.ImageList()
|
||||
getInput := compute.GetImageListInput{
|
||||
Name: config.DestImageList,
|
||||
}
|
||||
imList, err := imageListClient.GetImageList(&getInput)
|
||||
if err != nil {
|
||||
// If the list didn't exist, create it.
|
||||
ui.Say(fmt.Sprintf(err.Error()))
|
||||
ui.Say(fmt.Sprintf("Destination image list %s does not exist; Creating it...",
|
||||
config.DestImageList))
|
||||
|
||||
ilInput := compute.CreateImageListInput{
|
||||
Name: config.DestImageList,
|
||||
Description: "Packer-built image list",
|
||||
}
|
||||
|
||||
imList, err = imageListClient.CreateImageList(&ilInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating image list: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Message(fmt.Sprintf("Image list %s created!", imList.URI))
|
||||
}
|
||||
|
||||
// Now create and image list entry for the image into that list.
|
||||
machineImage := state.Get("machine_image").(string)
|
||||
version := len(imList.Entries) + 1
|
||||
entriesClient := client.ImageListEntries()
|
||||
entriesInput := compute.CreateImageListEntryInput{
|
||||
Name: config.DestImageList,
|
||||
MachineImages: []string{config.Identifier(machineImage)},
|
||||
Version: version,
|
||||
}
|
||||
entryInfo, err := entriesClient.CreateImageListEntry(&entriesInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating an image list entry: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("image_list_entry", entryInfo)
|
||||
ui.Message(fmt.Sprintf("created image list entry %s", entryInfo.Name))
|
||||
|
||||
machineImagesClient := client.MachineImages()
|
||||
getImagesInput := compute.GetMachineImageInput{
|
||||
Name: config.ImageName,
|
||||
}
|
||||
|
||||
// Update image list default to use latest version
|
||||
updateInput := compute.UpdateImageListInput{
|
||||
Default: version,
|
||||
Description: config.DestImageListDescription,
|
||||
Name: config.DestImageList,
|
||||
}
|
||||
_, err = imageListClient.UpdateImageList(&updateInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem updating default image list version: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Grab info about the machine image to return with the artifact
|
||||
imInfo, err := machineImagesClient.GetMachineImage(&getImagesInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem getting machine image info: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("machine_image_file", imInfo.File)
|
||||
state.Put("machine_image_name", imInfo.Name)
|
||||
state.Put("image_list_version", version)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepListImages) Cleanup(state multistep.StateBag) {
|
||||
// Nothing to do
|
||||
return
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepSecurity struct {
|
||||
CommType string
|
||||
SecurityListKey string
|
||||
secListName string
|
||||
secRuleName string
|
||||
}
|
||||
|
||||
func (s *stepSecurity) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
runID := state.Get("run_id").(string)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
|
||||
commType := ""
|
||||
if s.CommType == "ssh" {
|
||||
commType = "SSH"
|
||||
} else if s.CommType == "winrm" {
|
||||
commType = "WINRM"
|
||||
}
|
||||
secListName := fmt.Sprintf("Packer_%s_Allow_%s", commType, runID)
|
||||
|
||||
if _, ok := state.GetOk(secListName); ok {
|
||||
log.Println("SecList created in earlier step, continuing")
|
||||
// copy sec list name to proper key
|
||||
state.Put(s.SecurityListKey, secListName)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Configuring security lists and rules to enable %s access...", commType))
|
||||
log.Println(secListName)
|
||||
|
||||
secListClient := client.SecurityLists()
|
||||
secListInput := compute.CreateSecurityListInput{
|
||||
Description: fmt.Sprintf("Packer-generated security list to give packer %s access", commType),
|
||||
Name: config.Identifier(secListName),
|
||||
}
|
||||
_, err := secListClient.CreateSecurityList(&secListInput)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "already exists") {
|
||||
err = fmt.Errorf("Error creating security List to"+
|
||||
" allow Packer to connect to Oracle instance via %s: %s", commType, err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
// DOCS NOTE: user must have Compute_Operations role
|
||||
// Create security rule that allows Packer to connect via SSH or winRM
|
||||
var application string
|
||||
if commType == "SSH" {
|
||||
application = "/oracle/public/ssh"
|
||||
} else if commType == "WINRM" {
|
||||
// Check to see whether a winRM security application is already defined
|
||||
applicationClient := client.SecurityApplications()
|
||||
application = fmt.Sprintf("packer_winRM_%s", runID)
|
||||
applicationInput := compute.CreateSecurityApplicationInput{
|
||||
Description: "Allows Packer to connect to instance via winRM",
|
||||
DPort: "5985-5986",
|
||||
Name: application,
|
||||
Protocol: "TCP",
|
||||
}
|
||||
_, err = applicationClient.CreateSecurityApplication(&applicationInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating security application to"+
|
||||
" allow Packer to connect to Oracle instance via %s: %s", commType, err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("winrm_application", application)
|
||||
}
|
||||
secRulesClient := client.SecRules()
|
||||
secRuleName := fmt.Sprintf("Packer-allow-%s-Rule_%s", commType, runID)
|
||||
log.Println(secRuleName)
|
||||
secRulesInput := compute.CreateSecRuleInput{
|
||||
Action: "PERMIT",
|
||||
Application: application,
|
||||
Description: "Packer-generated security rule to allow ssh/winrm",
|
||||
DestinationList: "seclist:" + config.Identifier(secListName),
|
||||
Name: config.Identifier(secRuleName),
|
||||
SourceList: config.SSHSourceList,
|
||||
}
|
||||
|
||||
_, err = secRulesClient.CreateSecRule(&secRulesInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating security rule to"+
|
||||
" allow Packer to connect to Oracle instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put(s.SecurityListKey, secListName)
|
||||
state.Put(secListName, true)
|
||||
s.secListName = secListName
|
||||
s.secRuleName = secRuleName
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepSecurity) Cleanup(state multistep.StateBag) {
|
||||
if s.secListName == "" || s.secRuleName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
client := state.Get("client").(*compute.Client)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
ui.Say("Deleting temporary rules and lists...")
|
||||
|
||||
// delete security rules that Packer generated
|
||||
secRulesClient := client.SecRules()
|
||||
ruleInput := compute.DeleteSecRuleInput{
|
||||
Name: config.Identifier(s.secRuleName),
|
||||
}
|
||||
err := secRulesClient.DeleteSecRule(&ruleInput)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Error deleting the packer-generated security rule %s; "+
|
||||
"please delete manually. (error: %s)", s.secRuleName, err.Error()))
|
||||
}
|
||||
|
||||
// delete security list that Packer generated
|
||||
secListClient := client.SecurityLists()
|
||||
input := compute.DeleteSecurityListInput{Name: config.Identifier(s.secListName)}
|
||||
err = secListClient.DeleteSecurityList(&input)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+
|
||||
"please delete manually. (error : %s)", s.secListName, err.Error()))
|
||||
}
|
||||
|
||||
// Some extra cleanup if we used the winRM communicator
|
||||
if s.CommType == "winrm" {
|
||||
// Delete the packer-generated application
|
||||
application, ok := state.GetOk("winrm_application")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
applicationClient := client.SecurityApplications()
|
||||
deleteApplicationInput := compute.DeleteSecurityApplicationInput{
|
||||
Name: config.Identifier(application.(string)),
|
||||
}
|
||||
err = applicationClient.DeleteSecurityApplication(&deleteApplicationInput)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Error deleting the packer-generated winrm security application %s; "+
|
||||
"please delete manually. (error : %s)", application.(string), err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepSnapshot struct {
|
||||
cleanupSnap bool
|
||||
}
|
||||
|
||||
func (s *stepSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// get variables from state
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Creating Snapshot...")
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.Client)
|
||||
instanceID := state.Get("instance_id").(string)
|
||||
|
||||
// get instances client
|
||||
snapshotClient := client.Snapshots()
|
||||
|
||||
// Instances Input
|
||||
snapshotInput := &compute.CreateSnapshotInput{
|
||||
Instance: fmt.Sprintf("%s/%s", config.ImageName, instanceID),
|
||||
MachineImage: config.ImageName,
|
||||
Timeout: config.SnapshotTimeout,
|
||||
}
|
||||
|
||||
snap, err := snapshotClient.CreateSnapshot(snapshotInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating snapshot: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("snapshot", snap)
|
||||
state.Put("machine_image", snap.MachineImage)
|
||||
ui.Message(fmt.Sprintf("Created snapshot: %s.", snap.Name))
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepSnapshot) Cleanup(state multistep.StateBag) {
|
||||
// Delete the snapshot
|
||||
var snap *compute.Snapshot
|
||||
if snapshot, ok := state.GetOk("snapshot"); ok {
|
||||
snap = snapshot.(*compute.Snapshot)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Deleting Snapshot...")
|
||||
client := state.Get("client").(*compute.Client)
|
||||
snapClient := client.Snapshots()
|
||||
snapInput := compute.DeleteSnapshotInput{
|
||||
Snapshot: snap.Name,
|
||||
MachineImage: snap.MachineImage,
|
||||
}
|
||||
|
||||
err := snapClient.DeleteSnapshotResourceOnly(&snapInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem deleting snapshot: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepTerminatePVMaster struct {
|
||||
}
|
||||
|
||||
func (s *stepTerminatePVMaster) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// get variables from state
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
ui.Say("Deleting master Instance...")
|
||||
|
||||
client := state.Get("client").(*compute.Client)
|
||||
instanceInfo := state.Get("master_instance_info").(*compute.InstanceInfo)
|
||||
|
||||
// get instances client
|
||||
instanceClient := client.Instances()
|
||||
|
||||
// Instances Input
|
||||
input := &compute.DeleteInstanceInput{
|
||||
Name: instanceInfo.Name,
|
||||
ID: instanceInfo.ID,
|
||||
}
|
||||
|
||||
err := instanceClient.DeleteInstance(input)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Deleted master instance: %s.", instanceInfo.ID))
|
||||
state.Put("master_instance_deleted", true)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepTerminatePVMaster) Cleanup(state multistep.StateBag) {
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package classic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
type stepUploadImage struct {
|
||||
UploadImageCommand string
|
||||
segmentPath string
|
||||
}
|
||||
|
||||
type uploadCmdData struct {
|
||||
Username string
|
||||
Password string
|
||||
AccountID string
|
||||
ImageFile string
|
||||
SegmentPath string
|
||||
}
|
||||
|
||||
func (s *stepUploadImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
comm := state.Get("communicator").(packersdk.Communicator)
|
||||
config := state.Get("config").(*Config)
|
||||
runID := state.Get("run_id").(string)
|
||||
|
||||
imageFile := fmt.Sprintf("%s.tar.gz", config.ImageName)
|
||||
state.Put("image_file", imageFile)
|
||||
s.segmentPath = fmt.Sprintf("compute_images_segments/%s/_segment_/%s", imageFile, runID)
|
||||
|
||||
config.ctx.Data = uploadCmdData{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
AccountID: config.IdentityDomain,
|
||||
ImageFile: imageFile,
|
||||
SegmentPath: s.segmentPath,
|
||||
}
|
||||
uploadImageCmd, err := interpolate.Render(s.UploadImageCommand, &config.ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error processing image upload command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
command := fmt.Sprintf(`#!/bin/sh
|
||||
set -e
|
||||
mkdir /builder
|
||||
mkfs -t ext3 /dev/xvdb
|
||||
mount /dev/xvdb /builder
|
||||
chown opc:opc /builder
|
||||
cd /builder
|
||||
dd if=/dev/xvdc bs=8M status=progress | cp --sparse=always /dev/stdin diskimage.raw
|
||||
tar czSf ./diskimage.tar.gz ./diskimage.raw
|
||||
rm diskimage.raw
|
||||
%s`, uploadImageCmd)
|
||||
|
||||
dest := "/tmp/create-packer-diskimage.sh"
|
||||
comm.Upload(dest, strings.NewReader(command), nil)
|
||||
cmd := &packersdk.RemoteCmd{
|
||||
Command: fmt.Sprintf("sudo /bin/sh %s", dest),
|
||||
}
|
||||
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
|
||||
err = fmt.Errorf("Problem creating image`: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if cmd.ExitStatus() != 0 {
|
||||
err = fmt.Errorf("Create Disk Image command failed with exit code %d", cmd.ExitStatus())
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Uploaded image to object storage.")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepUploadImage) Cleanup(state multistep.StateBag) {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
if !cancelled && !halted {
|
||||
return
|
||||
}
|
||||
log.Printf("Some segments may need to be manually cleaned at '%s'", s.segmentPath)
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type StepKeyPair struct {
|
||||
Debug bool
|
||||
Comm *communicator.Config
|
||||
DebugKeyPath string
|
||||
}
|
||||
|
||||
func (s *StepKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if s.Comm.SSHPrivateKeyFile != "" {
|
||||
ui.Say("Using existing SSH private key")
|
||||
privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile()
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
key, err := ssh.ParsePrivateKey(privateKeyBytes)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error parsing 'ssh_private_key_file': %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.Comm.SSHPublicKey = ssh.MarshalAuthorizedKey(key.PublicKey())
|
||||
s.Comm.SSHPrivateKey = privateKeyBytes
|
||||
|
||||
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)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// ASN.1 DER encoded form
|
||||
privDer := x509.MarshalPKCS1PrivateKey(priv)
|
||||
privBlk := pem.Block{Type: "RSA PRIVATE KEY", Headers: nil, Bytes: privDer}
|
||||
|
||||
// Set the private key in the statebag for later
|
||||
state.Put("privateKey", string(pem.EncodeToMemory(&privBlk)))
|
||||
|
||||
// Marshal the public key into SSH compatible format
|
||||
pub, err := ssh.NewPublicKey(&priv.PublicKey)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error marshaling temporary SSH public key: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.Comm.SSHPublicKey = ssh.MarshalAuthorizedKey(pub)
|
||||
|
||||
// If we're in debug mode, output the private key to the working
|
||||
// directory.
|
||||
if s.Debug {
|
||||
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
|
||||
f, err := os.Create(s.DebugKeyPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error saving debug key: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Write the key out
|
||||
if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
|
||||
err = fmt.Errorf("Error saving debug key: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Chmod it so that it is SSH ready
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := f.Chmod(0600); err != nil {
|
||||
err = fmt.Errorf("Error setting permissions of debug key: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
|
||||
// Nothing to do
|
||||
}
|
|
@ -1,375 +0,0 @@
|
|||
Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
|
@ -1,57 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v36/core"
|
||||
)
|
||||
|
||||
// Artifact is an artifact implementation that contains a built Custom Image.
|
||||
type Artifact struct {
|
||||
Image core.Image
|
||||
Region string
|
||||
driver Driver
|
||||
|
||||
// StateData should store data such as GeneratedData
|
||||
// to be shared with post-processors
|
||||
StateData map[string]interface{}
|
||||
}
|
||||
|
||||
// BuilderId uniquely identifies the builder.
|
||||
func (a *Artifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
// Files lists the files associated with an artifact. We don't have any files
|
||||
// as the custom image is stored server side.
|
||||
func (a *Artifact) Files() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Id returns the OCID of the associated Image.
|
||||
func (a *Artifact) Id() string {
|
||||
return *a.Image.Id
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
var displayName string
|
||||
if a.Image.DisplayName != nil {
|
||||
displayName = *a.Image.DisplayName
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"An image was created: '%v' (OCID: %v) in region '%v'",
|
||||
displayName, *a.Image.Id, a.Region,
|
||||
)
|
||||
}
|
||||
|
||||
// State ...
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
return a.StateData[name]
|
||||
}
|
||||
|
||||
// Destroy deletes the custom image associated with the artifact.
|
||||
func (a *Artifact) Destroy() error {
|
||||
return a.driver.DeleteImage(context.TODO(), *a.Image.Id)
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestArtifactImpl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &Artifact{}
|
||||
if _, ok := raw.(packersdk.Artifact); !ok {
|
||||
t.Fatalf("Artifact should be artifact")
|
||||
}
|
||||
}
|
||||
|
||||
func TestArtifactState_StateData(t *testing.T) {
|
||||
expectedData := "this is the data"
|
||||
artifact := &Artifact{
|
||||
StateData: map[string]interface{}{"state_data": expectedData},
|
||||
}
|
||||
|
||||
// Valid state
|
||||
result := artifact.State("state_data")
|
||||
if result != expectedData {
|
||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
||||
}
|
||||
|
||||
// Invalid state
|
||||
result = artifact.State("invalid_key")
|
||||
if result != nil {
|
||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
||||
}
|
||||
|
||||
// Nil StateData should not fail and should return nil
|
||||
artifact = &Artifact{}
|
||||
result = artifact.State("key")
|
||||
if result != nil {
|
||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
// Package oci contains a packersdk.Builder implementation that builds Oracle
|
||||
// Bare Metal Cloud Services (OCI) images.
|
||||
package oci
|
||||
|
||||
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"
|
||||
ocommon "github.com/hashicorp/packer/builder/oracle/common"
|
||||
"github.com/oracle/oci-go-sdk/v36/core"
|
||||
)
|
||||
|
||||
// BuilderId uniquely identifies the builder
|
||||
const BuilderId = "packer.oracle.oci"
|
||||
|
||||
// OCI API version
|
||||
const ociAPIVersion = "20160918"
|
||||
|
||||
// Builder is a builder implementation that creates Oracle OCI custom images.
|
||||
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) {
|
||||
err := b.config.Prepare(raws...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
|
||||
driver, err := NewDriverOCI(&b.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Populate the state bag
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
state.Put("driver", driver)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&ocommon.StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
Comm: &b.config.Comm,
|
||||
DebugKeyPath: fmt.Sprintf("oci_%s.pem", b.config.PackerBuildName),
|
||||
},
|
||||
&stepCreateInstance{},
|
||||
&stepInstanceInfo{},
|
||||
&stepGetDefaultCredentials{
|
||||
Debug: b.config.PackerDebug,
|
||||
Comm: &b.config.Comm,
|
||||
BuildName: b.config.PackerBuildName,
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.Comm,
|
||||
Host: communicator.CommHost(b.config.Comm.Host(), "instance_ip"),
|
||||
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&commonsteps.StepProvision{},
|
||||
&commonsteps.StepCleanupTempKeys{
|
||||
Comm: &b.config.Comm,
|
||||
},
|
||||
&stepImage{},
|
||||
}
|
||||
|
||||
// Run the steps
|
||||
b.runner = commonsteps.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
|
||||
b.runner.Run(ctx, state)
|
||||
|
||||
// If there was an error, return that
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
region, err := b.config.configProvider.Region()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
image, ok := state.GetOk("image")
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the artifact and return it
|
||||
artifact := &Artifact{
|
||||
Image: image.(core.Image),
|
||||
Region: region,
|
||||
driver: driver,
|
||||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
|
||||
}
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
// Cancel terminates a running build.
|
|
@ -1,15 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &Builder{}
|
||||
if _, ok := raw.(packersdk.Builder); !ok {
|
||||
t.Fatalf("Builder should be a builder")
|
||||
}
|
||||
}
|
|
@ -1,415 +0,0 @@
|
|||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest,FlexShapeConfig
|
||||
|
||||
package oci
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"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/pathing"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
ocicommon "github.com/oracle/oci-go-sdk/v36/common"
|
||||
ociauth "github.com/oracle/oci-go-sdk/v36/common/auth"
|
||||
)
|
||||
|
||||
type CreateVNICDetails struct {
|
||||
// fields that can be specified under "create_vnic_details"
|
||||
AssignPublicIp *bool `mapstructure:"assign_public_ip" required:"false"`
|
||||
DefinedTags map[string]map[string]interface{} `mapstructure:"defined_tags" required:"false"`
|
||||
DisplayName *string `mapstructure:"display_name" required:"false"`
|
||||
FreeformTags map[string]string `mapstructure:"tags" required:"false"`
|
||||
HostnameLabel *string `mapstructure:"hostname_label" required:"false"`
|
||||
NsgIds []string `mapstructure:"nsg_ids" required:"false"`
|
||||
PrivateIp *string `mapstructure:"private_ip" required:"false"`
|
||||
SkipSourceDestCheck *bool `mapstructure:"skip_source_dest_check" required:"false"`
|
||||
SubnetId *string `mapstructure:"subnet_id" required:"false"`
|
||||
}
|
||||
|
||||
type ListImagesRequest struct {
|
||||
// fields that can be specified under "base_image_filter"
|
||||
CompartmentId *string `mapstructure:"compartment_id"`
|
||||
DisplayName *string `mapstructure:"display_name"`
|
||||
DisplayNameSearch *string `mapstructure:"display_name_search"`
|
||||
OperatingSystem *string `mapstructure:"operating_system"`
|
||||
OperatingSystemVersion *string `mapstructure:"operating_system_version"`
|
||||
Shape *string `mapstructure:"shape"`
|
||||
}
|
||||
|
||||
type FlexShapeConfig struct {
|
||||
Ocpus *float32 `mapstructure:"ocpus" required:"false"`
|
||||
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
configProvider ocicommon.ConfigurationProvider
|
||||
|
||||
// Instance Principals (OPTIONAL)
|
||||
// If set to true the following can't have non empty values
|
||||
// - AccessCfgFile
|
||||
// - AccessCfgFileAccount
|
||||
// - UserID
|
||||
// - TenancyID
|
||||
// - Region
|
||||
// - Fingerprint
|
||||
// - KeyFile
|
||||
// - PassPhrase
|
||||
InstancePrincipals bool `mapstructure:"use_instance_principals"`
|
||||
|
||||
AccessCfgFile string `mapstructure:"access_cfg_file"`
|
||||
AccessCfgFileAccount string `mapstructure:"access_cfg_file_account"`
|
||||
|
||||
// Access config overrides
|
||||
UserID string `mapstructure:"user_ocid"`
|
||||
TenancyID string `mapstructure:"tenancy_ocid"`
|
||||
Region string `mapstructure:"region"`
|
||||
Fingerprint string `mapstructure:"fingerprint"`
|
||||
KeyFile string `mapstructure:"key_file"`
|
||||
PassPhrase string `mapstructure:"pass_phrase"`
|
||||
UsePrivateIP bool `mapstructure:"use_private_ip"`
|
||||
|
||||
AvailabilityDomain string `mapstructure:"availability_domain"`
|
||||
CompartmentID string `mapstructure:"compartment_ocid"`
|
||||
|
||||
// Image
|
||||
BaseImageID string `mapstructure:"base_image_ocid"`
|
||||
BaseImageFilter ListImagesRequest `mapstructure:"base_image_filter"`
|
||||
ImageName string `mapstructure:"image_name"`
|
||||
ImageCompartmentID string `mapstructure:"image_compartment_ocid"`
|
||||
LaunchMode string `mapstructure:"image_launch_mode"`
|
||||
|
||||
// Instance
|
||||
InstanceName *string `mapstructure:"instance_name"`
|
||||
InstanceTags map[string]string `mapstructure:"instance_tags"`
|
||||
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"`
|
||||
Shape string `mapstructure:"shape"`
|
||||
ShapeConfig FlexShapeConfig `mapstructure:"shape_config"`
|
||||
BootVolumeSizeInGBs int64 `mapstructure:"disk_size"`
|
||||
|
||||
// Metadata optionally contains custom metadata key/value pairs provided in the
|
||||
// configuration. While this can be used to set metadata["user_data"] the explicit
|
||||
// "user_data" and "user_data_file" values will have precedence.
|
||||
// An instance's metadata can be obtained from at http://169.254.169.254 on the
|
||||
// launched instance.
|
||||
Metadata map[string]string `mapstructure:"metadata"`
|
||||
|
||||
// UserData and UserDataFile file are both optional and mutually exclusive.
|
||||
UserData string `mapstructure:"user_data"`
|
||||
UserDataFile string `mapstructure:"user_data_file"`
|
||||
|
||||
// Networking
|
||||
SubnetID string `mapstructure:"subnet_ocid"`
|
||||
CreateVnicDetails CreateVNICDetails `mapstructure:"create_vnic_details"`
|
||||
|
||||
// Tagging
|
||||
Tags map[string]string `mapstructure:"tags"`
|
||||
DefinedTags map[string]map[string]interface{} `mapstructure:"defined_tags"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (c *Config) ConfigProvider() ocicommon.ConfigurationProvider {
|
||||
return c.configProvider
|
||||
}
|
||||
|
||||
func (c *Config) Prepare(raws ...interface{}) error {
|
||||
|
||||
// Decode from template
|
||||
err := config.Decode(c, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateContext: &c.ctx,
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to mapstructure Config: %+v", err)
|
||||
}
|
||||
|
||||
var errs *packersdk.MultiError
|
||||
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
||||
errs = packersdk.MultiErrorAppend(errs, es...)
|
||||
}
|
||||
|
||||
var tenancyOCID string
|
||||
|
||||
if c.InstancePrincipals {
|
||||
// We could go through all keys in one go and report that the below set
|
||||
// of keys cannot coexist with use_instance_principals but decided to
|
||||
// split them and report them seperately so that the user sees the specific
|
||||
// key involved.
|
||||
var message string = " cannot be present when use_instance_principals is set to true."
|
||||
if c.AccessCfgFile != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("access_cfg_file"+message))
|
||||
}
|
||||
if c.AccessCfgFileAccount != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("access_cfg_file_account"+message))
|
||||
}
|
||||
if c.UserID != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user_ocid"+message))
|
||||
}
|
||||
if c.TenancyID != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("tenancy_ocid"+message))
|
||||
}
|
||||
if c.Region != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("region"+message))
|
||||
}
|
||||
if c.Fingerprint != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("fingerprint"+message))
|
||||
}
|
||||
if c.KeyFile != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("key_file"+message))
|
||||
}
|
||||
if c.PassPhrase != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("pass_phrase"+message))
|
||||
}
|
||||
// This check is used to facilitate testing. During testing a Mock struct
|
||||
// is assigned to c.configProvider otherwise testing fails because Instance
|
||||
// Principals cannot be obtained.
|
||||
if c.configProvider == nil {
|
||||
// Even though the previous configuraion checks might fail we don't want
|
||||
// to skip this step. It seems that the logic behind the checks in this
|
||||
// file is to check everything even getting the configProvider.
|
||||
c.configProvider, err = ociauth.InstancePrincipalConfigurationProvider()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
tenancyOCID, err = c.configProvider.TenancyOCID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Determine where the SDK config is located
|
||||
if c.AccessCfgFile == "" {
|
||||
c.AccessCfgFile, err = getDefaultOCISettingsPath()
|
||||
if err != nil {
|
||||
log.Println("Default OCI settings file not found")
|
||||
}
|
||||
}
|
||||
|
||||
if c.AccessCfgFileAccount == "" {
|
||||
c.AccessCfgFileAccount = "DEFAULT"
|
||||
}
|
||||
|
||||
var keyContent []byte
|
||||
if c.KeyFile != "" {
|
||||
path, err := pathing.ExpandUser(c.KeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read API signing key
|
||||
keyContent, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fileProvider, _ := ocicommon.ConfigurationProviderFromFileWithProfile(c.AccessCfgFile, c.AccessCfgFileAccount, c.PassPhrase)
|
||||
if c.Region == "" {
|
||||
var region string
|
||||
if fileProvider != nil {
|
||||
region, _ = fileProvider.Region()
|
||||
}
|
||||
if region == "" {
|
||||
c.Region = "us-phoenix-1"
|
||||
}
|
||||
}
|
||||
|
||||
providers := []ocicommon.ConfigurationProvider{
|
||||
ocicommon.NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
|
||||
}
|
||||
|
||||
if fileProvider != nil {
|
||||
providers = append(providers, fileProvider)
|
||||
}
|
||||
|
||||
// Load API access configuration from SDK
|
||||
configProvider, err := ocicommon.ComposingConfigurationProvider(providers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if userOCID, _ := configProvider.UserOCID(); userOCID == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'user_ocid' must be specified"))
|
||||
}
|
||||
|
||||
tenancyOCID, _ = configProvider.TenancyOCID()
|
||||
if tenancyOCID == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'tenancy_ocid' must be specified"))
|
||||
}
|
||||
|
||||
if fingerprint, _ := configProvider.KeyFingerprint(); fingerprint == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'fingerprint' must be specified"))
|
||||
}
|
||||
|
||||
if _, err := configProvider.PrivateRSAKey(); err != nil {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, fmt.Errorf("'key_file' must be correctly specified. %w", err))
|
||||
}
|
||||
|
||||
c.configProvider = configProvider
|
||||
}
|
||||
|
||||
if c.AvailabilityDomain == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'availability_domain' must be specified"))
|
||||
}
|
||||
|
||||
if c.CompartmentID == "" && tenancyOCID != "" {
|
||||
c.CompartmentID = tenancyOCID
|
||||
}
|
||||
|
||||
if c.ImageCompartmentID == "" {
|
||||
c.ImageCompartmentID = c.CompartmentID
|
||||
}
|
||||
|
||||
if c.Shape == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'shape' must be specified"))
|
||||
}
|
||||
|
||||
if strings.HasSuffix(c.Shape, "Flex") {
|
||||
if c.ShapeConfig.Ocpus == nil {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'Ocpus' must be specified when using flexible shapes"))
|
||||
}
|
||||
}
|
||||
|
||||
if c.ShapeConfig.MemoryInGBs != nil && c.ShapeConfig.Ocpus == nil {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'Ocpus' must be specified if memory_in_gbs is specified"))
|
||||
}
|
||||
|
||||
if (c.SubnetID == "") && (c.CreateVnicDetails.SubnetId == nil) {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'subnet_ocid' must be specified"))
|
||||
}
|
||||
|
||||
if c.CreateVnicDetails.SubnetId == nil {
|
||||
c.CreateVnicDetails.SubnetId = &c.SubnetID
|
||||
} else if (*c.CreateVnicDetails.SubnetId != c.SubnetID) && (c.SubnetID != "") {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'create_vnic_details[subnet]' must match 'subnet_ocid' if both are specified"))
|
||||
}
|
||||
|
||||
if (c.BaseImageID == "") && (c.BaseImageFilter == ListImagesRequest{}) {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'base_image_ocid' or 'base_image_filter' must be specified"))
|
||||
}
|
||||
|
||||
if c.BaseImageFilter.CompartmentId == nil {
|
||||
c.BaseImageFilter.CompartmentId = &c.CompartmentID
|
||||
}
|
||||
|
||||
if c.BaseImageFilter.Shape == nil {
|
||||
c.BaseImageFilter.Shape = &c.Shape
|
||||
}
|
||||
|
||||
// Validate tag lengths. TODO (hlowndes) maximum number of tags allowed.
|
||||
if c.Tags != nil {
|
||||
for k, v := range c.Tags {
|
||||
k = strings.TrimSpace(k)
|
||||
v = strings.TrimSpace(v)
|
||||
if len(k) > 100 {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Tag key length too long. Maximum 100 but found %d. Key: %s", len(k), k))
|
||||
}
|
||||
if len(k) == 0 {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Tag key empty in config"))
|
||||
}
|
||||
if len(v) > 100 {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Tag value length too long. Maximum 100 but found %d. Key: %s", len(v), k))
|
||||
}
|
||||
if len(v) == 0 {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Tag value empty in config"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.ImageName == "" {
|
||||
name, err := interpolate.Render("packer-{{timestamp}}", nil)
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs,
|
||||
fmt.Errorf("unable to parse image name: %s", err))
|
||||
} else {
|
||||
c.ImageName = name
|
||||
}
|
||||
}
|
||||
|
||||
// Optional UserData config
|
||||
if c.UserData != "" && c.UserDataFile != "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
|
||||
} else if c.UserDataFile != "" {
|
||||
if _, err := os.Stat(c.UserDataFile); err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
|
||||
}
|
||||
}
|
||||
// read UserDataFile into string.
|
||||
if c.UserDataFile != "" {
|
||||
fiData, err := ioutil.ReadFile(c.UserDataFile)
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Problem reading user_data_file: %s", err))
|
||||
}
|
||||
c.UserData = string(fiData)
|
||||
}
|
||||
// Test if UserData is encoded already, and if not, encode it
|
||||
if c.UserData != "" {
|
||||
if _, err := base64.StdEncoding.DecodeString(c.UserData); err != nil {
|
||||
log.Printf("[DEBUG] base64 encoding user data...")
|
||||
c.UserData = base64.StdEncoding.EncodeToString([]byte(c.UserData))
|
||||
}
|
||||
}
|
||||
|
||||
// Set default boot volume size to 50 if not set
|
||||
// Check if size set is allowed by OCI
|
||||
if c.BootVolumeSizeInGBs == 0 {
|
||||
c.BootVolumeSizeInGBs = 50
|
||||
} else if c.BootVolumeSizeInGBs < 50 || c.BootVolumeSizeInGBs > 16384 {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'disk_size' must be between 50 and 16384 GBs"))
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDefaultOCISettingsPath uses os/user to compute the default
|
||||
// config file location ($HOME/.oci/config).
|
||||
func getDefaultOCISettingsPath() (string, error) {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if u.HomeDir == "" {
|
||||
return "", fmt.Errorf("Unable to determine the home directory for the current user.")
|
||||
}
|
||||
|
||||
path := filepath.Join(u.HomeDir, ".oci", "config")
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
|
@ -1,300 +0,0 @@
|
|||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package oci
|
||||
|
||||
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"`
|
||||
InstancePrincipals *bool `mapstructure:"use_instance_principals" cty:"use_instance_principals" hcl:"use_instance_principals"`
|
||||
AccessCfgFile *string `mapstructure:"access_cfg_file" cty:"access_cfg_file" hcl:"access_cfg_file"`
|
||||
AccessCfgFileAccount *string `mapstructure:"access_cfg_file_account" cty:"access_cfg_file_account" hcl:"access_cfg_file_account"`
|
||||
UserID *string `mapstructure:"user_ocid" cty:"user_ocid" hcl:"user_ocid"`
|
||||
TenancyID *string `mapstructure:"tenancy_ocid" cty:"tenancy_ocid" hcl:"tenancy_ocid"`
|
||||
Region *string `mapstructure:"region" cty:"region" hcl:"region"`
|
||||
Fingerprint *string `mapstructure:"fingerprint" cty:"fingerprint" hcl:"fingerprint"`
|
||||
KeyFile *string `mapstructure:"key_file" cty:"key_file" hcl:"key_file"`
|
||||
PassPhrase *string `mapstructure:"pass_phrase" cty:"pass_phrase" hcl:"pass_phrase"`
|
||||
UsePrivateIP *bool `mapstructure:"use_private_ip" cty:"use_private_ip" hcl:"use_private_ip"`
|
||||
AvailabilityDomain *string `mapstructure:"availability_domain" cty:"availability_domain" hcl:"availability_domain"`
|
||||
CompartmentID *string `mapstructure:"compartment_ocid" cty:"compartment_ocid" hcl:"compartment_ocid"`
|
||||
BaseImageID *string `mapstructure:"base_image_ocid" cty:"base_image_ocid" hcl:"base_image_ocid"`
|
||||
BaseImageFilter *FlatListImagesRequest `mapstructure:"base_image_filter" cty:"base_image_filter" hcl:"base_image_filter"`
|
||||
ImageName *string `mapstructure:"image_name" cty:"image_name" hcl:"image_name"`
|
||||
ImageCompartmentID *string `mapstructure:"image_compartment_ocid" cty:"image_compartment_ocid" hcl:"image_compartment_ocid"`
|
||||
LaunchMode *string `mapstructure:"image_launch_mode" cty:"image_launch_mode" hcl:"image_launch_mode"`
|
||||
InstanceName *string `mapstructure:"instance_name" cty:"instance_name" hcl:"instance_name"`
|
||||
InstanceTags map[string]string `mapstructure:"instance_tags" cty:"instance_tags" hcl:"instance_tags"`
|
||||
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags" cty:"instance_defined_tags" hcl:"instance_defined_tags"`
|
||||
Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"`
|
||||
ShapeConfig *FlatFlexShapeConfig `mapstructure:"shape_config" cty:"shape_config" hcl:"shape_config"`
|
||||
BootVolumeSizeInGBs *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
|
||||
Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"`
|
||||
UserData *string `mapstructure:"user_data" cty:"user_data" hcl:"user_data"`
|
||||
UserDataFile *string `mapstructure:"user_data_file" cty:"user_data_file" hcl:"user_data_file"`
|
||||
SubnetID *string `mapstructure:"subnet_ocid" cty:"subnet_ocid" hcl:"subnet_ocid"`
|
||||
CreateVnicDetails *FlatCreateVNICDetails `mapstructure:"create_vnic_details" cty:"create_vnic_details" hcl:"create_vnic_details"`
|
||||
Tags map[string]string `mapstructure:"tags" cty:"tags" hcl:"tags"`
|
||||
DefinedTags map[string]map[string]interface{} `mapstructure:"defined_tags" cty:"defined_tags" hcl:"defined_tags"`
|
||||
}
|
||||
|
||||
// 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},
|
||||
"use_instance_principals": &hcldec.AttrSpec{Name: "use_instance_principals", Type: cty.Bool, Required: false},
|
||||
"access_cfg_file": &hcldec.AttrSpec{Name: "access_cfg_file", Type: cty.String, Required: false},
|
||||
"access_cfg_file_account": &hcldec.AttrSpec{Name: "access_cfg_file_account", Type: cty.String, Required: false},
|
||||
"user_ocid": &hcldec.AttrSpec{Name: "user_ocid", Type: cty.String, Required: false},
|
||||
"tenancy_ocid": &hcldec.AttrSpec{Name: "tenancy_ocid", Type: cty.String, Required: false},
|
||||
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||
"fingerprint": &hcldec.AttrSpec{Name: "fingerprint", Type: cty.String, Required: false},
|
||||
"key_file": &hcldec.AttrSpec{Name: "key_file", Type: cty.String, Required: false},
|
||||
"pass_phrase": &hcldec.AttrSpec{Name: "pass_phrase", Type: cty.String, Required: false},
|
||||
"use_private_ip": &hcldec.AttrSpec{Name: "use_private_ip", Type: cty.Bool, Required: false},
|
||||
"availability_domain": &hcldec.AttrSpec{Name: "availability_domain", Type: cty.String, Required: false},
|
||||
"compartment_ocid": &hcldec.AttrSpec{Name: "compartment_ocid", Type: cty.String, Required: false},
|
||||
"base_image_ocid": &hcldec.AttrSpec{Name: "base_image_ocid", Type: cty.String, Required: false},
|
||||
"base_image_filter": &hcldec.BlockSpec{TypeName: "base_image_filter", Nested: hcldec.ObjectSpec((*FlatListImagesRequest)(nil).HCL2Spec())},
|
||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||
"image_compartment_ocid": &hcldec.AttrSpec{Name: "image_compartment_ocid", Type: cty.String, Required: false},
|
||||
"image_launch_mode": &hcldec.AttrSpec{Name: "image_launch_mode", Type: cty.String, Required: false},
|
||||
"instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false},
|
||||
"instance_tags": &hcldec.AttrSpec{Name: "instance_tags", Type: cty.Map(cty.String), Required: false},
|
||||
"instance_defined_tags": &hcldec.AttrSpec{Name: "instance_defined_tags", Type: cty.Map(cty.String), Required: false},
|
||||
"shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false},
|
||||
"shape_config": &hcldec.BlockSpec{TypeName: "shape_config", Nested: hcldec.ObjectSpec((*FlatFlexShapeConfig)(nil).HCL2Spec())},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false},
|
||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
|
||||
"subnet_ocid": &hcldec.AttrSpec{Name: "subnet_ocid", Type: cty.String, Required: false},
|
||||
"create_vnic_details": &hcldec.BlockSpec{TypeName: "create_vnic_details", Nested: hcldec.ObjectSpec((*FlatCreateVNICDetails)(nil).HCL2Spec())},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.Map(cty.String), Required: false},
|
||||
"defined_tags": &hcldec.AttrSpec{Name: "defined_tags", Type: cty.Map(cty.String), Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatCreateVNICDetails is an auto-generated flat version of CreateVNICDetails.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatCreateVNICDetails struct {
|
||||
AssignPublicIp *bool `mapstructure:"assign_public_ip" required:"false" cty:"assign_public_ip" hcl:"assign_public_ip"`
|
||||
DefinedTags map[string]map[string]interface{} `mapstructure:"defined_tags" required:"false" cty:"defined_tags" hcl:"defined_tags"`
|
||||
DisplayName *string `mapstructure:"display_name" required:"false" cty:"display_name" hcl:"display_name"`
|
||||
FreeformTags map[string]string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"`
|
||||
HostnameLabel *string `mapstructure:"hostname_label" required:"false" cty:"hostname_label" hcl:"hostname_label"`
|
||||
NsgIds []string `mapstructure:"nsg_ids" required:"false" cty:"nsg_ids" hcl:"nsg_ids"`
|
||||
PrivateIp *string `mapstructure:"private_ip" required:"false" cty:"private_ip" hcl:"private_ip"`
|
||||
SkipSourceDestCheck *bool `mapstructure:"skip_source_dest_check" required:"false" cty:"skip_source_dest_check" hcl:"skip_source_dest_check"`
|
||||
SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id" hcl:"subnet_id"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatCreateVNICDetails.
|
||||
// FlatCreateVNICDetails is an auto-generated flat version of CreateVNICDetails.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*CreateVNICDetails) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatCreateVNICDetails)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a CreateVNICDetails.
|
||||
// This spec is used by HCL to read the fields of CreateVNICDetails.
|
||||
// The decoded values from this spec will then be applied to a FlatCreateVNICDetails.
|
||||
func (*FlatCreateVNICDetails) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"assign_public_ip": &hcldec.AttrSpec{Name: "assign_public_ip", Type: cty.Bool, Required: false},
|
||||
"defined_tags": &hcldec.AttrSpec{Name: "defined_tags", Type: cty.Map(cty.String), Required: false},
|
||||
"display_name": &hcldec.AttrSpec{Name: "display_name", Type: cty.String, Required: false},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.Map(cty.String), Required: false},
|
||||
"hostname_label": &hcldec.AttrSpec{Name: "hostname_label", Type: cty.String, Required: false},
|
||||
"nsg_ids": &hcldec.AttrSpec{Name: "nsg_ids", Type: cty.List(cty.String), Required: false},
|
||||
"private_ip": &hcldec.AttrSpec{Name: "private_ip", Type: cty.String, Required: false},
|
||||
"skip_source_dest_check": &hcldec.AttrSpec{Name: "skip_source_dest_check", Type: cty.Bool, Required: false},
|
||||
"subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatFlexShapeConfig is an auto-generated flat version of FlexShapeConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatFlexShapeConfig struct {
|
||||
Ocpus *float32 `mapstructure:"ocpus" required:"false" cty:"ocpus" hcl:"ocpus"`
|
||||
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false" cty:"memory_in_gbs" hcl:"memory_in_gbs"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatFlexShapeConfig.
|
||||
// FlatFlexShapeConfig is an auto-generated flat version of FlexShapeConfig.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*FlexShapeConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatFlexShapeConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a FlexShapeConfig.
|
||||
// This spec is used by HCL to read the fields of FlexShapeConfig.
|
||||
// The decoded values from this spec will then be applied to a FlatFlexShapeConfig.
|
||||
func (*FlatFlexShapeConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"ocpus": &hcldec.AttrSpec{Name: "ocpus", Type: cty.Number, Required: false},
|
||||
"memory_in_gbs": &hcldec.AttrSpec{Name: "memory_in_gbs", Type: cty.Number, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatListImagesRequest is an auto-generated flat version of ListImagesRequest.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatListImagesRequest struct {
|
||||
CompartmentId *string `mapstructure:"compartment_id" cty:"compartment_id" hcl:"compartment_id"`
|
||||
DisplayName *string `mapstructure:"display_name" cty:"display_name" hcl:"display_name"`
|
||||
DisplayNameSearch *string `mapstructure:"display_name_search" cty:"display_name_search" hcl:"display_name_search"`
|
||||
OperatingSystem *string `mapstructure:"operating_system" cty:"operating_system" hcl:"operating_system"`
|
||||
OperatingSystemVersion *string `mapstructure:"operating_system_version" cty:"operating_system_version" hcl:"operating_system_version"`
|
||||
Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatListImagesRequest.
|
||||
// FlatListImagesRequest is an auto-generated flat version of ListImagesRequest.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*ListImagesRequest) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatListImagesRequest)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a ListImagesRequest.
|
||||
// This spec is used by HCL to read the fields of ListImagesRequest.
|
||||
// The decoded values from this spec will then be applied to a FlatListImagesRequest.
|
||||
func (*FlatListImagesRequest) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"compartment_id": &hcldec.AttrSpec{Name: "compartment_id", Type: cty.String, Required: false},
|
||||
"display_name": &hcldec.AttrSpec{Name: "display_name", Type: cty.String, Required: false},
|
||||
"display_name_search": &hcldec.AttrSpec{Name: "display_name_search", Type: cty.String, Required: false},
|
||||
"operating_system": &hcldec.AttrSpec{Name: "operating_system", Type: cty.String, Required: false},
|
||||
"operating_system_version": &hcldec.AttrSpec{Name: "operating_system_version", Type: cty.String, Required: false},
|
||||
"shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,412 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-ini/ini"
|
||||
)
|
||||
|
||||
func testConfig(accessConfFile *os.File) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
|
||||
"availability_domain": "aaaa:PHX-AD-3",
|
||||
"access_cfg_file": accessConfFile.Name(),
|
||||
|
||||
// Image
|
||||
"base_image_ocid": "ocd1...",
|
||||
"image_name": "HelloWorld",
|
||||
|
||||
// Networking
|
||||
"subnet_ocid": "ocd1...",
|
||||
|
||||
// Comm
|
||||
"ssh_username": "opc",
|
||||
"use_private_ip": false,
|
||||
"metadata": map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
"defined_tags": map[string]map[string]interface{}{
|
||||
"namespace": {"key": "value"},
|
||||
},
|
||||
|
||||
// Instance Details
|
||||
"instance_name": "hello-world",
|
||||
"instance_tags": map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
"create_vnic_details": map[string]interface{}{
|
||||
"nsg_ids": []string{"ocd1..."},
|
||||
},
|
||||
"shape": "VM.Standard1.1",
|
||||
"disk_size": 60,
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
// Shared set-up and deferred deletion
|
||||
|
||||
cfg, keyFile, err := baseTestConfigWithTmpKeyFile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(keyFile.Name())
|
||||
|
||||
cfgFile, err := writeTestConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(cfgFile.Name())
|
||||
|
||||
// Temporarily set $HOME to temp directory to bypass default
|
||||
// access config loading.
|
||||
|
||||
tmpHome, err := ioutil.TempDir("", "packer_config_test")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error when creating temporary directory: %+v", err)
|
||||
}
|
||||
defer os.Remove(tmpHome)
|
||||
|
||||
home := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tmpHome)
|
||||
defer os.Setenv("HOME", home)
|
||||
|
||||
// Config tests
|
||||
t.Run("BaseConfig", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BaseImageFilterWithoutOCID", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
raw["base_image_ocid"] = ""
|
||||
raw["base_image_filter"] = map[string]interface{}{
|
||||
"display_name": "hello_world",
|
||||
}
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("BaseImageFilterDefault", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
|
||||
if *c.BaseImageFilter.Shape != raw["shape"] {
|
||||
t.Fatalf("Default base_image_filter shape %v does not equal config shape %v",
|
||||
*c.BaseImageFilter.Shape, raw["shape"])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("LaunchMode", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
raw["image_launch_mode"] = "NATIVE"
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("NoAccessConfig", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
raw["access_cfg_file"] = "/tmp/random/access/config/file/should/not/exist"
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
|
||||
expectedErrors := []string{
|
||||
"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
|
||||
}
|
||||
|
||||
if errs == nil {
|
||||
t.Fatalf("Expected errors %q but got none", expectedErrors)
|
||||
}
|
||||
|
||||
s := errs.Error()
|
||||
for _, expected := range expectedErrors {
|
||||
if !strings.Contains(s, expected) {
|
||||
t.Errorf("Expected %q to contain '%s'", s, expected)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("AccessConfigTemplateOnly", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
delete(raw, "access_cfg_file")
|
||||
raw["user_ocid"] = "ocid1..."
|
||||
raw["tenancy_ocid"] = "ocid1..."
|
||||
raw["fingerprint"] = "00:00..."
|
||||
raw["key_file"] = keyFile.Name()
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
|
||||
if errs != nil {
|
||||
t.Fatalf("err: %+v", errs)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
|
||||
tenancy, err := c.configProvider.TenancyOCID()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error getting tenancy ocid: %v", err)
|
||||
}
|
||||
|
||||
expected := "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
if tenancy != expected {
|
||||
t.Errorf("Expected tenancy: %s, got %s.", expected, tenancy)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
|
||||
region, err := c.configProvider.Region()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error getting region: %v", err)
|
||||
}
|
||||
|
||||
expected := "us-ashburn-1"
|
||||
if region != expected {
|
||||
t.Errorf("Expected region: %s, got %s.", expected, region)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// Test the correct errors are produced when required template keys are
|
||||
// omitted.
|
||||
requiredKeys := []string{"availability_domain", "base_image_ocid", "shape", "subnet_ocid"}
|
||||
for _, k := range requiredKeys {
|
||||
t.Run(k+"_required", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
delete(raw, k)
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
|
||||
if !strings.Contains(errs.Error(), k) {
|
||||
t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("ImageNameDefaultedIfEmpty", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
delete(raw, "image_name")
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
|
||||
if !strings.Contains(c.ImageName, "packer-") {
|
||||
t.Errorf("got default ImageName %q, want image name 'packer-{{timestamp}}'", c.ImageName)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("user_ocid_overridden", func(t *testing.T) {
|
||||
expected := "override"
|
||||
raw := testConfig(cfgFile)
|
||||
raw["user_ocid"] = expected
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
|
||||
user, _ := c.configProvider.UserOCID()
|
||||
if user != expected {
|
||||
t.Errorf("Expected ConfigProvider.UserOCID: %s, got %s", expected, user)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("tenancy_ocid_overidden", func(t *testing.T) {
|
||||
expected := "override"
|
||||
raw := testConfig(cfgFile)
|
||||
raw["tenancy_ocid"] = expected
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
|
||||
tenancy, _ := c.configProvider.TenancyOCID()
|
||||
if tenancy != expected {
|
||||
t.Errorf("Expected ConfigProvider.TenancyOCID: %s, got %s", expected, tenancy)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("region_overidden", func(t *testing.T) {
|
||||
expected := "override"
|
||||
raw := testConfig(cfgFile)
|
||||
raw["region"] = expected
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration %+v", errs)
|
||||
}
|
||||
|
||||
region, _ := c.configProvider.Region()
|
||||
if region != expected {
|
||||
t.Errorf("Expected ConfigProvider.Region: %s, got %s", expected, region)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fingerprint_overidden", func(t *testing.T) {
|
||||
expected := "override"
|
||||
raw := testConfig(cfgFile)
|
||||
raw["fingerprint"] = expected
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
if errs != nil {
|
||||
t.Fatalf("Unexpected error in configuration: %+v", errs)
|
||||
}
|
||||
|
||||
fingerprint, _ := c.configProvider.KeyFingerprint()
|
||||
if fingerprint != expected {
|
||||
t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
|
||||
}
|
||||
})
|
||||
|
||||
// Test the correct errors are produced when certain template keys
|
||||
// are present alongside use_instance_principals key.
|
||||
invalidKeys := []string{
|
||||
"access_cfg_file",
|
||||
"access_cfg_file_account",
|
||||
"user_ocid",
|
||||
"tenancy_ocid",
|
||||
"region",
|
||||
"fingerprint",
|
||||
"key_file",
|
||||
"pass_phrase",
|
||||
}
|
||||
for _, k := range invalidKeys {
|
||||
t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
raw["use_instance_principals"] = "true"
|
||||
raw[k] = "some_random_value"
|
||||
|
||||
var c Config
|
||||
|
||||
c.configProvider = instancePrincipalConfigurationProviderMock{}
|
||||
|
||||
errs := c.Prepare(raw)
|
||||
|
||||
if !strings.Contains(errs.Error(), k) {
|
||||
t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BaseTestConfig creates the base (DEFAULT) config including a temporary key
|
||||
// file.
|
||||
// NOTE: Caller is responsible for removing temporary key file.
|
||||
func baseTestConfigWithTmpKeyFile() (*ini.File, *os.File, error) {
|
||||
keyFile, err := generateRSAKeyFile()
|
||||
if err != nil {
|
||||
return nil, keyFile, err
|
||||
}
|
||||
// Build ini
|
||||
cfg := ini.Empty()
|
||||
section, _ := cfg.NewSection("DEFAULT")
|
||||
section.NewKey("region", "us-ashburn-1")
|
||||
section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
section.NewKey("fingerprint", "70:04:5z:b3:19:ab:90:75:a4:1f:50:d4:c7:c3:33:20")
|
||||
section.NewKey("key_file", keyFile.Name())
|
||||
|
||||
return cfg, keyFile, nil
|
||||
}
|
||||
|
||||
// WriteTestConfig writes a ini.File to a temporary file for use in unit tests.
|
||||
// NOTE: Caller is responsible for removing temporary file.
|
||||
func writeTestConfig(cfg *ini.File) (*os.File, error) {
|
||||
confFile, err := ioutil.TempFile("", "config_file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := confFile.Write([]byte("[DEFAULT]\n")); err != nil {
|
||||
os.Remove(confFile.Name())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := cfg.WriteTo(confFile); err != nil {
|
||||
os.Remove(confFile.Name())
|
||||
return nil, err
|
||||
}
|
||||
return confFile, nil
|
||||
}
|
||||
|
||||
// generateRSAKeyFile generates an RSA key file for use in unit tests.
|
||||
// NOTE: The caller is responsible for deleting the temporary file.
|
||||
func generateRSAKeyFile() (*os.File, error) {
|
||||
// Create temporary file for the key
|
||||
f, err := ioutil.TempFile("", "key")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate key
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2014)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ASN.1 DER encoded form
|
||||
privDer := x509.MarshalPKCS1PrivateKey(priv)
|
||||
privBlk := pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Headers: nil,
|
||||
Bytes: privDer,
|
||||
}
|
||||
|
||||
// Write the key out
|
||||
if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v36/core"
|
||||
)
|
||||
|
||||
// Driver interfaces between the builder steps and the OCI SDK.
|
||||
type Driver interface {
|
||||
CreateInstance(ctx context.Context, publicKey string) (string, error)
|
||||
CreateImage(ctx context.Context, id string) (core.Image, error)
|
||||
DeleteImage(ctx context.Context, id string) error
|
||||
GetInstanceIP(ctx context.Context, id string) (string, error)
|
||||
TerminateInstance(ctx context.Context, id string) error
|
||||
WaitForImageCreation(ctx context.Context, id string) error
|
||||
WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v36/core"
|
||||
)
|
||||
|
||||
// driverMock implements the Driver interface and communicates with Oracle
|
||||
// OCI.
|
||||
type driverMock struct {
|
||||
CreateInstanceID string
|
||||
CreateInstanceErr error
|
||||
|
||||
CreateImageID string
|
||||
CreateImageErr error
|
||||
|
||||
DeleteImageID string
|
||||
DeleteImageErr error
|
||||
|
||||
GetInstanceIPErr error
|
||||
|
||||
TerminateInstanceID string
|
||||
TerminateInstanceErr error
|
||||
|
||||
WaitForImageCreationErr error
|
||||
|
||||
WaitForInstanceStateErr error
|
||||
|
||||
cfg *Config
|
||||
}
|
||||
|
||||
// CreateInstance creates a new compute instance.
|
||||
func (d *driverMock) CreateInstance(ctx context.Context, publicKey string) (string, error) {
|
||||
if d.CreateInstanceErr != nil {
|
||||
return "", d.CreateInstanceErr
|
||||
}
|
||||
|
||||
d.CreateInstanceID = "ocid1..."
|
||||
|
||||
return d.CreateInstanceID, nil
|
||||
}
|
||||
|
||||
// CreateImage creates a new custom image.
|
||||
func (d *driverMock) CreateImage(ctx context.Context, id string) (core.Image, error) {
|
||||
if d.CreateImageErr != nil {
|
||||
return core.Image{}, d.CreateImageErr
|
||||
}
|
||||
d.CreateImageID = id
|
||||
return core.Image{Id: &id}, nil
|
||||
}
|
||||
|
||||
// DeleteImage mocks deleting a custom image.
|
||||
func (d *driverMock) DeleteImage(ctx context.Context, id string) error {
|
||||
if d.DeleteImageErr != nil {
|
||||
return d.DeleteImageErr
|
||||
}
|
||||
|
||||
d.DeleteImageID = id
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInstanceIP returns the public or private IP corresponding to the given instance id.
|
||||
func (d *driverMock) GetInstanceIP(ctx context.Context, id string) (string, error) {
|
||||
if d.GetInstanceIPErr != nil {
|
||||
return "", d.GetInstanceIPErr
|
||||
}
|
||||
if d.cfg.UsePrivateIP {
|
||||
return "private_ip", nil
|
||||
}
|
||||
return "ip", nil
|
||||
}
|
||||
|
||||
// TerminateInstance terminates a compute instance.
|
||||
func (d *driverMock) TerminateInstance(ctx context.Context, id string) error {
|
||||
if d.TerminateInstanceErr != nil {
|
||||
return d.TerminateInstanceErr
|
||||
}
|
||||
|
||||
d.TerminateInstanceID = id
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForImageCreation waits for a provisioning custom image to reach the
|
||||
// "AVAILABLE" state.
|
||||
func (d *driverMock) WaitForImageCreation(ctx context.Context, id string) error {
|
||||
return d.WaitForImageCreationErr
|
||||
}
|
||||
|
||||
// WaitForInstanceState waits for an instance to reach the a given terminal
|
||||
// state.
|
||||
func (d *driverMock) WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error {
|
||||
return d.WaitForInstanceStateErr
|
||||
}
|
|
@ -1,339 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v36/common"
|
||||
core "github.com/oracle/oci-go-sdk/v36/core"
|
||||
)
|
||||
|
||||
// driverOCI implements the Driver interface and communicates with Oracle
|
||||
// OCI.
|
||||
type driverOCI struct {
|
||||
computeClient core.ComputeClient
|
||||
vcnClient core.VirtualNetworkClient
|
||||
cfg *Config
|
||||
context context.Context
|
||||
}
|
||||
|
||||
var retryPolicy = &common.RetryPolicy{
|
||||
MaximumNumberAttempts: 10,
|
||||
ShouldRetryOperation: func(res common.OCIOperationResponse) bool {
|
||||
var e common.ServiceError
|
||||
if errors.As(res.Error, &e) {
|
||||
switch e.GetHTTPStatusCode() {
|
||||
case http.StatusTooManyRequests, http.StatusInternalServerError, http.StatusServiceUnavailable:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
NextDuration: func(res common.OCIOperationResponse) time.Duration {
|
||||
x := uint64(res.AttemptNumber)
|
||||
d := time.Duration(math.Pow(2, float64(atomic.LoadUint64(&x)))) * time.Second
|
||||
j := time.Duration(rand.Float64()*(2000)) * time.Millisecond
|
||||
w := d + j
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
var requestMetadata = common.RequestMetadata{
|
||||
RetryPolicy: retryPolicy,
|
||||
}
|
||||
|
||||
// NewDriverOCI Creates a new driverOCI with a connected compute client and a connected vcn client.
|
||||
func NewDriverOCI(cfg *Config) (Driver, error) {
|
||||
coreClient, err := core.NewComputeClientWithConfigurationProvider(cfg.configProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vcnClient, err := core.NewVirtualNetworkClientWithConfigurationProvider(cfg.configProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &driverOCI{
|
||||
computeClient: coreClient,
|
||||
vcnClient: vcnClient,
|
||||
cfg: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateInstance creates a new compute instance.
|
||||
func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (string, error) {
|
||||
metadata := map[string]string{
|
||||
"ssh_authorized_keys": publicKey,
|
||||
}
|
||||
if d.cfg.Metadata != nil {
|
||||
for key, value := range d.cfg.Metadata {
|
||||
metadata[key] = value
|
||||
}
|
||||
}
|
||||
if d.cfg.UserData != "" {
|
||||
metadata["user_data"] = d.cfg.UserData
|
||||
}
|
||||
|
||||
// Create VNIC details for instance
|
||||
CreateVnicDetails := core.CreateVnicDetails{
|
||||
AssignPublicIp: d.cfg.CreateVnicDetails.AssignPublicIp,
|
||||
DisplayName: d.cfg.CreateVnicDetails.DisplayName,
|
||||
HostnameLabel: d.cfg.CreateVnicDetails.HostnameLabel,
|
||||
NsgIds: d.cfg.CreateVnicDetails.NsgIds,
|
||||
PrivateIp: d.cfg.CreateVnicDetails.PrivateIp,
|
||||
SkipSourceDestCheck: d.cfg.CreateVnicDetails.SkipSourceDestCheck,
|
||||
SubnetId: d.cfg.CreateVnicDetails.SubnetId,
|
||||
DefinedTags: d.cfg.CreateVnicDetails.DefinedTags,
|
||||
FreeformTags: d.cfg.CreateVnicDetails.FreeformTags,
|
||||
}
|
||||
|
||||
// Determine base image ID
|
||||
var imageId *string
|
||||
if d.cfg.BaseImageID != "" {
|
||||
imageId = &d.cfg.BaseImageID
|
||||
} else {
|
||||
// Pull images and determine which image ID to use, if BaseImageId not specified
|
||||
response, err := d.computeClient.ListImages(ctx, core.ListImagesRequest{
|
||||
CompartmentId: d.cfg.BaseImageFilter.CompartmentId,
|
||||
DisplayName: d.cfg.BaseImageFilter.DisplayName,
|
||||
OperatingSystem: d.cfg.BaseImageFilter.OperatingSystem,
|
||||
OperatingSystemVersion: d.cfg.BaseImageFilter.OperatingSystemVersion,
|
||||
Shape: d.cfg.BaseImageFilter.Shape,
|
||||
LifecycleState: "AVAILABLE",
|
||||
SortBy: "TIMECREATED",
|
||||
SortOrder: "DESC",
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(response.Items) == 0 {
|
||||
return "", errors.New("base_image_filter returned no images")
|
||||
}
|
||||
if d.cfg.BaseImageFilter.DisplayNameSearch != nil {
|
||||
// Return most recent image that matches regex
|
||||
imageNameRegex, err := regexp.Compile(*d.cfg.BaseImageFilter.DisplayNameSearch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, image := range response.Items {
|
||||
if imageNameRegex.MatchString(*image.DisplayName) {
|
||||
imageId = image.Id
|
||||
break
|
||||
}
|
||||
}
|
||||
if imageId == nil {
|
||||
return "", errors.New("No image matched display_name_search criteria")
|
||||
}
|
||||
} else {
|
||||
// If no regex provided, simply return most recent image pulled
|
||||
imageId = response.Items[0].Id
|
||||
}
|
||||
}
|
||||
|
||||
// Create Source details which will be used to Launch Instance
|
||||
InstanceSourceDetails := core.InstanceSourceViaImageDetails{
|
||||
ImageId: imageId,
|
||||
BootVolumeSizeInGBs: &d.cfg.BootVolumeSizeInGBs,
|
||||
}
|
||||
|
||||
// Build instance details
|
||||
instanceDetails := core.LaunchInstanceDetails{
|
||||
AvailabilityDomain: &d.cfg.AvailabilityDomain,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
CreateVnicDetails: &CreateVnicDetails,
|
||||
DefinedTags: d.cfg.InstanceDefinedTags,
|
||||
DisplayName: d.cfg.InstanceName,
|
||||
FreeformTags: d.cfg.InstanceTags,
|
||||
Shape: &d.cfg.Shape,
|
||||
SourceDetails: InstanceSourceDetails,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
if d.cfg.ShapeConfig.Ocpus != nil {
|
||||
LaunchInstanceShapeConfigDetails := core.LaunchInstanceShapeConfigDetails{
|
||||
Ocpus: d.cfg.ShapeConfig.Ocpus,
|
||||
MemoryInGBs: d.cfg.ShapeConfig.MemoryInGBs,
|
||||
}
|
||||
instanceDetails.ShapeConfig = &LaunchInstanceShapeConfigDetails
|
||||
}
|
||||
|
||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
|
||||
LaunchInstanceDetails: instanceDetails,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return *instance.Id, nil
|
||||
}
|
||||
|
||||
// CreateImage creates a new custom image.
|
||||
func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, error) {
|
||||
res, err := d.computeClient.CreateImage(ctx, core.CreateImageRequest{CreateImageDetails: core.CreateImageDetails{
|
||||
CompartmentId: &d.cfg.ImageCompartmentID,
|
||||
InstanceId: &id,
|
||||
DisplayName: &d.cfg.ImageName,
|
||||
FreeformTags: d.cfg.Tags,
|
||||
DefinedTags: d.cfg.DefinedTags,
|
||||
LaunchMode: core.CreateImageDetailsLaunchModeEnum(d.cfg.LaunchMode),
|
||||
},
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return core.Image{}, err
|
||||
}
|
||||
|
||||
return res.Image, nil
|
||||
}
|
||||
|
||||
// DeleteImage deletes a custom image.
|
||||
func (d *driverOCI) DeleteImage(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInstanceIP returns the public or private IP corresponding to the given instance id.
|
||||
func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error) {
|
||||
vnics, err := d.computeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
|
||||
InstanceId: &id,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(vnics.Items) == 0 {
|
||||
return "", errors.New("instance has zero VNICs")
|
||||
}
|
||||
|
||||
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{
|
||||
VnicId: vnics.Items[0].VnicId,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error getting VNIC details: %s", err)
|
||||
}
|
||||
|
||||
if d.cfg.UsePrivateIP {
|
||||
return *vnic.PrivateIp, nil
|
||||
}
|
||||
|
||||
if vnic.PublicIp == nil {
|
||||
return "", fmt.Errorf("Error getting VNIC Public Ip for: %s", id)
|
||||
}
|
||||
|
||||
return *vnic.PublicIp, nil
|
||||
}
|
||||
|
||||
func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string) (string, string, error) {
|
||||
credentials, err := d.computeClient.GetWindowsInstanceInitialCredentials(ctx, core.GetWindowsInstanceInitialCredentialsRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return *credentials.InstanceCredentials.Username, *credentials.InstanceCredentials.Password, err
|
||||
}
|
||||
|
||||
// TerminateInstance terminates a compute instance.
|
||||
func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.TerminateInstance(ctx, core.TerminateInstanceRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// WaitForImageCreation waits for a provisioning custom image to reach the
|
||||
// "AVAILABLE" state.
|
||||
func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(image.LifecycleState), nil
|
||||
},
|
||||
id,
|
||||
[]string{"PROVISIONING"},
|
||||
"AVAILABLE",
|
||||
0, //Unlimited Retries
|
||||
5*time.Second, //5 second wait between retries
|
||||
)
|
||||
}
|
||||
|
||||
// WaitForInstanceState waits for an instance to reach the a given terminal
|
||||
// state.
|
||||
func (d *driverOCI) WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(instance.LifecycleState), nil
|
||||
},
|
||||
id,
|
||||
waitStates,
|
||||
terminalState,
|
||||
0, //Unlimited Retries
|
||||
5*time.Second, //5 second wait between retries
|
||||
)
|
||||
}
|
||||
|
||||
// WaitForResourceToReachState checks the response of a request through a
|
||||
// polled get and waits until the desired state or until the max retried has
|
||||
// been reached.
|
||||
func waitForResourceToReachState(getResourceState func(string) (string, error), id string, waitStates []string, terminalState string, maxRetries int, waitDuration time.Duration) error {
|
||||
for i := 0; maxRetries == 0 || i < maxRetries; i++ {
|
||||
state, err := getResourceState(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if stringSliceContains(waitStates, state) {
|
||||
time.Sleep(waitDuration)
|
||||
continue
|
||||
} else if state == terminalState {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Unexpected resource state %q, expecting a waiting state %s or terminal state %q ", state, waitStates, terminalState)
|
||||
}
|
||||
return fmt.Errorf("Maximum number of retries (%d) exceeded; resource did not reach state %q", maxRetries, terminalState)
|
||||
}
|
||||
|
||||
// stringSliceContains loops through a slice of strings returning a boolean
|
||||
// based on whether a given value is contained in the slice.
|
||||
func stringSliceContains(slice []string, value string) bool {
|
||||
for _, elem := range slice {
|
||||
if elem == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v36/common"
|
||||
)
|
||||
|
||||
// Mock struct to be used during testing to obtain Instance Principals.
|
||||
type instancePrincipalConfigurationProviderMock struct {
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) PrivateRSAKey() (*rsa.PrivateKey, error) {
|
||||
return rsa.GenerateKey(rand.Reader, 1024)
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) KeyID() (string, error) {
|
||||
return "some_random_key_id", nil
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) TenancyOCID() (string, error) {
|
||||
return "some_random_tenancy", nil
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) UserOCID() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) KeyFingerprint() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) Region() (string, error) {
|
||||
return "some_random_region", nil
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) AuthType() (common.AuthConfig, error) {
|
||||
return common.AuthConfig{
|
||||
AuthType: common.InstancePrincipal,
|
||||
IsFromConfigFile: false,
|
||||
OboToken: nil}, nil
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepCreateInstance struct{}
|
||||
|
||||
func (s *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
var (
|
||||
driver = state.Get("driver").(Driver)
|
||||
ui = state.Get("ui").(packersdk.Ui)
|
||||
config = state.Get("config").(*Config)
|
||||
)
|
||||
|
||||
ui.Say("Creating instance...")
|
||||
|
||||
instanceID, err := driver.CreateInstance(ctx, string(config.Comm.SSHPublicKey))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Problem creating instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("instance_id", instanceID)
|
||||
|
||||
ui.Say(fmt.Sprintf("Created instance (%s).", instanceID))
|
||||
|
||||
ui.Say("Waiting for instance to enter 'RUNNING' state...")
|
||||
|
||||
if err = driver.WaitForInstanceState(ctx, instanceID, []string{"STARTING", "PROVISIONING"}, "RUNNING"); err != nil {
|
||||
err = fmt.Errorf("Error waiting for instance to start: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Instance 'RUNNING'.")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
idRaw, ok := state.GetOk("instance_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id := idRaw.(string)
|
||||
|
||||
ui.Say(fmt.Sprintf("Terminating instance (%s)...", id))
|
||||
|
||||
if err := driver.TerminateInstance(context.TODO(), id); err != nil {
|
||||
err = fmt.Errorf("Error terminating instance. Please terminate manually: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return
|
||||
}
|
||||
|
||||
err := driver.WaitForInstanceState(context.TODO(), id, []string{"TERMINATING"}, "TERMINATED")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error terminating instance. Please terminate manually: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say("Terminated instance.")
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepCreateInstance(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("publicKey", "key")
|
||||
|
||||
step := new(stepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
instanceIDRaw, ok := state.GetOk("instance_id")
|
||||
if !ok {
|
||||
t.Fatalf("should have machine")
|
||||
}
|
||||
|
||||
step.Cleanup(state)
|
||||
|
||||
if driver.TerminateInstanceID != instanceIDRaw.(string) {
|
||||
t.Fatalf(
|
||||
"should've deleted instance (%s != %s)",
|
||||
driver.TerminateInstanceID, instanceIDRaw.(string))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_CreateInstanceErr(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("publicKey", "key")
|
||||
|
||||
step := new(stepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
driver.CreateInstanceErr = errors.New("error")
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("instance_id"); ok {
|
||||
t.Fatalf("should NOT have instance_id")
|
||||
}
|
||||
|
||||
step.Cleanup(state)
|
||||
|
||||
if driver.TerminateInstanceID != "" {
|
||||
t.Fatalf("Should not have tried to terminate an instance")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_WaitForInstanceStateErr(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("publicKey", "key")
|
||||
|
||||
step := new(stepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
driver.WaitForInstanceStateErr = errors.New("error")
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstance_TerminateInstanceErr(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("publicKey", "key")
|
||||
|
||||
step := new(stepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
_, ok := state.GetOk("instance_id")
|
||||
if !ok {
|
||||
t.Fatalf("should have machine")
|
||||
}
|
||||
|
||||
driver.TerminateInstanceErr = errors.New("error")
|
||||
step.Cleanup(state)
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateInstanceCleanup_WaitForInstanceStateErr(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("publicKey", "key")
|
||||
|
||||
step := new(stepCreateInstance)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
driver.WaitForInstanceStateErr = errors.New("error")
|
||||
step.Cleanup(state)
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepGetDefaultCredentials struct {
|
||||
Debug bool
|
||||
Comm *communicator.Config
|
||||
BuildName string
|
||||
}
|
||||
|
||||
func (s *stepGetDefaultCredentials) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
var (
|
||||
driver = state.Get("driver").(*driverOCI)
|
||||
ui = state.Get("ui").(packersdk.Ui)
|
||||
id = state.Get("instance_id").(string)
|
||||
)
|
||||
|
||||
// Skip if we're not using winrm
|
||||
if s.Comm.Type != "winrm" {
|
||||
log.Printf("[INFO] Not using winrm communicator, skipping get password...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// If we already have a password, skip it
|
||||
if s.Comm.WinRMPassword != "" {
|
||||
ui.Say("Skipping waiting for password since WinRM password set...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
username, password, err := driver.GetInstanceInitialCredentials(ctx, id)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error getting instance's credentials: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
s.Comm.WinRMPassword = password
|
||||
s.Comm.WinRMUser = username
|
||||
|
||||
if s.Debug {
|
||||
ui.Message(fmt.Sprintf(
|
||||
"[DEBUG] (OCI default credentials): Credentials (since debug is enabled): %s", password))
|
||||
}
|
||||
|
||||
// store so that we can access this later during provisioning
|
||||
state.Put("winrm_password", s.Comm.WinRMPassword)
|
||||
packersdk.LogSecretFilter.Set(s.Comm.WinRMPassword)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepGetDefaultCredentials) Cleanup(state multistep.StateBag) {
|
||||
// no cleanup
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepImage struct{}
|
||||
|
||||
func (s *stepImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
var (
|
||||
driver = state.Get("driver").(Driver)
|
||||
ui = state.Get("ui").(packersdk.Ui)
|
||||
instanceID = state.Get("instance_id").(string)
|
||||
)
|
||||
|
||||
ui.Say("Creating image from instance...")
|
||||
|
||||
image, err := driver.CreateImage(ctx, instanceID)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating image from instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = driver.WaitForImageCreation(ctx, *image.Id)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error waiting for image creation to finish: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// TODO(apryde): This is stale as .LifecycleState has changed to
|
||||
// AVAILABLE at this point. Does it matter?
|
||||
state.Put("image", image)
|
||||
|
||||
ui.Say("Image created.")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepImage) Cleanup(state multistep.StateBag) {
|
||||
// Nothing to do
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepImage(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("instance_id", "ocid1...")
|
||||
|
||||
step := new(stepImage)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("image"); !ok {
|
||||
t.Fatalf("should have image")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepImage_CreateImageErr(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("instance_id", "ocid1...")
|
||||
|
||||
step := new(stepImage)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
driver.CreateImageErr = errors.New("error")
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("image"); ok {
|
||||
t.Fatalf("should NOT have image")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepImage_WaitForImageCreationErr(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("instance_id", "ocid1...")
|
||||
|
||||
step := new(stepImage)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
driver.WaitForImageCreationErr = errors.New("error")
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("image"); ok {
|
||||
t.Fatalf("should not have image")
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepInstanceInfo struct{}
|
||||
|
||||
func (s *stepInstanceInfo) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
var (
|
||||
driver = state.Get("driver").(Driver)
|
||||
ui = state.Get("ui").(packersdk.Ui)
|
||||
id = state.Get("instance_id").(string)
|
||||
)
|
||||
|
||||
ip, err := driver.GetInstanceIP(ctx, id)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error getting instance's IP: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("instance_ip", ip)
|
||||
|
||||
ui.Say(fmt.Sprintf("Instance has IP: %s.", ip))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepInstanceInfo) Cleanup(state multistep.StateBag) {
|
||||
// no cleanup
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestInstanceInfo(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("instance_id", "ocid1...")
|
||||
|
||||
step := new(stepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
instanceIPRaw, ok := state.GetOk("instance_ip")
|
||||
if !ok {
|
||||
t.Fatalf("should have instance_ip")
|
||||
}
|
||||
|
||||
if instanceIPRaw.(string) != "ip" {
|
||||
t.Fatalf("should've got ip ('%s' != 'ip')", instanceIPRaw.(string))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceInfoPrivateIP(t *testing.T) {
|
||||
baseTestConfig := baseTestConfig()
|
||||
baseTestConfig.UsePrivateIP = true
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", baseTestConfig)
|
||||
state.Put("driver", &driverMock{cfg: baseTestConfig})
|
||||
state.Put("hook", &packersdk.MockHook{})
|
||||
state.Put("ui", &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
state.Put("instance_id", "ocid1...")
|
||||
|
||||
step := new(stepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
instanceIPRaw, ok := state.GetOk("instance_ip")
|
||||
if !ok {
|
||||
t.Fatalf("should have instance_ip")
|
||||
}
|
||||
|
||||
if instanceIPRaw.(string) != "private_ip" {
|
||||
t.Fatalf("should've got ip ('%s' != 'private_ip')", instanceIPRaw.(string))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceInfo_GetInstanceIPErr(t *testing.T) {
|
||||
state := testState()
|
||||
state.Put("instance_id", "ocid1...")
|
||||
|
||||
step := new(stepInstanceInfo)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*driverMock)
|
||||
driver.GetInstanceIPErr = errors.New("error")
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("instance_ip"); ok {
|
||||
t.Fatalf("should NOT have instance_ip")
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package oci
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
// TODO(apryde): It would be good not to have to write a key file to disk to
|
||||
// load the config.
|
||||
func baseTestConfig() *Config {
|
||||
_, keyFile, err := baseTestConfigWithTmpKeyFile()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var c Config
|
||||
err = c.Prepare(map[string]interface{}{
|
||||
"availability_domain": "aaaa:US-ASHBURN-AD-1",
|
||||
|
||||
// Image
|
||||
"base_image_ocid": "ocid1.image.oc1.iad.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"shape": "VM.Standard1.1",
|
||||
"image_name": "HelloWorld",
|
||||
"region": "us-ashburn-1",
|
||||
|
||||
// Networking
|
||||
"subnet_ocid": "ocid1.subnet.oc1.iad.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
|
||||
// AccessConfig
|
||||
"user_ocid": "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"tenancy_ocid": "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"fingerprint": "70:04:5z:b3:19:ab:90:75:a4:1f:50:d4:c7:c3:33:20",
|
||||
"key_file": keyFile.Name(),
|
||||
|
||||
// Comm
|
||||
"ssh_username": "opc",
|
||||
"use_private_ip": false,
|
||||
})
|
||||
|
||||
// Once we have a config object they key file isn't re-read so we can
|
||||
// remove it now.
|
||||
os.Remove(keyFile.Name())
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
func testState() multistep.StateBag {
|
||||
baseTestConfig := baseTestConfig()
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", baseTestConfig)
|
||||
state.Put("driver", &driverMock{cfg: baseTestConfig})
|
||||
state.Put("hook", &packersdk.MockHook{})
|
||||
state.Put("ui", &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
return state
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var OraclePluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
OraclePluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
|
@ -22,8 +22,6 @@ import (
|
|||
lxdbuilder "github.com/hashicorp/packer/builder/lxd"
|
||||
nullbuilder "github.com/hashicorp/packer/builder/null"
|
||||
oneandonebuilder "github.com/hashicorp/packer/builder/oneandone"
|
||||
oracleclassicbuilder "github.com/hashicorp/packer/builder/oracle/classic"
|
||||
oracleocibuilder "github.com/hashicorp/packer/builder/oracle/oci"
|
||||
profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks"
|
||||
tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm"
|
||||
tritonbuilder "github.com/hashicorp/packer/builder/triton"
|
||||
|
@ -62,8 +60,6 @@ var Builders = map[string]packersdk.Builder{
|
|||
"lxd": new(lxdbuilder.Builder),
|
||||
"null": new(nullbuilder.Builder),
|
||||
"oneandone": new(oneandonebuilder.Builder),
|
||||
"oracle-classic": new(oracleclassicbuilder.Builder),
|
||||
"oracle-oci": new(oracleocibuilder.Builder),
|
||||
"profitbricks": new(profitbricksbuilder.Builder),
|
||||
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
|
||||
"triton": new(tritonbuilder.Builder),
|
||||
|
|
|
@ -40,6 +40,8 @@ import (
|
|||
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"
|
||||
oracleclassicbuilder "github.com/hashicorp/packer-plugin-oracle/builder/classic"
|
||||
oracleocibuilder "github.com/hashicorp/packer-plugin-oracle/builder/oci"
|
||||
oscbsubuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsu"
|
||||
oscbsusurrogatebuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsusurrogate"
|
||||
oscbsuvolumebuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsuvolume"
|
||||
|
@ -95,6 +97,8 @@ var VendoredBuilders = map[string]packersdk.Builder{
|
|||
"linode": new(linodebuilder.Builder),
|
||||
"ncloud": new(ncloudbuilder.Builder),
|
||||
"openstack": new(openstackbuilder.Builder),
|
||||
"oracle-classic": new(oracleclassicbuilder.Builder),
|
||||
"oracle-oci": new(oracleocibuilder.Builder),
|
||||
"proxmox": new(proxmoxiso.Builder),
|
||||
"proxmox-iso": new(proxmoxiso.Builder),
|
||||
"proxmox-clone": new(proxmoxclone.Builder),
|
||||
|
|
5
go.mod
5
go.mod
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/digitalocean/godo v1.60.0
|
||||
github.com/dsnet/compress v0.0.1
|
||||
github.com/exoscale/packer-plugin-exoscale v0.1.1
|
||||
github.com/go-ini/ini v1.25.4
|
||||
github.com/go-ini/ini v1.62.0
|
||||
github.com/go-resty/resty/v2 v2.3.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/google/go-cmp v0.5.5
|
||||
|
@ -33,7 +33,7 @@ require (
|
|||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
||||
github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79
|
||||
github.com/hashicorp/go-oracle-terraform v0.17.0
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/hashicorp/hcl/v2 v2.10.0
|
||||
|
@ -52,6 +52,7 @@ require (
|
|||
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-oracle v0.0.3 // indirect
|
||||
github.com/hashicorp/packer-plugin-outscale v0.0.1
|
||||
github.com/hashicorp/packer-plugin-parallels v0.0.1
|
||||
github.com/hashicorp/packer-plugin-proxmox v0.0.2
|
||||
|
|
5
go.sum
5
go.sum
|
@ -262,6 +262,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
|
|||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
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-ini/ini v1.62.0/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=
|
||||
|
@ -439,6 +440,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
|||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE=
|
||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79/go.mod h1:09jT3Y/OIsjTjQ2+3bkVNPDKqWcGIYYvjB2BEKVUdvc=
|
||||
github.com/hashicorp/go-oracle-terraform v0.17.0 h1:gFiaeKf9MXLobhv7uqzPuDFULL6ymJpw1NJCXwSOgJ4=
|
||||
github.com/hashicorp/go-oracle-terraform v0.17.0/go.mod h1:09jT3Y/OIsjTjQ2+3bkVNPDKqWcGIYYvjB2BEKVUdvc=
|
||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
|
||||
|
@ -520,6 +523,8 @@ github.com/hashicorp/packer-plugin-ncloud v0.0.2 h1:MGvGkOVfzeosqOSs5dteghLwv9VR
|
|||
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=
|
||||
github.com/hashicorp/packer-plugin-openstack v0.0.2/go.mod h1:rHAdd4+JmI+1z98Zx+lVOehgzLZT1Rjo2YgtS0NNvwM=
|
||||
github.com/hashicorp/packer-plugin-oracle v0.0.3 h1:yQEAfCD+TQqEWjrHLJTfdJis7axhwknzCKB07gnTZDA=
|
||||
github.com/hashicorp/packer-plugin-oracle v0.0.3/go.mod h1:EfUBTOzh5WtQu6PbcBcylKXiZSyyNw5XbzJ2/CUxay8=
|
||||
github.com/hashicorp/packer-plugin-outscale v0.0.1 h1:BrL8hKypNYrvP3NR+d+xX03SZKB08yTgXPRnH9piUI8=
|
||||
github.com/hashicorp/packer-plugin-outscale v0.0.1/go.mod h1:6jEWfJO7TgAbaL3e+St1bN5PoIC/MmDIsYqNUzAHF1w=
|
||||
github.com/hashicorp/packer-plugin-parallels v0.0.1 h1:fcaaiGWdU1+X4IGadXdUhJ2si1ZA3apXS9tMNJXln2A=
|
||||
|
|
|
@ -1,320 +0,0 @@
|
|||
---
|
||||
description: |
|
||||
The oracle-classic builder is able to create new custom images for use with
|
||||
Oracle Cloud Infrastructure Classic Compute.
|
||||
page_title: Oracle Cloud Infrastructure Classic - Builders
|
||||
---
|
||||
|
||||
# Oracle Cloud Infrastructure Classic Compute Builder
|
||||
|
||||
Type: `oracle-classic`
|
||||
Artifact BuilderId: `packer.oracle.classic`
|
||||
|
||||
The `oracle-classic` Packer builder is able to create custom images for use
|
||||
with [Oracle Cloud Infrastructure Classic
|
||||
Compute](https://cloud.oracle.com/compute-classic). The builder takes a base
|
||||
image, runs any provisioning necessary on the base image after launching it,
|
||||
and finally snapshots it creating a reusable custom image.
|
||||
|
||||
It is recommended that you familiarise yourself with the [Key Concepts and
|
||||
Terminology](https://docs.oracle.com/en/cloud/iaas-classic/compute-iaas-cloud/stcsg/getting-started-oracle-compute-cloud-service.html#GUID-6CB9D494-4F3C-4B78-BD03-127983FEC357)
|
||||
prior to using this builder if you have not done so already.
|
||||
|
||||
The builder _does not_ manage images. Once it creates an image, it is up to you
|
||||
to use it or delete it.
|
||||
|
||||
## Authorization
|
||||
|
||||
This builder authenticates API calls to Oracle Cloud Infrastructure Classic
|
||||
Compute using basic authentication (user name and password). To read more, see
|
||||
the [authentication
|
||||
documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/Authentication.html)
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
There are many configuration options available for the `oracle-classic`
|
||||
builder. This builder currently only works with the SSH communicator.
|
||||
|
||||
### Required
|
||||
|
||||
- `api_endpoint` (string) - This is your custom API endpoint for sending
|
||||
requests. Instructions for determining your API endpoint can be found
|
||||
[here](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/SendRequests.html)
|
||||
|
||||
- `dest_image_list` (string) - Where to save the machine image to once you've
|
||||
provisioned it. If the provided image list does not exist, Packer will
|
||||
create it.
|
||||
|
||||
- `identity_domain` (string) - This is your customer-specific identity domain
|
||||
as generated by Oracle. If you don't know what your identity domain is, ask
|
||||
your account administrator. For a little more information, see the Oracle
|
||||
[documentation](https://docs.oracle.com/en/cloud/get-started/subscriptions-cloud/ocuid/identity-domain-overview.html#GUID-7969F881-5F4D-443E-B86C-9044C8085B8A).
|
||||
|
||||
- `source_image_list` (string) - This is what image you want to use as your
|
||||
base image. See the
|
||||
[documentation](https://docs.oracle.com/en/cloud/iaas-classic/compute-iaas-cloud/stcsg/managing-machine-images.html#GUID-652E6797-A5B8-40A7-860F-F27FF3E42471)
|
||||
for more details. You may use either a public image list, or a private
|
||||
image list. To see what public image lists are available, you can use the
|
||||
CLI, as described
|
||||
[here](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stopc/image-lists-stclr-and-nmcli.html#GUID-DB7E75FE-F752-4FF7-AB70-3C8DCDFCA0FA)
|
||||
|
||||
- `source_image_list_entry` (string) - The entry identifying the machine
|
||||
image to use in the image list. Defaults to the latest available entry.
|
||||
|
||||
- `password` (string) - Your account password.
|
||||
|
||||
- `shape` (string) - The template that determines the number of CPUs, amount
|
||||
of memory, and other resources allocated to a newly created instance. For
|
||||
more information about shapes, see the documentation
|
||||
[here](https://docs.oracle.com/en/cloud/iaas-classic/compute-iaas-cloud/stcsg/creating-instances.html#GUID-1DD0FA71-AC7B-461C-B8C1-14892725AA69).
|
||||
|
||||
- `username` (string) - Your account username.
|
||||
|
||||
### Optional
|
||||
|
||||
- `attributes` (string) - (string) - Attributes to apply when launching the
|
||||
instance. Note that you need to be careful about escaping characters due to
|
||||
the templates being JSON. It is often more convenient to use
|
||||
`attributes_file`, instead. You may only define either `attributes` or
|
||||
`attributes_file`, not both.
|
||||
|
||||
- `attributes_file` (string) - Path to a json file that will be used for the
|
||||
attributes when launching the instance. You may only define either
|
||||
`attributes` or `attributes_file`, not both.
|
||||
|
||||
- `image_description` (string) - a description for your destination image
|
||||
list. If you don't provide one, Packer will provide a generic description.
|
||||
|
||||
- `ssh_username` (string) - The username that Packer will use to SSH into the
|
||||
instance; required if using SSH. The default oracle user with sudo
|
||||
privileges is `opc`, so you may set `ssh_username` to `opc` if you have not
|
||||
yet configured users on your machine. If you have already configured users
|
||||
on your machine, you may prompt Packer to use one of those instead. For
|
||||
more detail, see the
|
||||
[documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/accessing-oracle-linux-instance-using-ssh.html).
|
||||
|
||||
- `image_name` (string) - The name to assign to the resulting custom image.
|
||||
|
||||
- `persistent_volume_size` (int) - Size in gigabytes of the persistent boot
|
||||
storage volume to build the image on. Use this if you want a bigger volume
|
||||
than what instance storage provides. Note that using this option puts the
|
||||
builder into a "persistent volume" mode, which is substantially different
|
||||
than the default snapshot mode. Please see the configuration section below
|
||||
for additional configuration options.
|
||||
|
||||
- `snapshot_timeout` (string) - How long to wait for a snapshot to be
|
||||
created. Expects a positive golang Time.Duration string, which is a
|
||||
sequence of decimal numbers and a unit suffix; valid suffixes are `ns`
|
||||
(nanoseconds), `us` (microseconds), `ms` (milliseconds), `s` (seconds), `m`
|
||||
(minutes), and `h` (hours). Examples of valid inputs: `100ms`, `250ms`,
|
||||
`1s`, `2.5s`, `2.5m`, `1m30s`. Example: `"snapshot_timeout": "15m"`.
|
||||
Default: `20m`.
|
||||
|
||||
### Persistent Volume Build
|
||||
|
||||
You will use this type of build if you've set the `persistent_volume_size`
|
||||
option. If you need a bigger disk than what you normally get with instance
|
||||
storage, you'll want to set this.
|
||||
|
||||
In the _persistent volume_ mode, things are built a little differently.
|
||||
Normally, we launch an instance, then provision it and take a snapshot, which
|
||||
becomes your machine image. This relies on the disk of the created instance
|
||||
being large enough to perform your entire provisioning process. If that disk
|
||||
size isn't sufficient, we can build with a persistent volume of arbitrary size.
|
||||
|
||||
First, we create a persistent volume of the requested size. This volume is
|
||||
bootable and initialized with your image list. We start an instance with this
|
||||
volume as the boot volume. After this instance launches, we provision and
|
||||
terminate it, leaving the persistent volume around.
|
||||
|
||||
Next, we create a second instance, the "builder", this time booting from
|
||||
instance storage. We also attach a new persistent volume, making it twice the
|
||||
size of the original. We connect to this instance and copy the contents of the
|
||||
first volume into a tarball file on the second volume. We then upload this file
|
||||
to Object Storage Classic, and create a new machine image with it.
|
||||
|
||||
If this is set, a few more options become available.
|
||||
|
||||
- `builder_communicator` (communicator) - This represents an
|
||||
[`ssh communicator`](/docs/communicators/ssh),
|
||||
and can be configured as such. If you use a different builder image, you
|
||||
may need to change the `ssh_username`, for example. That might look like
|
||||
this:
|
||||
|
||||
```json
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"builder_communicator": {
|
||||
"ssh_username": "soandso"
|
||||
},
|
||||
"type": "oracle-classic"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
See below for more details on configuring this communicator.
|
||||
|
||||
- `builder_image_list` (string) - This is the image to use for the builder
|
||||
instance. This _must_ be a linux image, and Oracle Linux is recommended.
|
||||
Default: `/oracle/public/OL_7.2_UEKR4_x86_64`.
|
||||
|
||||
- `builder_image_list_entry` (string) - The entry identifying the machine
|
||||
image to use in the image list. If `builder_image_list` is unset, this
|
||||
defaults to `5`, which is a working image as of this time. Otherwise, it
|
||||
defaults to the latest entry. Set this to `0` to force it to use the latest
|
||||
entry when using the default `builder_image_list`.
|
||||
|
||||
- `builder_shape` (string) - The template that determines the number of CPUs,
|
||||
amount of memory, and other resources allocated to the builder instance.
|
||||
Default: `oc3`.
|
||||
|
||||
- `builder_upload_image_command` (string) - The command to run to upload the
|
||||
image to Object Storage Classic. This is for advanced users only, and you
|
||||
should consult the default in code to decide on the changes to make. For
|
||||
most users the default should suffice. If you choose to write your own,
|
||||
this command is a template engine and can make use of the following
|
||||
variables: `{{ .Username }}`, `{{ .Password }}`, `{{ .AccountID }}`,
|
||||
`{{ .ImageFile }}`, and `{{ .SegmentPath }}`.
|
||||
|
||||
### Communicator Configuration
|
||||
|
||||
The `builder_communicator` has the following options:
|
||||
|
||||
#### Optional:
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
|
||||
|
||||
## Basic Example
|
||||
|
||||
Here is a basic example. Note that account specific configuration has been
|
||||
obfuscated; you will need to add a working `username`, `password`,
|
||||
`identity_domain`, and `api_endpoint` in order for the example to work.
|
||||
|
||||
```json
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"type": "oracle-classic",
|
||||
"username": "myuser@myaccount.com",
|
||||
"password": "supersecretpasswordhere",
|
||||
"identity_domain": "#######",
|
||||
"api_endpoint": "https://api-###.compute.###.oraclecloud.com/",
|
||||
"source_image_list": "/oracle/public/OL_7.2_UEKR4_x86_64",
|
||||
"shape": "oc3",
|
||||
"image_name": "Packer_Builder_Test_{{timestamp}}",
|
||||
"attributes": "{\"userdata\": {\"pre-bootstrap\": {\"script\": [\"...\"]}}}",
|
||||
"dest_image_list": "Packer_Builder_Test_List"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": ["echo hello"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Example -- Windows
|
||||
|
||||
Attributes file is optional for connecting via ssh, but required for winrm.
|
||||
|
||||
The following file contains the bare minimum necessary to get winRM working;
|
||||
you have to give it the password to give to the "Administrator" user, which
|
||||
will be the one winrm connects to. You must also whitelist your computer to
|
||||
connect via winRM -- the empty braces below whitelist any computer to access
|
||||
winRM but you can make it more secure by only allowing your build machine
|
||||
access. See the
|
||||
[docs](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/automating-instance-initialization-using-opc-init.html#GUID-A0A107D6-3B38-47F4-8FC8-96D50D99379B)
|
||||
for more details on how to define a trusted host.
|
||||
|
||||
Save this file as `windows_attributes.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"userdata": {
|
||||
"administrator_password": "password",
|
||||
"winrm": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Following is a minimal but working Packer config that references this
|
||||
attributes file:
|
||||
|
||||
```json
|
||||
{
|
||||
"variables": {
|
||||
"opc_username": "{{ env `OPC_USERNAME`}}",
|
||||
"opc_password": "{{ env `OPC_PASSWORD`}}",
|
||||
"opc_identity_domain": "{{env `OPC_IDENTITY_DOMAIN`}}",
|
||||
"opc_api_endpoint": "{{ env `OPC_ENDPOINT`}}"
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
"type": "oracle-classic",
|
||||
"username": "{{ user `opc_username`}}",
|
||||
"password": "{{ user `opc_password`}}",
|
||||
"identity_domain": "{{ user `opc_identity_domain`}}",
|
||||
"api_endpoint": "{{ user `opc_api_endpoint`}}",
|
||||
"source_image_list": "/Compute-{{ user `opc_identity_domain` }}/{{ user `opc_username`}}/Microsoft_Windows_Server_2012_R2-17.3.6-20170930-124649",
|
||||
"attributes_file": "./windows_attributes.json",
|
||||
"shape": "oc3",
|
||||
"image_name": "Packer_Windows_Demo_{{timestamp}}",
|
||||
"dest_image_list": "Packer_Windows_Demo",
|
||||
"communicator": "winrm",
|
||||
"winrm_username": "Administrator",
|
||||
"winrm_password": "password"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "powershell",
|
||||
"inline": "Write-Output(\"HELLO WORLD\")"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Persistent Volume Example
|
||||
|
||||
Here is an example using a persistent volume. Note the `persistent_volume_size`
|
||||
setting.
|
||||
|
||||
```json
|
||||
{
|
||||
"variables": {
|
||||
"opc_username": "{{ env `OPC_USERNAME`}}",
|
||||
"opc_password": "{{ env `OPC_PASSWORD`}}",
|
||||
"opc_identity_domain": "{{env `OPC_IDENTITY_DOMAIN`}}",
|
||||
"opc_api_endpoint": "{{ env `OPC_ENDPOINT`}}"
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
"type": "oracle-classic",
|
||||
"username": "{{ user `opc_username`}}",
|
||||
"password": "{{ user `opc_password`}}",
|
||||
"identity_domain": "{{ user `opc_identity_domain`}}",
|
||||
"api_endpoint": "{{ user `opc_api_endpoint`}}",
|
||||
"source_image_list": "/oracle/public/OL_7.2_UEKR4_x86_64",
|
||||
"persistent_volume_size": 15,
|
||||
"image_name": "Packer_Builder_Test_{{timestamp}}",
|
||||
"dest_image_list": "Packer_Builder_Test_List",
|
||||
"ssh_username": "opc",
|
||||
"shape": "oc3"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": ["echo hello"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
description: Packer is able to create custom images using Oracle Cloud Infrastructure.
|
||||
page_title: Oracle - Builders
|
||||
---
|
||||
|
||||
# Oracle Builder
|
||||
|
||||
Packer is able to create custom images on both Oracle Cloud Infrastructure and
|
||||
Oracle Cloud Infrastructure Classic Compute. Packer comes with builders
|
||||
designed to support both platforms. Please choose the one that's right for you:
|
||||
|
||||
- [oracle-classic](/docs/builders/oracle-classic) - Create custom images
|
||||
in Oracle Cloud Infrastructure Classic Compute by launching a source
|
||||
instance and creating an image list from a snapshot of it after
|
||||
provisioning.
|
||||
|
||||
- [oracle-oci](/docs/builders/oracle-oci) - Create custom images in
|
||||
Oracle Cloud Infrastructure (OCI) by launching a base instance and creating
|
||||
an image from it after provisioning.
|
|
@ -1,362 +0,0 @@
|
|||
---
|
||||
description: |
|
||||
The oracle-oci builder is able to create new custom images for use with Oracle
|
||||
Cloud Infrastructure (OCI).
|
||||
page_title: Oracle OCI - Builders
|
||||
---
|
||||
|
||||
# Oracle Cloud Infrastructure (OCI) Builder
|
||||
|
||||
Type: `oracle-oci`
|
||||
Artifact BuilderId: `packer.oracle.oci`
|
||||
|
||||
The `oracle-oci` Packer builder is able to create new custom images for use
|
||||
with [Oracle Cloud Infrastructure](https://cloud.oracle.com) (OCI). The builder
|
||||
takes a base image, runs any provisioning necessary on the base image after
|
||||
launching it, and finally snapshots it creating a reusable custom image.
|
||||
|
||||
It is recommended that you familiarise yourself with the [Key Concepts and
|
||||
Terminology](https://docs.us-phoenix-1.oraclecloud.com/Content/GSG/Concepts/concepts.htm)
|
||||
prior to using this builder if you have not done so already.
|
||||
|
||||
The builder _does not_ manage images. Once it creates an image, it is up to you
|
||||
to use it or delete it.
|
||||
|
||||
## Authorization
|
||||
|
||||
The Oracle OCI API requires that requests be signed with the RSA public key
|
||||
associated with your
|
||||
[IAM](https://docs.us-phoenix-1.oraclecloud.com/Content/Identity/Concepts/overview.htm)
|
||||
user account. For a comprehensive example of how to configure the required
|
||||
authentication see the documentation on [Required Keys and
|
||||
OCIDs](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/apisigningkey.htm)
|
||||
([Oracle Cloud
|
||||
IDs](https://docs.us-phoenix-1.oraclecloud.com/Content/General/Concepts/identifiers.htm)).
|
||||
Alternatively you can use [Instance
|
||||
Principals](https://docs.cloud.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm)
|
||||
in which case you don't need the above user authorization.
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
There are many configuration options available for the `oracle-oci` builder. In
|
||||
addition to the options listed here, a
|
||||
[communicator](/docs/templates/legacy_json_templates/communicator) can be configured for this
|
||||
builder.
|
||||
|
||||
In addition to the options defined there, a private key file
|
||||
can also be supplied to override the typical auto-generated key:
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
|
||||
|
||||
### Required
|
||||
|
||||
- `availability_domain` (string) - The name of the [Availability
|
||||
Domain](https://docs.us-phoenix-1.oraclecloud.com/Content/General/Concepts/regions.htm)
|
||||
within which a new instance is launched and provisioned. The names of the
|
||||
Availability Domains have a prefix that is specific to your
|
||||
[tenancy](https://docs.us-phoenix-1.oraclecloud.com/Content/GSG/Concepts/concepts.htm#two).
|
||||
|
||||
To get a list of the Availability Domains, use the
|
||||
[ListAvailabilityDomains](https://docs.us-phoenix-1.oraclecloud.com/api/#/en/identity/latest/AvailabilityDomain/ListAvailabilityDomains)
|
||||
operation, which is available in the IAM Service API.
|
||||
|
||||
- `base_image_ocid` (string) - The OCID of the [base
|
||||
image](https://docs.us-phoenix-1.oraclecloud.com/Content/Compute/References/images.htm)
|
||||
to use. This is the unique identifier of the image that will be used to
|
||||
launch a new instance and provision it.
|
||||
|
||||
To get a list of the accepted image OCIDs, use the
|
||||
[ListImages](https://docs.us-phoenix-1.oraclecloud.com/api/#/en/iaas/latest/Image/ListImages)
|
||||
operation available in the Core Services API.
|
||||
|
||||
- `base_image_filter` (map of strings) - As an alternative to providing `base_image_ocid`,
|
||||
the user can supply search criteria, and Packer will use the the most recent image that meets
|
||||
all search criteria. If no image meets all search criteria, Packer returns an error. The
|
||||
following fields, if specified, must match exactly:
|
||||
|
||||
- `compartment_id` - The OCID of the compartment to find the image. If not specified, will use `compartment_ocid`
|
||||
used for the instance.
|
||||
- `display_name` - The full name of the image, e.g., `Oracle-Linux-7.8-2020.05.26-0`
|
||||
- `operating_system` - The operating system used on the image, e.g., `Oracle Linux`
|
||||
- `operating_system_version` - The version of the operating system on the image, e.g., `7.8`
|
||||
- `shape` - A shape that the image supports. If not specified, will use `shape` used for the instance
|
||||
|
||||
Additionally, the following field takes a regular expression:
|
||||
|
||||
- `display_name_search` - a regular expression for the display name, e.g., `^Oracle-Linux`. This
|
||||
is ignored if `display_name` is also specified under `base_image_filter`. If no images match
|
||||
the expression, Packer returns an error. If multiple images match, the most recent is used.
|
||||
|
||||
`base_image_filter` is ignored if `base_image_ocid` is also specified.
|
||||
|
||||
- `compartment_ocid` (string) - The OCID of the
|
||||
[compartment](https://docs.us-phoenix-1.oraclecloud.com/Content/GSG/Tasks/choosingcompartments.htm) that the instance will run in.
|
||||
|
||||
- `shape` (string) - The template that determines the number of CPUs, amount
|
||||
of memory, and other resources allocated to a newly created instance.
|
||||
|
||||
To get a list of the available shapes, use the
|
||||
[ListShapes](https://docs.us-phoenix-1.oraclecloud.com/api/#/en/iaas/20160918/Shape/ListShapes)
|
||||
operation available in the Core Services API.
|
||||
|
||||
When using flexible shapes, ocpus must be set.
|
||||
|
||||
- `subnet_ocid` (string) - The name of the subnet within which a new instance
|
||||
is launched and provisioned.
|
||||
|
||||
To get a list of your subnets, use the
|
||||
[ListSubnets](https://docs.us-phoenix-1.oraclecloud.com/api/#/en/iaas/latest/Subnet/ListSubnets)
|
||||
operation available in the Core Services API.
|
||||
|
||||
Note: the subnet must be configured to allow access via your chosen
|
||||
[communicator](/docs/communicators) (communicator defaults to
|
||||
[SSH tcp/22](/docs/communicators/ssh#ssh_port)).
|
||||
|
||||
### Optional
|
||||
|
||||
- `use_instance_principals` (boolean) - Whether to use [Instance
|
||||
Principals](https://docs.cloud.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm)
|
||||
instead of User Principals. If this key is set to true, setting any one of the `access_cfg_file`,
|
||||
`access_cfg_file_account`, `region`, `tenancy_ocid`, `user_ocid`, `key_file`, `fingerprint`,
|
||||
`pass_phrase` will result in configuration validation errors.
|
||||
Defaults to `false`.
|
||||
|
||||
- `access_cfg_file` (string) - The path to the [OCI config
|
||||
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm).
|
||||
This cannot be used along with the `use_instance_principals` key.
|
||||
Defaults to `$HOME/.oci/config`.
|
||||
|
||||
- `access_cfg_file_account` (string) - The specific account in the [OCI config
|
||||
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) to use.
|
||||
This cannot be used along with the `use_instance_principals` key.
|
||||
Defaults to `DEFAULT`.
|
||||
|
||||
- `region` (string) - An Oracle Cloud Infrastructure region. Overrides value provided by the
|
||||
[OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
|
||||
if present. This cannot be used along with the `use_instance_principals` key.
|
||||
|
||||
- `tenancy_ocid` (string) - The OCID of your tenancy. Overrides value provided by the [OCI config
|
||||
file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if present.
|
||||
This cannot be used along with the `use_instance_principals` key.
|
||||
|
||||
- `user_ocid` (string) - The OCID of the user calling the OCI API. Overrides value provided by the
|
||||
[OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
|
||||
if present. This cannot be used along with the `use_instance_principals` key.
|
||||
|
||||
- `key_file` (string) - Full path and filename of the OCI API signing key. Overrides value provided
|
||||
by the [OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
|
||||
if present. This cannot be used along with the `use_instance_principals` key.
|
||||
|
||||
- `fingerprint` (string) - Fingerprint for the OCI API signing key. Overrides value provided by the
|
||||
[OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if
|
||||
present. This cannot be used along with the `use_instance_principals` key.
|
||||
|
||||
- `pass_phrase` (string) - Pass phrase used to decrypt the OCI API signing key. Overrides value provided
|
||||
by the [OCI config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm)
|
||||
if present. This cannot be used along with the `use_instance_principals` key.
|
||||
|
||||
- `image_name` (string) - The name to assign to the resulting custom image.
|
||||
|
||||
- `image_compartment_ocid` (string) - The OCID of the target compartment for the resulting image. Defaults to `compartment_ocid`.
|
||||
|
||||
- `instance_name` (string) - The name to assign to the instance used for the image creation process.
|
||||
If not set a name of the form `instanceYYYYMMDDhhmmss` will be used.
|
||||
|
||||
- `instance_tags` (map of strings) - Add one or more freeform tags to the instance used for the
|
||||
image creation process.
|
||||
|
||||
- `instance_defined_tags` (map of maps of strings) - Add one or more defined tags for a given namespace
|
||||
to the instance used for the image creation process.
|
||||
|
||||
- `create_vnic_details` (map of strings) - Specify details for the virtual network interface card (VNIC)
|
||||
that is attached to the instance. Possible keys (all optional) are: `assign_public_ip` (bool),
|
||||
`display_name` (string), `hostname_lable` (string), `nsg_ids` (list), `private_ip` (string),
|
||||
`skip_source_dest_check` (bool), `subnet_id` (string), `tags` (map of string), and `defined_tags`
|
||||
(map of maps of strings). See
|
||||
[the Oracle docs](https://docs.cloud.oracle.com/en-us/iaas/Content/Network/Tasks/managingVNICs.htm)
|
||||
for more information about VNICs.
|
||||
|
||||
- `disk_size` (int64) - The size of the boot volume in GBs. Minimum value is 50 and maximum value is 16384 (16TB).
|
||||
Sets the [BootVolumeSizeInGBs](https://godoc.org/github.com/oracle/oci-go-sdk/core#InstanceConfigurationInstanceSourceViaImageDetails)
|
||||
when launching the instance. Defaults to `50`.
|
||||
|
||||
- `image_launch_mode` (string) - Specifies the configuration mode for launching instances.
|
||||
Valid values are `"NATIVE"`, `"EMULATED"`, `"PARAVIRTUALIZED"`, and `"CUSTOM"`. See the
|
||||
[Oracle CLI docs](https://docs.cloud.oracle.com/en-us/iaas/tools/oci-cli/2.12.5/oci_cli_docs/cmdref/compute/image/create.html#cmdoption-launch-mode)
|
||||
for more information about these modes.
|
||||
|
||||
- `use_private_ip` (boolean) - Use private ip addresses to connect to the
|
||||
instance via ssh.
|
||||
|
||||
- `shape_config` (object) - The shape configuration for an instance. The shape configuration determines the resources
|
||||
allocated to an instance. Options:
|
||||
- `ocpus` (required when using flexible shapes or memory_in_gbs is set) (float32) - The total number of OCPUs available to the instance.
|
||||
- `memory_in_gbs` (optional) (float32) - The total amount of memory, in gigabytes, available to the instance.
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
|
||||
- `metadata` (map of strings) - Metadata optionally contains custom metadata
|
||||
key/value pairs provided in the configuration. While this can be used to
|
||||
set metadata\["user_data"\] the explicit "user_data" and
|
||||
"user_data_file" values will have precedence. An instance's metadata can
|
||||
be obtained from at [http://169.254.169.254](http://169.254.169.254) on
|
||||
the launched instance.
|
||||
<!-- markdown-link-check-enable -->
|
||||
|
||||
- `user_data` (string) - User data to be used by cloud-init. See [the Oracle
|
||||
docs](https://docs.us-phoenix-1.oraclecloud.com/api/#/en/iaas/20160918/LaunchInstanceDetails)
|
||||
for more details. Generally speaking, it is easier to use the
|
||||
`user_data_file`, but you can use this option to put either the plaintext
|
||||
data or the base64 encoded data directly into your Packer config. Packer
|
||||
will not automatically wait for a user script to finish before shutting
|
||||
down the instance this must be handled in a provisioner.
|
||||
|
||||
- `user_data_file` (string) - Path to a file to be used as user data by
|
||||
cloud-init. See [the Oracle
|
||||
docs](https://docs.us-phoenix-1.oraclecloud.com/api/#/en/iaas/20160918/LaunchInstanceDetails)
|
||||
for more details. Example: `"user_data_file": "./boot_config/myscript.sh"`
|
||||
|
||||
- `tags` (map of strings) - Add one or more freeform tags to the resulting
|
||||
custom image. See [the Oracle
|
||||
docs](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/taggingoverview.htm)
|
||||
for more details. Example:
|
||||
|
||||
```yaml
|
||||
'tags':
|
||||
'tag1': 'value1'
|
||||
'tag2': 'value2'
|
||||
```
|
||||
|
||||
- `defined_tags` (map of map of strings) - Add one or more defined tags for a given namespace to the resulting
|
||||
custom image. See [the Oracle
|
||||
docs](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/taggingoverview.htm)
|
||||
for more details. Example:
|
||||
|
||||
```yaml
|
||||
'tags':
|
||||
'namespace': { 'tag1': 'value1', 'tag2': 'value2' }
|
||||
```
|
||||
|
||||
## Basic Example
|
||||
|
||||
Here is a basic example. Note that account specific configuration has been
|
||||
substituted with the letter `a` and OCIDS have been shortened for brevity.
|
||||
|
||||
```json
|
||||
{
|
||||
"availability_domain": "aaaa:PHX-AD-1",
|
||||
"base_image_ocid": "ocid1.image.oc1.phx.aaaaaaaa5yu6pw3riqtuhxzov7fdngi4tsteganmao54nq3pyxu3hxcuzmoa",
|
||||
"compartment_ocid": "ocid1.compartment.oc1..aaa",
|
||||
"image_name": "ExampleImage",
|
||||
"shape": "VM.Standard1.1",
|
||||
"ssh_username": "opc",
|
||||
"subnet_ocid": "ocid1.subnet.oc1..aaa",
|
||||
"type": "oracle-oci"
|
||||
}
|
||||
```
|
||||
|
||||
## Using Instance Principals
|
||||
|
||||
Here is a basic example. Note that account specific configuration has been
|
||||
substituted with the letter `a` and OCIDS have been shortened for brevity.
|
||||
|
||||
```json
|
||||
{
|
||||
"use_instance_principals": "true",
|
||||
"availability_domain": "aaaa:PHX-AD-1",
|
||||
"base_image_ocid": "ocid1.image.oc1.phx.aaaaaaaa5yu6pw3riqtuhxzov7fdngi4tsteganmao54nq3pyxu3hxcuzmoa",
|
||||
"compartment_ocid": "ocid1.compartment.oc1..aaa",
|
||||
"image_name": "ExampleImage",
|
||||
"shape": "VM.Standard2.1",
|
||||
"ssh_username": "opc",
|
||||
"subnet_ocid": "ocid1.subnet.oc1..aaa",
|
||||
"type": "oracle-oci"
|
||||
}
|
||||
```
|
||||
|
||||
```shell-session
|
||||
[opc@packerhost ~]$ packer build packer.json
|
||||
oracle-oci: output will be in this color.
|
||||
|
||||
==> oracle-oci: Creating temporary ssh key for instance...
|
||||
==> oracle-oci: Creating instance...
|
||||
==> oracle-oci: Created instance (ocid1.instance.oc1.phx.aaa).
|
||||
==> oracle-oci: Waiting for instance to enter 'RUNNING' state...
|
||||
==> oracle-oci: Instance 'RUNNING'.
|
||||
==> oracle-oci: Instance has IP: 10.10.10.10.
|
||||
==> oracle-oci: Using ssh communicator to connect: 10.10.10.10
|
||||
==> oracle-oci: Waiting for SSH to become available...
|
||||
==> oracle-oci: Connected to SSH!
|
||||
==> oracle-oci: Creating image from instance...
|
||||
==> oracle-oci: Image created.
|
||||
==> oracle-oci: Terminating instance (ocid1.instance.oc1.phx.aaa)...
|
||||
==> oracle-oci: Terminated instance.
|
||||
Build 'oracle-oci' finished.
|
||||
|
||||
==> Builds finished. The artifacts of successful builds are:
|
||||
--> oracle-oci: An image was created: 'ExampleImage' (OCID: ocid1.image.oc1.phx.aaa) in region 'us-phoenix-1'
|
||||
[opc@packerhost ~]$
|
||||
```
|
||||
|
||||
## Assigning Tags and Network Security Groups to the Instance
|
||||
|
||||
Tags are useful for breaking down costs and usage. The keys `instance_tags`
|
||||
and `instance_defined_tags` are assigned to the temporary instance,
|
||||
whereas `tags` and `defined_tags` are assigned to the resulting image.
|
||||
|
||||
Network Security Groups (NSGs) are used for granting networking permissions
|
||||
to the instance. Depending on network (VCN and subnet) setup, this may be
|
||||
required for Packer to successfully SSH into the instance. NSGs are a property
|
||||
of the virtual network interface card (VNIC) attached to the instance, and
|
||||
are listed in `nsg_ids` under `create_vnic_details`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "base-image-{{isotime \"20060102030405\"}}",
|
||||
"type": "oracle-oci",
|
||||
"availability_domain": "aaaa:PHX-AD-1",
|
||||
"base_image_ocid": "ocid1.image.oc1.iad.aaa",
|
||||
"compartment_ocid": "ocid1.compartment.oc1..aaa",
|
||||
"image_name": "my-image-{{isotime \"20060102030405\"}}",
|
||||
"shape": "VM.Standard.E2.1",
|
||||
"subnet_ocid": "ocid1.subnet.oc1.iad.aaa",
|
||||
"use_private_ip": "true",
|
||||
"instance_name": "packer-build-{{isotime \"20060102030405\"}}",
|
||||
"instance_tags": { "testing": "yes" },
|
||||
"instance_defined_tags": {
|
||||
"Operations": {
|
||||
"Team": "CostCenter",
|
||||
"Environment": "prod"
|
||||
}
|
||||
},
|
||||
"create_vnic_details": {
|
||||
"assign_public_ip": "false",
|
||||
"display_name": "testing-123",
|
||||
"nsg_ids": ["ocid1.networksecuritygroup.oc1.iad.aaa"]
|
||||
},
|
||||
"tags": {
|
||||
"CreationDate": "{{isotime \"20060102 03:04:05 MST\"}}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Base Image Filter Example
|
||||
|
||||
Note that `base_image_filter` gets passed as a string, then interpreted as a
|
||||
regular expression. This means that all back-slashes must be doubled, e.g.,
|
||||
use `\\w+` to mean `\w+`, and `\\\\` to create the regular expression equivalent
|
||||
of `\\` (which will search for a literal back-slash).
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "base-image-{{isotime \"20060102030405\"}}",
|
||||
"type": "oracle-oci",
|
||||
"availability_domain": "aaaa:PHX-AD-1",
|
||||
"base_image_filter": {
|
||||
"operating_system": "Oracle Linux",
|
||||
"operating_system_version": "7.8",
|
||||
"display_name_search": "^Oracle-Linux-7\\.8-2020\\.\\d+"
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
|
@ -720,23 +720,6 @@
|
|||
"title": "1&1",
|
||||
"path": "builders/oneandone"
|
||||
},
|
||||
{
|
||||
"title": "Oracle",
|
||||
"routes": [
|
||||
{
|
||||
"title": "Overview",
|
||||
"path": "builders/oracle"
|
||||
},
|
||||
{
|
||||
"title": "Oracle Classic",
|
||||
"path": "builders/oracle/classic"
|
||||
},
|
||||
{
|
||||
"title": "Oracle OCI",
|
||||
"path": "builders/oracle/oci"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "ProfitBricks",
|
||||
"path": "builders/profitbricks"
|
||||
|
|
|
@ -100,6 +100,12 @@
|
|||
"pluginTier": "community",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Oracle",
|
||||
"path": "oracle",
|
||||
"repo": "hashicorp/packer-plugin-oracle",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Outscale",
|
||||
"path": "outscale",
|
||||
|
|
Loading…
Reference in New Issue