diff --git a/.web-docs/components/builder/linode/README.md b/.web-docs/components/builder/linode/README.md index 2654932..b9fffa9 100644 --- a/.web-docs/components/builder/linode/README.md +++ b/.web-docs/components/builder/linode/README.md @@ -75,9 +75,11 @@ can also be supplied to override the typical auto-generated key: -- `interface` ([]Interface) - Network Interfaces to add to this Linode’s Configuration Profile. Singular repeatable +- `interface` ([]Interface) - Legacy Config Network Interfaces to add to this Linode’s Configuration Profile. Singular repeatable block containing a `purpose`, a `label`, and an `ipam_address` field. +- `linode_interface` ([]LinodeInterface) - Newer Linode Network Interfaces to add to this Linode. + - `authorized_keys` ([]string) - Public SSH keys need to be appended to the Linode instance. - `authorized_users` ([]string) - Users whose SSH keys need to be appended to the Linode instance. @@ -125,14 +127,223 @@ can also be supplied to override the typical auto-generated key: - `image_regions` ([]string) - The regions where the outcome image will be replicated to. +- `interface_generation` (string) - Specifies the interface type for the Linode. The value can be either + `legacy_config` or `linode`. The default value is determined by the + `interfaces_for_new_linodes` setting in the account settings. + -#### Interface +#### Linode Interface + +This section outlines the fields configurable for a newer Linode interface object. + + + +- `firewall_id` (\*int) - The enabled firewall to secure a VPC or public interface. Not allowed for VLAN interfaces. + +- `default_route` (\*InterfaceDefaultRoute) - Indicates if the interface serves as the default route when multiple interfaces are + eligible for this role. + +- `public` (\*PublicInterface) - Public interface settings. A Linode can have only one public interface. + A public interface can have both IPv4 and IPv6 configurations. + +- `vpc` (\*VPCInterface) - VPC interface settings. + +- `vlan` (\*VLANInterface) - VLAN interface settings. + + + + +##### Linode Interface Default Route configuration object (InterfaceDefaultRoute) + +###### Optional + + + +- `ipv4` (\*bool) - Whether this interface is used for the IPv4 default route. + +- `ipv6` (\*bool) - Whether this interface is used for the IPv6 default route. + + + + +##### Public Linode Interface configuration object (PublicInterface) + +###### Optional + + + +- `ipv4` (\*PublicInterfaceIPv4) - IPv4 address settings for this public interface. If omitted, + a public IPv4 address is automatically allocated. + +- `ipv6` (\*PublicInterfaceIPv6) - IPv6 address settings for the public interface. + + + + +##### Public Linode Interface IPv4 configuration object (PublicInterfaceIPv4) + +###### Optional + + + +- `address` ([]PublicInterfaceIPv4Address) - Blocks of IPv4 addresses to assign to this interface. Setting any to auto + allocates a public IPv4 address. + + + + +##### Public Linode Interface IPv4 Address configuration object (PublicInterfaceIPv4Address) + +###### Required + + + +- `address` (\*string) - The interface's public IPv4 address. You can specify which public IPv4 + address to configure for the interface. Setting this to auto automatically + allocates a public address. + + + + +###### Optional + + + +- `primary` (\*bool) - The IPv4 primary address configures the source address for routes within + the Linode on the corresponding network interface. + + - Don't set this to false if there's only one address in the addresses array. + - If more than one address is provided, primary can be set to true for one address. + - If only one address is present in the addresses array, this address is automatically set as the primary address. + + + + +##### Public Linode Interface IPv6 configuration object (PublicInterfaceIPv6) + +###### Optional + + + +- `ranges` ([]PublicInterfaceIPv6Range) - IPv6 address ranges to assign to this interface. If omitted, no ranges are assigned. + + + + +##### Public Linode Interface IPv6 Range configuration object (PublicInterfaceIPv6Range) + +###### Required + + + +- `range` (string) - Your assigned IPv6 range in CIDR notation (2001:0db8::1/64) or prefix (/64). + + - The prefix of /64 or /56 block of IPv6 addresses. + - If provided in CIDR notation, the prefix must be within the assigned ranges for the Linode. + + + + +##### VPC Linode Interface configuration object (VPCInterface) + +###### Required + + + +- `subnet_id` (int) - The VPC subnet identifier for this interface. Your subnet’s VPC must be in + the same data center (region) as the Linode. + + + + +###### Optional + + + +- `ipv4` (\*VPCInterfaceIPv4) - Interfaces can be configured with IPv4 addresses or ranges. + + + + +##### VPC Linode Interface IPv4 configuration object (VPCInterfaceIPv4) + +###### Optional + + + +- `addresses` ([]VPCInterfaceIPv4Address) - IPv4 address settings for this VPC interface. + +- `ranges` ([]VPCInterfaceIPv4Range) - VPC IPv4 ranges. + + + + +##### VPC Linode Interface IPv4 Address configuration object (VPCInterfaceIPv4Address) + +###### Required -This section outlines the fields configurable for a single interface object. + -##### Required Interface Common Attributes +- `address` (\*string) - Specifies which IPv4 address to use in the VPC subnet. You can specify which + VPC Ipv4 address in the subnet to configure for the interface. You can't use + an IPv4 address taken from another Linode or interface, or the first two or + last two addresses in the VPC subnet. When address is set to `auto`, an IP + address from the subnet is automatically assigned. + + + + +###### Optional + + + +- `primary` (\*bool) - The IPv4 primary address is used to configure the source address for routes + within the Linode on the corresponding network interface. + +- `nat_1_1_address` (\*string) - The 1:1 NAT IPv4 address used to associate a public IPv4 address with the + interface's VPC subnet IPv4 address. + + + + +##### VPC Linode Interface IPv4 Range configuration object (VPCInterfaceIPv4Range) + +###### Required + + + +- `range` (string) - VPC IPv4 ranges. + + + + +##### VLAN Linode Interface configuration object (VLANInterface) + +###### Required + + + +- `vlan_label` (string) - The VLAN's unique label. VLAN interfaces on the same Linode must have a unique `vlan_label`. + + + + +###### Optional + + + +- `ipam_address` (\*string) - This VLAN interface's private IPv4 address in classless inter-domain routing (CIDR) notation. + + + + +#### Legacy Config Interface + +This section outlines the fields configurable for a single legacy config interface object. + +##### Required Config Interface Common Attributes @@ -141,7 +352,7 @@ This section outlines the fields configurable for a single interface object. -##### Optional Interface Common Attributes +##### Optional Config Interface Common Attributes @@ -174,7 +385,7 @@ This section outlines the fields configurable for a single interface object. -###### VPC Interface IPv4 configuration object +###### VPC Config Interface IPv4 configuration object (InterfaceIPv4) @@ -369,3 +580,78 @@ build { } } ``` + +## Linode Interface Example + +**HCL2** + +```hcl +locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") } + +source "linode" "example" { + image = "linode/ubuntu24.04" + image_description = "My Private Image" + image_label = "private-image-${local.timestamp}" + instance_label = "temporary-linode-${local.timestamp}" + instance_type = "g6-standard-1" + region = "us-mia" + ssh_username = "root" + interface_generation = "linode" + + linode_interface { + firewall_id = 12345 + public { + ipv4 { + address { + address = "auto" + primary = true + } + } + } + } +} + +build { + sources = ["source.linode.example"] +} +``` + +**JSON** + +```json +{ + "source": { + "linode": { + "example": { + "image": "linode/ubuntu24.04", + "linode_token": "YOUR API TOKEN", + "region": "us-mia", + "instance_type": "g6-nanode-1", + "instance_label": "temporary-linode-{{timestamp}}", + "image_label": "private-image-{{timestamp}}", + "image_description": "My Private Image", + "ssh_username": "root", + "interface_generation": "linode", + "linode_interface": { + "firewall_id": 2930969, + "public": { + "ipv4": { + "addresses": [ + { + "address": "auto", + "primary": true + } + ] + } + } + } + } + } + }, + "build": { + "sources": [ + "source.linode.example" + ] + } +} +``` diff --git a/builder/linode/builder_test.go b/builder/linode/builder_test.go index c3621de..592bbc4 100644 --- a/builder/linode/builder_test.go +++ b/builder/linode/builder_test.go @@ -7,6 +7,7 @@ import ( "time" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" + "github.com/linode/linodego" ) func testConfig() map[string]any { @@ -453,7 +454,7 @@ func TestBuilderPrepare_StackScripts(t *testing.T) { } } -func TestBuilderPrepare_NetworkInterfaces(t *testing.T) { +func TestBuilderPrepare_ConfigNetworkInterfaces(t *testing.T) { var b Builder config := testConfig() @@ -514,6 +515,93 @@ func TestBuilderPrepare_NetworkInterfaces(t *testing.T) { } } +func TestBuilderPrepare_LinodeNetworkInterfaces(t *testing.T) { + var b Builder + config := testConfig() + + // Test optional + delete(config, "linode_interface") + + _, warnings, err := b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expectedLinodeInterfaces := []LinodeInterface{ + { + FirewallID: linodego.Pointer(123), + DefaultRoute: &InterfaceDefaultRoute{ + IPv4: linodego.Pointer(true), + IPv6: linodego.Pointer(true), + }, + Public: &PublicInterface{ + IPv4: &PublicInterfaceIPv4{ + Addresses: []PublicInterfaceIPv4Address{ + { + Address: linodego.Pointer("auto"), + Primary: linodego.Pointer(true), + }, + }, + }, + IPv6: &PublicInterfaceIPv6{ + Ranges: []PublicInterfaceIPv6Range{ + { + Range: "/64", + }, + }, + }, + }, + }, + { + FirewallID: linodego.Pointer(123), + DefaultRoute: &InterfaceDefaultRoute{ + IPv4: linodego.Pointer(false), + IPv6: linodego.Pointer(false), + }, + VPC: &VPCInterface{ + SubnetID: 12345, + IPv4: &VPCInterfaceIPv4{ + Addresses: []VPCInterfaceIPv4Address{ + { + Address: linodego.Pointer("auto"), + Primary: linodego.Pointer(false), + NAT1To1Address: linodego.Pointer("auto"), + }, + }, + }, + }, + }, + { + DefaultRoute: &InterfaceDefaultRoute{ + IPv4: linodego.Pointer(false), + IPv6: linodego.Pointer(false), + }, + VLAN: &VLANInterface{ + VLANLabel: "vlan-1", + IPAMAddress: linodego.Pointer("10.0.0.1/24"), + }, + }, + } + + // Test set + config["linode_interface"] = expectedLinodeInterfaces + b = Builder{} + _, warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + if !reflect.DeepEqual(b.config.LinodeInterfaces, expectedLinodeInterfaces) { + t.Errorf("got %v, expected %v", b.config.LinodeInterfaces, expectedLinodeInterfaces) + } +} + func TestBuilderPrepare_CloudInit(t *testing.T) { var b Builder config := testConfig() diff --git a/builder/linode/config.go b/builder/linode/config.go index 6670dff..0c0c3ff 100644 --- a/builder/linode/config.go +++ b/builder/linode/config.go @@ -69,10 +69,13 @@ type Config struct { ctx interpolate.Context Comm communicator.Config `mapstructure:",squash"` - // Network Interfaces to add to this Linode’s Configuration Profile. Singular repeatable + // Legacy Config Network Interfaces to add to this Linode’s Configuration Profile. Singular repeatable // block containing a `purpose`, a `label`, and an `ipam_address` field. Interfaces []Interface `mapstructure:"interface" required:"false"` + // Newer Linode Network Interfaces to add to this Linode. + LinodeInterfaces []LinodeInterface `mapstructure:"linode_interface" required:"false"` + // The id of the region to launch the Linode instance in. Images are available in all // regions, but there will be less delay when deploying from the region where the image // was taken. See [regions](https://api.linode.com/v4/regions) for more information on @@ -155,6 +158,11 @@ type Config struct { // The regions where the outcome image will be replicated to. ImageRegions []string `mapstructure:"image_regions" required:"false"` + + // Specifies the interface type for the Linode. The value can be either + // `legacy_config` or `linode`. The default value is determined by the + // `interfaces_for_new_linodes` setting in the account settings. + InterfaceGeneration string `mapstructure:"interface_generation" required:"false"` } func createRandomRootPassword() (string, error) { diff --git a/builder/linode/config.hcl2spec.go b/builder/linode/config.hcl2spec.go index f0e4dca..1291ee9 100644 --- a/builder/linode/config.hcl2spec.go +++ b/builder/linode/config.hcl2spec.go @@ -10,86 +10,88 @@ import ( // 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"` - PersonalAccessToken *string `mapstructure:"linode_token" cty:"linode_token" hcl:"linode_token"` - APICAPath *string `mapstructure:"api_ca_path" cty:"api_ca_path" hcl:"api_ca_path"` - 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"` - Interfaces []FlatInterface `mapstructure:"interface" required:"false" cty:"interface" hcl:"interface"` - Region *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` - AuthorizedKeys []string `mapstructure:"authorized_keys" required:"false" cty:"authorized_keys" hcl:"authorized_keys"` - AuthorizedUsers []string `mapstructure:"authorized_users" required:"false" cty:"authorized_users" hcl:"authorized_users"` - InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type" hcl:"instance_type"` - Label *string `mapstructure:"instance_label" required:"false" cty:"instance_label" hcl:"instance_label"` - Tags []string `mapstructure:"instance_tags" required:"false" cty:"instance_tags" hcl:"instance_tags"` - Image *string `mapstructure:"image" required:"true" cty:"image" hcl:"image"` - SwapSize *int `mapstructure:"swap_size" required:"false" cty:"swap_size" hcl:"swap_size"` - PrivateIP *bool `mapstructure:"private_ip" required:"false" cty:"private_ip" hcl:"private_ip"` - RootPass *string `mapstructure:"root_pass" required:"false" cty:"root_pass" hcl:"root_pass"` - ImageLabel *string `mapstructure:"image_label" required:"false" cty:"image_label" hcl:"image_label"` - Description *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"` - StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"` - StackScriptData map[string]string `mapstructure:"stackscript_data" required:"false" cty:"stackscript_data" hcl:"stackscript_data"` - StackScriptID *int `mapstructure:"stackscript_id" required:"false" cty:"stackscript_id" hcl:"stackscript_id"` - ImageCreateTimeout *string `mapstructure:"image_create_timeout" required:"false" cty:"image_create_timeout" hcl:"image_create_timeout"` - CloudInit *bool `mapstructure:"cloud_init" required:"false" cty:"cloud_init" hcl:"cloud_init"` - Metadata *FlatMetadata `mapstructure:"metadata" required:"false" cty:"metadata" hcl:"metadata"` - FirewallID *int `mapstructure:"firewall_id" required:"false" cty:"firewall_id" hcl:"firewall_id"` - ImageRegions []string `mapstructure:"image_regions" required:"false" cty:"image_regions" hcl:"image_regions"` + 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"` + PersonalAccessToken *string `mapstructure:"linode_token" cty:"linode_token" hcl:"linode_token"` + APICAPath *string `mapstructure:"api_ca_path" cty:"api_ca_path" hcl:"api_ca_path"` + 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"` + Interfaces []FlatInterface `mapstructure:"interface" required:"false" cty:"interface" hcl:"interface"` + LinodeInterfaces []FlatLinodeInterface `mapstructure:"linode_interface" required:"false" cty:"linode_interface" hcl:"linode_interface"` + Region *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` + AuthorizedKeys []string `mapstructure:"authorized_keys" required:"false" cty:"authorized_keys" hcl:"authorized_keys"` + AuthorizedUsers []string `mapstructure:"authorized_users" required:"false" cty:"authorized_users" hcl:"authorized_users"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type" hcl:"instance_type"` + Label *string `mapstructure:"instance_label" required:"false" cty:"instance_label" hcl:"instance_label"` + Tags []string `mapstructure:"instance_tags" required:"false" cty:"instance_tags" hcl:"instance_tags"` + Image *string `mapstructure:"image" required:"true" cty:"image" hcl:"image"` + SwapSize *int `mapstructure:"swap_size" required:"false" cty:"swap_size" hcl:"swap_size"` + PrivateIP *bool `mapstructure:"private_ip" required:"false" cty:"private_ip" hcl:"private_ip"` + RootPass *string `mapstructure:"root_pass" required:"false" cty:"root_pass" hcl:"root_pass"` + ImageLabel *string `mapstructure:"image_label" required:"false" cty:"image_label" hcl:"image_label"` + Description *string `mapstructure:"image_description" required:"false" cty:"image_description" hcl:"image_description"` + StateTimeout *string `mapstructure:"state_timeout" required:"false" cty:"state_timeout" hcl:"state_timeout"` + StackScriptData map[string]string `mapstructure:"stackscript_data" required:"false" cty:"stackscript_data" hcl:"stackscript_data"` + StackScriptID *int `mapstructure:"stackscript_id" required:"false" cty:"stackscript_id" hcl:"stackscript_id"` + ImageCreateTimeout *string `mapstructure:"image_create_timeout" required:"false" cty:"image_create_timeout" hcl:"image_create_timeout"` + CloudInit *bool `mapstructure:"cloud_init" required:"false" cty:"cloud_init" hcl:"cloud_init"` + Metadata *FlatMetadata `mapstructure:"metadata" required:"false" cty:"metadata" hcl:"metadata"` + FirewallID *int `mapstructure:"firewall_id" required:"false" cty:"firewall_id" hcl:"firewall_id"` + ImageRegions []string `mapstructure:"image_regions" required:"false" cty:"image_regions" hcl:"image_regions"` + InterfaceGeneration *string `mapstructure:"interface_generation" required:"false" cty:"interface_generation" hcl:"interface_generation"` } // FlatMapstructure returns a new FlatConfig. @@ -164,6 +166,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "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}, "interface": &hcldec.BlockListSpec{TypeName: "interface", Nested: hcldec.ObjectSpec((*FlatInterface)(nil).HCL2Spec())}, + "linode_interface": &hcldec.BlockListSpec{TypeName: "linode_interface", Nested: hcldec.ObjectSpec((*FlatLinodeInterface)(nil).HCL2Spec())}, "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, "authorized_keys": &hcldec.AttrSpec{Name: "authorized_keys", Type: cty.List(cty.String), Required: false}, "authorized_users": &hcldec.AttrSpec{Name: "authorized_users", Type: cty.List(cty.String), Required: false}, @@ -184,6 +187,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "metadata": &hcldec.BlockSpec{TypeName: "metadata", Nested: hcldec.ObjectSpec((*FlatMetadata)(nil).HCL2Spec())}, "firewall_id": &hcldec.AttrSpec{Name: "firewall_id", Type: cty.Number, Required: false}, "image_regions": &hcldec.AttrSpec{Name: "image_regions", Type: cty.List(cty.String), Required: false}, + "interface_generation": &hcldec.AttrSpec{Name: "interface_generation", Type: cty.String, Required: false}, } return s } diff --git a/builder/linode/linode_interfaces.go b/builder/linode/linode_interfaces.go new file mode 100644 index 0000000..9281aee --- /dev/null +++ b/builder/linode/linode_interfaces.go @@ -0,0 +1,119 @@ +//go:generate packer-sdc struct-markdown +//go:generate packer-sdc mapstructure-to-hcl2 -type LinodeInterface,InterfaceDefaultRoute,PublicInterface,PublicInterfaceIPv4,PublicInterfaceIPv6,PublicInterfaceIPv4Address,PublicInterfaceIPv6Range,VPCInterface,VPCInterfaceIPv4,VPCInterfaceIPv4Address,VPCInterfaceIPv4Range,VLANInterface +package linode + +type LinodeInterface struct { + // The enabled firewall to secure a VPC or public interface. Not allowed for VLAN interfaces. + FirewallID *int `mapstructure:"firewall_id" required:"false"` + + // Indicates if the interface serves as the default route when multiple interfaces are + // eligible for this role. + DefaultRoute *InterfaceDefaultRoute `mapstructure:"default_route" required:"false"` + + // Public interface settings. A Linode can have only one public interface. + // A public interface can have both IPv4 and IPv6 configurations. + Public *PublicInterface `mapstructure:"public" required:"false"` + + // VPC interface settings. + VPC *VPCInterface `mapstructure:"vpc" required:"false"` + + // VLAN interface settings. + VLAN *VLANInterface `mapstructure:"vlan" required:"false"` +} + +type InterfaceDefaultRoute struct { + // Whether this interface is used for the IPv4 default route. + IPv4 *bool `mapstructure:"ipv4" required:"false"` + + // Whether this interface is used for the IPv6 default route. + IPv6 *bool `mapstructure:"ipv6" required:"false"` +} + +type PublicInterface struct { + // IPv4 address settings for this public interface. If omitted, + // a public IPv4 address is automatically allocated. + IPv4 *PublicInterfaceIPv4 `mapstructure:"ipv4" required:"false"` + + // IPv6 address settings for the public interface. + IPv6 *PublicInterfaceIPv6 `mapstructure:"ipv6" required:"false"` +} + +type PublicInterfaceIPv4 struct { + // Blocks of IPv4 addresses to assign to this interface. Setting any to auto + // allocates a public IPv4 address. + Addresses []PublicInterfaceIPv4Address `mapstructure:"address" required:"false"` +} + +type PublicInterfaceIPv4Address struct { + // The interface's public IPv4 address. You can specify which public IPv4 + // address to configure for the interface. Setting this to auto automatically + // allocates a public address. + Address *string `mapstructure:"address" required:"true"` + + // The IPv4 primary address configures the source address for routes within + // the Linode on the corresponding network interface. + // + // - Don't set this to false if there's only one address in the addresses array. + // - If more than one address is provided, primary can be set to true for one address. + // - If only one address is present in the addresses array, this address is automatically set as the primary address. + Primary *bool `mapstructure:"primary" required:"false"` +} + +type PublicInterfaceIPv6 struct { + // IPv6 address ranges to assign to this interface. If omitted, no ranges are assigned. + Ranges []PublicInterfaceIPv6Range `mapstructure:"ranges" required:"false"` +} + +type PublicInterfaceIPv6Range struct { + // Your assigned IPv6 range in CIDR notation (2001:0db8::1/64) or prefix (/64). + // + // - The prefix of /64 or /56 block of IPv6 addresses. + // - If provided in CIDR notation, the prefix must be within the assigned ranges for the Linode. + Range string `mapstructure:"range" required:"true"` +} + +type VPCInterface struct { + // The VPC subnet identifier for this interface. Your subnet’s VPC must be in + // the same data center (region) as the Linode. + SubnetID int `mapstructure:"subnet_id" required:"true"` + + // Interfaces can be configured with IPv4 addresses or ranges. + IPv4 *VPCInterfaceIPv4 `mapstructure:"ipv4" required:"false"` +} +type VPCInterfaceIPv4 struct { + // IPv4 address settings for this VPC interface. + Addresses []VPCInterfaceIPv4Address `mapstructure:"addresses" required:"false"` + + // VPC IPv4 ranges. + Ranges []VPCInterfaceIPv4Range `mapstructure:"ranges" required:"false"` +} + +type VPCInterfaceIPv4Address struct { + // Specifies which IPv4 address to use in the VPC subnet. You can specify which + // VPC Ipv4 address in the subnet to configure for the interface. You can't use + // an IPv4 address taken from another Linode or interface, or the first two or + // last two addresses in the VPC subnet. When address is set to `auto`, an IP + // address from the subnet is automatically assigned. + Address *string `mapstructure:"address" required:"true"` + + // The IPv4 primary address is used to configure the source address for routes + // within the Linode on the corresponding network interface. + Primary *bool `mapstructure:"primary" required:"false"` + + // The 1:1 NAT IPv4 address used to associate a public IPv4 address with the + // interface's VPC subnet IPv4 address. + NAT1To1Address *string `mapstructure:"nat_1_1_address" required:"false"` +} + +type VPCInterfaceIPv4Range struct { + // VPC IPv4 ranges. + Range string `mapstructure:"range" required:"true"` +} + +type VLANInterface struct { + // The VLAN's unique label. VLAN interfaces on the same Linode must have a unique `vlan_label`. + VLANLabel string `mapstructure:"vlan_label" required:"true"` + + // This VLAN interface's private IPv4 address in classless inter-domain routing (CIDR) notation. + IPAMAddress *string `mapstructure:"ipam_address" required:"false"` +} diff --git a/builder/linode/linode_interfaces.hcl2spec.go b/builder/linode/linode_interfaces.hcl2spec.go new file mode 100644 index 0000000..24ac5f7 --- /dev/null +++ b/builder/linode/linode_interfaces.hcl2spec.go @@ -0,0 +1,308 @@ +// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. + +package linode + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatInterfaceDefaultRoute is an auto-generated flat version of InterfaceDefaultRoute. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatInterfaceDefaultRoute struct { + IPv4 *bool `mapstructure:"ipv4" required:"false" cty:"ipv4" hcl:"ipv4"` + IPv6 *bool `mapstructure:"ipv6" required:"false" cty:"ipv6" hcl:"ipv6"` +} + +// FlatMapstructure returns a new FlatInterfaceDefaultRoute. +// FlatInterfaceDefaultRoute is an auto-generated flat version of InterfaceDefaultRoute. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*InterfaceDefaultRoute) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatInterfaceDefaultRoute) +} + +// HCL2Spec returns the hcl spec of a InterfaceDefaultRoute. +// This spec is used by HCL to read the fields of InterfaceDefaultRoute. +// The decoded values from this spec will then be applied to a FlatInterfaceDefaultRoute. +func (*FlatInterfaceDefaultRoute) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "ipv4": &hcldec.AttrSpec{Name: "ipv4", Type: cty.Bool, Required: false}, + "ipv6": &hcldec.AttrSpec{Name: "ipv6", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatLinodeInterface is an auto-generated flat version of LinodeInterface. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatLinodeInterface struct { + FirewallID *int `mapstructure:"firewall_id" required:"false" cty:"firewall_id" hcl:"firewall_id"` + DefaultRoute *FlatInterfaceDefaultRoute `mapstructure:"default_route" required:"false" cty:"default_route" hcl:"default_route"` + Public *FlatPublicInterface `mapstructure:"public" required:"false" cty:"public" hcl:"public"` + VPC *FlatVPCInterface `mapstructure:"vpc" required:"false" cty:"vpc" hcl:"vpc"` + VLAN *FlatVLANInterface `mapstructure:"vlan" required:"false" cty:"vlan" hcl:"vlan"` +} + +// FlatMapstructure returns a new FlatLinodeInterface. +// FlatLinodeInterface is an auto-generated flat version of LinodeInterface. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*LinodeInterface) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatLinodeInterface) +} + +// HCL2Spec returns the hcl spec of a LinodeInterface. +// This spec is used by HCL to read the fields of LinodeInterface. +// The decoded values from this spec will then be applied to a FlatLinodeInterface. +func (*FlatLinodeInterface) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "firewall_id": &hcldec.AttrSpec{Name: "firewall_id", Type: cty.Number, Required: false}, + "default_route": &hcldec.BlockSpec{TypeName: "default_route", Nested: hcldec.ObjectSpec((*FlatInterfaceDefaultRoute)(nil).HCL2Spec())}, + "public": &hcldec.BlockSpec{TypeName: "public", Nested: hcldec.ObjectSpec((*FlatPublicInterface)(nil).HCL2Spec())}, + "vpc": &hcldec.BlockSpec{TypeName: "vpc", Nested: hcldec.ObjectSpec((*FlatVPCInterface)(nil).HCL2Spec())}, + "vlan": &hcldec.BlockSpec{TypeName: "vlan", Nested: hcldec.ObjectSpec((*FlatVLANInterface)(nil).HCL2Spec())}, + } + return s +} + +// FlatPublicInterface is an auto-generated flat version of PublicInterface. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatPublicInterface struct { + IPv4 *FlatPublicInterfaceIPv4 `mapstructure:"ipv4" required:"false" cty:"ipv4" hcl:"ipv4"` + IPv6 *FlatPublicInterfaceIPv6 `mapstructure:"ipv6" required:"false" cty:"ipv6" hcl:"ipv6"` +} + +// FlatMapstructure returns a new FlatPublicInterface. +// FlatPublicInterface is an auto-generated flat version of PublicInterface. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*PublicInterface) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPublicInterface) +} + +// HCL2Spec returns the hcl spec of a PublicInterface. +// This spec is used by HCL to read the fields of PublicInterface. +// The decoded values from this spec will then be applied to a FlatPublicInterface. +func (*FlatPublicInterface) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "ipv4": &hcldec.BlockSpec{TypeName: "ipv4", Nested: hcldec.ObjectSpec((*FlatPublicInterfaceIPv4)(nil).HCL2Spec())}, + "ipv6": &hcldec.BlockSpec{TypeName: "ipv6", Nested: hcldec.ObjectSpec((*FlatPublicInterfaceIPv6)(nil).HCL2Spec())}, + } + return s +} + +// FlatPublicInterfaceIPv4 is an auto-generated flat version of PublicInterfaceIPv4. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatPublicInterfaceIPv4 struct { + Addresses []FlatPublicInterfaceIPv4Address `mapstructure:"address" required:"false" cty:"address" hcl:"address"` +} + +// FlatMapstructure returns a new FlatPublicInterfaceIPv4. +// FlatPublicInterfaceIPv4 is an auto-generated flat version of PublicInterfaceIPv4. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*PublicInterfaceIPv4) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPublicInterfaceIPv4) +} + +// HCL2Spec returns the hcl spec of a PublicInterfaceIPv4. +// This spec is used by HCL to read the fields of PublicInterfaceIPv4. +// The decoded values from this spec will then be applied to a FlatPublicInterfaceIPv4. +func (*FlatPublicInterfaceIPv4) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "address": &hcldec.BlockListSpec{TypeName: "address", Nested: hcldec.ObjectSpec((*FlatPublicInterfaceIPv4Address)(nil).HCL2Spec())}, + } + return s +} + +// FlatPublicInterfaceIPv4Address is an auto-generated flat version of PublicInterfaceIPv4Address. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatPublicInterfaceIPv4Address struct { + Address *string `mapstructure:"address" required:"true" cty:"address" hcl:"address"` + Primary *bool `mapstructure:"primary" required:"false" cty:"primary" hcl:"primary"` +} + +// FlatMapstructure returns a new FlatPublicInterfaceIPv4Address. +// FlatPublicInterfaceIPv4Address is an auto-generated flat version of PublicInterfaceIPv4Address. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*PublicInterfaceIPv4Address) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPublicInterfaceIPv4Address) +} + +// HCL2Spec returns the hcl spec of a PublicInterfaceIPv4Address. +// This spec is used by HCL to read the fields of PublicInterfaceIPv4Address. +// The decoded values from this spec will then be applied to a FlatPublicInterfaceIPv4Address. +func (*FlatPublicInterfaceIPv4Address) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "address": &hcldec.AttrSpec{Name: "address", Type: cty.String, Required: false}, + "primary": &hcldec.AttrSpec{Name: "primary", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatPublicInterfaceIPv6 is an auto-generated flat version of PublicInterfaceIPv6. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatPublicInterfaceIPv6 struct { + Ranges []FlatPublicInterfaceIPv6Range `mapstructure:"ranges" required:"false" cty:"ranges" hcl:"ranges"` +} + +// FlatMapstructure returns a new FlatPublicInterfaceIPv6. +// FlatPublicInterfaceIPv6 is an auto-generated flat version of PublicInterfaceIPv6. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*PublicInterfaceIPv6) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPublicInterfaceIPv6) +} + +// HCL2Spec returns the hcl spec of a PublicInterfaceIPv6. +// This spec is used by HCL to read the fields of PublicInterfaceIPv6. +// The decoded values from this spec will then be applied to a FlatPublicInterfaceIPv6. +func (*FlatPublicInterfaceIPv6) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "ranges": &hcldec.BlockListSpec{TypeName: "ranges", Nested: hcldec.ObjectSpec((*FlatPublicInterfaceIPv6Range)(nil).HCL2Spec())}, + } + return s +} + +// FlatPublicInterfaceIPv6Range is an auto-generated flat version of PublicInterfaceIPv6Range. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatPublicInterfaceIPv6Range struct { + Range *string `mapstructure:"range" required:"true" cty:"range" hcl:"range"` +} + +// FlatMapstructure returns a new FlatPublicInterfaceIPv6Range. +// FlatPublicInterfaceIPv6Range is an auto-generated flat version of PublicInterfaceIPv6Range. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*PublicInterfaceIPv6Range) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPublicInterfaceIPv6Range) +} + +// HCL2Spec returns the hcl spec of a PublicInterfaceIPv6Range. +// This spec is used by HCL to read the fields of PublicInterfaceIPv6Range. +// The decoded values from this spec will then be applied to a FlatPublicInterfaceIPv6Range. +func (*FlatPublicInterfaceIPv6Range) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "range": &hcldec.AttrSpec{Name: "range", Type: cty.String, Required: false}, + } + return s +} + +// FlatVLANInterface is an auto-generated flat version of VLANInterface. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatVLANInterface struct { + VLANLabel *string `mapstructure:"vlan_label" required:"true" cty:"vlan_label" hcl:"vlan_label"` + IPAMAddress *string `mapstructure:"ipam_address" required:"false" cty:"ipam_address" hcl:"ipam_address"` +} + +// FlatMapstructure returns a new FlatVLANInterface. +// FlatVLANInterface is an auto-generated flat version of VLANInterface. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*VLANInterface) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVLANInterface) +} + +// HCL2Spec returns the hcl spec of a VLANInterface. +// This spec is used by HCL to read the fields of VLANInterface. +// The decoded values from this spec will then be applied to a FlatVLANInterface. +func (*FlatVLANInterface) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "vlan_label": &hcldec.AttrSpec{Name: "vlan_label", Type: cty.String, Required: false}, + "ipam_address": &hcldec.AttrSpec{Name: "ipam_address", Type: cty.String, Required: false}, + } + return s +} + +// FlatVPCInterface is an auto-generated flat version of VPCInterface. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatVPCInterface struct { + SubnetID *int `mapstructure:"subnet_id" required:"true" cty:"subnet_id" hcl:"subnet_id"` + IPv4 *FlatVPCInterfaceIPv4 `mapstructure:"ipv4" required:"false" cty:"ipv4" hcl:"ipv4"` +} + +// FlatMapstructure returns a new FlatVPCInterface. +// FlatVPCInterface is an auto-generated flat version of VPCInterface. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*VPCInterface) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVPCInterface) +} + +// HCL2Spec returns the hcl spec of a VPCInterface. +// This spec is used by HCL to read the fields of VPCInterface. +// The decoded values from this spec will then be applied to a FlatVPCInterface. +func (*FlatVPCInterface) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.Number, Required: false}, + "ipv4": &hcldec.BlockSpec{TypeName: "ipv4", Nested: hcldec.ObjectSpec((*FlatVPCInterfaceIPv4)(nil).HCL2Spec())}, + } + return s +} + +// FlatVPCInterfaceIPv4 is an auto-generated flat version of VPCInterfaceIPv4. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatVPCInterfaceIPv4 struct { + Addresses []FlatVPCInterfaceIPv4Address `mapstructure:"addresses" required:"false" cty:"addresses" hcl:"addresses"` + Ranges []FlatVPCInterfaceIPv4Range `mapstructure:"ranges" required:"false" cty:"ranges" hcl:"ranges"` +} + +// FlatMapstructure returns a new FlatVPCInterfaceIPv4. +// FlatVPCInterfaceIPv4 is an auto-generated flat version of VPCInterfaceIPv4. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*VPCInterfaceIPv4) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVPCInterfaceIPv4) +} + +// HCL2Spec returns the hcl spec of a VPCInterfaceIPv4. +// This spec is used by HCL to read the fields of VPCInterfaceIPv4. +// The decoded values from this spec will then be applied to a FlatVPCInterfaceIPv4. +func (*FlatVPCInterfaceIPv4) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "addresses": &hcldec.BlockListSpec{TypeName: "addresses", Nested: hcldec.ObjectSpec((*FlatVPCInterfaceIPv4Address)(nil).HCL2Spec())}, + "ranges": &hcldec.BlockListSpec{TypeName: "ranges", Nested: hcldec.ObjectSpec((*FlatVPCInterfaceIPv4Range)(nil).HCL2Spec())}, + } + return s +} + +// FlatVPCInterfaceIPv4Address is an auto-generated flat version of VPCInterfaceIPv4Address. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatVPCInterfaceIPv4Address struct { + Address *string `mapstructure:"address" required:"true" cty:"address" hcl:"address"` + Primary *bool `mapstructure:"primary" required:"false" cty:"primary" hcl:"primary"` + NAT1To1Address *string `mapstructure:"nat_1_1_address" required:"false" cty:"nat_1_1_address" hcl:"nat_1_1_address"` +} + +// FlatMapstructure returns a new FlatVPCInterfaceIPv4Address. +// FlatVPCInterfaceIPv4Address is an auto-generated flat version of VPCInterfaceIPv4Address. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*VPCInterfaceIPv4Address) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVPCInterfaceIPv4Address) +} + +// HCL2Spec returns the hcl spec of a VPCInterfaceIPv4Address. +// This spec is used by HCL to read the fields of VPCInterfaceIPv4Address. +// The decoded values from this spec will then be applied to a FlatVPCInterfaceIPv4Address. +func (*FlatVPCInterfaceIPv4Address) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "address": &hcldec.AttrSpec{Name: "address", Type: cty.String, Required: false}, + "primary": &hcldec.AttrSpec{Name: "primary", Type: cty.Bool, Required: false}, + "nat_1_1_address": &hcldec.AttrSpec{Name: "nat_1_1_address", Type: cty.String, Required: false}, + } + return s +} + +// FlatVPCInterfaceIPv4Range is an auto-generated flat version of VPCInterfaceIPv4Range. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatVPCInterfaceIPv4Range struct { + Range *string `mapstructure:"range" required:"true" cty:"range" hcl:"range"` +} + +// FlatMapstructure returns a new FlatVPCInterfaceIPv4Range. +// FlatVPCInterfaceIPv4Range is an auto-generated flat version of VPCInterfaceIPv4Range. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*VPCInterfaceIPv4Range) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVPCInterfaceIPv4Range) +} + +// HCL2Spec returns the hcl spec of a VPCInterfaceIPv4Range. +// This spec is used by HCL to read the fields of VPCInterfaceIPv4Range. +// The decoded values from this spec will then be applied to a FlatVPCInterfaceIPv4Range. +func (*FlatVPCInterfaceIPv4Range) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "range": &hcldec.AttrSpec{Name: "range", Type: cty.String, Required: false}, + } + return s +} diff --git a/builder/linode/step_create_linode.go b/builder/linode/step_create_linode.go index 44deff4..8f936a3 100644 --- a/builder/linode/step_create_linode.go +++ b/builder/linode/step_create_linode.go @@ -37,6 +37,95 @@ func flattenConfigInterface(i Interface) linodego.InstanceConfigInterfaceCreateO } } +func flattenPublicInterface(public *PublicInterface) *linodego.PublicInterfaceCreateOptions { + if public == nil { + return nil + } + result := &linodego.PublicInterfaceCreateOptions{} + if public.IPv4 != nil { + addresses := make([]linodego.PublicInterfaceIPv4AddressCreateOptions, len(public.IPv4.Addresses)) + for i, addr := range public.IPv4.Addresses { + addresses[i] = linodego.PublicInterfaceIPv4AddressCreateOptions{ + Address: addr.Address, + Primary: addr.Primary, + } + } + result.IPv4 = &linodego.PublicInterfaceIPv4CreateOptions{ + Addresses: linodego.Pointer(addresses), + } + } + if public.IPv6 != nil { + ranges := make([]linodego.PublicInterfaceIPv6RangeCreateOptions, len(public.IPv6.Ranges)) + for i, r := range public.IPv6.Ranges { + ranges[i] = linodego.PublicInterfaceIPv6RangeCreateOptions{ + Range: r.Range, + } + } + result.IPv6 = &linodego.PublicInterfaceIPv6CreateOptions{ + Ranges: linodego.Pointer(ranges), + } + } + return result +} + +func flattenVPCInterface(vpc *VPCInterface) *linodego.VPCInterfaceCreateOptions { + if vpc == nil { + return nil + } + result := &linodego.VPCInterfaceCreateOptions{ + SubnetID: vpc.SubnetID, + } + if vpc.IPv4 != nil { + addresses := make([]linodego.VPCInterfaceIPv4AddressCreateOptions, len(vpc.IPv4.Addresses)) + ranges := make([]linodego.VPCInterfaceIPv4RangeCreateOptions, len(vpc.IPv4.Ranges)) + for i, addr := range vpc.IPv4.Addresses { + addresses[i] = linodego.VPCInterfaceIPv4AddressCreateOptions{ + Address: addr.Address, + } + } + for i, r := range vpc.IPv4.Ranges { + ranges[i] = linodego.VPCInterfaceIPv4RangeCreateOptions{ + Range: r.Range, + } + } + result.IPv4 = &linodego.VPCInterfaceIPv4CreateOptions{ + Addresses: linodego.Pointer(addresses), + Ranges: linodego.Pointer(ranges), + } + } + return result +} + +func flattenVLANInterface(vlan *VLANInterface) *linodego.VLANInterface { + if vlan == nil { + return nil + } + result := &linodego.VLANInterface{ + VLANLabel: vlan.VLANLabel, + } + if vlan.IPAMAddress != nil { + result.IPAMAddress = vlan.IPAMAddress + } + return result +} + +func flattenLinodeInterface(li LinodeInterface) (opts linodego.LinodeInterfaceCreateOptions) { + opts.FirewallID = linodego.Pointer(li.FirewallID) + + if li.DefaultRoute != nil { + opts.DefaultRoute = &linodego.InterfaceDefaultRoute{ + IPv4: li.DefaultRoute.IPv4, + IPv6: li.DefaultRoute.IPv6, + } + } + + opts.Public = flattenPublicInterface(li.Public) + opts.VPC = flattenVPCInterface(li.VPC) + opts.VLAN = flattenVLANInterface(li.VLAN) + + return +} + func flattenMetadata(m Metadata) *linodego.InstanceMetadataOptions { if m.UserData == "" { return nil @@ -57,27 +146,40 @@ func (s *stepCreateLinode) Run(ctx context.Context, state multistep.StateBag) mu ui.Say("Creating Linode...") + createOpts := linodego.InstanceCreateOptions{ + RootPass: c.Comm.Password(), + AuthorizedKeys: []string{}, + AuthorizedUsers: []string{}, + PrivateIP: c.PrivateIP, + Region: c.Region, + StackScriptID: c.StackScriptID, + StackScriptData: c.StackScriptData, + Type: c.InstanceType, + Label: c.Label, + Image: c.Image, + SwapSize: &c.SwapSize, + Tags: c.Tags, + FirewallID: c.FirewallID, + Metadata: flattenMetadata(c.Metadata), + InterfaceGeneration: linodego.InterfaceGeneration(c.InterfaceGeneration), + } + interfaces := make([]linodego.InstanceConfigInterfaceCreateOptions, len(c.Interfaces)) for i, v := range c.Interfaces { interfaces[i] = flattenConfigInterface(v) } - createOpts := linodego.InstanceCreateOptions{ - RootPass: c.Comm.Password(), - AuthorizedKeys: []string{}, - AuthorizedUsers: []string{}, - Interfaces: interfaces, - PrivateIP: c.PrivateIP, - Region: c.Region, - StackScriptID: c.StackScriptID, - StackScriptData: c.StackScriptData, - Type: c.InstanceType, - Label: c.Label, - Image: c.Image, - SwapSize: &c.SwapSize, - Tags: c.Tags, - FirewallID: c.FirewallID, - Metadata: flattenMetadata(c.Metadata), + linodeInterfaces := make([]linodego.LinodeInterfaceCreateOptions, len(c.LinodeInterfaces)) + for i, v := range c.LinodeInterfaces { + linodeInterfaces[i] = flattenLinodeInterface(v) + } + + if len(interfaces) > 0 { + createOpts.Interfaces = interfaces + } + + if len(linodeInterfaces) > 0 { + createOpts.LinodeInterfaces = linodeInterfaces } if pubKey := string(c.Comm.SSHPublicKey); pubKey != "" { diff --git a/docs/builders/linode.mdx b/docs/builders/linode.mdx index 07c0039..8bc5142 100644 --- a/docs/builders/linode.mdx +++ b/docs/builders/linode.mdx @@ -49,15 +49,103 @@ can also be supplied to override the typical auto-generated key: @include 'builder/linode/Config-not-required.mdx' -#### Interface +#### Linode Interface -This section outlines the fields configurable for a single interface object. +This section outlines the fields configurable for a newer Linode interface object. -##### Required Interface Common Attributes +@include 'builder/linode/LinodeInterface-not-required.mdx' + +##### Linode Interface Default Route configuration object (InterfaceDefaultRoute) + +###### Optional + +@include 'builder/linode/InterfaceDefaultRoute-not-required.mdx' + +##### Public Linode Interface configuration object (PublicInterface) + +###### Optional + +@include 'builder/linode/PublicInterface-not-required.mdx' + +##### Public Linode Interface IPv4 configuration object (PublicInterfaceIPv4) + +###### Optional + +@include 'builder/linode/PublicInterfaceIPv4-not-required.mdx' + +##### Public Linode Interface IPv4 Address configuration object (PublicInterfaceIPv4Address) + +###### Required + +@include 'builder/linode/PublicInterfaceIPv4Address-required.mdx' + +###### Optional + +@include 'builder/linode/PublicInterfaceIPv4Address-not-required.mdx' + +##### Public Linode Interface IPv6 configuration object (PublicInterfaceIPv6) + +###### Optional + +@include 'builder/linode/PublicInterfaceIPv6-not-required.mdx' + +##### Public Linode Interface IPv6 Range configuration object (PublicInterfaceIPv6Range) + +###### Required + +@include 'builder/linode/PublicInterfaceIPv6Range-required.mdx' + +##### VPC Linode Interface configuration object (VPCInterface) + +###### Required + +@include 'builder/linode/VPCInterface-required.mdx' + +###### Optional + +@include 'builder/linode/VPCInterface-not-required.mdx' + +##### VPC Linode Interface IPv4 configuration object (VPCInterfaceIPv4) + +###### Optional + +@include 'builder/linode/VPCInterfaceIPv4-not-required.mdx' + +##### VPC Linode Interface IPv4 Address configuration object (VPCInterfaceIPv4Address) + +###### Required + +@include 'builder/linode/VPCInterfaceIPv4Address-required.mdx' + +###### Optional + +@include 'builder/linode/VPCInterfaceIPv4Address-not-required.mdx' + +##### VPC Linode Interface IPv4 Range configuration object (VPCInterfaceIPv4Range) + +###### Required + +@include 'builder/linode/VPCInterfaceIPv4Range-required.mdx' + +##### VLAN Linode Interface configuration object (VLANInterface) + +###### Required + +@include 'builder/linode/VLANInterface-required.mdx' + +###### Optional + +@include 'builder/linode/VLANInterface-not-required.mdx' + +#### Legacy Config Interface + +This section outlines the fields configurable for a single legacy config interface object. + +##### Required Config Interface Common Attributes @include 'builder/linode/Interface-required.mdx' -##### Optional Interface Common Attributes +##### Optional Config Interface Common Attributes @include 'builder/linode/Interface-not-required.mdx' @@ -69,7 +157,7 @@ This section outlines the fields configurable for a single interface object. @include 'builder/linode/VPCInterfaceAttributes-not-required.mdx' -###### VPC Interface IPv4 configuration object +###### VPC Config Interface IPv4 configuration object (InterfaceIPv4) @include 'builder/linode/InterfaceIPv4-not-required.mdx' @@ -253,3 +341,77 @@ build { } ``` +## Linode Interface Example + +**HCL2** + +```hcl +locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") } + +source "linode" "example" { + image = "linode/ubuntu24.04" + image_description = "My Private Image" + image_label = "private-image-${local.timestamp}" + instance_label = "temporary-linode-${local.timestamp}" + instance_type = "g6-standard-1" + region = "us-mia" + ssh_username = "root" + interface_generation = "linode" + + linode_interface { + firewall_id = 12345 + public { + ipv4 { + address { + address = "auto" + primary = true + } + } + } + } +} + +build { + sources = ["source.linode.example"] +} +``` + +**JSON** + +```json +{ + "source": { + "linode": { + "example": { + "image": "linode/ubuntu24.04", + "linode_token": "YOUR API TOKEN", + "region": "us-mia", + "instance_type": "g6-nanode-1", + "instance_label": "temporary-linode-{{timestamp}}", + "image_label": "private-image-{{timestamp}}", + "image_description": "My Private Image", + "ssh_username": "root", + "interface_generation": "linode", + "linode_interface": { + "firewall_id": 2930969, + "public": { + "ipv4": { + "addresses": [ + { + "address": "auto", + "primary": true + } + ] + } + } + } + } + } + }, + "build": { + "sources": [ + "source.linode.example" + ] + } +} +``` \ No newline at end of file