diff --git a/docs/data-sources/nodebalancer.md b/docs/data-sources/nodebalancer.md index 83d0f71b6..f41c7f357 100644 --- a/docs/data-sources/nodebalancer.md +++ b/docs/data-sources/nodebalancer.md @@ -27,7 +27,7 @@ The following arguments are supported: In addition to all arguments above, the following attributes are exported: -* `label` - The label of the Linode NodeBalancer +* `label` - The label of the Linode NodeBalancer. * `client_conn_throttle` - Throttle connections per second (0-20). @@ -35,23 +35,23 @@ In addition to all arguments above, the following attributes are exported: * **NOTE: This attribute may not be generally available.** -* `created` – When this Linode NodeBalancer was created +* `created` – When this Linode NodeBalancer was created. * `linode_id` - The ID of a Linode Instance where the NodeBalancer should be attached. * `tags` - A list of tags applied to this object. Tags are case-insensitive and are for organizational purposes only. -* `hostname` - This NodeBalancer's hostname, ending with .ip.linodeusercontent.com +* `hostname` - This NodeBalancer's hostname, ending with .ip.linodeusercontent.com. -* `ipv4` - The Public IPv4 Address of this NodeBalancer +* `ipv4` - The Public IPv4 Address of this NodeBalancer. -* `ipv6` - The Public IPv6 Address of this NodeBalancer +* `ipv6` - The Public IPv6 Address of this NodeBalancer. * `region` - The Region where this Linode NodeBalancer is located. NodeBalancers only support backends in the same Region. -* [`transfer`](#transfer) - The network transfer stats for the current month +* [`transfer`](#transfer) - The network transfer stats for the current month. -* `updated` – When this Linode NodeBalancer was last updated +* `updated` – When this Linode NodeBalancer was last updated. * [`firewalls`](#firewalls) - A list of Firewalls assigned to this NodeBalancer. @@ -59,15 +59,21 @@ In addition to all arguments above, the following attributes are exported: * [`lke_cluster`](#lke_cluster) - The LKE cluster that manages this NodeBalancer, if any. The list will be empty if this NodeBalancer isn't related to an LKE cluster. +* `type` - The type of this NodeBalancer. + +* `frontend_address_type` - Indicates whether incoming requests are routed to NodeBalancers using VPC frontend IPs or public frontend IPs. + +* `frontend_vpc_subnet_id` - The VPC subnet assigned to this NodeBalancer. + ### transfer The following attributes are available on transfer: -* `in` - The total transfer, in MB, used by this NodeBalancer for the current month +* `in` - The total transfer, in MB, used by this NodeBalancer for the current month. -* `out` - The total inbound transfer, in MB, used for this NodeBalancer for the current month +* `out` - The total inbound transfer, in MB, used for this NodeBalancer for the current month. -* `total` - The total outbound transfer, in MB, used for this NodeBalancer for the current month +* `total` - The total outbound transfer, in MB, used for this NodeBalancer for the current month. ### firewalls @@ -81,13 +87,13 @@ The following attributes are available on firewalls: * [`inbound`](#inbound-and-outbound) - A firewall rule that specifies what inbound network traffic is allowed. -* `inbound_policy` - The default behavior for inbound traffic. (`ACCEPT`, `DROP`) +* `inbound_policy` - The default behavior for inbound traffic. (`ACCEPT`, `DROP`). * [`outbound`](#inbound-and-outbound) - A firewall rule that specifies what outbound network traffic is allowed. -* `outbound_policy` - The default behavior for outbound traffic. (`ACCEPT`, `DROP`) +* `outbound_policy` - The default behavior for outbound traffic. (`ACCEPT`, `DROP`). -* `status` - The status of the firewall. (`enabled`, `disabled`, `deleted`) +* `status` - The status of the firewall. (`enabled`, `disabled`, `deleted`). * `created` - When this firewall was created. @@ -101,7 +107,7 @@ The following arguments are supported in the inbound and outbound rule blocks: * `action` - Controls whether traffic is accepted or dropped by this rule. Overrides the Firewall’s inbound_policy if this is an inbound rule, or the outbound_policy if this is an outbound rule. -* `protocol` - The network protocol this rule controls. (`TCP`, `UDP`, `ICMP`) +* `protocol` - The network protocol this rule controls (`TCP`, `UDP`, `ICMP`). * `ports` - A string representation of ports and/or port ranges (i.e. "443" or "80-90, 91"). diff --git a/docs/data-sources/nodebalancer_vpc.md b/docs/data-sources/nodebalancer_vpc.md index 9ffba02f3..9b4d71ef7 100644 --- a/docs/data-sources/nodebalancer_vpc.md +++ b/docs/data-sources/nodebalancer_vpc.md @@ -36,8 +36,10 @@ This data source exports the following attributes: * `ipv4_range` - A CIDR range for the VPC's IPv4 addresses. The NodeBalancer sources IP addresses from this range when routing traffic to the backend VPC nodes. -* `ipv6_range` - A CIDR range for the VPC's IPv6 addresses. The NodeBalancer sources IP addresses from this range when routing traffic to the backend VPC nodes. +* `ipv6_range` - A CIDR range for the VPC's IPv6 addresses allocated as the NodeBalancer's frontend IPs. * `subnet_id` - The ID of this configuration's VPC subnet. * `vpc_id` - The ID of this configuration's VPC. + +* `purpose` - Indicates whether the VPC configuration applies to backend nodes that serve requests or to the NodeBalancer frontend which manages incoming traffic. diff --git a/docs/data-sources/nodebalancer_vpcs.md b/docs/data-sources/nodebalancer_vpcs.md index 3da7191ed..fe28b8128 100644 --- a/docs/data-sources/nodebalancer_vpcs.md +++ b/docs/data-sources/nodebalancer_vpcs.md @@ -78,12 +78,14 @@ Each VPC configuration will be stored in the `vpc_configs` attribute and will ex * `ipv4_range` - A CIDR range for the VPC's IPv4 addresses. The NodeBalancer sources IP addresses from this range when routing traffic to the backend VPC nodes. -* `ipv6_range` - A CIDR range for the VPC's IPv6 addresses. The NodeBalancer sources IP addresses from this range when routing traffic to the backend VPC nodes. +* `ipv6_range` - A CIDR range for the VPC's IPv6 addresses allocated as the NodeBalancer's frontend IPs. * `subnet_id` - The ID of this configuration's VPC subnet. * `vpc_id` - The ID of this configuration's VPC. +* `purpose` - Indicates whether the VPC configuration applies to backend nodes that serve requests or to the NodeBalancer frontend which manages incoming traffic. + ## Filterable Fields * `id` @@ -97,3 +99,5 @@ Each VPC configuration will be stored in the `vpc_configs` attribute and will ex * `subnet_id` * `vpc_id` + +* `purpose` diff --git a/docs/data-sources/nodebalancers.md b/docs/data-sources/nodebalancers.md index 922418354..a990c725e 100644 --- a/docs/data-sources/nodebalancers.md +++ b/docs/data-sources/nodebalancers.md @@ -39,7 +39,7 @@ The following arguments are supported: * `order_by` - (Optional) The attribute to order the results by. See the [Filterable Fields section](#filterable-fields) for a list of valid fields. -* `order` - (Optional) The order in which results should be returned. (`asc`, `desc`; default `asc`) +* `order` - (Optional) The order in which results should be returned (`asc`, `desc`; default `asc`). ### Filter @@ -47,39 +47,43 @@ The following arguments are supported: * `values` - (Required) A list of values for the filter to allow. These values should all be in string form. -* `match_by` - (Optional) The method to match the field by. (`exact`, `regex`, `substring`; default `exact`) +* `match_by` - (Optional) The method to match the field by (`exact`, `regex`, `substring`; default `exact`). ## Attributes Reference Each Linode NodeBalancer will be stored in the `nodebalancers` attribute and will export the following attributes: -* `label` - The label of the Linode NodeBalancer +* `label` - The label of the Linode NodeBalancer. -* `client_conn_throttle` - Throttle connections per second (0-20) +* `client_conn_throttle` - Throttle connections per second (0-20). * `client_udp_sess_throttle` - Throttle UDP sessions per second (0-20). * **NOTE: This attribute may not be generally available.** -* `created` – When this Linode NodeBalancer was created - -* `linode_id` - The ID of a Linode Instance where the NodeBalancer should be attached +* `created` – When this Linode NodeBalancer was created. * `tags` - A list of tags applied to this object. Tags are case-insensitive and are for organizational purposes only. -* `hostname` - This NodeBalancer's hostname, ending with .ip.linodeusercontent.com +* `hostname` - This NodeBalancer's hostname, ending with .ip.linodeusercontent.com. -* `id` - The Linode NodeBalancer's unique ID +* `id` - The Linode NodeBalancer's unique ID. -* `ipv4` - The Public IPv4 Address of this NodeBalancer +* `ipv4` - The Public IPv4 Address of this NodeBalancer. -* `ipv6` - The Public IPv6 Address of this NodeBalancer +* `ipv6` - The Public IPv6 Address of this NodeBalancer. * `region` - The Region where this Linode NodeBalancer is located. NodeBalancers only support backends in the same Region. -* `updated` – When this Linode NodeBalancer was last updated +* `updated` – When this Linode NodeBalancer was last updated. + +* [`transfer`](#transfer) - The network transfer stats for the current month. + +* `type` - The type of this NodeBalancer. + +* `frontend_address_type` - Indicates whether incoming requests are routed to NodeBalancers using VPC frontend IPs or public frontend IPs. -* [`transfer`](#transfer) - The network transfer stats for the current month +* `frontend_vpc_subnet_id` - The VPC subnet assigned to this NodeBalancer. * [`lke_cluster`](#lke_cluster) - The LKE cluster that manages this NodeBalancer, if any. The list will be empty if this NodeBalancer isn't related to an LKE cluster. @@ -87,11 +91,11 @@ Each Linode NodeBalancer will be stored in the `nodebalancers` attribute and wil The following attributes are available on transfer: -* `in` - The total transfer, in MB, used by this NodeBalancer for the current month +* `in` - The total transfer, in MB, used by this NodeBalancer for the current month. -* `out` - The total inbound transfer, in MB, used for this NodeBalancer for the current month +* `out` - The total inbound transfer, in MB, used for this NodeBalancer for the current month. -* `total` - The total outbound transfer, in MB, used for this NodeBalancer for the current month +* `total` - The total outbound transfer, in MB, used for this NodeBalancer for the current month. ### lke_cluster diff --git a/docs/resources/nodebalancer.md b/docs/resources/nodebalancer.md index 93be3a9a9..da6d192bb 100644 --- a/docs/resources/nodebalancer.md +++ b/docs/resources/nodebalancer.md @@ -76,6 +76,8 @@ The following arguments are supported: * `tags` - (Optional) A list of tags applied to this object. Tags are case-insensitive and are for organizational purposes only. +* `type` - (Optional) The type of the NodeBalancer (`common`, `premium`, `premium_40gb`). Defaults to `common`. + * `ipv4` - (Optional) The Public IPv4 address to assign to this NodeBalancer. When provided, the address must be a reserved IPv4 address that is unassigned and owned by the account. *Changing `ipv4` forces the creation of a new Linode NodeBalancer.* ## Attributes Reference @@ -100,6 +102,12 @@ This resource exports the following attributes: * [`lke_cluster`](#lke_cluster) - The LKE cluster that manages this NodeBalancer, if any. The list will be empty if this NodeBalancer isn't related to an LKE cluster. +* [`frontend_vpcs`](#frontend_vpcs) - For internal load balancing, where the NodeBalancer is within a VPC, indicate a VPC subnet_id. For greater flexibility, you can specify the IP range within the subnet used for allocation. NOTE: VPC-attached NodeBalancers may not currently be available to all users and may require the `api_version` provider argument must be set to `v4beta`. + +* `frontend_address_type` - Indicates whether incoming requests are routed to NodeBalancers using VPC frontend IPs or public frontend IPs. + +* `frontend_vpc_subnet_id` - The VPC subnet assigned to this NodeBalancer. + ### transfer The following attributes are available on transfer: @@ -164,6 +172,18 @@ The following arguments are supported under each entry of the `vpcs` attribute: * `ipv4_range_auto_assign` - (Optional, Write-Only) Enables the use of a larger ipv4_range subnet for multiple NodeBalancers within the same VPC by allocating smaller /30 subnets for each NodeBalancer's backends. +#### frontend_vpcs + +-> **Limited Availability** Frontend VPC-attached NodeBalancers may not currently be available to all users and may require the `api_version` provider argument must be set to `v4beta`. + +The following arguments are supported under each entry of the `frontend_vpcs` attribute: + +* `subnet_id` - (Required) The VPC's subnet ID for the VPC based NodeBalancer. + +* `ipv4_range` - (Optional, Write-Only) A CIDR range for the VPC's IPv4 addresses allocated as the NodeBalancer's frontend IPs. + +* `ipv6_range` - (Optional, Write-Only) A CIDR range for the VPC's IPv6 addresses allocated as the NodeBalancer's frontend IPs. + ### lke_cluster The following attributes are available on `lke_cluster`: diff --git a/go.mod b/go.mod index 3fc42e5eb..757188b22 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/linode/linodego/v2 v2.3.0 github.com/stretchr/testify v1.11.1 golang.org/x/crypto v0.53.0 - golang.org/x/sync v0.21.0 + golang.org/x/sync v0.22.0 ) require ( @@ -98,21 +98,21 @@ require ( github.com/zclconf/go-cty v1.18.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.36.0 // indirect - golang.org/x/net v0.55.0 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/net v0.56.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.46.0 // indirect golang.org/x/term v0.44.0 // indirect - golang.org/x/text v0.38.0 // indirect + golang.org/x/text v0.40.0 // indirect golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.45.0 // indirect + golang.org/x/tools v0.47.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect google.golang.org/grpc v1.79.3 // indirect google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.2 // indirect + gopkg.in/ini.v1 v1.67.3 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.35.5 // indirect k8s.io/apimachinery v0.35.5 // indirect @@ -125,3 +125,5 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) + +replace github.com/linode/linodego/v2 => github.com/linode/linodego/v2 v2.4.2-0.20260717110001-25bfd3ada3f5 diff --git a/go.sum b/go.sum index cb581e17f..4ffcab2b4 100644 --- a/go.sum +++ b/go.sum @@ -189,8 +189,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/linode/linodego/k8s v1.26.1 h1:7/Wln3A7DPPEiS2KfaJexSbbFHbFE/xwokUU2R2TTOc= github.com/linode/linodego/k8s v1.26.1/go.mod h1:54MTxB+ecs+mHnLOIRT7SbqR7y9mJ1t626CdWaP9qGs= -github.com/linode/linodego/v2 v2.3.0 h1:kyPK0kL1pR+RzfdGj3QdM4ivaE9WM+E4U+hfnU0xX78= -github.com/linode/linodego/v2 v2.3.0/go.mod h1:rsITrtsKFKBi+pEnznDbbF9ZGBou9S8UccCm1yHLbpI= +github.com/linode/linodego/v2 v2.4.2-0.20260717110001-25bfd3ada3f5 h1:Q0j/vHf2kT0ji4WBilnRT6KpvTA7x8vlLCPcC2Eu3eE= +github.com/linode/linodego/v2 v2.4.2-0.20260717110001-25bfd3ada3f5/go.mod h1:LRzvYK/LFaGNqFmDxg1fZBtrolZOPvIdX9bssjwy5BA= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -284,21 +284,21 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= -golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -320,15 +320,15 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= @@ -352,8 +352,8 @@ gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnf gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss= -gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= +gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw= +gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/linode/acceptance/util.go b/linode/acceptance/util.go index 8a84a17f6..1bbe230b7 100644 --- a/linode/acceptance/util.go +++ b/linode/acceptance/util.go @@ -644,6 +644,7 @@ func GetRandomObjectStorageEndpoint() (*linodego.ObjectStorageEndpoint, error) { return nil, err } + // #nosec G404 -- Test data, doesn't need to be cryptographically secure rand.Shuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) @@ -779,10 +780,35 @@ func GetRandomRegionWithCaps(capabilities []linodego.RegionCapability, regionTyp return "", fmt.Errorf("no region found with the provided caps") } - // #nosec G404 -- Test data, doesn't need to be cryptography + // #nosec G404 -- Test data, doesn't need to be cryptographically secure return regions[rand.Intn(len(regions))], nil } +func GetRandomRegionSupportingPremiumNodeBalancers() (string, error) { + premiumRegions := []string{ + "nl-ams", + "jp-tyo-3", + "sg-sin-2", + "de-fra-2", + "in-bom-2", + "gb-lon", + "us-lax", + "id-cgk", + "us-mia", + "it-mil", + "jp-osa", + "in-maa", + "se-sto", + "br-gru", + "us-sea", + "fr-par", + "us-iad", + } + + // #nosec G404 -- Test data, doesn't need to be cryptography + return premiumRegions[rand.Intn(len(premiumRegions))], nil +} + func GetTestClient() (*linodego.Client, error) { token := os.Getenv("LINODE_TOKEN") if token == "" { diff --git a/linode/nb/datasource_test.go b/linode/nb/datasource_test.go index 4e1a0f98d..524140c8a 100644 --- a/linode/nb/datasource_test.go +++ b/linode/nb/datasource_test.go @@ -24,6 +24,7 @@ func TestAccDataSourceNodeBalancer_basic(t *testing.T) { resName := "data.linode_nodebalancer.foobar" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -32,10 +33,11 @@ func TestAccDataSourceNodeBalancer_basic(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.DataBasic(t, nodebalancerName, testRegion), + Config: tmpl.DataBasic(t, nodebalancerName, testRegion, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerExists, resource.TestCheckResourceAttr(resName, "label", nodebalancerName), + resource.TestCheckResourceAttr(resName, "type", "common"), resource.TestCheckResourceAttr(resName, "client_conn_throttle", "20"), resource.TestCheckResourceAttr(resName, "client_udp_sess_throttle", "10"), resource.TestCheckResourceAttr(resName, "region", testRegion), @@ -51,6 +53,8 @@ func TestAccDataSourceNodeBalancer_basic(t *testing.T) { resource.TestCheckResourceAttr(resName, "tags.#", "1"), resource.TestCheckResourceAttr(resName, "tags.0", "tf_test"), resource.TestCheckResourceAttr(resName, "lke_cluster.#", "0"), + resource.TestCheckResourceAttr(resName, "frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resName, "frontend_vpc_subnet_id"), ), }, }, @@ -73,6 +77,7 @@ func TestAccDataSourceNodeBalancer_firewalls(t *testing.T) { Check: resource.ComposeTestCheckFunc( checkNodeBalancerExists, resource.TestCheckResourceAttr(resName, "label", nodebalancerName), + resource.TestCheckResourceAttr(resName, "type", "common"), resource.TestCheckResourceAttr(resName, "client_conn_throttle", "20"), resource.TestCheckResourceAttr(resName, "region", testRegion), resource.TestCheckResourceAttrSet(resName, "hostname"), @@ -107,6 +112,8 @@ func TestAccDataSourceNodeBalancer_firewalls(t *testing.T) { resource.TestCheckResourceAttr(resName, "firewalls.0.outbound.0.ipv6.0", "2001:db8::/32"), resource.TestCheckResourceAttr(resName, "firewalls.0.tags.#", "1"), resource.TestCheckResourceAttr(resName, "firewalls.0.tags.0", "test"), + resource.TestCheckResourceAttr(resName, "frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resName, "frontend_vpc_subnet_id"), ), }, }, @@ -119,7 +126,8 @@ func TestAccDataSourceNodeBalancer_vpc(t *testing.T) { dsName := "data.linode_nodebalancer.test" nodebalancerName := acctest.RandomWithPrefix("tf-test") - targetRegion, err := acceptance.GetRandomRegionWithCaps([]linodego.RegionCapability{linodego.CapabilityNodeBalancers, linodego.CapabilityVPCs}, "core") + // Use random region that supports premium NodeBalancers. + targetRegion, err := acceptance.GetRandomRegionSupportingPremiumNodeBalancers() if err != nil { log.Fatal(err) } @@ -134,6 +142,11 @@ func TestAccDataSourceNodeBalancer_vpc(t *testing.T) { Config: tmpl.DataVPC(t, nodebalancerName, targetRegion), Check: checkNodeBalancerExists, ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("type"), + knownvalue.StringExact("common"), + ), statecheck.ExpectKnownValue( dsName, tfjsonpath.New("vpcs").AtSliceIndex(0).AtMapKey("subnet_id"), @@ -271,6 +284,69 @@ func TestAccDataSourceNodeBalancer_lkeClusterAPL(t *testing.T) { tfjsonpath.New("lke_cluster").AtSliceIndex(0).AtMapKey("url"), knownvalue.NotNull(), ), + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("frontend_address_type"), + knownvalue.StringExact("public"), + ), + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("frontend_vpc_subnet_id"), + knownvalue.Null(), + ), + }, + }, + }, + }) +} + +func TestAccDataSourceNodeBalancer_frontendVPC(t *testing.T) { + t.Parallel() + + dsName := "data.linode_nodebalancer.test" + nodebalancerName := acctest.RandomWithPrefix("tf-test") + + // Use random region that supports premium NodeBalancers. + targetRegion, err := acceptance.GetRandomRegionSupportingPremiumNodeBalancers() + if err != nil { + log.Fatal(err) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + ProtoV6ProviderFactories: acceptance.ProtoV6ProviderFactories, + CheckDestroy: checkNodeBalancerDestroy, + + Steps: []resource.TestStep{ + { + Config: tmpl.DataFrontendVPC(t, nodebalancerName, targetRegion), + Check: checkNodeBalancerExists, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("type"), + knownvalue.StringExact("premium"), + ), + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("vpcs").AtSliceIndex(0).AtMapKey("subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("vpcs").AtSliceIndex(0).AtMapKey("ipv4_range"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("frontend_address_type"), + knownvalue.StringExact("vpc"), + ), + statecheck.ExpectKnownValue( + dsName, + tfjsonpath.New("frontend_vpc_subnet_id"), + knownvalue.NotNull(), + ), }, }, }, diff --git a/linode/nb/framework_datasource_schema.go b/linode/nb/framework_datasource_schema.go index 35f66de21..2262d6652 100644 --- a/linode/nb/framework_datasource_schema.go +++ b/linode/nb/framework_datasource_schema.go @@ -103,6 +103,18 @@ var DataSourceAttributes = map[string]schema.Attribute{ }, }, }, + "type": schema.StringAttribute{ + Description: "The type of this NodeBalancer.", + Computed: true, + }, + "frontend_address_type": schema.StringAttribute{ + Description: "Indicates whether incoming requests are routed to NodeBalancers using VPC frontend IPs or public frontend IPs.", + Computed: true, + }, + "frontend_vpc_subnet_id": schema.Int64Attribute{ + Description: "The VPC subnet assigned to this NodeBalancer.", + Computed: true, + }, } // dataSourceAttributesVPC contains all user-configurable fields for a VPC NodeBalancer. diff --git a/linode/nb/framework_models.go b/linode/nb/framework_models.go index 0a9935996..7bb43a3a4 100644 --- a/linode/nb/framework_models.go +++ b/linode/nb/framework_models.go @@ -33,6 +33,10 @@ type NodeBalancerModel struct { Tags types.Set `tfsdk:"tags"` Firewalls types.List `tfsdk:"firewalls"` VPCs types.List `tfsdk:"vpcs"` + FrontendVPCs types.List `tfsdk:"frontend_vpcs"` + Type types.String `tfsdk:"type"` + FrontendAddressType types.String `tfsdk:"frontend_address_type"` + FrontendVPCSubnetID types.Int64 `tfsdk:"frontend_vpc_subnet_id"` LKECluster types.List `tfsdk:"lke_cluster"` } @@ -90,6 +94,7 @@ func (data *NodeBalancerModel) Flatten( ) diag.Diagnostics { data.ID = helper.KeepOrUpdateString(data.ID, strconv.Itoa(nodebalancer.ID), preserveKnown) data.Label = helper.KeepOrUpdateStringPointer(data.Label, nodebalancer.Label, preserveKnown) + data.Type = helper.KeepOrUpdateString(data.Type, string(nodebalancer.Type), preserveKnown) tags, diags := types.SetValueFrom(ctx, types.StringType, helper.StringSliceToFramework(nodebalancer.Tags)) if diags.HasError() { @@ -126,24 +131,61 @@ func (data *NodeBalancerModel) Flatten( return diags } - data.Firewalls = helper.KeepOrUpdateValue(data.Firewalls, *fws, preserveKnown) + data.Firewalls = *fws - vpcConfigsModels := helper.MapSlice( - vpcConfigs, + // Filter out backend VPC configs, matching `vpcs` attribute. + backendVPCConfigs := filterVPCConfigsByPurpose(vpcConfigs, linodego.NodeBalancerVPCConfigPurposeBackend) + + backendVPCConfigsModels := helper.MapSlice( + backendVPCConfigs, func(vpcConfig linodego.NodeBalancerVPCConfig) (result ResourceVPCModel) { result.FlattenVPCConfig(&vpcConfig) return result }, ) - vpcs, diags := types.ListValueFrom(ctx, frameworkResourceSchemaVPCs.Type(), vpcConfigsModels) + backendVPCs, diags := types.ListValueFrom(ctx, frameworkResourceSchemaVPCs.Type(), backendVPCConfigsModels) if diags.HasError() { return diags } + // We cannot set frontend VPC configs as-is as the values returned from the API might be modified + // and different than what was provided in the config. `frontend_vpcs` is an Optional, WriteOnly attribute. + // In order to avoid diff after import, `subnet_id` is extracted from the API response and used to populate + // `frontend_vpc_subnet_id` attribute, omitting `ipv4_range` and `ipv6_range` attributes. + frontendVPCConfigs := filterVPCConfigsByPurpose(vpcConfigs, linodego.NodeBalancerVPCConfigPurposeFrontend) + if len(frontendVPCConfigs) > 0 { + subnetID := frontendVPCConfigs[0].SubnetID + + frontendVPCConfigsModels := []ResourceFrontendVPCModel{ + { + BaseVPCModel: BaseVPCModel{ + SubnetID: types.Int64Value(int64(subnetID)), + }, + }, + } + frontendVPCs, diags := types.ListValueFrom(ctx, frameworkResourceSchemaFrontendVPCs.Type(), frontendVPCConfigsModels) + if diags.HasError() { + return diags + } + data.FrontendVPCs = helper.KeepOrUpdateValue(data.FrontendVPCs, frontendVPCs, preserveKnown) + } + + data.FrontendAddressType = helper.KeepOrUpdateString( + data.FrontendAddressType, + string(nodebalancer.FrontendAddressType), + preserveKnown, + ) + + data.FrontendVPCSubnetID = helper.KeepOrUpdateInt64Pointer( + data.FrontendVPCSubnetID, + helper.IntPtrToInt64Ptr(nodebalancer.FrontendVPCSubnetID), + preserveKnown, + ) + // TODO: Make use of new KeepOrUpdate helpers once Linode Interfaces has been merged. - // In the meantime, enabling preserveKnown will break the diff logic for computed fields. - data.VPCs = helper.KeepOrUpdateValue(data.VPCs, vpcs, false) + // In the meantime, enabling preserveKnown will break the diff logic for computed fields. + data.VPCs = helper.KeepOrUpdateValue(data.VPCs, backendVPCs, false) lkeCluster, diags := FlattenLKECluster(ctx, nodebalancer.LKECluster) if diags.HasError() { @@ -154,9 +196,23 @@ func (data *NodeBalancerModel) Flatten( return nil } +func filterVPCConfigsByPurpose( + vpcConfigs []linodego.NodeBalancerVPCConfig, + purpose linodego.NodeBalancerVPCConfigPurpose, +) []linodego.NodeBalancerVPCConfig { + filtered := make([]linodego.NodeBalancerVPCConfig, 0) + for _, vpcConfig := range vpcConfigs { + if vpcConfig.Purpose == purpose { + filtered = append(filtered, vpcConfig) + } + } + return filtered +} + func (data *NodeBalancerModel) CopyFrom(other NodeBalancerModel, preserveKnown bool) { data.ID = helper.KeepOrUpdateValue(data.ID, other.ID, preserveKnown) data.Label = helper.KeepOrUpdateValue(data.Label, other.Label, preserveKnown) + data.Type = helper.KeepOrUpdateValue(data.Type, other.Type, preserveKnown) data.Region = helper.KeepOrUpdateValue(data.Region, other.Region, preserveKnown) data.ClientConnThrottle = helper.KeepOrUpdateValue( data.ClientConnThrottle, other.ClientConnThrottle, preserveKnown, @@ -174,6 +230,9 @@ func (data *NodeBalancerModel) CopyFrom(other NodeBalancerModel, preserveKnown b data.Tags = helper.KeepOrUpdateValue(data.Tags, other.Tags, preserveKnown) data.Firewalls = helper.KeepOrUpdateValue(data.Firewalls, other.Firewalls, preserveKnown) data.VPCs = helper.KeepOrUpdateValue(data.VPCs, other.VPCs, preserveKnown) + data.FrontendVPCs = helper.KeepOrUpdateValue(data.FrontendVPCs, other.FrontendVPCs, preserveKnown) + data.FrontendAddressType = helper.KeepOrUpdateValue(data.FrontendAddressType, other.FrontendAddressType, preserveKnown) + data.FrontendVPCSubnetID = helper.KeepOrUpdateValue(data.FrontendVPCSubnetID, other.FrontendVPCSubnetID, preserveKnown) data.LKECluster = helper.KeepOrUpdateValue(data.LKECluster, other.LKECluster, preserveKnown) } @@ -279,6 +338,9 @@ type NodeBalancerDataSourceModel struct { Tags types.Set `tfsdk:"tags"` Firewalls []NBFirewallModel `tfsdk:"firewalls"` VPCs types.List `tfsdk:"vpcs"` + Type types.String `tfsdk:"type"` + FrontendAddressType types.String `tfsdk:"frontend_address_type"` + FrontendVPCSubnetID types.Int64 `tfsdk:"frontend_vpc_subnet_id"` LKECluster types.List `tfsdk:"lke_cluster"` } @@ -303,6 +365,7 @@ func (data *NodeBalancerDataSourceModel) Flatten( ) diag.Diagnostics { data.ID = types.Int64Value(int64(nodebalancer.ID)) data.Label = types.StringPointerValue(nodebalancer.Label) + data.Type = types.StringValue(string(nodebalancer.Type)) data.Region = types.StringValue(nodebalancer.Region) data.ClientConnThrottle = types.Int64Value(int64(nodebalancer.ClientConnThrottle)) data.ClientUDPSessThrottle = types.Int64Value(int64(nodebalancer.ClientUDPSessThrottle)) @@ -311,6 +374,8 @@ func (data *NodeBalancerDataSourceModel) Flatten( data.IPv6 = types.StringPointerValue(nodebalancer.IPv6) data.Created = timetypes.NewRFC3339TimePointerValue(nodebalancer.Created) data.Updated = timetypes.NewRFC3339TimePointerValue(nodebalancer.Updated) + data.FrontendAddressType = types.StringValue(string(nodebalancer.FrontendAddressType)) + data.FrontendVPCSubnetID = types.Int64PointerValue(helper.IntPtrToInt64Ptr(nodebalancer.FrontendVPCSubnetID)) transfer, diags := FlattenTransfer(ctx, nodebalancer.Transfer) if diags.HasError() { @@ -405,6 +470,10 @@ type ResourceVPCModel struct { IPv4RangeAutoAssign types.Bool `tfsdk:"ipv4_range_auto_assign"` } +type ResourceFrontendVPCModel struct { + BaseVPCModel +} + func (m *ResourceVPCModel) ToLinodego() (*linodego.NodeBalancerVPCOptions, diag.Diagnostics) { var d diag.Diagnostics @@ -424,6 +493,24 @@ func (m *ResourceVPCModel) ToLinodego() (*linodego.NodeBalancerVPCOptions, diag. }, d } +func (m *ResourceFrontendVPCModel) ToLinodego() (*linodego.NodeBalancerFrontendVPCOptions, diag.Diagnostics) { + var d diag.Diagnostics + + subnetID := helper.FrameworkSafeInt64ToInt( + m.SubnetID.ValueInt64(), + &d, + ) + if d.HasError() { + return nil, d + } + + return &linodego.NodeBalancerFrontendVPCOptions{ + SubnetID: subnetID, + IPv4Range: m.IPv4Range.ValueString(), + IPv6Range: m.IPv6Range.ValueString(), + }, d +} + func vpcModelsToLinodego( ctx context.Context, vpcs types.List, @@ -447,6 +534,29 @@ func vpcModelsToLinodego( ), d } +func frontendVPCModelsToLinodego( + ctx context.Context, + vpcs types.List, +) ([]linodego.NodeBalancerFrontendVPCOptions, diag.Diagnostics) { + var d diag.Diagnostics + + var frontendVPCModels []ResourceFrontendVPCModel + + d.Append(vpcs.ElementsAs(ctx, &frontendVPCModels, false)...) + if d.HasError() { + return nil, d + } + + return helper.MapSlice( + frontendVPCModels, + func(vpcModel ResourceFrontendVPCModel) linodego.NodeBalancerFrontendVPCOptions { + result, localD := vpcModel.ToLinodego() + d.Append(localD...) + return *result + }, + ), d +} + type DataSourceVPCModel struct { BaseVPCModel } diff --git a/linode/nb/framework_models_unit_test.go b/linode/nb/framework_models_unit_test.go index 9eada414c..63ad2ee67 100644 --- a/linode/nb/framework_models_unit_test.go +++ b/linode/nb/framework_models_unit_test.go @@ -70,6 +70,7 @@ func TestFlattenNodeBalancer(t *testing.T) { Out: &transferOut, Total: &transferTotal, }, + Type: linodego.NBTypeCommon, LKECluster: &linodego.NodeBalancerLKECluster{ ID: 1234, Label: "test-cluster", @@ -87,6 +88,7 @@ func TestFlattenNodeBalancer(t *testing.T) { SubnetID: 789, VPCID: 321, IPv4Range: "10.0.0.4/30", + Purpose: "backend", }, } @@ -101,6 +103,7 @@ func TestFlattenNodeBalancer(t *testing.T) { assert.False(t, diags.HasError()) assert.Equal(t, types.StringValue("123"), nodeBalancerModel.ID) + assert.Equal(t, types.StringValue("common"), nodeBalancerModel.Type) assert.Equal(t, types.StringValue("us-east"), nodeBalancerModel.Region) assert.Equal(t, types.Int64Value(10), nodeBalancerModel.ClientConnThrottle) assert.Equal(t, types.Int64Value(5), nodeBalancerModel.ClientUDPSessThrottle) diff --git a/linode/nb/framework_resource.go b/linode/nb/framework_resource.go index a143e3a26..b26345484 100644 --- a/linode/nb/framework_resource.go +++ b/linode/nb/framework_resource.go @@ -68,6 +68,7 @@ func (r *Resource) Create( Label: data.Label.ValueStringPointer(), ClientConnThrottle: &clientConnThrottle, ClientUDPSessThrottle: &clientUDPSessThrottle, + Type: linodego.NodeBalancerPlanType(data.Type.ValueString()), } if !data.FirewallID.IsNull() { @@ -90,6 +91,16 @@ func (r *Resource) Create( createOpts.VPCs = vpcs } + if !data.FrontendVPCs.IsNull() { + frontendVPCs, d := frontendVPCModelsToLinodego(ctx, data.FrontendVPCs) + resp.Diagnostics.Append(d...) + if resp.Diagnostics.HasError() { + return + } + + createOpts.FrontendVPCs = frontendVPCs + } + if !data.Tags.IsNull() { resp.Diagnostics.Append(data.Tags.ElementsAs(ctx, &createOpts.Tags, false)...) if resp.Diagnostics.HasError() { @@ -125,6 +136,9 @@ func (r *Resource) Create( return } + // safeListVPCConfigs lists VPC configs for backend and frontend nodes. + // Backend VPC configs are configured through the "vpcs" attribute and + // frontend VPC configs are configured through the "frontend_vpcs" attribute. vpcConfigs := safeListVPCConfigs( ctx, client, @@ -388,17 +402,21 @@ func upgradeNodebalancerResourceStateV0toV1( } nbDataV1 := NodeBalancerModel{ - ID: nbDataV0.ID, - Label: nbDataV0.Label, - Region: nbDataV0.Region, - ClientConnThrottle: nbDataV0.ClientConnThrottle, - Hostname: nbDataV0.Hostname, - IPv4: iptypes.NewIPv4AddressPointerValue(nbDataV0.IPv4.ValueStringPointer()), - IPv6: nbDataV0.IPv6, - Created: timetypes.RFC3339{StringValue: nbDataV0.Created}, - Updated: timetypes.RFC3339{StringValue: nbDataV0.Updated}, - Tags: nbDataV0.Tags, - Firewalls: types.ListNull(firewallObjType), + ID: nbDataV0.ID, + Label: nbDataV0.Label, + Region: nbDataV0.Region, + ClientConnThrottle: nbDataV0.ClientConnThrottle, + Hostname: nbDataV0.Hostname, + IPv4: iptypes.NewIPv4AddressPointerValue(nbDataV0.IPv4.ValueStringPointer()), + IPv6: nbDataV0.IPv6, + Created: timetypes.RFC3339{StringValue: nbDataV0.Created}, + Updated: timetypes.RFC3339{StringValue: nbDataV0.Updated}, + Tags: nbDataV0.Tags, + Firewalls: types.ListNull(firewallObjType), + Type: types.StringValue("common"), + FrontendVPCs: types.ListNull(frontendVPCObjType), + FrontendAddressType: types.StringNull(), + FrontendVPCSubnetID: types.Int64Null(), } var transferMap map[string]string diff --git a/linode/nb/framework_resource_schema.go b/linode/nb/framework_resource_schema.go index ae9c4d969..9add649ca 100644 --- a/linode/nb/framework_resource_schema.go +++ b/linode/nb/framework_resource_schema.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego/v2" "github.com/linode/terraform-provider-linode/v4/linode/firewall" "github.com/linode/terraform-provider-linode/v4/linode/helper" linodesetplanmodifier "github.com/linode/terraform-provider-linode/v4/linode/helper/setplanmodifiers" @@ -26,6 +27,12 @@ const ( NBLabelErrorMessage = "Labels may only contain letters, number, dashes, and underscores." ) +var nodeBalancerTypesStrings = []string{ + string(linodego.NBTypeCommon), + string(linodego.NBTypePremium), + string(linodego.NBTypePremium40GB), +} + var firewallObjType = types.ObjectType{ AttrTypes: map[string]attr.Type{ "id": types.Int64Type, @@ -41,6 +48,14 @@ var firewallObjType = types.ObjectType{ }, } +var frontendVPCObjType = types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "subnet_id": types.Int64Type, + "ipv4_range": types.StringType, + "ipv6_range": types.StringType, + }, +} + var frameworkResourceSchemaVPCs = schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "subnet_id": schema.Int64Attribute{ @@ -87,6 +102,35 @@ var frameworkResourceSchemaVPCs = schema.NestedAttributeObject{ }, } +var frameworkResourceSchemaFrontendVPCs = schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "subnet_id": schema.Int64Attribute{ + Description: "The VPC's subnet ID for the VPC based NodeBalancer.", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + int64planmodifier.RequiresReplace(), + }, + }, + "ipv4_range": schema.StringAttribute{ + Description: "A CIDR range for the VPC's IPv4 addresses allocated as the NodeBalancer's frontend IPs.", + Optional: true, + WriteOnly: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "ipv6_range": schema.StringAttribute{ + Description: "A CIDR range for the VPC's IPv6 addresses allocated as the NodeBalancer's frontend IPs.", + Optional: true, + WriteOnly: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, +} + var frameworkResourceSchema = schema.Schema{ Version: 1, Attributes: map[string]schema.Attribute{ @@ -203,10 +247,9 @@ var frameworkResourceSchema = schema.Schema{ }, }, "vpcs": schema.ListNestedAttribute{ - Description: "A list of VPCs to be assigned to this NodeBalancer.", + Description: "A VPC configuration for backend nodes.", Optional: true, PlanModifiers: []planmodifier.List{ - listplanmodifier.UseStateForUnknown(), listplanmodifier.RequiresReplace(), }, NestedObject: frameworkResourceSchemaVPCs, @@ -238,6 +281,43 @@ var frameworkResourceSchema = schema.Schema{ }, }, }, + "frontend_vpcs": schema.ListNestedAttribute{ + Description: "For internal load balancing, where the NodeBalancer is within a VPC, " + + "indicate a VPC subnet_id. For greater flexibility, you can specify the IP range within the subnet used for allocation.", + Optional: true, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + listplanmodifier.UseStateForUnknown(), + }, + NestedObject: frameworkResourceSchemaFrontendVPCs, + }, + "type": schema.StringAttribute{ + Description: "The type of NodeBalancer. Possible values are 'common', 'premium' or 'premium_40gb'.", + Optional: true, + Computed: true, + Default: stringdefault.StaticString("common"), + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.OneOf(nodeBalancerTypesStrings...), + }, + }, + "frontend_address_type": schema.StringAttribute{ + Description: "Indicates whether incoming requests are routed to NodeBalancers using VPC frontend IPs or public frontend IPs.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "frontend_vpc_subnet_id": schema.Int64Attribute{ + Description: "The VPC subnet assigned to this NodeBalancer.", + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, }, } diff --git a/linode/nb/framework_resource_test.go b/linode/nb/framework_resource_test.go index c337171cf..4425f9d95 100644 --- a/linode/nb/framework_resource_test.go +++ b/linode/nb/framework_resource_test.go @@ -109,6 +109,7 @@ func TestAccResourceNodeBalancer_basic_smoke(t *testing.T) { resName := "linode_nodebalancer.foobar" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -117,14 +118,17 @@ func TestAccResourceNodeBalancer_basic_smoke(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.Basic(t, nodebalancerName, testRegion), + Config: tmpl.Basic(t, nodebalancerName, testRegion, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerExists, resource.TestCheckResourceAttr(resName, "label", nodebalancerName), resource.TestCheckResourceAttr(resName, "client_conn_throttle", "20"), resource.TestCheckResourceAttr(resName, "client_udp_sess_throttle", "10"), resource.TestCheckResourceAttr(resName, "region", testRegion), + resource.TestCheckResourceAttr(resName, "type", "common"), + resource.TestCheckResourceAttr(resName, "frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resName, "frontend_vpc_subnet_id"), resource.TestCheckResourceAttrSet(resName, "hostname"), resource.TestCheckResourceAttrSet(resName, "ipv4"), resource.TestCheckResourceAttrSet(resName, "ipv6"), @@ -140,7 +144,7 @@ func TestAccResourceNodeBalancer_basic_smoke(t *testing.T) { ResourceName: resName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"created", "updated", "firewall_id"}, // Ignore strict comparison for these attributes + ImportStateVerifyIgnore: []string{"created", "updated", "firewall_id", "firewalls"}, // Ignore strict comparison for these attributes }, }, }) @@ -151,6 +155,7 @@ func TestAccResourceNodeBalancer_update(t *testing.T) { resName := "linode_nodebalancer.foobar" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -159,14 +164,17 @@ func TestAccResourceNodeBalancer_update(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.Basic(t, nodebalancerName, testRegion), + Config: tmpl.Basic(t, nodebalancerName, testRegion, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerExists, resource.TestCheckResourceAttr(resName, "label", nodebalancerName), resource.TestCheckResourceAttr(resName, "client_conn_throttle", "20"), resource.TestCheckResourceAttr(resName, "client_udp_sess_throttle", "10"), resource.TestCheckResourceAttr(resName, "region", testRegion), + resource.TestCheckResourceAttr(resName, "type", "common"), + resource.TestCheckResourceAttr(resName, "frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resName, "frontend_vpc_subnet_id"), resource.TestCheckResourceAttrSet(resName, "hostname"), resource.TestCheckResourceAttrSet(resName, "ipv4"), resource.TestCheckResourceAttrSet(resName, "ipv6"), @@ -184,7 +192,10 @@ func TestAccResourceNodeBalancer_update(t *testing.T) { resource.TestCheckResourceAttr(resName, "client_conn_throttle", "0"), resource.TestCheckResourceAttr(resName, "client_udp_sess_throttle", "5"), resource.TestCheckResourceAttr(resName, "region", testRegion), + resource.TestCheckResourceAttr(resName, "type", "common"), + resource.TestCheckResourceAttr(resName, "frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resName, "frontend_vpc_subnet_id"), resource.TestCheckResourceAttrSet(resName, "hostname"), resource.TestCheckResourceAttrSet(resName, "ipv4"), resource.TestCheckResourceAttrSet(resName, "ipv6"), @@ -222,6 +233,7 @@ func TestAccResourceNodeBalancer_firewall(t *testing.T) { Check: resource.ComposeTestCheckFunc( checkNodeBalancerExists, resource.TestCheckResourceAttr(resName, "label", nodebalancerName), + resource.TestCheckResourceAttr(resName, "type", "common"), resource.TestCheckResourceAttr(resName, "client_conn_throttle", "20"), acceptance.CheckResourceAttrGreaterThan(resName, "firewalls.#", 0), resource.TestCheckResourceAttr(resName, "firewalls.0.label", fmt.Sprintf("%v-fw", nodebalancerName)), @@ -244,6 +256,8 @@ func TestAccResourceNodeBalancer_firewall(t *testing.T) { resource.TestCheckResourceAttr(resName, "firewalls.0.outbound.0.ipv6.0", "2001:db8::/32"), resource.TestCheckResourceAttr(resName, "firewalls.0.tags.#", "1"), resource.TestCheckResourceAttr(resName, "firewalls.0.tags.0", "test"), + resource.TestCheckResourceAttr(resName, "frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resName, "frontend_vpc_subnet_id"), ), }, { @@ -267,7 +281,8 @@ func TestAccResourceNodeBalancer_vpc(t *testing.T) { resName := "linode_nodebalancer.test" nodebalancerName := acctest.RandomWithPrefix("tf-test") - targetRegion, err := acceptance.GetRandomRegionWithCaps([]linodego.RegionCapability{linodego.CapabilityNodeBalancers, linodego.CapabilityVPCs}, "core") + // Use random region that supports premium NodeBalancers. + targetRegion, err := acceptance.GetRandomRegionSupportingPremiumNodeBalancers() if err != nil { log.Fatal(err) } @@ -308,6 +323,85 @@ func TestAccResourceNodeBalancer_vpc(t *testing.T) { }) } +func TestAccResourceNodeBalancer_frontendVPC(t *testing.T) { + t.Parallel() + + resName := "linode_nodebalancer.test" + nodebalancerName := acctest.RandomWithPrefix("tf-test") + + // Use random region that supports premium NodeBalancers. + targetRegion, err := acceptance.GetRandomRegionSupportingPremiumNodeBalancers() + if err != nil { + log.Fatal(err) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + ProtoV6ProviderFactories: acceptance.ProtoV6ProviderFactories, + CheckDestroy: checkNodeBalancerDestroy, + + Steps: []resource.TestStep{ + { + Config: tmpl.FrontendVPC(t, nodebalancerName, targetRegion), + Check: checkNodeBalancerExists, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("vpcs").AtSliceIndex(0).AtMapKey("subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("vpcs").AtSliceIndex(0).AtMapKey("ipv4_range"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("frontend_vpcs").AtSliceIndex(0).AtMapKey("subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("frontend_vpcs").AtSliceIndex(0).AtMapKey("ipv4_range"), + knownvalue.Null(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("frontend_vpcs").AtSliceIndex(0).AtMapKey("ipv6_range"), + knownvalue.Null(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("frontend_address_type"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("frontend_vpc_subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + resName, + tfjsonpath.New("type"), + knownvalue.StringExact("premium"), + ), + }, + }, + { + ResourceName: resName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"created", "updated", "firewall_id"}, // Ignore strict comparison for these attributes + }, + }, + }) +} + func TestLinodeNodeBalancer_UpgradeV0(t *testing.T) { t.Parallel() diff --git a/linode/nb/tmpl/basic.gotf b/linode/nb/tmpl/basic.gotf index 4c30b6400..4d9b1420d 100644 --- a/linode/nb/tmpl/basic.gotf +++ b/linode/nb/tmpl/basic.gotf @@ -4,6 +4,7 @@ resource "linode_nodebalancer" "foobar" { label = "{{.Label}}" + type = "{{.Type}}" region = "{{ .Region }}" client_conn_throttle = 20 client_udp_sess_throttle = 10 diff --git a/linode/nb/tmpl/data_frontend_vpc.gotf b/linode/nb/tmpl/data_frontend_vpc.gotf new file mode 100644 index 000000000..090cca81b --- /dev/null +++ b/linode/nb/tmpl/data_frontend_vpc.gotf @@ -0,0 +1,9 @@ +{{ define "nodebalancer_data_frontend_vpc" }} + +{{ template "nodebalancer_frontend_vpc" . }} + +data "linode_nodebalancer" "test" { + id = linode_nodebalancer.test.id +} + +{{ end }} \ No newline at end of file diff --git a/linode/nb/tmpl/frontend_vpc.gotf b/linode/nb/tmpl/frontend_vpc.gotf new file mode 100644 index 000000000..e998819ad --- /dev/null +++ b/linode/nb/tmpl/frontend_vpc.gotf @@ -0,0 +1,68 @@ +{{ define "nodebalancer_frontend_vpc" }} + +{{ template "e2e_test_firewall" . }} + +resource "linode_nodebalancer" "test" { + label = "{{ .Label }}" + region = "{{ .Region }}" + firewall_id = linode_firewall.e2e_test_firewall.id + type = "premium" + + vpcs = [ + { + subnet_id = linode_vpc_subnet.test.id + ipv4_range = "10.0.0.4/30" + ipv4_range_auto_assign = true + } + ] + + frontend_vpcs = [ + { + subnet_id = linode_vpc_subnet.frontend_subnet_test.id + } + ] +} + +resource "linode_vpc" "test" { + label = "{{ .Label }}" + region = "{{ .Region }}" + ipv6 = [ + { + range = "auto" + } + ] +} + +resource "linode_vpc_subnet" "test" { + vpc_id = linode_vpc.test.id + label = "tf-test" + ipv4 = "10.0.0.0/24" + ipv6 = [ + { + range = "auto" + } + ] +} + +resource "linode_vpc" "frontend_vpc_test" { + label = "{{ .Label }}-frontend" + region = "{{ .Region }}" + ipv6 = [ + { + range = "auto" + } + ] +} + +resource "linode_vpc_subnet" "frontend_subnet_test" { + vpc_id = linode_vpc.frontend_vpc_test.id + label = "tf-test-frontend" + ipv4 = "10.0.0.0/24" + ipv6 = [ + { + range = "auto" + } + ] +} + +{{ end }} \ No newline at end of file diff --git a/linode/nb/tmpl/template.go b/linode/nb/tmpl/template.go index 31cf4e603..c2f05dc0f 100644 --- a/linode/nb/tmpl/template.go +++ b/linode/nb/tmpl/template.go @@ -9,15 +9,17 @@ import ( type TemplateData struct { Label string Region string + Type string K8sVersion string NodeBalancerID int } -func Basic(t testing.TB, nodebalancer, region string) string { +func Basic(t testing.TB, nodebalancer, region, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_basic", TemplateData{ Label: nodebalancer, Region: region, + Type: nbType, }) } @@ -29,11 +31,12 @@ func Updates(t testing.TB, nodebalancer, region string) string { }) } -func DataBasic(t testing.TB, nodebalancer, region string) string { +func DataBasic(t testing.TB, nodebalancer, region, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_data_basic", TemplateData{ Label: nodebalancer, Region: region, + Type: nbType, }) } @@ -69,6 +72,14 @@ func VPC(t testing.TB, nodebalancer, region string) string { }) } +func FrontendVPC(t testing.TB, nodebalancer, region string) string { + return acceptance.ExecuteTemplate(t, + "nodebalancer_frontend_vpc", TemplateData{ + Label: nodebalancer, + Region: region, + }) +} + func DataVPC(t testing.TB, nodebalancer, region string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_data_vpc", TemplateData{ @@ -77,6 +88,14 @@ func DataVPC(t testing.TB, nodebalancer, region string) string { }) } +func DataFrontendVPC(t testing.TB, nodebalancer, region string) string { + return acceptance.ExecuteTemplate(t, + "nodebalancer_data_frontend_vpc", TemplateData{ + Label: nodebalancer, + Region: region, + }) +} + func ReservedIP(t testing.TB, nodebalancer, region string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_reserved_ip", TemplateData{ diff --git a/linode/nbconfig/datasource_test.go b/linode/nbconfig/datasource_test.go index eb684e3f8..45300e63e 100644 --- a/linode/nbconfig/datasource_test.go +++ b/linode/nbconfig/datasource_test.go @@ -17,6 +17,7 @@ func TestAccDataSourceNodeBalancerConfig_basic(t *testing.T) { resName := "data.linode_nodebalancer_config.foofig" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -25,7 +26,7 @@ func TestAccDataSourceNodeBalancerConfig_basic(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.DataBasic(t, nodebalancerName, testRegion), + Config: tmpl.DataBasic(t, nodebalancerName, testRegion, nbType), Check: resource.ComposeAggregateTestCheckFunc( checkNodeBalancerConfigExists, resource.TestCheckResourceAttr(resName, "port", "8080"), diff --git a/linode/nbconfig/resource_test.go b/linode/nbconfig/resource_test.go index f4c0757b3..ec9dbe4ae 100644 --- a/linode/nbconfig/resource_test.go +++ b/linode/nbconfig/resource_test.go @@ -37,6 +37,7 @@ func TestAccResourceNodeBalancerConfig_basic(t *testing.T) { resName := "linode_nodebalancer_config.foofig" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreventPostDestroyRefresh: true, PreCheck: func() { acceptance.PreCheck(t) }, @@ -45,7 +46,7 @@ func TestAccResourceNodeBalancerConfig_basic(t *testing.T) { ExternalProviders: acceptance.HttpExternalProviders, Steps: []resource.TestStep{ { - Config: tmpl.Basic(t, nodebalancerName, testRegion), + Config: tmpl.Basic(t, nodebalancerName, testRegion, nbType), ResourceName: resName, Check: resource.ComposeAggregateTestCheckFunc( checkNodeBalancerConfigExists, @@ -86,6 +87,7 @@ func TestAccResourceNodeBalancerConfig_ssl(t *testing.T) { resName := "linode_nodebalancer_config.foofig" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreventPostDestroyRefresh: true, PreCheck: func() { acceptance.PreCheck(t) }, @@ -94,7 +96,7 @@ func TestAccResourceNodeBalancerConfig_ssl(t *testing.T) { ExternalProviders: acceptance.HttpExternalProviders, Steps: []resource.TestStep{ { - Config: tmpl.SSL(t, nodebalancerName, testRegion, tmpl.TestCertifcate, tmpl.TestPrivateKey), + Config: tmpl.SSL(t, nodebalancerName, testRegion, nbType, tmpl.TestCertifcate, tmpl.TestPrivateKey), ResourceName: resName, Check: resource.ComposeAggregateTestCheckFunc( checkNodeBalancerConfigExists, @@ -119,6 +121,7 @@ func TestAccResourceNodeBalancerConfig_update(t *testing.T) { resName := "linode_nodebalancer_config.foofig" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -127,7 +130,7 @@ func TestAccResourceNodeBalancerConfig_update(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.Basic(t, nodebalancerName, testRegion), + Config: tmpl.Basic(t, nodebalancerName, testRegion, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerConfigExists, resource.TestCheckResourceAttr(resName, "port", "8080"), @@ -142,7 +145,7 @@ func TestAccResourceNodeBalancerConfig_update(t *testing.T) { ), }, { - Config: tmpl.Updates(t, nodebalancerName, testRegion), + Config: tmpl.Updates(t, nodebalancerName, testRegion, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerConfigExists, resource.TestCheckResourceAttr(resName, "port", "8088"), @@ -167,6 +170,7 @@ func TestAccResourceNodeBalancerConfig_proxyProtocol(t *testing.T) { resName := "linode_nodebalancer_config.foofig" nodebalancerName := acctest.RandomWithPrefix("tf_test") + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -175,7 +179,7 @@ func TestAccResourceNodeBalancerConfig_proxyProtocol(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.ProxyProtocol(t, nodebalancerName, testRegion), + Config: tmpl.ProxyProtocol(t, nodebalancerName, testRegion, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerConfigExists, resource.TestCheckResourceAttr(resName, "port", "80"), diff --git a/linode/nbconfig/tmpl/template.go b/linode/nbconfig/tmpl/template.go index 0f4b5e9f8..8ee67366c 100644 --- a/linode/nbconfig/tmpl/template.go +++ b/linode/nbconfig/tmpl/template.go @@ -105,27 +105,29 @@ type TemplateData struct { SSLKey string } -func Basic(t testing.TB, nodebalancerName, region string) string { +func Basic(t testing.TB, nodebalancerName, region, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_config_basic", TemplateData{ NodeBalancer: nodebalancer.TemplateData{ Label: nodebalancerName, Region: region, + Type: nbType, }, }) } -func Updates(t testing.TB, nodebalancerName, region string) string { +func Updates(t testing.TB, nodebalancerName, region, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_config_updates", TemplateData{ NodeBalancer: nodebalancer.TemplateData{ Label: nodebalancerName, Region: region, + Type: nbType, }, }) } -func SSL(t testing.TB, nodebalancerName, region, cert, privKey string) string { +func SSL(t testing.TB, nodebalancerName, region, nbType, cert, privKey string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_config_ssl", TemplateData{ SSLCert: cert, @@ -133,26 +135,29 @@ func SSL(t testing.TB, nodebalancerName, region, cert, privKey string) string { NodeBalancer: nodebalancer.TemplateData{ Label: nodebalancerName, Region: region, + Type: nbType, }, }) } -func ProxyProtocol(t testing.TB, nodebalancerName, region string) string { +func ProxyProtocol(t testing.TB, nodebalancerName, region, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_config_proxy_protocol", TemplateData{ NodeBalancer: nodebalancer.TemplateData{ Label: nodebalancerName, Region: region, + Type: nbType, }, }) } -func DataBasic(t testing.TB, nodebalancerName, region string) string { +func DataBasic(t testing.TB, nodebalancerName, region, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_config_data_basic", TemplateData{ NodeBalancer: nodebalancer.TemplateData{ Label: nodebalancerName, Region: region, + Type: nbType, }, }) } diff --git a/linode/nbnode/datasource_test.go b/linode/nbnode/datasource_test.go index ac045d15c..6a61c73a6 100644 --- a/linode/nbnode/datasource_test.go +++ b/linode/nbnode/datasource_test.go @@ -22,6 +22,7 @@ func TestAccDataSourceNodeBalancerNode_basic(t *testing.T) { resName := "data.linode_nodebalancer_node.foonode" nodebalancerName := acctest.RandomWithPrefix("tf_test") rootPass := acctest.RandString(64) + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -30,7 +31,7 @@ func TestAccDataSourceNodeBalancerNode_basic(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.DataBasic(t, nodebalancerName, testRegion, rootPass), + Config: tmpl.DataBasic(t, nodebalancerName, testRegion, rootPass, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerNodeExists, resource.TestCheckResourceAttr(resName, "label", nodebalancerName), @@ -49,6 +50,7 @@ func TestAccDataSourceNodeBalancerNode_vpc(t *testing.T) { dsName := "data.linode_nodebalancer_node.test" label := acctest.RandomWithPrefix("tf-test") rootPass := acctest.RandString(64) + nbType := "common" targetRegion, err := acceptance.GetRandomRegionWithCaps([]linodego.RegionCapability{linodego.CapabilityNodeBalancers, linodego.CapabilityVPCs}, "core") if err != nil { @@ -62,7 +64,7 @@ func TestAccDataSourceNodeBalancerNode_vpc(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.DataVPC(t, label, targetRegion, rootPass), + Config: tmpl.DataVPC(t, label, targetRegion, rootPass, nbType), ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownValue( dsName, @@ -82,7 +84,7 @@ func TestAccDataSourceNodeBalancerNode_vpc(t *testing.T) { statecheck.ExpectKnownValue( dsName, tfjsonpath.New("address"), - knownvalue.StringExact("10.0.0.10:80"), + knownvalue.StringExact("10.0.0.50:80"), ), statecheck.ExpectKnownValue( dsName, diff --git a/linode/nbnode/resource_test.go b/linode/nbnode/resource_test.go index 49beed961..65cb6a1a2 100644 --- a/linode/nbnode/resource_test.go +++ b/linode/nbnode/resource_test.go @@ -37,7 +37,7 @@ func TestAccResourceNodeBalancerNode_basic(t *testing.T) { resName := "linode_nodebalancer_node.foonode" nodeName := acctest.RandomWithPrefix("tf_test") - config := tmpl.Basic(t, nodeName, testRegion, acctest.RandString(64)) + config := tmpl.Basic(t, nodeName, testRegion, acctest.RandString(64), "common") resource.Test(t, resource.TestCase{ PreventPostDestroyRefresh: true, @@ -72,6 +72,7 @@ func TestAccResourceNodeBalancerNode_update(t *testing.T) { resName := "linode_nodebalancer_node.foonode" nodeName := acctest.RandomWithPrefix("tf_test") rootPass := acctest.RandString(64) + nbType := "common" resource.Test(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -80,7 +81,7 @@ func TestAccResourceNodeBalancerNode_update(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.Basic(t, nodeName, testRegion, rootPass), + Config: tmpl.Basic(t, nodeName, testRegion, rootPass, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerNodeExists, resource.TestCheckResourceAttr(resName, "label", nodeName), @@ -88,7 +89,7 @@ func TestAccResourceNodeBalancerNode_update(t *testing.T) { ), }, { - Config: tmpl.Updates(t, nodeName, testRegion, rootPass), + Config: tmpl.Updates(t, nodeName, testRegion, rootPass, nbType), Check: resource.ComposeTestCheckFunc( checkNodeBalancerNodeExists, resource.TestCheckResourceAttr(resName, "label", fmt.Sprintf("%s_r", nodeName)), @@ -111,6 +112,7 @@ func TestAccResourceNodeBalancerNode_vpc(t *testing.T) { resName := "linode_nodebalancer_node.test" label := acctest.RandomWithPrefix("tf-test") rootPass := acctest.RandString(64) + nbType := "common" targetRegion, err := acceptance.GetRandomRegionWithCaps([]linodego.RegionCapability{linodego.CapabilityNodeBalancers, linodego.CapabilityVPCs}, "core") if err != nil { @@ -124,7 +126,7 @@ func TestAccResourceNodeBalancerNode_vpc(t *testing.T) { Steps: []resource.TestStep{ { - Config: tmpl.VPC(t, label, targetRegion, rootPass), + Config: tmpl.VPC(t, label, targetRegion, rootPass, nbType), ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownValue( resName, @@ -144,7 +146,7 @@ func TestAccResourceNodeBalancerNode_vpc(t *testing.T) { statecheck.ExpectKnownValue( resName, tfjsonpath.New("address"), - knownvalue.StringExact("10.0.0.10:80"), + knownvalue.StringExact("10.0.0.50:80"), ), statecheck.ExpectKnownValue( resName, @@ -159,7 +161,7 @@ func TestAccResourceNodeBalancerNode_vpc(t *testing.T) { }, }, { - Config: tmpl.VPC(t, label, targetRegion, rootPass), + Config: tmpl.VPC(t, label, targetRegion, rootPass, nbType), }, { ResourceName: resName, diff --git a/linode/nbnode/tmpl/template.go b/linode/nbnode/tmpl/template.go index 1621262b1..578610c4f 100644 --- a/linode/nbnode/tmpl/template.go +++ b/linode/nbnode/tmpl/template.go @@ -21,7 +21,7 @@ type InstanceTemplateData struct { RootPass string } -func Basic(t testing.TB, nodebalancer, region string, rootPass string) string { +func Basic(t testing.TB, nodebalancer, region, rootPass, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_node_basic", TemplateData{ @@ -36,12 +36,13 @@ func Basic(t testing.TB, nodebalancer, region string, rootPass string) string { NodeBalancer: tmpl.TemplateData{ Label: nodebalancer, Region: region, + Type: nbType, }, }, }) } -func Updates(t testing.TB, nodebalancer, region string, rootPass string) string { +func Updates(t testing.TB, nodebalancer, region, rootPass, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_node_updates", TemplateData{ @@ -56,12 +57,13 @@ func Updates(t testing.TB, nodebalancer, region string, rootPass string) string NodeBalancer: tmpl.TemplateData{ Label: nodebalancer, Region: region, + Type: nbType, }, }, }) } -func VPC(t testing.TB, nodebalancer, region string, _ string) string { +func VPC(t testing.TB, nodebalancer, region, nbType string, _ string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_node_vpc", TemplateData{ @@ -70,12 +72,13 @@ func VPC(t testing.TB, nodebalancer, region string, _ string) string { NodeBalancer: tmpl.TemplateData{ Label: nodebalancer, Region: region, + Type: nbType, }, }, }) } -func DataBasic(t testing.TB, nodebalancer, region string, rootPass string) string { +func DataBasic(t testing.TB, nodebalancer, region, rootPass, nbType string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_node_data_basic", TemplateData{ @@ -90,12 +93,13 @@ func DataBasic(t testing.TB, nodebalancer, region string, rootPass string) strin NodeBalancer: tmpl.TemplateData{ Label: nodebalancer, Region: region, + Type: nbType, }, }, }) } -func DataVPC(t testing.TB, nodebalancer, region string, _ string) string { +func DataVPC(t testing.TB, nodebalancer, region, nbType string, _ string) string { return acceptance.ExecuteTemplate(t, "nodebalancer_node_data_vpc", TemplateData{ @@ -104,6 +108,7 @@ func DataVPC(t testing.TB, nodebalancer, region string, _ string) string { NodeBalancer: tmpl.TemplateData{ Label: nodebalancer, Region: region, + Type: nbType, }, }, }) diff --git a/linode/nbnode/tmpl/vpc_base.gotf b/linode/nbnode/tmpl/vpc_base.gotf index 2146daf9a..feb95da67 100644 --- a/linode/nbnode/tmpl/vpc_base.gotf +++ b/linode/nbnode/tmpl/vpc_base.gotf @@ -14,21 +14,25 @@ resource "linode_vpc_subnet" "test" { } resource "linode_instance" "test" { - label = "{{ .Label }}" + label = "{{ .Label }}" + type = "g6-nanode-1" region = "{{ .Config.NodeBalancer.Region }}" - type = "g6-nanode-1" - booted = false -} + firewall_id = linode_firewall.e2e_test_firewall.id -resource "linode_instance_config" "test" { - linode_id = linode_instance.test.id - label = "test-config" + config { + label = "config" + kernel = "linode/latest-64bit" + root_device = "/dev/sda" + helpers { + network = true + } - interface { - purpose = "vpc" - subnet_id = linode_vpc_subnet.test.id - ipv4 { - vpc = "10.0.0.10" + interface { + purpose = "vpc" + subnet_id = linode_vpc_subnet.test.id + ipv4 { + vpc = "${cidrhost(linode_vpc_subnet.test.ipv4, 50)}" + } } } } @@ -46,7 +50,7 @@ resource "linode_nodebalancer" "test" { } ] - depends_on = [linode_instance_config.test] + depends_on = [linode_instance.test] } resource "linode_nodebalancer_config" "test" { @@ -65,11 +69,11 @@ resource "linode_nodebalancer_node" "test" { nodebalancer_id = linode_nodebalancer.test.id config_id = linode_nodebalancer_config.test.id subnet_id = linode_vpc_subnet.test.id - address = "10.0.0.10:80" + address = "10.0.0.50:80" label = "{{ .Label }}" weight = 50 - depends_on = [linode_instance_config.test] + depends_on = [linode_instance.test] } {{ end }} \ No newline at end of file diff --git a/linode/nbs/datasource_test.go b/linode/nbs/datasource_test.go index c23a95664..1805ae79e 100644 --- a/linode/nbs/datasource_test.go +++ b/linode/nbs/datasource_test.go @@ -32,6 +32,7 @@ func TestAccDataSourceNodeBalancers_basic(t *testing.T) { { Config: tmpl.DataBasic(t, nbLabel, nbRegion), Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "nodebalancers.#", "2"), resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.0.id"), resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.0.client_conn_throttle"), @@ -41,6 +42,7 @@ func TestAccDataSourceNodeBalancers_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.0.ipv4"), resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.0.ipv6"), resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.label", nbLabel+"-0"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.type", "common"), resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.region", nbRegion), resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.transfer.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.0.transfer.0.in"), @@ -50,6 +52,29 @@ func TestAccDataSourceNodeBalancers_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.tags.0", "tf_test_1"), resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.0.updated"), resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.lke_cluster.#", "0"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resourceName, "nodebalancers.0.frontend_vpc_subnet_id"), + + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.id"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.client_conn_throttle"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.client_udp_sess_throttle"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.created"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.hostname"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.ipv6"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.label", nbLabel+"-1"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.type", "common"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.region", nbRegion), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.transfer.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.transfer.0.in"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.transfer.0.out"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.transfer.0.total"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.tags.#", "2"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.tags.0", "tf_test_1"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.tags.1", "tf_test_2"), + resource.TestCheckResourceAttrSet(resourceName, "nodebalancers.1.updated"), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.1.frontend_address_type", "public"), + resource.TestCheckNoResourceAttr(resourceName, "nodebalancers.1.frontend_vpc_subnet_id"), ), }, { @@ -69,6 +94,7 @@ func TestAccDataSourceNodeBalancers_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "nodebalancers.#", "1"), resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.region", nbRegion), + resource.TestCheckResourceAttr(resourceName, "nodebalancers.0.type", "common"), acceptance.CheckListContains(resourceName, "nodebalancers.0.tags", "tf_test_2"), ), }, diff --git a/linode/nbs/framework_models.go b/linode/nbs/framework_models.go index 5df269ffd..41b71e44b 100644 --- a/linode/nbs/framework_models.go +++ b/linode/nbs/framework_models.go @@ -35,6 +35,9 @@ type NodeBalancerModel struct { Updated timetypes.RFC3339 `tfsdk:"updated"` Transfer types.List `tfsdk:"transfer"` Tags types.Set `tfsdk:"tags"` + Type types.String `tfsdk:"type"` + FrontendAddressType types.String `tfsdk:"frontend_address_type"` + FrontendVPCSubnetID types.Int64 `tfsdk:"frontend_vpc_subnet_id"` LKECluster types.List `tfsdk:"lke_cluster"` } @@ -58,6 +61,7 @@ func (data *NodeBalancerModel) flattenNodeBalancer( ) diag.Diagnostics { data.ID = types.Int64Value(int64(nodebalancer.ID)) data.Label = types.StringPointerValue(nodebalancer.Label) + data.Type = types.StringValue(string(nodebalancer.Type)) data.Region = types.StringValue(nodebalancer.Region) data.ClientConnThrottle = types.Int64Value(int64(nodebalancer.ClientConnThrottle)) data.ClientUDPSessThrottle = types.Int64Value(int64(nodebalancer.ClientUDPSessThrottle)) @@ -66,6 +70,8 @@ func (data *NodeBalancerModel) flattenNodeBalancer( data.Ipv6 = types.StringPointerValue(nodebalancer.IPv6) data.Created = timetypes.NewRFC3339TimePointerValue(nodebalancer.Created) data.Updated = timetypes.NewRFC3339TimePointerValue(nodebalancer.Updated) + data.FrontendAddressType = types.StringValue(string(nodebalancer.FrontendAddressType)) + data.FrontendVPCSubnetID = types.Int64PointerValue(helper.IntPtrToInt64Ptr(nodebalancer.FrontendVPCSubnetID)) transfer, diags := nb.FlattenTransfer(ctx, nodebalancer.Transfer) if diags.HasError() { diff --git a/linode/nbs/tmpl/data_basic.gotf b/linode/nbs/tmpl/data_basic.gotf index d8102f554..0c0f8225e 100644 --- a/linode/nbs/tmpl/data_basic.gotf +++ b/linode/nbs/tmpl/data_basic.gotf @@ -3,10 +3,9 @@ {{ template "nbs_data_base" . }} data "linode_nodebalancers" "nbs" { - filter { - name = "label" - values = [linode_nodebalancer.foobar.0.label] - } + depends_on = [linode_nodebalancer.foobar] + order_by = "label" + order = "asc" } {{ end }} \ No newline at end of file diff --git a/linode/nbvpc/framework_datasource_schema.go b/linode/nbvpc/framework_datasource_schema.go index 396f1f175..560263c8b 100644 --- a/linode/nbvpc/framework_datasource_schema.go +++ b/linode/nbvpc/framework_datasource_schema.go @@ -14,7 +14,6 @@ var DataSourceSchema = schema.Schema{ Description: "The ID of the NodeBalancer-VPC configuration.", Required: true, }, - "ipv4_range": schema.StringAttribute{ Description: "A CIDR range for the VPC's IPv4 addresses. " + "The NodeBalancer sources IP addresses from this range when " + @@ -22,12 +21,10 @@ var DataSourceSchema = schema.Schema{ Computed: true, }, "ipv6_range": schema.StringAttribute{ - Description: "A CIDR range for the VPC's IPv6 addresses. " + - "The NodeBalancer sources IP addresses from this range when " + - "routing traffic to the backend VPC nodes.", + Description: "A CIDR range for the VPC's IPv6 addresses allocated " + + "as the NodeBalancer's frontend IPs.", Computed: true, }, - "subnet_id": schema.Int64Attribute{ Description: "The ID of this configuration's VPC subnet.", Computed: true, @@ -36,5 +33,11 @@ var DataSourceSchema = schema.Schema{ Description: "The ID of this configuration's VPC.", Computed: true, }, + "purpose": schema.StringAttribute{ + Description: "Indicates whether the VPC configuration applies to " + + "backend nodes that serve requests or to the NodeBalancer frontend, " + + "which manages incoming traffic.", + Computed: true, + }, }, } diff --git a/linode/nbvpc/framework_datasource_test.go b/linode/nbvpc/framework_datasource_test.go index d7468a9c2..d3e4cd87e 100644 --- a/linode/nbvpc/framework_datasource_test.go +++ b/linode/nbvpc/framework_datasource_test.go @@ -56,7 +56,6 @@ func TestAccDataSource_basic(t *testing.T) { tfjsonpath.New("subnet_id"), knownvalue.NotNull(), ), - statecheck.ExpectKnownValue( dataSourceName, tfjsonpath.New("ipv4_range"), @@ -67,6 +66,73 @@ func TestAccDataSource_basic(t *testing.T) { tfjsonpath.New("ipv6_range"), knownvalue.NotNull(), ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("purpose"), + knownvalue.StringExact("backend"), + ), + }, + }, + }, + }) +} + +func TestAccDataSource_frontendVPC(t *testing.T) { + t.Parallel() + + dataSourceName := "data.linode_nodebalancer_vpc.test" + + label := acctest.RandomWithPrefix("tf-test") + + // Use random region that supports premium NodeBalancers. + targetRegion, err := acceptance.GetRandomRegionSupportingPremiumNodeBalancers() + if err != nil { + log.Fatal(err) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + ProtoV6ProviderFactories: acceptance.ProtoV6ProviderFactories, + + Steps: []resource.TestStep{ + { + Config: tmpl.DataFrontendVPC(t, label, targetRegion), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("nodebalancer_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("vpc_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("ipv4_range"), + knownvalue.StringExact("10.0.0.2/32"), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("ipv6_range"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("purpose"), + knownvalue.StringExact("frontend"), + ), }, }, }, diff --git a/linode/nbvpc/framework_models.go b/linode/nbvpc/framework_models.go index 6ca7605e3..71d60f9f0 100644 --- a/linode/nbvpc/framework_models.go +++ b/linode/nbvpc/framework_models.go @@ -12,6 +12,7 @@ type DataSourceModel struct { IPv6Range types.String `tfsdk:"ipv6_range"` SubnetID types.Int64 `tfsdk:"subnet_id"` VPCID types.Int64 `tfsdk:"vpc_id"` + Purpose types.String `tfsdk:"purpose"` } func (m *DataSourceModel) Flatten(vpcConfig *linodego.NodeBalancerVPCConfig) *DataSourceModel { @@ -23,6 +24,7 @@ func (m *DataSourceModel) Flatten(vpcConfig *linodego.NodeBalancerVPCConfig) *Da m.VPCID = types.Int64Value(int64(vpcConfig.VPCID)) m.SubnetID = types.Int64Value(int64(vpcConfig.SubnetID)) + m.Purpose = types.StringValue(string(vpcConfig.Purpose)) return m } diff --git a/linode/nbvpc/tmpl/data_base.gotf b/linode/nbvpc/tmpl/data_base.gotf index c2770919a..140bcff88 100644 --- a/linode/nbvpc/tmpl/data_base.gotf +++ b/linode/nbvpc/tmpl/data_base.gotf @@ -16,6 +16,53 @@ resource "linode_nodebalancer" "test" { ] } +resource "linode_nodebalancer_config" "test" { + nodebalancer_id = linode_nodebalancer.test.id + port = 80 + protocol = "http" + check = "http" + check_path = "/foo" + check_attempts = 3 + check_timeout = 30 + stickiness = "http_cookie" + algorithm = "source" +} + +resource "linode_nodebalancer_node" "test" { + nodebalancer_id = linode_nodebalancer.test.id + config_id = linode_nodebalancer_config.test.id + subnet_id = linode_vpc_subnet.test.id + address = "${cidrhost(linode_vpc_subnet.test.ipv4, 50)}:80" + label = "tf-test" + weight = 50 + + depends_on = [linode_instance.test] +} + +resource "linode_instance" "test" { + label = "{{ .Label }}" + type = "g6-nanode-1" + region = "{{ .Region }}" + firewall_id = linode_firewall.e2e_test_firewall.id + + config { + label = "config" + kernel = "linode/latest-64bit" + root_device = "/dev/sda" + helpers { + network = true + } + + interface { + purpose = "vpc" + subnet_id = linode_vpc_subnet.test.id + ipv4 { + vpc = "${cidrhost(linode_vpc_subnet.test.ipv4, 50)}" + } + } + } +} + resource "linode_vpc" "test" { label = "{{ .Label }}" region = "{{ .Region }}" diff --git a/linode/nbvpc/tmpl/data_base_frontend_vpc.gotf b/linode/nbvpc/tmpl/data_base_frontend_vpc.gotf new file mode 100644 index 000000000..6e70d23c0 --- /dev/null +++ b/linode/nbvpc/tmpl/data_base_frontend_vpc.gotf @@ -0,0 +1,39 @@ +{{ define "nbvpc_data_base_frontend_vpc" }} + +{{ template "e2e_test_firewall" . }} + +resource "linode_nodebalancer" "test" { + label = "{{ .Label }}" + region = "{{ .Region }}" + firewall_id = linode_firewall.e2e_test_firewall.id + type = "premium" + + frontend_vpcs = [ + { + subnet_id = linode_vpc_subnet.frontend_subnet_test.id + } + ] +} + +resource "linode_vpc" "frontend_vpc_test" { + label = "{{ .Label }}-frontend" + region = "{{ .Region }}" + ipv6 = [ + { + range = "auto" + } + ] +} + +resource "linode_vpc_subnet" "frontend_subnet_test" { + vpc_id = linode_vpc.frontend_vpc_test.id + label = "tf-test-frontend" + ipv4 = "10.0.0.0/24" + ipv6 = [ + { + range = "auto" + } + ] +} + +{{ end }} \ No newline at end of file diff --git a/linode/nbvpc/tmpl/data_frontend_vpc.gotf b/linode/nbvpc/tmpl/data_frontend_vpc.gotf new file mode 100644 index 000000000..322f925a1 --- /dev/null +++ b/linode/nbvpc/tmpl/data_frontend_vpc.gotf @@ -0,0 +1,14 @@ +{{ define "nbvpc_data_frontend_vpc" }} + +{{ template "nbvpc_data_base_frontend_vpc" . }} + +data "linode_nodebalancer_vpcs" "test" { + nodebalancer_id = linode_nodebalancer.test.id +} + +data "linode_nodebalancer_vpc" "test" { + nodebalancer_id = linode_nodebalancer.test.id + id = data.linode_nodebalancer_vpcs.test.vpc_configs.0.id +} + +{{ end }} \ No newline at end of file diff --git a/linode/nbvpc/tmpl/template.go b/linode/nbvpc/tmpl/template.go index e9df8c1e0..acddf6651 100644 --- a/linode/nbvpc/tmpl/template.go +++ b/linode/nbvpc/tmpl/template.go @@ -18,3 +18,11 @@ func DataBasic(t testing.TB, label, region string) string { Region: region, }) } + +func DataFrontendVPC(t testing.TB, label, region string) string { + return acceptance.ExecuteTemplate(t, + "nbvpc_data_frontend_vpc", TemplateData{ + Label: label, + Region: region, + }) +} diff --git a/linode/nbvpcs/framework_datasource_test.go b/linode/nbvpcs/framework_datasource_test.go index 19fbde20c..d2f910823 100644 --- a/linode/nbvpcs/framework_datasource_test.go +++ b/linode/nbvpcs/framework_datasource_test.go @@ -68,6 +68,155 @@ func TestAccDataSource_basic(t *testing.T) { firstConfigPath.AtMapKey("ipv6_range"), knownvalue.NotNull(), ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("purpose"), + knownvalue.StringExact("backend"), + ), + }, + }, + { + Config: tmpl.DataFilter(t, label, targetRegion), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("nodebalancer_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("vpc_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("ipv4_range"), + knownvalue.StringExact("10.0.0.4/30"), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("ipv6_range"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("purpose"), + knownvalue.StringExact("backend"), + ), + }, + }, + }, + }) +} + +func TestAccDataSource_frontendVPC(t *testing.T) { + t.Parallel() + + dataSourceName := "data.linode_nodebalancer_vpcs.test" + + label := acctest.RandomWithPrefix("tf-test") + + // Use random region that supports premium NodeBalancers. + targetRegion, err := acceptance.GetRandomRegionSupportingPremiumNodeBalancers() + if err != nil { + log.Fatal(err) + } + + firstConfigPath := tfjsonpath.New("vpc_configs").AtSliceIndex(0) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + ProtoV6ProviderFactories: acceptance.ProtoV6ProviderFactories, + + Steps: []resource.TestStep{ + { + Config: tmpl.DataFrontendVPC(t, label, targetRegion), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("nodebalancer_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("vpc_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("ipv4_range"), + knownvalue.StringExact("10.0.0.2/32"), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("ipv6_range"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("purpose"), + knownvalue.StringExact("frontend"), + ), + }, + }, + { + Config: tmpl.DataFrontendVPCFilter(t, label, targetRegion), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + tfjsonpath.New("nodebalancer_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("subnet_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("vpc_id"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("ipv4_range"), + knownvalue.StringExact("10.0.0.2/32"), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("ipv6_range"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + dataSourceName, + firstConfigPath.AtMapKey("purpose"), + knownvalue.StringExact("frontend"), + ), }, }, }, diff --git a/linode/nbvpcs/framework_schema_datasource.go b/linode/nbvpcs/framework_schema_datasource.go index f63754873..0df3fa2e1 100644 --- a/linode/nbvpcs/framework_schema_datasource.go +++ b/linode/nbvpcs/framework_schema_datasource.go @@ -13,6 +13,7 @@ var filterConfig = frameworkfilter.Config{ "nodebalancer_id": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeInt}, "subnet_id": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeInt}, "vpc_id": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeInt}, + "purpose": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeString}, } var frameworkDatasourceSchema = schema.Schema{ diff --git a/linode/nbvpcs/tmpl/data_frontend_vpc.gotf b/linode/nbvpcs/tmpl/data_frontend_vpc.gotf new file mode 100644 index 000000000..453fed957 --- /dev/null +++ b/linode/nbvpcs/tmpl/data_frontend_vpc.gotf @@ -0,0 +1,9 @@ +{{ define "nbvpcs_data_frontend_vpc" }} + +{{ template "nbvpc_data_base_frontend_vpc" . }} + +data "linode_nodebalancer_vpcs" "test" { + nodebalancer_id = linode_nodebalancer.test.id +} + +{{ end }} \ No newline at end of file diff --git a/linode/nbvpcs/tmpl/data_frontend_vpc_filter.gotf b/linode/nbvpcs/tmpl/data_frontend_vpc_filter.gotf new file mode 100644 index 000000000..2755d638b --- /dev/null +++ b/linode/nbvpcs/tmpl/data_frontend_vpc_filter.gotf @@ -0,0 +1,14 @@ +{{ define "nbvpcs_data_frontend_vpc_filter" }} + +{{ template "nbvpc_data_base_frontend_vpc" . }} + +data "linode_nodebalancer_vpcs" "test" { + nodebalancer_id = linode_nodebalancer.test.id + + filter { + name = "purpose" + values = ["frontend"] + } +} + +{{ end }} \ No newline at end of file diff --git a/linode/nbvpcs/tmpl/template.go b/linode/nbvpcs/tmpl/template.go index 0dff60a44..5312fe0d4 100644 --- a/linode/nbvpcs/tmpl/template.go +++ b/linode/nbvpcs/tmpl/template.go @@ -26,3 +26,19 @@ func DataFilter(t testing.TB, label, region string) string { Region: region, }) } + +func DataFrontendVPC(t testing.TB, label, region string) string { + return acceptance.ExecuteTemplate(t, + "nbvpcs_data_frontend_vpc", TemplateData{ + Label: label, + Region: region, + }) +} + +func DataFrontendVPCFilter(t testing.TB, label, region string) string { + return acceptance.ExecuteTemplate(t, + "nbvpcs_data_frontend_vpc_filter", TemplateData{ + Label: label, + Region: region, + }) +}