extract oracle plugin (#10962)

This commit is contained in:
Megan Marsh 2021-04-22 02:50:00 -07:00 committed by GitHub
parent 6b59525408
commit f161f2bed2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 18 additions and 5625 deletions

View File

@ -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
}

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}
}

View File

@ -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...)
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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))
}

View File

@ -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.")
}

View File

@ -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())
}
}

View File

@ -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))
}

View File

@ -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.")
}

View File

@ -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.")
}

View File

@ -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
}

View File

@ -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()))
}
}
}

View File

@ -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
}

View File

@ -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) {
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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.

View File

@ -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)
}

View File

@ -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")
}
}

View File

@ -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.

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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.")
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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),

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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"]
}
]
}
```

View File

@ -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.

View File

@ -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+"
}
...
}
```

View File

@ -720,23 +720,6 @@
"title": "1&amp;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"

View File

@ -100,6 +100,12 @@
"pluginTier": "community",
"version": "latest"
},
{
"title": "Oracle",
"path": "oracle",
"repo": "hashicorp/packer-plugin-oracle",
"version": "latest"
},
{
"title": "Outscale",
"path": "outscale",