Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/resources/machine_config_v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ The following attributes are exported:

* `authorized_users` - (Optional) Linode user accounts (seperated by commas) whose Linode SSH keys will be permitted root access to the created node. (string)
* `create_private_ip` - (Optional) Create private IP for the instance. Default `false` (bool)
* `use_interfaces` - (Optional) Enable Linode interface/VPC networking (new networking stack). Conflicts with `create_private_ip`. Default `false` (bool)
* `vpc_subnet_id` - (Required with `use_interfaces`) VPC subnet ID to attach when interface networking is enabled. (string)
* `vpc_private_ip` - (Optional) IPv4 address to request on the VPC interface when using interface networking. (string)
* `public_interface_firewall_id` - (Optional) Firewall ID to attach to the public interface when using interface networking. (string)
* `vpc_interface_firewall_id` - (Optional) Firewall ID to attach to the VPC interface when using interface networking. (string)
* `docker_port` - (Optional) Docker Port. Default `2376` (string)
* `image` - (Optional) Specifies the Linode Instance image which determines the OS distribution and base files. Default `linode/ubuntu18.04` (string)
* `instance_type` - (Optional) Specifies the Linode Instance type which determines CPU, memory, disk size, etc. Default `g6-standard-4` (string)
Expand All @@ -246,6 +251,7 @@ The following attributes are exported:
* `ssh_user` - (Optional) SSH username. Default `root` (string)
* `stackscript` - (Optional) Specifies the Linode StackScript to use to create the instance. (string)
* `stackscript_data` - (Optional) A JSON string specifying data for the selected StackScript. (string)
* `user_data` - (Optional) Cloud-init user data for the Linode Metadata service; supply plain text. (string)
* `swap_size` - (Optional) Linode Instance Swap Size (MB). Default `512` (string)
* `tags` - (Optional) A comma separated list of tags to apply to the the Linode resource (string)
* `token` - (Optional/Sensitive) Linode API token. Mandatory on Rancher v2.0.x and v2.1.x. Use `rancher2_cloud_credential` from Rancher v2.2.x (string)
Expand Down
37 changes: 37 additions & 0 deletions rancher2/resource_rancher2_machine_config_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
Expand All @@ -18,6 +19,42 @@ func resourceRancher2MachineConfigV2() *schema.Resource {
Update: resourceRancher2MachineConfigV2Update,
Delete: resourceRancher2MachineConfigV2Delete,
Schema: machineConfigV2Fields(),
CustomizeDiff: func(d *schema.ResourceDiff, i interface{}) error {
v := d.Get("linode_config")
configs, ok := v.([]interface{})
if !ok || len(configs) == 0 || configs[0] == nil {
return nil
}

cfg, ok := configs[0].(map[string]interface{})
if !ok {
return nil
}

useInterfaces, _ := cfg["use_interfaces"].(bool)

if useInterfaces {
subnetID, ok := cfg["vpc_subnet_id"].(string)
// Treat missing or non-string values as empty to ensure validation
if !ok || strings.TrimSpace(subnetID) == "" {
return fmt.Errorf("linode_config.0.vpc_subnet_id must be set when linode_config.0.use_interfaces is true")
}
} else {
if subnetID, ok := cfg["vpc_subnet_id"].(string); ok && strings.TrimSpace(subnetID) != "" {
return fmt.Errorf("linode_config.0.use_interfaces must be true when linode_config.0.vpc_subnet_id is set")
}
if vpcIP, ok := cfg["vpc_private_ip"].(string); ok && strings.TrimSpace(vpcIP) != "" {
return fmt.Errorf("linode_config.0.use_interfaces must be true when linode_config.0.vpc_private_ip is set")
}
if publicFirewallID, ok := cfg["public_interface_firewall_id"].(string); ok && strings.TrimSpace(publicFirewallID) != "" {
return fmt.Errorf("linode_config.0.use_interfaces must be true when linode_config.0.public_interface_firewall_id is set")
}
if vpcFirewallID, ok := cfg["vpc_interface_firewall_id"].(string); ok && strings.TrimSpace(vpcFirewallID) != "" {
return fmt.Errorf("linode_config.0.use_interfaces must be true when linode_config.0.vpc_interface_firewall_id is set")
}
}
return nil
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(3 * time.Minute),
Update: schema.DefaultTimeout(10 * time.Minute),
Expand Down
44 changes: 41 additions & 3 deletions rancher2/schema_machine_config_v2_linode.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ func machineConfigV2LinodeFields() map[string]*schema.Schema {
Description: "Linode user accounts (seperated by commas) whose Linode SSH keys will be permitted root access to the created node",
},
"create_private_ip": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{
"linode_config.0.use_interfaces",
},
Description: "Create private IP for the instance",
},
"docker_port": {
Expand Down Expand Up @@ -76,6 +79,11 @@ func machineConfigV2LinodeFields() map[string]*schema.Schema {
Optional: true,
Description: "A JSON string specifying data for the selected StackScript",
},
"user_data": {
Type: schema.TypeString,
Optional: true,
Description: "Cloud-init user data for the Linode Metadata service",
},
Comment thread
matttrach marked this conversation as resolved.
"swap_size": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -98,6 +106,36 @@ func machineConfigV2LinodeFields() map[string]*schema.Schema {
Optional: true,
Description: "Prefix the User-Agent in Linode API calls with some 'product/version'",
},
"use_interfaces": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{
"linode_config.0.create_private_ip",
},
Description: "Enable Linode interface/VPC networking instead of legacy private IP mode",
},
"vpc_subnet_id": {
Type: schema.TypeString,
Optional: true,
RequiredWith: []string{"linode_config.0.use_interfaces"},
Description: "VPC subnet ID to attach when using interface/VPC networking",
},
"vpc_private_ip": {
Type: schema.TypeString,
Optional: true,
Description: "Optional IPv4 address to request on the VPC interface (interface networking only)",
},
"public_interface_firewall_id": {
Type: schema.TypeString,
Optional: true,
Description: "Firewall ID to attach to the public interface when using interface networking",
},
"vpc_interface_firewall_id": {
Type: schema.TypeString,
Optional: true,
Description: "Firewall ID to attach to the VPC interface when using interface networking",
},
}

return s
Expand Down
88 changes: 70 additions & 18 deletions rancher2/structure_machine_config_v2_linode.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,30 @@ const (
//Types

type machineConfigV2Linode struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
AuthorizedUsers string `json:"authorizedUsers,omitempty" yaml:"authorizedUsers,omitempty"`
CreatePrivateIP bool `json:"createPrivateIp,omitempty" yaml:"createPrivateIp,omitempty"`
DockerPort string `json:"dockerPort,omitempty" yaml:"dockerPort,omitempty"`
Image string `json:"image,omitempty" yaml:"image,omitempty"`
InstanceType string `json:"instanceType,omitempty" yaml:"instanceType,omitempty"`
Label string `json:"label,omitempty" yaml:"label,omitempty"`
Region string `json:"region,omitempty" yaml:"region,omitempty"`
RootPass string `json:"rootPass,omitempty" yaml:"rootPass,omitempty"`
SSHPort string `json:"sshPort,omitempty" yaml:"sshPort,omitempty"`
SSHUser string `json:"sshUser,omitempty" yaml:"sshUser,omitempty"`
StackScript string `json:"stackscript,omitempty" yaml:"stackscript,omitempty"`
StackscriptData string `json:"stackscriptData,omitempty" yaml:"stackscriptData,omitempty"`
SwapSize string `json:"swapSize,omitempty" yaml:"swapSize,omitempty"`
Tags string `json:"tags,omitempty" yaml:"tags,omitempty"`
Token string `json:"token,omitempty" yaml:"token,omitempty"`
UAPrefix string `json:"uaPrefix,omitempty" yaml:"uaPrefix,omitempty"`
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
AuthorizedUsers string `json:"authorizedUsers,omitempty" yaml:"authorizedUsers,omitempty"`
CreatePrivateIP bool `json:"createPrivateIp,omitempty" yaml:"createPrivateIp,omitempty"`
DockerPort string `json:"dockerPort,omitempty" yaml:"dockerPort,omitempty"`
Image string `json:"image,omitempty" yaml:"image,omitempty"`
InstanceType string `json:"instanceType,omitempty" yaml:"instanceType,omitempty"`
Label string `json:"label,omitempty" yaml:"label,omitempty"`
Region string `json:"region,omitempty" yaml:"region,omitempty"`
RootPass string `json:"rootPass,omitempty" yaml:"rootPass,omitempty"`
SSHPort string `json:"sshPort,omitempty" yaml:"sshPort,omitempty"`
SSHUser string `json:"sshUser,omitempty" yaml:"sshUser,omitempty"`
StackScript string `json:"stackscript,omitempty" yaml:"stackscript,omitempty"`
StackscriptData string `json:"stackscriptData,omitempty" yaml:"stackscriptData,omitempty"`
SwapSize string `json:"swapSize,omitempty" yaml:"swapSize,omitempty"`
Tags string `json:"tags,omitempty" yaml:"tags,omitempty"`
Token string `json:"token,omitempty" yaml:"token,omitempty"`
UserData string `json:"userData,omitempty" yaml:"userData,omitempty"`
UAPrefix string `json:"uaPrefix,omitempty" yaml:"uaPrefix,omitempty"`
UseInterfaces bool `json:"useInterfaces,omitempty" yaml:"useInterfaces,omitempty"`
VPCInterfaceFirewallID string `json:"vpcInterfaceFirewallId,omitempty" yaml:"vpcInterfaceFirewallId,omitempty"`
VPCPrivateIP string `json:"vpcPrivateIp,omitempty" yaml:"vpcPrivateIp,omitempty"`
VPCSubnetID string `json:"vpcSubnetId,omitempty" yaml:"vpcSubnetId,omitempty"`
PublicInterfaceFirewallID string `json:"publicInterfaceFirewallId,omitempty" yaml:"publicInterfaceFirewallId,omitempty"`
}

type MachineConfigV2Linode struct {
Expand Down Expand Up @@ -95,6 +101,10 @@ func flattenMachineConfigV2Linode(in *MachineConfigV2Linode) []interface{} {
obj["stackscript_data"] = in.StackscriptData
}

if len(in.UserData) > 0 {
obj["user_data"] = in.UserData
}

if len(in.SwapSize) > 0 {
obj["swap_size"] = in.SwapSize
}
Expand All @@ -107,6 +117,24 @@ func flattenMachineConfigV2Linode(in *MachineConfigV2Linode) []interface{} {
obj["token"] = in.Token
}

obj["use_interfaces"] = in.UseInterfaces

if len(in.VPCSubnetID) > 0 {
obj["vpc_subnet_id"] = in.VPCSubnetID
}

if len(in.VPCPrivateIP) > 0 {
obj["vpc_private_ip"] = in.VPCPrivateIP
}

if len(in.PublicInterfaceFirewallID) > 0 {
obj["public_interface_firewall_id"] = in.PublicInterfaceFirewallID
}

if len(in.VPCInterfaceFirewallID) > 0 {
obj["vpc_interface_firewall_id"] = in.VPCInterfaceFirewallID
}

if len(in.UAPrefix) > 0 {
obj["ua_prefix"] = in.UAPrefix
}
Expand Down Expand Up @@ -180,6 +208,10 @@ func expandMachineConfigV2Linode(p []interface{}, source *MachineConfigV2) *Mach
obj.StackscriptData = v
}

if v, ok := in["user_data"].(string); ok && len(v) > 0 {
obj.UserData = v
}

if v, ok := in["swap_size"].(string); ok && len(v) > 0 {
obj.SwapSize = v
}
Expand All @@ -192,6 +224,26 @@ func expandMachineConfigV2Linode(p []interface{}, source *MachineConfigV2) *Mach
obj.Token = v
}

if v, ok := in["use_interfaces"].(bool); ok {
obj.UseInterfaces = v
}

if v, ok := in["vpc_subnet_id"].(string); ok && len(v) > 0 {
obj.VPCSubnetID = v
}

if v, ok := in["vpc_private_ip"].(string); ok && len(v) > 0 {
obj.VPCPrivateIP = v
}

if v, ok := in["public_interface_firewall_id"].(string); ok && len(v) > 0 {
obj.PublicInterfaceFirewallID = v
}

if v, ok := in["vpc_interface_firewall_id"].(string); ok && len(v) > 0 {
obj.VPCInterfaceFirewallID = v
}

if v, ok := in["ua_prefix"].(string); ok && len(v) > 0 {
obj.UAPrefix = v
}
Expand Down
129 changes: 129 additions & 0 deletions rancher2/structure_machine_config_v2_linode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package rancher2

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestFlattenMachineConfigV2Linode(t *testing.T) {
input := &MachineConfigV2Linode{
machineConfigV2Linode: machineConfigV2Linode{
AuthorizedUsers: "user1,user2",
CreatePrivateIP: false,
DockerPort: "2377",
Image: "linode/ubuntu22.04",
InstanceType: "g6-standard-2",
Label: "example",
Region: "us-east",
RootPass: "secret",
SSHPort: "2222",
SSHUser: "ubuntu",
StackScript: "user/stack",
StackscriptData: "{\"foo\":\"bar\"}",
UserData: "#cloud-config\npackages:\n- htop\n",
SwapSize: "256",
Tags: "tag1,tag2",
Token: "token",
UAPrefix: "tf-provider/1.0",
UseInterfaces: true,
VPCSubnetID: "456",
VPCPrivateIP: "10.0.0.10",
PublicInterfaceFirewallID: "789",
VPCInterfaceFirewallID: "321",
},
}

expected := []interface{}{
map[string]interface{}{
"authorized_users": "user1,user2",
"create_private_ip": false,
"docker_port": "2377",
"image": "linode/ubuntu22.04",
"instance_type": "g6-standard-2",
"label": "example",
"region": "us-east",
"root_pass": "secret",
"ssh_port": "2222",
"ssh_user": "ubuntu",
"stackscript": "user/stack",
"stackscript_data": "{\"foo\":\"bar\"}",
"user_data": "#cloud-config\npackages:\n- htop\n",
"swap_size": "256",
"tags": "tag1,tag2",
"token": "token",
"use_interfaces": true,
"vpc_subnet_id": "456",
"vpc_private_ip": "10.0.0.10",
"public_interface_firewall_id": "789",
"vpc_interface_firewall_id": "321",
"ua_prefix": "tf-provider/1.0",
},
}

assert.Equal(t, expected, flattenMachineConfigV2Linode(input))
}

func TestExpandMachineConfigV2Linode(t *testing.T) {
input := []interface{}{
map[string]interface{}{
"authorized_users": "user1,user2",
"create_private_ip": false,
"docker_port": "2377",
"image": "linode/ubuntu22.04",
"instance_type": "g6-standard-2",
"label": "example",
"region": "us-east",
"root_pass": "secret",
"ssh_port": "2222",
"ssh_user": "ubuntu",
"stackscript": "user/stack",
"stackscript_data": "{\"foo\":\"bar\"}",
"user_data": "#cloud-config\npackages:\n- htop\n",
"swap_size": "256",
"tags": "tag1,tag2",
"token": "token",
"use_interfaces": true,
"vpc_subnet_id": "456",
"vpc_private_ip": "10.0.0.10",
"public_interface_firewall_id": "789",
"vpc_interface_firewall_id": "321",
"ua_prefix": "tf-provider/1.0",
},
}

source := &MachineConfigV2{}
got := expandMachineConfigV2Linode(input, source)

expected := &MachineConfigV2Linode{
machineConfigV2Linode: machineConfigV2Linode{
AuthorizedUsers: "user1,user2",
CreatePrivateIP: false,
DockerPort: "2377",
Image: "linode/ubuntu22.04",
InstanceType: "g6-standard-2",
Label: "example",
Region: "us-east",
RootPass: "secret",
SSHPort: "2222",
SSHUser: "ubuntu",
StackScript: "user/stack",
StackscriptData: "{\"foo\":\"bar\"}",
UserData: "#cloud-config\npackages:\n- htop\n",
SwapSize: "256",
Tags: "tag1,tag2",
Token: "token",
UAPrefix: "tf-provider/1.0",
UseInterfaces: true,
VPCSubnetID: "456",
VPCPrivateIP: "10.0.0.10",
PublicInterfaceFirewallID: "789",
VPCInterfaceFirewallID: "321",
},
}
expected.TypeMeta.Kind = machineConfigV2LinodeKind
expected.TypeMeta.APIVersion = machineConfigV2LinodeAPIVersion

assert.Equal(t, expected, got)
assert.Equal(t, expected.TypeMeta, source.TypeMeta)
}
Loading