feat: initialize Terraform Plugin Framework (#23)

* feat: initialize Terraform Plugin Framework

* fix docker-compose path for tests

* fix: ensure documentation can be generated with old provider SDK and new plugin framework

* lint
This commit is contained in:
Mateusz Filipowicz
2025-02-24 00:11:41 +01:00
committed by GitHub
parent ccde06a322
commit 325d7b7f20
91 changed files with 1005 additions and 705 deletions

View File

@@ -8,7 +8,7 @@ description: |-
# unifi_account (Data Source) # unifi_account (Data Source)
`unifi_account` data source can be used to retrieve RADIUS user accounts unifi_account data source can be used to retrieve RADIUS user accounts
@@ -30,5 +30,3 @@ description: |-
- `password` (String, Sensitive) The password of the account. - `password` (String, Sensitive) The password of the account.
- `tunnel_medium_type` (Number) See RFC2868 section 3.2 - `tunnel_medium_type` (Number) See RFC2868 section 3.2
- `tunnel_type` (Number) See RFC2868 section 3.1 - `tunnel_type` (Number) See RFC2868 section 3.1

View File

@@ -28,5 +28,3 @@ data "unifi_ap_group" "default" {
### Read-Only ### Read-Only
- `id` (String) The ID of this AP group. - `id` (String) The ID of this AP group.

View File

@@ -83,5 +83,3 @@ data "unifi_network" "my_network" {
- `wan_type_v6` (String) Specifies the IPV6 WAN connection type. Must be one of either `disabled`, `static`, or `dhcpv6`. - `wan_type_v6` (String) Specifies the IPV6 WAN connection type. Must be one of either `disabled`, `static`, or `dhcpv6`.
- `wan_username` (String) Specifies the IPV4 WAN username. - `wan_username` (String) Specifies the IPV4 WAN username.
- `x_wan_password` (String) Specifies the IPV4 WAN password. - `x_wan_password` (String) Specifies the IPV4 WAN password.

View File

@@ -28,5 +28,3 @@ data "unifi_port_profile" "all" {
### Read-Only ### Read-Only
- `id` (String) The ID of this port profile. - `id` (String) The ID of this port profile.

View File

@@ -23,5 +23,3 @@ description: |-
### Read-Only ### Read-Only
- `id` (String) The ID of this AP group. - `id` (String) The ID of this AP group.

View File

@@ -42,5 +42,3 @@ data "unifi_user" "client" {
- `network_id` (String) The network ID for this user. - `network_id` (String) The network ID for this user.
- `note` (String) A note with additional information for the user. - `note` (String) A note with additional information for the user.
- `user_group_id` (String) The user group ID for the user. - `user_group_id` (String) The user group ID for the user.

View File

@@ -25,5 +25,3 @@ description: |-
- `id` (String) The ID of this AP group. - `id` (String) The ID of this AP group.
- `qos_rate_max_down` (Number) - `qos_rate_max_down` (Number)
- `qos_rate_max_up` (Number) - `qos_rate_max_up` (Number)

View File

@@ -13,15 +13,13 @@ It is not recommended to use your own account for management of your controller.
Terraform is recommended. You can create a **Limited Admin** with **Local Access Only** and Terraform is recommended. You can create a **Limited Admin** with **Local Access Only** and
provide that information for authentication. Two-factor authentication is not supported in the provider. provide that information for authentication. Two-factor authentication is not supported in the provider.
It is recommended to use API Key authentication, if you are on controller version 9.0.108 or higher.
## Example Usage ## Example Usage
```terraform ```terraform
provider "unifi" { provider "unifi" {
api_key = var.api_key # optionally use UNIFI_API_KEY env var
username = var.username # optionally use UNIFI_USERNAME env var username = var.username # optionally use UNIFI_USERNAME env var
password = var.password # optionally use UNIFI_PASSWORD env var password = var.password # optionally use UNIFI_PASSWORD env var
api_key = var.api_key # optionally use UNIFI_API_KEY env var
api_url = var.api_url # optionally use UNIFI_API env var api_url = var.api_url # optionally use UNIFI_API env var
# you may need to allow insecure TLS communications unless you have configured # you may need to allow insecure TLS communications unless you have configured
@@ -33,25 +31,14 @@ provider "unifi" {
} }
``` ```
### Obtaining an API Key
1. Open your Site in UniFi Site Manager
2. Click on Control Plane -> Admins & Users.
3. Select your Admin user.
4. Click Create API Key.
5. Add a name for your API Key.
6. Copy the key and store it securely, as it will only be displayed once.
7. Click Done to ensure the key is hashed and securely stored.
8. Use the API Key 🎉
<!-- schema generated by tfplugindocs --> <!-- schema generated by tfplugindocs -->
## Schema ## Schema
### Optional ### Optional
- `allow_insecure` (Boolean) Skip verification of TLS certificates of API requests. You may need to set this to `true` if you are using your local API without setting up a signed certificate. Can be specified with the `UNIFI_INSECURE` environment variable. - `allow_insecure` (Boolean) Skip verification of TLS certificates of API requests. You may need to set this to `true` if you are using your local API without setting up a signed certificate. Can be specified with the `UNIFI_INSECURE` environment variable.
- `api_key` (String) API key for the user accessing the API. Can be specified with the `UNIFI_API_KEY` environment variable. Requires controller version 9.0.108 or higher and `username` and `password` to be empty - `api_key` (String, Sensitive) API Key for the user accessing the API. Can be specified with the `UNIFI_API_KEY` environment variable. Controller version 9.0.108 or later is required.
- `api_url` (String) URL of the controller API. Can be specified with the `UNIFI_API` environment variable. You should **NOT** supply the path (`/api`), the SDK will discover the appropriate paths. This is to support UDM Pro style API paths as well as more standard controller paths. - `api_url` (String) URL of the controller API. Can be specified with the `UNIFI_API` environment variable. You should **NOT** supply the path (`/api`), the SDK will discover the appropriate paths. This is to support UDM Pro style API paths as well as more standard controller paths.
- `password` (String) Password for the user accessing the API. Can be specified with the `UNIFI_PASSWORD` environment variable. - `password` (String, Sensitive) Password for the user accessing the API. Can be specified with the `UNIFI_PASSWORD` environment variable.
- `site` (String) The site in the Unifi controller this provider will manage. Can be specified with the `UNIFI_SITE` environment variable. Default: `default` - `site` (String) The site in the Unifi controller this provider will manage. Can be specified with the `UNIFI_SITE` environment variable. Default: `default`
- `username` (String) Local user name for the Unifi controller API. Can be specified with the `UNIFI_USERNAME` environment variable. - `username` (String) Local user name for the Unifi controller API. Can be specified with the `UNIFI_USERNAME` environment variable.

View File

@@ -41,5 +41,3 @@ NOTE: MAC-based authentication accounts can only be used for wireless and wired
### Read-Only ### Read-Only
- `id` (String) The ID of the account. - `id` (String) The ID of the account.

View File

@@ -90,6 +90,5 @@ Optional:
- `aggregate_num_ports` (Number) Number of ports in the aggregate. - `aggregate_num_ports` (Number) Number of ports in the aggregate.
- `name` (String) Human-readable name of the port. - `name` (String) Human-readable name of the port.
- `op_mode` (String) Operating mode of the port, valid values are `switch`, `mirror`, and `aggregate`. Defaults to `switch`. - `op_mode` (String) Operating mode of the port, valid values are `switch`, `mirror`, and `aggregate`. Defaults to `switch`.
- `poe_mode` (String) PoE mode of the port; valid values are `auto`, `pasv24`, `passthrough`, and `off`.
- `port_profile_id` (String) ID of the Port Profile used on this port. - `port_profile_id` (String) ID of the Port Profile used on this port.

View File

@@ -43,5 +43,3 @@ resource "unifi_dynamic_dns" "test" {
### Read-Only ### Read-Only
- `id` (String) The ID of the dynamic DNS. - `id` (String) The ID of the dynamic DNS.

View File

@@ -41,5 +41,3 @@ resource "unifi_firewall_group" "can_print" {
### Read-Only ### Read-Only
- `id` (String) The ID of the firewall group. - `id` (String) The ID of the firewall group.

View File

@@ -67,9 +67,9 @@ resource "unifi_network" "wan" {
- `dhcpd_boot_filename` (String) Specifies the file to PXE boot from on the dhcpd_boot_server. - `dhcpd_boot_filename` (String) Specifies the file to PXE boot from on the dhcpd_boot_server.
- `dhcpd_boot_server` (String) Specifies the IPv4 address of a TFTP server to network boot from. - `dhcpd_boot_server` (String) Specifies the IPv4 address of a TFTP server to network boot from.
- `domain_name` (String) The domain name of this network. - `domain_name` (String) The domain name of this network.
- `enabled` (Boolean) Specifies whether this network is enabled or not. Defaults to `true`.
- `igmp_snooping` (Boolean) Specifies whether IGMP snooping is enabled or not. - `igmp_snooping` (Boolean) Specifies whether IGMP snooping is enabled or not.
- `internet_access_enabled` (Boolean) Specifies whether this network should be allowed to access the internet or not. Defaults to `true`. - `internet_access_enabled` (Boolean) Specifies whether this network should be allowed to access the internet or not. Defaults to `true`.
- `intra_network_access_enabled` (Boolean) Specifies whether this network should be allowed to access other local networks or not. Defaults to `true`.
- `ipv6_interface_type` (String) Specifies which type of IPv6 connection to use. Must be one of either `static`, `pd`, or `none`. Defaults to `none`. - `ipv6_interface_type` (String) Specifies which type of IPv6 connection to use. Must be one of either `static`, `pd`, or `none`. Defaults to `none`.
- `ipv6_pd_interface` (String) Specifies which WAN interface to use for IPv6 PD. Must be one of either `wan` or `wan2`. - `ipv6_pd_interface` (String) Specifies which WAN interface to use for IPv6 PD. Must be one of either `wan` or `wan2`.
- `ipv6_pd_prefixid` (String) Specifies the IPv6 Prefix ID. - `ipv6_pd_prefixid` (String) Specifies the IPv6 Prefix ID.
@@ -82,6 +82,7 @@ resource "unifi_network" "wan" {
- `ipv6_static_subnet` (String) Specifies the static IPv6 subnet when `ipv6_interface_type` is 'static'. - `ipv6_static_subnet` (String) Specifies the static IPv6 subnet when `ipv6_interface_type` is 'static'.
- `multicast_dns` (Boolean) Specifies whether Multicast DNS (mDNS) is enabled or not on the network (Controller >=v7). - `multicast_dns` (Boolean) Specifies whether Multicast DNS (mDNS) is enabled or not on the network (Controller >=v7).
- `network_group` (String) The group of the network. Defaults to `LAN`. - `network_group` (String) The group of the network. Defaults to `LAN`.
- `network_isolation_enabled` (Boolean) Specifies whether this network should be isolated from other networks or not. Defaults to `false`.
- `site` (String) The name of the site to associate the network with. - `site` (String) The name of the site to associate the network with.
- `subnet` (String) The subnet of the network. Must be a valid CIDR address. - `subnet` (String) The subnet of the network. Must be a valid CIDR address.
- `vlan_id` (Number) The VLAN ID of the network. - `vlan_id` (Number) The VLAN ID of the network.

View File

@@ -31,5 +31,3 @@ description: |-
### Read-Only ### Read-Only
- `id` (String) The ID of the port forwarding rule. - `id` (String) The ID of the port forwarding rule.

View File

@@ -46,6 +46,7 @@ resource "unifi_port_profile" "poe_disabled" {
- `dot1x_idle_timeout` (Number) The timeout, in seconds, to use when using the MAC Based 802.1X control. Can be between 0 and 65535 Defaults to `300`. - `dot1x_idle_timeout` (Number) The timeout, in seconds, to use when using the MAC Based 802.1X control. Can be between 0 and 65535 Defaults to `300`.
- `egress_rate_limit_kbps` (Number) The egress rate limit, in kpbs, for the port profile. Can be between `64` and `9999999`. - `egress_rate_limit_kbps` (Number) The egress rate limit, in kpbs, for the port profile. Can be between `64` and `9999999`.
- `egress_rate_limit_kbps_enabled` (Boolean) Enable egress rate limiting for the port profile. Defaults to `false`. - `egress_rate_limit_kbps_enabled` (Boolean) Enable egress rate limiting for the port profile. Defaults to `false`.
- `excluded_network_ids` (Set of String) List of network IDs to exclude on the port profile when forward is set to customize.
- `forward` (String) The type forwarding to use for the port profile. Can be `all`, `native`, `customize` or `disabled`. Defaults to `native`. - `forward` (String) The type forwarding to use for the port profile. Can be `all`, `native`, `customize` or `disabled`. Defaults to `native`.
- `full_duplex` (Boolean) Enable full duplex for the port profile. Defaults to `false`. - `full_duplex` (Boolean) Enable full duplex for the port profile. Defaults to `false`.
- `isolation` (Boolean) Enable port isolation for the port profile. Defaults to `false`. - `isolation` (Boolean) Enable port isolation for the port profile. Defaults to `false`.
@@ -62,7 +63,7 @@ resource "unifi_port_profile" "poe_disabled" {
- `priority_queue3_level` (Number) The priority queue 3 level for the port profile. Can be between 0 and 100. - `priority_queue3_level` (Number) The priority queue 3 level for the port profile. Can be between 0 and 100.
- `priority_queue4_level` (Number) The priority queue 4 level for the port profile. Can be between 0 and 100. - `priority_queue4_level` (Number) The priority queue 4 level for the port profile. Can be between 0 and 100.
- `site` (String) The name of the site to associate the port profile with. - `site` (String) The name of the site to associate the port profile with.
- `speed` (Number) The link speed to set for the port profile. Can be one of `10`, `100`, `1000`, `2500`, `5000`, `10000`, `20000`, `25000`, `40000`, `50000` or `100000` - `speed` (Number) The link speed to set for the port profile in Mbps. Can be one of `10`, `100`, `1000`, `2500`, `5000`, `10000`, `20000`, `25000`, `40000`, `50000` or `100000`. When `autoneg` is true, this setting is ignored.
- `stormctrl_bcast_enabled` (Boolean) Enable broadcast Storm Control for the port profile. Defaults to `false`. - `stormctrl_bcast_enabled` (Boolean) Enable broadcast Storm Control for the port profile. Defaults to `false`.
- `stormctrl_bcast_level` (Number) The broadcast Storm Control level for the port profile. Can be between 0 and 100. - `stormctrl_bcast_level` (Number) The broadcast Storm Control level for the port profile. Can be between 0 and 100.
- `stormctrl_bcast_rate` (Number) The broadcast Storm Control rate for the port profile. Can be between 0 and 14880000. - `stormctrl_bcast_rate` (Number) The broadcast Storm Control rate for the port profile. Can be between 0 and 14880000.
@@ -74,11 +75,9 @@ resource "unifi_port_profile" "poe_disabled" {
- `stormctrl_ucast_level` (Number) The unknown unicast Storm Control level for the port profile. Can be between 0 and 100. - `stormctrl_ucast_level` (Number) The unknown unicast Storm Control level for the port profile. Can be between 0 and 100.
- `stormctrl_ucast_rate` (Number) The unknown unicast Storm Control rate for the port profile. Can be between 0 and 14880000. - `stormctrl_ucast_rate` (Number) The unknown unicast Storm Control rate for the port profile. Can be between 0 and 14880000.
- `stp_port_mode` (Boolean) Enable spanning tree protocol on the port profile. Defaults to `true`. - `stp_port_mode` (Boolean) Enable spanning tree protocol on the port profile. Defaults to `true`.
- `tagged_networkconf_ids` (Set of String) The IDs of networks to tag traffic with for the port profile. - `tagged_vlan_mgmt` (String) The VLAN management type for the port profile. Can be one of 'auto', 'block_all', or 'custom'.
- `voice_networkconf_id` (String) The ID of network to use as the voice network on the port profile. - `voice_networkconf_id` (String) The ID of network to use as the voice network on the port profile.
### Read-Only ### Read-Only
- `id` (String) The ID of the port profile. - `id` (String) The ID of the port profile.

View File

@@ -60,5 +60,3 @@ Required:
Optional: Optional:
- `port` (Number) Port of authentication service. Defaults to `1812`. - `port` (Number) Port of authentication service. Defaults to `1812`.

View File

@@ -49,5 +49,3 @@ Optional:
- `comment` (String) Comment. - `comment` (String) Comment.
- `key` (String) Public SSH key. - `key` (String) Public SSH key.

View File

@@ -29,5 +29,3 @@ description: |-
### Read-Only ### Read-Only
- `id` (String) The ID of the settings. - `id` (String) The ID of the settings.

View File

@@ -18,14 +18,9 @@ description: |-
### Optional ### Optional
- `dhcp_relay_servers` (List of String) The DHCP relay servers. - `dhcp_relay_servers` (List of String) The DHCP relay servers.
- `firewall_guest_default_log` (Boolean) Whether the guest firewall log is enabled.
- `firewall_lan_default_log` (Boolean) Whether the LAN firewall log is enabled.
- `firewall_wan_default_log` (Boolean) Whether the WAN firewall log is enabled.
- `multicast_dns_enabled` (Boolean) Whether multicast DNS is enabled. - `multicast_dns_enabled` (Boolean) Whether multicast DNS is enabled.
- `site` (String) The name of the site to associate the settings with. - `site` (String) The name of the site to associate the settings with.
### Read-Only ### Read-Only
- `id` (String) The ID of the settings. - `id` (String) The ID of the settings.

View File

@@ -56,5 +56,3 @@ resource "unifi_static_route" "interface" {
### Read-Only ### Read-Only
- `id` (String) The ID of the static route. - `id` (String) The ID of the static route.

View File

@@ -52,5 +52,3 @@ resource "unifi_user" "test" {
- `hostname` (String) The hostname of the user. - `hostname` (String) The hostname of the user.
- `id` (String) The ID of the user. - `id` (String) The ID of the user.
- `ip` (String) The IP address of the user. - `ip` (String) The IP address of the user.

View File

@@ -55,7 +55,7 @@ resource "unifi_wlan" "wifi" {
### Required ### Required
- `name` (String) The SSID of the network. - `name` (String) The SSID of the network. SSID length must be between 1 and 32 characters.
- `security` (String) The type of WiFi security for this network. Valid values are: `wpapsk`, `wpaeap`, and `open`. - `security` (String) The type of WiFi security for this network. Valid values are: `wpapsk`, `wpaeap`, and `open`.
- `user_group_id` (String) ID of the user group to use for this network. - `user_group_id` (String) ID of the user group to use for this network.

3
go.mod
View File

@@ -193,8 +193,11 @@ require (
github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.22.0 // indirect github.com/hashicorp/terraform-exec v0.22.0 // indirect
github.com/hashicorp/terraform-json v0.24.0 // indirect github.com/hashicorp/terraform-json v0.24.0 // indirect
github.com/hashicorp/terraform-plugin-framework v1.14.1 // indirect
github.com/hashicorp/terraform-plugin-framework-validators v0.17.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect github.com/hashicorp/terraform-plugin-go v0.26.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/hashicorp/terraform-plugin-mux v0.18.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.4 // indirect github.com/hashicorp/terraform-registry-address v0.2.4 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect github.com/hashicorp/yamux v0.1.2 // indirect

6
go.sum
View File

@@ -492,10 +492,16 @@ github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4
github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow= github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow=
github.com/hashicorp/terraform-plugin-docs v0.20.1 h1:Fq7E/HrU8kuZu3hNliZGwloFWSYfWEOWnylFhYQIoys= github.com/hashicorp/terraform-plugin-docs v0.20.1 h1:Fq7E/HrU8kuZu3hNliZGwloFWSYfWEOWnylFhYQIoys=
github.com/hashicorp/terraform-plugin-docs v0.20.1/go.mod h1:Yz6HoK7/EgzSrHPB9J/lWFzwl9/xep2OPnc5jaJDV90= github.com/hashicorp/terraform-plugin-docs v0.20.1/go.mod h1:Yz6HoK7/EgzSrHPB9J/lWFzwl9/xep2OPnc5jaJDV90=
github.com/hashicorp/terraform-plugin-framework v1.14.1 h1:jaT1yvU/kEKEsxnbrn4ZHlgcxyIfjvZ41BLdlLk52fY=
github.com/hashicorp/terraform-plugin-framework v1.14.1/go.mod h1:xNUKmvTs6ldbwTuId5euAtg37dTxuyj3LHS3uj7BHQ4=
github.com/hashicorp/terraform-plugin-framework-validators v0.17.0 h1:0uYQcqqgW3BMyyve07WJgpKorXST3zkpzvrOnf3mpbg=
github.com/hashicorp/terraform-plugin-framework-validators v0.17.0/go.mod h1:VwdfgE/5Zxm43flraNa0VjcvKQOGVrcO4X8peIri0T0=
github.com/hashicorp/terraform-plugin-go v0.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiypAfFQmw8aE65O2M= github.com/hashicorp/terraform-plugin-go v0.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiypAfFQmw8aE65O2M=
github.com/hashicorp/terraform-plugin-go v0.26.0/go.mod h1:+CXjuLDiFgqR+GcrM5a2E2Kal5t5q2jb0E3D57tTdNY= github.com/hashicorp/terraform-plugin-go v0.26.0/go.mod h1:+CXjuLDiFgqR+GcrM5a2E2Kal5t5q2jb0E3D57tTdNY=
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
github.com/hashicorp/terraform-plugin-mux v0.18.0 h1:7491JFSpWyAe0v9YqBT+kel7mzHAbO5EpxxT0cUL/Ms=
github.com/hashicorp/terraform-plugin-mux v0.18.0/go.mod h1:Ho1g4Rr8qv0qTJlcRKfjjXTIO67LNbDtM6r+zHUNHJQ=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 h1:WNMsTLkZf/3ydlgsuXePa3jvZFwAJhruxTxP/c1Viuw= github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1 h1:WNMsTLkZf/3ydlgsuXePa3jvZFwAJhruxTxP/c1Viuw=
github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1/go.mod h1:P6o64QS97plG44iFzSM6rAn6VJIC/Sy9a9IkEtl79K4= github.com/hashicorp/terraform-plugin-sdk/v2 v2.36.1/go.mod h1:P6o64QS97plG44iFzSM6rAn6VJIC/Sy9a9IkEtl79K4=
github.com/hashicorp/terraform-plugin-testing v1.11.0 h1:MeDT5W3YHbONJt2aPQyaBsgQeAIckwPX41EUHXEn29A= github.com/hashicorp/terraform-plugin-testing v1.11.0 h1:MeDT5W3YHbONJt2aPQyaBsgQeAIckwPX41EUHXEn29A=

View File

@@ -1,56 +0,0 @@
package provider
import (
"fmt"
"net"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func cidrValidate(raw interface{}, key string) ([]string, []error) {
v, ok := raw.(string)
if !ok {
return nil, []error{fmt.Errorf("expected string, got %T", raw)}
}
_, _, err := net.ParseCIDR(v)
if err != nil {
return nil, []error{err}
}
return nil, nil
}
func cidrDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
_, oldNet, err := net.ParseCIDR(old)
if err != nil {
return false
}
_, newNet, err := net.ParseCIDR(new)
if err != nil {
return false
}
return oldNet.String() == newNet.String()
}
func cidrZeroBased(cidr string) string {
_, cidrNet, err := net.ParseCIDR(cidr)
if err != nil {
return ""
}
return cidrNet.String()
}
func cidrOneBased(cidr string) string {
_, cidrNet, err := net.ParseCIDR(cidr)
if err != nil {
return ""
}
cidrNet.IP[3]++
return cidrNet.String()
}

View File

@@ -0,0 +1,78 @@
package provider
import (
"errors"
"fmt"
"github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/go-version"
"log"
"net/http"
"strings"
)
func IsServerErrorContains(err error, messageContains string) bool {
if err == nil {
return false
}
var se *unifi.ServerError
if errors.As(err, &se) {
if strings.Contains(se.Message, messageContains) {
return true
}
// check details
if se.Details != nil {
for _, m := range se.Details {
if strings.Contains(m.Message, messageContains) {
return true
}
}
}
}
return false
}
type ClientConfig struct {
Username string
Password string
ApiKey string
Url string
Site string
Insecure bool
HttpConfigurer func() http.RoundTripper
}
func NewClient(cfg *ClientConfig) (*Client, error) {
unifiClient, err := unifi.NewClient(&unifi.ClientConfig{
URL: cfg.Url,
User: cfg.Username,
Password: cfg.Password,
APIKey: cfg.ApiKey,
HttpRoundTripperProvider: cfg.HttpConfigurer,
ValidationMode: unifi.DisableValidation,
Logger: unifi.NewDefaultLogger(unifi.WarnLevel),
})
if err != nil {
return nil, err
}
err = CheckMinimumControllerVersion(unifiClient.Version())
log.Printf("[TRACE] Unifi controller version: %q", unifiClient.Version())
if err != nil {
return nil, err
}
c := &Client{
Client: unifiClient,
Site: cfg.Site,
Version: version.Must(version.NewVersion(unifiClient.Version())),
}
if cfg.ApiKey != "" && !c.SupportsApiKeyAuthentication() {
return nil, fmt.Errorf("API key authentication is not supported on this controller version: %s, you must be on %s or higher", c.Version, ControllerVersionApiKeyAuth)
}
return c, nil
}
type Client struct {
unifi.Client
Site string
Version *version.Version
}

View File

@@ -11,37 +11,37 @@ func asVersion(versionString string) *version.Version {
} }
var ( var (
controllerV6 = asVersion("6.0.0") ControllerV6 = asVersion("6.0.0")
controllerV7 = asVersion("7.0.0") ControllerV7 = asVersion("7.0.0")
controllerVersionApiKeyAuth = asVersion("9.0.108") ControllerVersionApiKeyAuth = asVersion("9.0.108")
// https://community.ui.com/releases/UniFi-Network-Controller-6-1-61/62f1ad38-1ac5-430c-94b0-becbb8f71d7d // https://community.ui.com/releases/UniFi-Network-Controller-6-1-61/62f1ad38-1ac5-430c-94b0-becbb8f71d7d
controllerVersionWPA3 = asVersion("6.1.61") ControllerVersionWPA3 = asVersion("6.1.61")
) )
func (c *client) IsControllerV6() bool { func (c *Client) IsControllerV6() bool {
return c.version.GreaterThanOrEqual(controllerV6) return c.Version.GreaterThanOrEqual(ControllerV6)
} }
func (c *client) IsControllerV7() bool { func (c *Client) IsControllerV7() bool {
return c.version.GreaterThanOrEqual(controllerV7) return c.Version.GreaterThanOrEqual(ControllerV7)
} }
func (c *client) SupportsApiKeyAuthentication() bool { func (c *Client) SupportsApiKeyAuthentication() bool {
return c.version.GreaterThanOrEqual(controllerVersionApiKeyAuth) return c.Version.GreaterThanOrEqual(ControllerVersionApiKeyAuth)
} }
func (c *client) SupportsWPA3() bool { func (c *Client) SupportsWPA3() bool {
return c.version.GreaterThanOrEqual(controllerVersionWPA3) return c.Version.GreaterThanOrEqual(ControllerVersionWPA3)
} }
func checkMinimumControllerVersion(versionString string) error { func CheckMinimumControllerVersion(versionString string) error {
v, err := version.NewVersion(versionString) v, err := version.NewVersion(versionString)
if err != nil { if err != nil {
return err return err
} }
if v.LessThan(controllerV6) { if v.LessThan(ControllerV6) {
return fmt.Errorf("Controller version %q or greater is required to use the provider, found %q.", controllerV6, v) return fmt.Errorf("Controller version %q or greater is required to use the provider, found %q.", ControllerV6, v)
} }
return nil return nil
} }

View File

@@ -1,216 +1,13 @@
package provider package provider
import ( const (
"context" ProviderUsernameDescription = "Local user name for the Unifi controller API. Can be specified with the `UNIFI_USERNAME` environment variable."
"crypto/tls" ProviderPasswordDescription = "Password for the user accessing the API. Can be specified with the `UNIFI_PASSWORD` environment variable."
"errors" ProviderAPIKeyDescription = "API Key for the user accessing the API. Can be specified with the `UNIFI_API_KEY` environment variable. Controller version 9.0.108 or later is required."
"fmt" ProviderAPIURLDescription = "URL of the controller API. Can be specified with the `UNIFI_API` environment variable. " +
"github.com/hashicorp/go-version" "You should **NOT** supply the path (`/api`), the SDK will discover the appropriate paths. This is to support UDM Pro style API paths as well as more standard controller paths."
"log" ProviderSiteDescription = "The site in the Unifi controller this provider will manage. Can be specified with the `UNIFI_SITE` environment variable. Default: `default`"
"net" ProviderAllowInsecureDescription = "Skip verification of TLS certificates of API requests. You may need to set this to `true` " +
"net/http"
"strings"
"time"
"github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func init() {
schema.DescriptionKind = schema.StringMarkdown
schema.SchemaDescriptionBuilder = func(s *schema.Schema) string {
desc := s.Description
if s.Default != nil {
desc += fmt.Sprintf(" Defaults to `%v`.", s.Default)
}
if s.Deprecated != "" {
desc += " " + s.Deprecated
}
return strings.TrimSpace(desc)
}
}
func New(version string) func() *schema.Provider {
return func() *schema.Provider {
p := &schema.Provider{
Schema: map[string]*schema.Schema{
"username": {
Description: "Local user name for the Unifi controller API. Can be specified with the `UNIFI_USERNAME` " +
"environment variable.",
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_USERNAME", ""),
},
"password": {
Description: "Password for the user accessing the API. Can be specified with the `UNIFI_PASSWORD` " +
"environment variable.",
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_PASSWORD", ""),
},
"api_key": {
Description: "API Key for the user accessing the API. Can be specified with the `UNIFI_API_KEY` " +
"environment variable. Controller version 9.0.108 or later is required.",
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_API_KEY", ""),
},
"api_url": {
Description: "URL of the controller API. Can be specified with the `UNIFI_API` environment variable. " +
"You should **NOT** supply the path (`/api`), the SDK will discover the appropriate paths. This is " +
"to support UDM Pro style API paths as well as more standard controller paths.",
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_API", ""),
},
"site": {
Description: "The site in the Unifi controller this provider will manage. Can be specified with " +
"the `UNIFI_SITE` environment variable. Default: `default`",
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_SITE", "default"),
},
"allow_insecure": {
Description: "Skip verification of TLS certificates of API requests. You may need to set this to `true` " +
"if you are using your local API without setting up a signed certificate. Can be specified with the " + "if you are using your local API without setting up a signed certificate. Can be specified with the " +
"`UNIFI_INSECURE` environment variable.", "`UNIFI_INSECURE` environment variable."
Type: schema.TypeBool, )
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_INSECURE", false),
},
},
DataSourcesMap: map[string]*schema.Resource{
"unifi_ap_group": dataAPGroup(),
"unifi_network": dataNetwork(),
"unifi_port_profile": dataPortProfile(),
"unifi_radius_profile": dataRADIUSProfile(),
"unifi_user_group": dataUserGroup(),
"unifi_user": dataUser(),
"unifi_account": dataAccount(),
},
ResourcesMap: map[string]*schema.Resource{
// TODO: "unifi_ap_group"
"unifi_device": resourceDevice(),
"unifi_dynamic_dns": resourceDynamicDNS(),
"unifi_firewall_group": resourceFirewallGroup(),
"unifi_firewall_rule": resourceFirewallRule(),
"unifi_network": resourceNetwork(),
"unifi_port_forward": resourcePortForward(),
"unifi_port_profile": resourcePortProfile(),
"unifi_radius_profile": resourceRadiusProfile(),
"unifi_site": resourceSite(),
"unifi_static_route": resourceStaticRoute(),
"unifi_user_group": resourceUserGroup(),
"unifi_user": resourceUser(),
"unifi_wlan": resourceWLAN(),
"unifi_account": resourceAccount(),
"unifi_setting_mgmt": resourceSettingMgmt(),
"unifi_setting_radius": resourceSettingRadius(),
"unifi_setting_usg": resourceSettingUsg(),
},
}
p.ConfigureContextFunc = configure(version, p)
return p
}
}
func createHTTPTransport(insecure bool, subsystem string) http.RoundTripper {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecure,
},
}
t := logging.NewSubsystemLoggingHTTPTransport(subsystem, transport)
return t
}
func configure(v string, p *schema.Provider) schema.ConfigureContextFunc {
return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
user := d.Get("username").(string)
pass := d.Get("password").(string)
apiKey := d.Get("api_key").(string)
if apiKey != "" && (user != "" || pass != "") {
return nil, diag.FromErr(errors.New("only one of `username`/`password` or `api_key` can be set"))
} else if apiKey == "" && (user == "" || pass == "") {
return nil, diag.FromErr(errors.New("either `username` and `password` or `api_key` must be set"))
}
baseURL := d.Get("api_url").(string)
site := d.Get("site").(string)
insecure := d.Get("allow_insecure").(bool)
unifiClient, err := unifi.NewClient(&unifi.ClientConfig{
URL: baseURL,
User: user,
Password: pass,
APIKey: apiKey,
HttpRoundTripperProvider: func() http.RoundTripper {
return createHTTPTransport(insecure, "unifi")
},
ValidationMode: unifi.DisableValidation,
Logger: unifi.NewDefaultLogger(unifi.WarnLevel),
})
if err != nil {
return nil, diag.FromErr(err)
}
err = checkMinimumControllerVersion(unifiClient.Version())
log.Printf("[TRACE] Unifi controller version: %q", unifiClient.Version())
if err != nil {
return nil, diag.FromErr(err)
}
c := &client{
c: unifiClient,
site: site,
version: version.Must(version.NewVersion(unifiClient.Version())),
}
if apiKey != "" && !c.SupportsApiKeyAuthentication() {
return nil, diag.FromErr(fmt.Errorf("API key authentication is not supported on this controller version: %s, you must be on %s or higher", c.version, controllerVersionApiKeyAuth))
}
return c, nil
}
}
func IsServerErrorContains(err error, messageContains string) bool {
if err == nil {
return false
}
var se *unifi.ServerError
if errors.As(err, &se) {
if strings.Contains(se.Message, messageContains) {
return true
}
// check details
if se.Details != nil {
for _, m := range se.Details {
if strings.Contains(m.Message, messageContains) {
return true
}
}
}
}
return false
}
type client struct {
c unifi.Client
site string
version *version.Version
}

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"testing" "testing"

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"context" "context"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -9,7 +10,7 @@ import (
func dataAccount() *schema.Resource { func dataAccount() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Description: "`unifi_account` data source can be used to retrieve RADIUS user accounts", Description: "unifi_account data source can be used to retrieve RADIUS user accounts",
ReadContext: dataAccountRead, ReadContext: dataAccountRead,
@@ -57,15 +58,15 @@ func dataAccount() *schema.Resource {
} }
func dataAccountRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func dataAccountRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
name := d.Get("name").(string) name := d.Get("name").(string)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
accounts, err := c.c.ListAccount(ctx, site) accounts, err := c.ListAccount(ctx, site)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"context" "context"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -35,15 +36,15 @@ func dataAPGroup() *schema.Resource {
} }
func dataAPGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func dataAPGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
name := d.Get("name").(string) name := d.Get("name").(string)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
groups, err := c.c.ListAPGroup(ctx, site) groups, err := c.ListAPGroup(ctx, site)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"testing" "testing"

View File

@@ -1,7 +1,9 @@
package provider package v1
import ( import (
"context" "context"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -279,19 +281,19 @@ func dataNetwork() *schema.Resource {
} }
func dataNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func dataNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
name := d.Get("name").(string) name := d.Get("name").(string)
site := d.Get("site").(string) site := d.Get("site").(string)
id := d.Get("id").(string) id := d.Get("id").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
if (name == "" && id == "") || (name != "" && id != "") { if (name == "" && id == "") || (name != "" && id != "") {
return diag.Errorf("One of 'name' OR 'id' is required") return diag.Errorf("One of 'name' OR 'id' is required")
} }
networks, err := c.c.ListNetwork(ctx, site) networks, err := c.ListNetwork(ctx, site)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -327,7 +329,7 @@ func dataNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface
d.Set("name", n.Name) d.Set("name", n.Name)
d.Set("purpose", n.Purpose) d.Set("purpose", n.Purpose)
d.Set("vlan_id", n.VLAN) d.Set("vlan_id", n.VLAN)
d.Set("subnet", cidrZeroBased(n.IPSubnet)) d.Set("subnet", utils.CidrZeroBased(n.IPSubnet))
d.Set("network_group", n.NetworkGroup) d.Set("network_group", n.NetworkGroup)
d.Set("dhcp_dns", dhcpDNS) d.Set("dhcp_dns", dhcpDNS)
d.Set("dhcp_start", n.DHCPDStart) d.Set("dhcp_start", n.DHCPDStart)

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"testing" "testing"
"github.com/hashicorp/go-version" "github.com/hashicorp/go-version"
@@ -14,7 +15,7 @@ func TestAccDataNetwork_byName(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("error parsing version: %s", err) t.Fatalf("error parsing version: %s", err)
} }
if v.LessThan(controllerV7) { if v.LessThan(provider.ControllerV7) {
defaultName = "LAN" defaultName = "LAN"
} }
@@ -41,7 +42,7 @@ func TestAccDataNetwork_byID(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("error parsing version: %s", err) t.Fatalf("error parsing version: %s", err)
} }
if v.LessThan(controllerV7) { if v.LessThan(provider.ControllerV7) {
defaultName = "LAN" defaultName = "LAN"
} }

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"context" "context"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -36,15 +37,15 @@ func dataPortProfile() *schema.Resource {
} }
func dataPortProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func dataPortProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
name := d.Get("name").(string) name := d.Get("name").(string)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
groups, err := c.c.ListPortProfile(ctx, site) groups, err := c.ListPortProfile(ctx, site)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"testing" "testing"

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"context" "context"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -36,15 +37,15 @@ func dataRADIUSProfile() *schema.Resource {
} }
func dataRADIUSProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func dataRADIUSProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
name := d.Get("name").(string) name := d.Get("name").(string)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
profiles, err := c.c.ListRADIUSProfile(ctx, site) profiles, err := c.ListRADIUSProfile(ctx, site)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"context" "context"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"strings" "strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -91,20 +92,20 @@ func dataUser() *schema.Resource {
} }
func dataUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func dataUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
macResp, err := c.c.GetUserByMAC(ctx, site, strings.ToLower(mac)) macResp, err := c.GetUserByMAC(ctx, site, strings.ToLower(mac))
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
resp, err := c.c.GetUser(ctx, site, macResp.ID) resp, err := c.GetUser(ctx, site, macResp.ID)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"context" "context"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -45,15 +46,15 @@ func dataUserGroup() *schema.Resource {
} }
func dataUserGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func dataUserGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
name := d.Get("name").(string) name := d.Get("name").(string)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
groups, err := c.c.ListUserGroup(ctx, site) groups, err := c.ListUserGroup(ctx, site)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"testing" "testing"

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"context" "context"

View File

@@ -0,0 +1,20 @@
package v1
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"net"
)
func cidrDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
_, oldNet, err := net.ParseCIDR(old)
if err != nil {
return false
}
_, newNet, err := net.ParseCIDR(new)
if err != nil {
return false
}
return oldNet.String() == newNet.String()
}

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"regexp" "regexp"

View File

@@ -0,0 +1,165 @@
package v1
import (
"context"
"crypto/tls"
"errors"
"fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"net"
"net/http"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func init() {
schema.DescriptionKind = schema.StringMarkdown
schema.SchemaDescriptionBuilder = func(s *schema.Schema) string {
desc := s.Description
if s.Default != nil {
desc += fmt.Sprintf(" Defaults to `%v`.", s.Default)
}
if s.Deprecated != "" {
desc += " " + s.Deprecated
}
return strings.TrimSpace(desc)
}
}
func New(version string) func() *schema.Provider {
return func() *schema.Provider {
p := &schema.Provider{
Schema: map[string]*schema.Schema{
"username": {
Description: provider.ProviderUsernameDescription,
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_USERNAME", ""),
},
"password": {
Description: provider.ProviderPasswordDescription,
Type: schema.TypeString,
Optional: true,
Sensitive: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_PASSWORD", ""),
},
"api_key": {
Description: provider.ProviderAPIKeyDescription,
Type: schema.TypeString,
Optional: true,
Sensitive: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_API_KEY", ""),
},
"api_url": {
Description: provider.ProviderAPIURLDescription,
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_API", ""),
},
"site": {
Description: provider.ProviderSiteDescription,
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_SITE", "default"),
},
"allow_insecure": {
Description: provider.ProviderAllowInsecureDescription,
Type: schema.TypeBool,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("UNIFI_INSECURE", false),
},
},
DataSourcesMap: map[string]*schema.Resource{
"unifi_ap_group": dataAPGroup(),
"unifi_network": dataNetwork(),
"unifi_port_profile": dataPortProfile(),
"unifi_radius_profile": dataRADIUSProfile(),
"unifi_user_group": dataUserGroup(),
"unifi_user": dataUser(),
"unifi_account": dataAccount(),
},
ResourcesMap: map[string]*schema.Resource{
// TODO: "unifi_ap_group"
"unifi_device": resourceDevice(),
"unifi_dynamic_dns": resourceDynamicDNS(),
"unifi_firewall_group": resourceFirewallGroup(),
"unifi_firewall_rule": resourceFirewallRule(),
"unifi_network": resourceNetwork(),
"unifi_port_forward": resourcePortForward(),
"unifi_port_profile": resourcePortProfile(),
"unifi_radius_profile": resourceRadiusProfile(),
"unifi_site": resourceSite(),
"unifi_static_route": resourceStaticRoute(),
"unifi_user_group": resourceUserGroup(),
"unifi_user": resourceUser(),
"unifi_wlan": resourceWLAN(),
"unifi_account": resourceAccount(),
"unifi_setting_mgmt": resourceSettingMgmt(),
"unifi_setting_radius": resourceSettingRadius(),
"unifi_setting_usg": resourceSettingUsg(),
},
}
p.ConfigureContextFunc = configure(version, p)
return p
}
}
func createHTTPTransport(insecure bool, subsystem string) http.RoundTripper {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: insecure,
},
}
t := logging.NewSubsystemLoggingHTTPTransport(subsystem, transport)
return t
}
func configure(v string, p *schema.Provider) schema.ConfigureContextFunc {
return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
user := d.Get("username").(string)
pass := d.Get("password").(string)
apiKey := d.Get("api_key").(string)
if apiKey != "" && (user != "" || pass != "") {
return nil, diag.FromErr(errors.New("only one of `username`/`password` or `api_key` can be set"))
} else if apiKey == "" && (user == "" || pass == "") {
return nil, diag.FromErr(errors.New("either `username` and `password` or `api_key` must be set"))
}
baseURL := d.Get("api_url").(string)
site := d.Get("site").(string)
insecure := d.Get("allow_insecure").(bool)
c, err := provider.NewClient(&provider.ClientConfig{
Username: user,
Password: pass,
ApiKey: apiKey,
Url: baseURL,
Site: site,
HttpConfigurer: func() http.RoundTripper {
return createHTTPTransport(insecure, "unifi")
},
})
if err != nil {
return nil, diag.FromErr(err)
}
return c, nil
}
}

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"bytes" "bytes"
@@ -38,7 +38,7 @@ func TestMain(m *testing.M) {
} }
func runAcceptanceTests(m *testing.M) int { func runAcceptanceTests(m *testing.M) int {
dc, err := compose.NewDockerCompose("../../docker-compose.yaml") dc, err := compose.NewDockerCompose("../../../docker-compose.yaml")
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -1,8 +1,9 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -74,7 +75,7 @@ func resourceAccount() *schema.Resource {
} }
func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceAccountGetResourceData(d) req, err := resourceAccountGetResourceData(d)
if err != nil { if err != nil {
@@ -83,10 +84,10 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta int
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateAccount(ctx, site, req) resp, err := c.CreateAccount(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -97,11 +98,11 @@ func resourceAccountCreate(ctx context.Context, d *schema.ResourceData, meta int
} }
func resourceAccountUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceAccountUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req, err := resourceAccountGetResourceData(d) req, err := resourceAccountGetResourceData(d)
@@ -112,7 +113,7 @@ func resourceAccountUpdate(ctx context.Context, d *schema.ResourceData, meta int
req.ID = d.Id() req.ID = d.Id()
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateAccount(ctx, site, req) resp, err := c.UpdateAccount(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -121,16 +122,16 @@ func resourceAccountUpdate(ctx context.Context, d *schema.ResourceData, meta int
} }
func resourceAccountDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceAccountDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
//name := d.Get("name").(string) //name := d.Get("name").(string)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
id := d.Id() id := d.Id()
err := c.c.DeleteAccount(ctx, site, id) err := c.DeleteAccount(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }
@@ -138,16 +139,16 @@ func resourceAccountDelete(ctx context.Context, d *schema.ResourceData, meta int
} }
func resourceAccountRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceAccountRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetAccount(ctx, site, id) resp, err := c.GetAccount(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,9 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -138,11 +139,11 @@ func resourceDevice() *schema.Resource {
} }
func resourceDeviceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func resourceDeviceImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
if colons := strings.Count(id, ":"); colons == 1 || colons == 6 { if colons := strings.Count(id, ":"); colons == 1 || colons == 6 {
@@ -154,7 +155,7 @@ func resourceDeviceImport(ctx context.Context, d *schema.ResourceData, meta inte
if macAddressRegexp.MatchString(id) { if macAddressRegexp.MatchString(id) {
// look up id by mac // look up id by mac
mac := cleanMAC(id) mac := cleanMAC(id)
device, err := c.c.GetDeviceByMAC(ctx, site, mac) device, err := c.GetDeviceByMAC(ctx, site, mac)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -174,11 +175,11 @@ func resourceDeviceImport(ctx context.Context, d *schema.ResourceData, meta inte
} }
func resourceDeviceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDeviceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
@@ -187,7 +188,7 @@ func resourceDeviceCreate(ctx context.Context, d *schema.ResourceData, meta inte
} }
mac = cleanMAC(mac) mac = cleanMAC(mac)
device, err := c.c.GetDeviceByMAC(ctx, site, mac) device, err := c.GetDeviceByMAC(ctx, site, mac)
if device == nil { if device == nil {
return diag.Errorf("device not found using mac %q", mac) return diag.Errorf("device not found using mac %q", mac)
@@ -201,7 +202,7 @@ func resourceDeviceCreate(ctx context.Context, d *schema.ResourceData, meta inte
return diag.Errorf("Device must be adopted before it can be managed") return diag.Errorf("Device must be adopted before it can be managed")
} }
err := c.c.AdoptDevice(ctx, site, mac) err := c.AdoptDevice(ctx, site, mac)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -217,11 +218,11 @@ func resourceDeviceCreate(ctx context.Context, d *schema.ResourceData, meta inte
} }
func resourceDeviceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDeviceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req, err := resourceDeviceGetResourceData(d) req, err := resourceDeviceGetResourceData(d)
@@ -232,7 +233,7 @@ func resourceDeviceUpdate(ctx context.Context, d *schema.ResourceData, meta inte
req.ID = d.Id() req.ID = d.Id()
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateDevice(ctx, site, req) resp, err := c.UpdateDevice(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -246,7 +247,7 @@ func resourceDeviceUpdate(ctx context.Context, d *schema.ResourceData, meta inte
} }
func resourceDeviceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDeviceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
if !d.Get("forget_on_destroy").(bool) { if !d.Get("forget_on_destroy").(bool) {
return nil return nil
@@ -256,10 +257,10 @@ func resourceDeviceDelete(ctx context.Context, d *schema.ResourceData, meta inte
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.ForgetDevice(ctx, site, mac) err := c.ForgetDevice(ctx, site, mac)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -273,16 +274,16 @@ func resourceDeviceDelete(ctx context.Context, d *schema.ResourceData, meta inte
} }
func resourceDeviceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDeviceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetDevice(ctx, site, id) resp, err := c.GetDevice(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -388,13 +389,13 @@ func fromPortOverride(po unifi.DevicePortOverrides) (map[string]interface{}, err
} }
func waitForDeviceState(ctx context.Context, d *schema.ResourceData, meta interface{}, targetState unifi.DeviceState, pendingStates []unifi.DeviceState, timeout time.Duration) (*unifi.Device, error) { func waitForDeviceState(ctx context.Context, d *schema.ResourceData, meta interface{}, targetState unifi.DeviceState, pendingStates []unifi.DeviceState, timeout time.Duration) (*unifi.Device, error) {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
// Always consider unknown to be a pending state. // Always consider unknown to be a pending state.
@@ -409,7 +410,7 @@ func waitForDeviceState(ctx context.Context, d *schema.ResourceData, meta interf
Pending: pending, Pending: pending,
Target: []string{targetState.String()}, Target: []string{targetState.String()},
Refresh: func() (interface{}, string, error) { Refresh: func() (interface{}, string, error) {
device, err := c.c.GetDeviceByMAC(ctx, site, mac) device, err := c.GetDeviceByMAC(ctx, site, mac)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
err = nil err = nil

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"context" "context"

View File

@@ -1,8 +1,9 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -75,7 +76,7 @@ func resourceDynamicDNS() *schema.Resource {
} }
func resourceDynamicDNSCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDynamicDNSCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceDynamicDNSGetResourceData(d) req, err := resourceDynamicDNSGetResourceData(d)
if err != nil { if err != nil {
@@ -84,10 +85,10 @@ func resourceDynamicDNSCreate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateDynamicDNS(ctx, site, req) resp, err := c.CreateDynamicDNS(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -126,16 +127,16 @@ func resourceDynamicDNSSetResourceData(resp *unifi.DynamicDNS, d *schema.Resourc
} }
func resourceDynamicDNSRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDynamicDNSRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetDynamicDNS(ctx, site, id) resp, err := c.GetDynamicDNS(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -148,7 +149,7 @@ func resourceDynamicDNSRead(ctx context.Context, d *schema.ResourceData, meta in
} }
func resourceDynamicDNSUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDynamicDNSUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceDynamicDNSGetResourceData(d) req, err := resourceDynamicDNSGetResourceData(d)
if err != nil { if err != nil {
@@ -159,11 +160,11 @@ func resourceDynamicDNSUpdate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateDynamicDNS(ctx, site, req) resp, err := c.UpdateDynamicDNS(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -172,15 +173,15 @@ func resourceDynamicDNSUpdate(ctx context.Context, d *schema.ResourceData, meta
} }
func resourceDynamicDNSDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceDynamicDNSDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeleteDynamicDNS(ctx, site, id) err := c.DeleteDynamicDNS(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"testing" "testing"

View File

@@ -1,8 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -57,7 +59,7 @@ func resourceFirewallGroup() *schema.Resource {
} }
func resourceFirewallGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceFirewallGroupGetResourceData(d) req, err := resourceFirewallGroupGetResourceData(d)
if err != nil { if err != nil {
@@ -66,12 +68,12 @@ func resourceFirewallGroupCreate(ctx context.Context, d *schema.ResourceData, me
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateFirewallGroup(ctx, site, req) resp, err := c.CreateFirewallGroup(ctx, site, req)
if err != nil { if err != nil {
if IsServerErrorContains(err, "api.err.FirewallGroupExisted") { if provider.IsServerErrorContains(err, "api.err.FirewallGroupExisted") {
return diag.Errorf("firewall groups must have unique names: %s", err) return diag.Errorf("firewall groups must have unique names: %s", err)
} }
return diag.FromErr(err) return diag.FromErr(err)
@@ -83,7 +85,7 @@ func resourceFirewallGroupCreate(ctx context.Context, d *schema.ResourceData, me
} }
func resourceFirewallGroupGetResourceData(d *schema.ResourceData) (*unifi.FirewallGroup, error) { func resourceFirewallGroupGetResourceData(d *schema.ResourceData) (*unifi.FirewallGroup, error) {
members, err := setToStringSlice(d.Get("members").(*schema.Set)) members, err := utils.SetToStringSlice(d.Get("members").(*schema.Set))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -99,22 +101,22 @@ func resourceFirewallGroupSetResourceData(resp *unifi.FirewallGroup, d *schema.R
d.Set("site", site) d.Set("site", site)
d.Set("name", resp.Name) d.Set("name", resp.Name)
d.Set("type", resp.GroupType) d.Set("type", resp.GroupType)
d.Set("members", stringSliceToSet(resp.GroupMembers)) d.Set("members", utils.StringSliceToSet(resp.GroupMembers))
return nil return nil
} }
func resourceFirewallGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetFirewallGroup(ctx, site, id) resp, err := c.GetFirewallGroup(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -127,7 +129,7 @@ func resourceFirewallGroupRead(ctx context.Context, d *schema.ResourceData, meta
} }
func resourceFirewallGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceFirewallGroupGetResourceData(d) req, err := resourceFirewallGroupGetResourceData(d)
if err != nil { if err != nil {
@@ -138,11 +140,11 @@ func resourceFirewallGroupUpdate(ctx context.Context, d *schema.ResourceData, me
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateFirewallGroup(ctx, site, req) resp, err := c.UpdateFirewallGroup(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -151,16 +153,16 @@ func resourceFirewallGroupUpdate(ctx context.Context, d *schema.ResourceData, me
} }
func resourceFirewallGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeleteFirewallGroup(ctx, site, id) err := c.DeleteFirewallGroup(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,8 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"regexp" "regexp"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
@@ -210,7 +212,7 @@ func resourceFirewallRule() *schema.Resource {
} }
func resourceFirewallRuleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallRuleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceFirewallRuleGetResourceData(d) req, err := resourceFirewallRuleGetResourceData(d)
if err != nil { if err != nil {
@@ -219,12 +221,12 @@ func resourceFirewallRuleCreate(ctx context.Context, d *schema.ResourceData, met
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateFirewallRule(ctx, site, req) resp, err := c.CreateFirewallRule(ctx, site, req)
if err != nil { if err != nil {
if IsServerErrorContains(err, "api.err.FirewallGroupTypeExists") { if provider.IsServerErrorContains(err, "api.err.FirewallGroupTypeExists") {
return diag.Errorf("firewall rule groups must be of different group types (ie. a port group and address group): %s", err) return diag.Errorf("firewall rule groups must be of different group types (ie. a port group and address group): %s", err)
} }
@@ -237,12 +239,12 @@ func resourceFirewallRuleCreate(ctx context.Context, d *schema.ResourceData, met
} }
func resourceFirewallRuleGetResourceData(d *schema.ResourceData) (*unifi.FirewallRule, error) { func resourceFirewallRuleGetResourceData(d *schema.ResourceData) (*unifi.FirewallRule, error) {
srcFirewallGroupIDs, err := setToStringSlice(d.Get("src_firewall_group_ids").(*schema.Set)) srcFirewallGroupIDs, err := utils.SetToStringSlice(d.Get("src_firewall_group_ids").(*schema.Set))
if err != nil { if err != nil {
return nil, err return nil, err
} }
dstFirewallGroupIDs, err := setToStringSlice(d.Get("dst_firewall_group_ids").(*schema.Set)) dstFirewallGroupIDs, err := utils.SetToStringSlice(d.Get("dst_firewall_group_ids").(*schema.Set))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -300,7 +302,7 @@ func resourceFirewallRuleSetResourceData(resp *unifi.FirewallRule, d *schema.Res
d.Set("state_related", resp.StateRelated) d.Set("state_related", resp.StateRelated)
d.Set("src_network_type", resp.SrcNetworkType) d.Set("src_network_type", resp.SrcNetworkType)
d.Set("src_firewall_group_ids", stringSliceToSet(resp.SrcFirewallGroupIDs)) d.Set("src_firewall_group_ids", utils.StringSliceToSet(resp.SrcFirewallGroupIDs))
d.Set("src_mac", resp.SrcMACAddress) d.Set("src_mac", resp.SrcMACAddress)
d.Set("src_address", resp.SrcAddress) d.Set("src_address", resp.SrcAddress)
d.Set("src_address_ipv6", resp.SrcAddressIPV6) d.Set("src_address_ipv6", resp.SrcAddressIPV6)
@@ -308,7 +310,7 @@ func resourceFirewallRuleSetResourceData(resp *unifi.FirewallRule, d *schema.Res
d.Set("src_port", resp.SrcPort) d.Set("src_port", resp.SrcPort)
d.Set("dst_network_type", resp.DstNetworkType) d.Set("dst_network_type", resp.DstNetworkType)
d.Set("dst_firewall_group_ids", stringSliceToSet(resp.DstFirewallGroupIDs)) d.Set("dst_firewall_group_ids", utils.StringSliceToSet(resp.DstFirewallGroupIDs))
d.Set("dst_address", resp.DstAddress) d.Set("dst_address", resp.DstAddress)
d.Set("dst_address_ipv6", resp.DstAddressIPV6) d.Set("dst_address_ipv6", resp.DstAddressIPV6)
d.Set("dst_network_id", resp.DstNetworkID) d.Set("dst_network_id", resp.DstNetworkID)
@@ -318,16 +320,16 @@ func resourceFirewallRuleSetResourceData(resp *unifi.FirewallRule, d *schema.Res
} }
func resourceFirewallRuleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallRuleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetFirewallRule(ctx, site, id) resp, err := c.GetFirewallRule(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -340,7 +342,7 @@ func resourceFirewallRuleRead(ctx context.Context, d *schema.ResourceData, meta
} }
func resourceFirewallRuleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallRuleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceFirewallRuleGetResourceData(d) req, err := resourceFirewallRuleGetResourceData(d)
if err != nil { if err != nil {
@@ -351,11 +353,11 @@ func resourceFirewallRuleUpdate(ctx context.Context, d *schema.ResourceData, met
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateFirewallRule(ctx, site, req) resp, err := c.UpdateFirewallRule(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -364,15 +366,15 @@ func resourceFirewallRuleUpdate(ctx context.Context, d *schema.ResourceData, met
} }
func resourceFirewallRuleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceFirewallRuleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeleteFirewallRule(ctx, site, id) err := c.DeleteFirewallRule(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,9 +1,11 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"regexp" "regexp"
"strings" "strings"
@@ -90,7 +92,7 @@ func resourceNetwork() *schema.Resource {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
DiffSuppressFunc: cidrDiffSuppress, DiffSuppressFunc: cidrDiffSuppress,
ValidateFunc: cidrValidate, ValidateFunc: utils.CidrValidate,
}, },
"network_group": { "network_group": {
Description: "The group of the network.", Description: "The group of the network.",
@@ -382,7 +384,7 @@ func resourceNetwork() *schema.Resource {
} }
func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceNetworkGetResourceData(d, meta) req, err := resourceNetworkGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -391,10 +393,10 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta int
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateNetwork(ctx, site, req) resp, err := c.CreateNetwork(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -405,18 +407,18 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, meta int
} }
func resourceNetworkGetResourceData(d *schema.ResourceData, meta interface{}) (*unifi.Network, error) { func resourceNetworkGetResourceData(d *schema.ResourceData, meta interface{}) (*unifi.Network, error) {
// c := meta.(*client) // c := meta.(*provider.Client)
vlan := d.Get("vlan_id").(int) vlan := d.Get("vlan_id").(int)
dhcpDNS, err := listToStringSlice(d.Get("dhcp_dns").([]interface{})) dhcpDNS, err := utils.ListToStringSlice(d.Get("dhcp_dns").([]interface{}))
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to convert dhcp_dns to string slice: %w", err) return nil, fmt.Errorf("unable to convert dhcp_dns to string slice: %w", err)
} }
dhcpV6DNS, err := listToStringSlice(d.Get("dhcp_v6_dns").([]interface{})) dhcpV6DNS, err := utils.ListToStringSlice(d.Get("dhcp_v6_dns").([]interface{}))
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to convert dhcp_v6_dns to string slice: %w", err) return nil, fmt.Errorf("unable to convert dhcp_v6_dns to string slice: %w", err)
} }
wanDNS, err := listToStringSlice(d.Get("wan_dns").([]interface{})) wanDNS, err := utils.ListToStringSlice(d.Get("wan_dns").([]interface{}))
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to convert wan_dns to string slice: %w", err) return nil, fmt.Errorf("unable to convert wan_dns to string slice: %w", err)
} }
@@ -425,7 +427,7 @@ func resourceNetworkGetResourceData(d *schema.ResourceData, meta interface{}) (*
Name: d.Get("name").(string), Name: d.Get("name").(string),
Purpose: d.Get("purpose").(string), Purpose: d.Get("purpose").(string),
VLAN: vlan, VLAN: vlan,
IPSubnet: cidrOneBased(d.Get("subnet").(string)), IPSubnet: utils.CidrOneBased(d.Get("subnet").(string)),
NetworkGroup: d.Get("network_group").(string), NetworkGroup: d.Get("network_group").(string),
DHCPDStart: d.Get("dhcp_start").(string), DHCPDStart: d.Get("dhcp_start").(string),
DHCPDStop: d.Get("dhcp_stop").(string), DHCPDStop: d.Get("dhcp_stop").(string),
@@ -571,7 +573,7 @@ func resourceNetworkSetResourceData(resp *unifi.Network, d *schema.ResourceData,
d.Set("name", resp.Name) d.Set("name", resp.Name)
d.Set("purpose", resp.Purpose) d.Set("purpose", resp.Purpose)
d.Set("vlan_id", vlan) d.Set("vlan_id", vlan)
d.Set("subnet", cidrZeroBased(resp.IPSubnet)) d.Set("subnet", utils.CidrZeroBased(resp.IPSubnet))
d.Set("network_group", resp.NetworkGroup) d.Set("network_group", resp.NetworkGroup)
d.Set("dhcp_dns", dhcpDNS) d.Set("dhcp_dns", dhcpDNS)
@@ -624,16 +626,16 @@ func resourceNetworkSetResourceData(resp *unifi.Network, d *schema.ResourceData,
} }
func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetNetwork(ctx, site, id) resp, err := c.GetNetwork(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -646,7 +648,7 @@ func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, meta inter
} }
func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceNetworkGetResourceData(d, meta) req, err := resourceNetworkGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -656,11 +658,11 @@ func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta int
req.ID = d.Id() req.ID = d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateNetwork(ctx, site, req) resp, err := c.UpdateNetwork(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -669,15 +671,15 @@ func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta int
} }
func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
id := d.Id() id := d.Id()
err := c.c.DeleteNetwork(ctx, site, id) err := c.DeleteNetwork(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }
@@ -685,11 +687,11 @@ func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, meta int
} }
func importNetwork(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func importNetwork(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
if strings.Contains(id, ":") { if strings.Contains(id, ":") {
@@ -701,7 +703,7 @@ func importNetwork(ctx context.Context, d *schema.ResourceData, meta interface{}
if strings.HasPrefix(id, "name=") { if strings.HasPrefix(id, "name=") {
targetName := strings.TrimPrefix(id, "name=") targetName := strings.TrimPrefix(id, "name=")
var err error var err error
if id, err = getNetworkIDByName(ctx, c.c, targetName, site); err != nil { if id, err = getNetworkIDByName(ctx, c.Client, targetName, site); err != nil {
return nil, err return nil, err
} }
} }

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"net" "net"
"regexp" "regexp"
"strconv" "strconv"
@@ -412,7 +413,7 @@ func TestAccNetwork_mdns(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{ resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { PreCheck: func() {
preCheck(t) preCheck(t)
preCheckMinVersion(t, controllerV7) preCheckMinVersion(t, provider.ControllerV7)
}, },
ProviderFactories: providerFactories, ProviderFactories: providerFactories,
// TODO: CheckDestroy: , // TODO: CheckDestroy: ,

View File

@@ -1,8 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -94,7 +96,7 @@ func resourcePortForward() *schema.Resource {
ValidateFunc: validation.Any( ValidateFunc: validation.Any(
validation.StringInSlice([]string{"any"}, false), validation.StringInSlice([]string{"any"}, false),
validation.IsIPv4Address, validation.IsIPv4Address,
cidrValidate, utils.CidrValidate,
), ),
}, },
}, },
@@ -102,7 +104,7 @@ func resourcePortForward() *schema.Resource {
} }
func resourcePortForwardCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortForwardCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourcePortForwardGetResourceData(d) req, err := resourcePortForwardGetResourceData(d)
if err != nil { if err != nil {
@@ -111,9 +113,9 @@ func resourcePortForwardCreate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreatePortForward(ctx, site, req) resp, err := c.CreatePortForward(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -153,15 +155,15 @@ func resourcePortForwardSetResourceData(resp *unifi.PortForward, d *schema.Resou
} }
func resourcePortForwardRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortForwardRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetPortForward(ctx, site, id) resp, err := c.GetPortForward(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -174,7 +176,7 @@ func resourcePortForwardRead(ctx context.Context, d *schema.ResourceData, meta i
} }
func resourcePortForwardUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortForwardUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourcePortForwardGetResourceData(d) req, err := resourcePortForwardGetResourceData(d)
if err != nil { if err != nil {
@@ -185,11 +187,11 @@ func resourcePortForwardUpdate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdatePortForward(ctx, site, req) resp, err := c.UpdatePortForward(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -198,15 +200,15 @@ func resourcePortForwardUpdate(ctx context.Context, d *schema.ResourceData, meta
} }
func resourcePortForwardDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortForwardDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeletePortForward(ctx, site, id) err := c.DeletePortForward(ctx, site, id)
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,8 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -256,7 +258,7 @@ func resourcePortProfile() *schema.Resource {
} }
func resourcePortProfileCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortProfileCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourcePortProfileGetResourceData(d) req, err := resourcePortProfileGetResourceData(d)
if err != nil { if err != nil {
@@ -265,9 +267,9 @@ func resourcePortProfileCreate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreatePortProfile(ctx, site, req) resp, err := c.CreatePortProfile(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -278,12 +280,12 @@ func resourcePortProfileCreate(ctx context.Context, d *schema.ResourceData, meta
} }
func resourcePortProfileGetResourceData(d *schema.ResourceData) (*unifi.PortProfile, error) { func resourcePortProfileGetResourceData(d *schema.ResourceData) (*unifi.PortProfile, error) {
portSecurityMacAddress, err := setToStringSlice(d.Get("port_security_mac_address").(*schema.Set)) portSecurityMacAddress, err := utils.SetToStringSlice(d.Get("port_security_mac_address").(*schema.Set))
if err != nil { if err != nil {
return nil, err return nil, err
} }
excludedNetworkIDs, err := setToStringSlice(d.Get("excluded_network_ids").(*schema.Set)) excludedNetworkIDs, err := utils.SetToStringSlice(d.Get("excluded_network_ids").(*schema.Set))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -334,7 +336,7 @@ func resourcePortProfileSetResourceData(resp *unifi.PortProfile, d *schema.Resou
d.Set("dot1x_idle_timeout", resp.Dot1XIDleTimeout) d.Set("dot1x_idle_timeout", resp.Dot1XIDleTimeout)
d.Set("egress_rate_limit_kbps", resp.EgressRateLimitKbps) d.Set("egress_rate_limit_kbps", resp.EgressRateLimitKbps)
d.Set("egress_rate_limit_kbps_enabled", resp.EgressRateLimitKbpsEnabled) d.Set("egress_rate_limit_kbps_enabled", resp.EgressRateLimitKbpsEnabled)
d.Set("excluded_network_ids", stringSliceToSet(resp.ExcludedNetworkIDs)) d.Set("excluded_network_ids", utils.StringSliceToSet(resp.ExcludedNetworkIDs))
d.Set("forward", resp.Forward) d.Set("forward", resp.Forward)
d.Set("full_duplex", resp.FullDuplex) d.Set("full_duplex", resp.FullDuplex)
d.Set("isolation", resp.Isolation) d.Set("isolation", resp.Isolation)
@@ -345,7 +347,7 @@ func resourcePortProfileSetResourceData(resp *unifi.PortProfile, d *schema.Resou
d.Set("op_mode", resp.OpMode) d.Set("op_mode", resp.OpMode)
d.Set("poe_mode", resp.PoeMode) d.Set("poe_mode", resp.PoeMode)
d.Set("port_security_enabled", resp.PortSecurityEnabled) d.Set("port_security_enabled", resp.PortSecurityEnabled)
d.Set("port_security_mac_address", stringSliceToSet(resp.PortSecurityMACAddress)) d.Set("port_security_mac_address", utils.StringSliceToSet(resp.PortSecurityMACAddress))
d.Set("priority_queue1_level", resp.PriorityQueue1Level) d.Set("priority_queue1_level", resp.PriorityQueue1Level)
d.Set("priority_queue2_level", resp.PriorityQueue2Level) d.Set("priority_queue2_level", resp.PriorityQueue2Level)
d.Set("priority_queue3_level", resp.PriorityQueue3Level) d.Set("priority_queue3_level", resp.PriorityQueue3Level)
@@ -369,15 +371,15 @@ func resourcePortProfileSetResourceData(resp *unifi.PortProfile, d *schema.Resou
} }
func resourcePortProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetPortProfile(ctx, site, id) resp, err := c.GetPortProfile(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -390,7 +392,7 @@ func resourcePortProfileRead(ctx context.Context, d *schema.ResourceData, meta i
} }
func resourcePortProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourcePortProfileGetResourceData(d) req, err := resourcePortProfileGetResourceData(d)
if err != nil { if err != nil {
@@ -401,11 +403,11 @@ func resourcePortProfileUpdate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdatePortProfile(ctx, site, req) resp, err := c.UpdatePortProfile(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -414,15 +416,15 @@ func resourcePortProfileUpdate(ctx context.Context, d *schema.ResourceData, meta
} }
func resourcePortProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourcePortProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeletePortProfile(ctx, site, id) err := c.DeletePortProfile(ctx, site, id)
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,9 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"strings" "strings"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
@@ -234,7 +235,7 @@ func fromAcctServer(sshKey unifi.RADIUSProfileAcctServers) (map[string]interface
} }
func resourceRadiusProfileCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceRadiusProfileCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceRadiusProfileGetResourceData(d) req, err := resourceRadiusProfileGetResourceData(d)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
@@ -242,9 +243,9 @@ func resourceRadiusProfileCreate(ctx context.Context, d *schema.ResourceData, me
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateRADIUSProfile(ctx, site, req) resp, err := c.CreateRADIUSProfile(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -302,15 +303,15 @@ func resourceRadiusProfileSetResourceData(resp *unifi.RADIUSProfile, d *schema.R
} }
func resourceRadiusProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceRadiusProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetRADIUSProfile(ctx, site, id) resp, err := c.GetRADIUSProfile(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -323,7 +324,7 @@ func resourceRadiusProfileRead(ctx context.Context, d *schema.ResourceData, meta
} }
func resourceRadiusProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceRadiusProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceRadiusProfileGetResourceData(d) req, err := resourceRadiusProfileGetResourceData(d)
if err != nil { if err != nil {
@@ -334,11 +335,11 @@ func resourceRadiusProfileUpdate(ctx context.Context, d *schema.ResourceData, me
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateRADIUSProfile(ctx, site, req) resp, err := c.UpdateRADIUSProfile(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -347,25 +348,25 @@ func resourceRadiusProfileUpdate(ctx context.Context, d *schema.ResourceData, me
} }
func resourceRadiusProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceRadiusProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeleteRADIUSProfile(ctx, site, id) err := c.DeleteRADIUSProfile(ctx, site, id)
return diag.FromErr(err) return diag.FromErr(err)
} }
func importRadiusProfile(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func importRadiusProfile(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
if strings.Contains(id, ":") { if strings.Contains(id, ":") {
@@ -377,7 +378,7 @@ func importRadiusProfile(ctx context.Context, d *schema.ResourceData, meta inter
if strings.HasPrefix(id, "name=") { if strings.HasPrefix(id, "name=") {
targetName := strings.TrimPrefix(id, "name=") targetName := strings.TrimPrefix(id, "name=")
var err error var err error
if id, err = getRadiusProfileIDByName(ctx, c.c, targetName, site); err != nil { if id, err = getRadiusProfileIDByName(ctx, c.Client, targetName, site); err != nil {
return nil, err return nil, err
} }
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,9 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -141,7 +142,7 @@ func resourceSettingMgmtGetResourceData(d *schema.ResourceData, meta interface{}
} }
func resourceSettingMgmtCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingMgmtCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceSettingMgmtGetResourceData(d, meta) req, err := resourceSettingMgmtGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -150,10 +151,10 @@ func resourceSettingMgmtCreate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.UpdateSettingMgmt(ctx, site, req) resp, err := c.UpdateSettingMgmt(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -177,14 +178,14 @@ func resourceSettingMgmtSetResourceData(resp *unifi.SettingMgmt, d *schema.Resou
} }
func resourceSettingMgmtRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingMgmtRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetSettingMgmt(ctx, site) resp, err := c.GetSettingMgmt(ctx, site)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -197,7 +198,7 @@ func resourceSettingMgmtRead(ctx context.Context, d *schema.ResourceData, meta i
} }
func resourceSettingMgmtUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingMgmtUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceSettingMgmtGetResourceData(d, meta) req, err := resourceSettingMgmtGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -207,10 +208,10 @@ func resourceSettingMgmtUpdate(ctx context.Context, d *schema.ResourceData, meta
req.ID = d.Id() req.ID = d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.UpdateSettingMgmt(ctx, site, req) resp, err := c.UpdateSettingMgmt(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"sync" "sync"

View File

@@ -1,8 +1,9 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -98,7 +99,7 @@ func resourceSettingRadiusGetResourceData(d *schema.ResourceData, meta interface
} }
func resourceSettingRadiusCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingRadiusCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceSettingRadiusGetResourceData(d, meta) req, err := resourceSettingRadiusGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -107,10 +108,10 @@ func resourceSettingRadiusCreate(ctx context.Context, d *schema.ResourceData, me
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.UpdateSettingRadius(ctx, site, req) resp, err := c.UpdateSettingRadius(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -133,14 +134,14 @@ func resourceSettingRadiusSetResourceData(resp *unifi.SettingRadius, d *schema.R
} }
func resourceSettingRadiusRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingRadiusRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetSettingRadius(ctx, site) resp, err := c.GetSettingRadius(ctx, site)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -153,7 +154,7 @@ func resourceSettingRadiusRead(ctx context.Context, d *schema.ResourceData, meta
} }
func resourceSettingRadiusUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingRadiusUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceSettingRadiusGetResourceData(d, meta) req, err := resourceSettingRadiusGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -163,10 +164,10 @@ func resourceSettingRadiusUpdate(ctx context.Context, d *schema.ResourceData, me
req.ID = d.Id() req.ID = d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.UpdateSettingRadius(ctx, site, req) resp, err := c.UpdateSettingRadius(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"sync" "sync"

View File

@@ -1,9 +1,11 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"sync" "sync"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
@@ -73,18 +75,18 @@ func resourceSettingUsg() *schema.Resource {
} }
func resourceSettingUsgUpdateResourceData(d *schema.ResourceData, meta interface{}, setting *unifi.SettingUsg) error { func resourceSettingUsgUpdateResourceData(d *schema.ResourceData, meta interface{}, setting *unifi.SettingUsg) error {
c := meta.(*client) c := meta.(*provider.Client)
//nolint // GetOkExists is deprecated, but using here: //nolint // GetOkExists is deprecated, but using here:
if mdns, hasMdns := d.GetOkExists("multicast_dns_enabled"); hasMdns { if mdns, hasMdns := d.GetOkExists("multicast_dns_enabled"); hasMdns {
if c.IsControllerV7() { if c.IsControllerV7() {
return fmt.Errorf("multicast_dns_enabled is not supported on controller version %v", c.version) return fmt.Errorf("multicast_dns_enabled is not supported on controller version %v", c.Version)
} }
setting.MdnsEnabled = mdns.(bool) setting.MdnsEnabled = mdns.(bool)
} }
dhcpRelay, err := listToStringSlice(d.Get("dhcp_relay_servers").([]interface{})) dhcpRelay, err := utils.ListToStringSlice(d.Get("dhcp_relay_servers").([]interface{}))
if err != nil { if err != nil {
return fmt.Errorf("unable to convert dhcp_relay_servers to string slice: %w", err) return fmt.Errorf("unable to convert dhcp_relay_servers to string slice: %w", err)
} }
@@ -98,14 +100,14 @@ func resourceSettingUsgUpdateResourceData(d *schema.ResourceData, meta interface
} }
func resourceSettingUsgUpsert(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingUsgUpsert(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req, err := c.c.GetSettingUsg(ctx, c.site) req, err := c.GetSettingUsg(ctx, c.Site)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -115,7 +117,7 @@ func resourceSettingUsgUpsert(ctx context.Context, d *schema.ResourceData, meta
return diag.FromErr(err) return diag.FromErr(err)
} }
resp, err := c.c.UpdateSettingUsg(ctx, site, req) resp, err := c.UpdateSettingUsg(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -147,14 +149,14 @@ func resourceSettingUsgSetResourceData(resp *unifi.SettingUsg, d *schema.Resourc
} }
func resourceSettingUsgRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSettingUsgRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetSettingUsg(ctx, site) resp, err := c.GetSettingUsg(ctx, site)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,9 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -43,10 +44,10 @@ func resourceSite() *schema.Resource {
} }
func resourceSiteImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { func resourceSiteImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
_, err := c.c.GetSite(ctx, id) _, err := c.GetSite(ctx, id)
if err != nil { if err != nil {
if !errors.Is(err, unifi.ErrNotFound) { if !errors.Is(err, unifi.ErrNotFound) {
return nil, err return nil, err
@@ -57,7 +58,7 @@ func resourceSiteImport(ctx context.Context, d *schema.ResourceData, meta interf
} }
// lookup site by name // lookup site by name
sites, err := c.c.ListSites(ctx) sites, err := c.ListSites(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -73,11 +74,11 @@ func resourceSiteImport(ctx context.Context, d *schema.ResourceData, meta interf
} }
func resourceSiteCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSiteCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
description := d.Get("description").(string) description := d.Get("description").(string)
resp, err := c.c.CreateSite(ctx, description) resp, err := c.CreateSite(ctx, description)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -95,11 +96,11 @@ func resourceSiteSetResourceData(resp *unifi.Site, d *schema.ResourceData) diag.
} }
func resourceSiteRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSiteRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site, err := c.c.GetSite(ctx, id) site, err := c.GetSite(ctx, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -112,7 +113,7 @@ func resourceSiteRead(ctx context.Context, d *schema.ResourceData, meta interfac
} }
func resourceSiteUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSiteUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := &unifi.Site{ site := &unifi.Site{
ID: d.Id(), ID: d.Id(),
@@ -120,7 +121,7 @@ func resourceSiteUpdate(ctx context.Context, d *schema.ResourceData, meta interf
Description: d.Get("description").(string), Description: d.Get("description").(string),
} }
resp, err := c.c.UpdateSite(ctx, site.Name, site.Description) resp, err := c.UpdateSite(ctx, site.Name, site.Description)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -129,8 +130,8 @@ func resourceSiteUpdate(ctx context.Context, d *schema.ResourceData, meta interf
} }
func resourceSiteDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceSiteDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
_, err := c.c.DeleteSite(ctx, id) _, err := c.DeleteSite(ctx, id)
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"context" "context"

View File

@@ -1,9 +1,11 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -46,7 +48,7 @@ func resourceStaticRoute() *schema.Resource {
Description: "The network subnet address.", Description: "The network subnet address.",
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
ValidateFunc: cidrValidate, ValidateFunc: utils.CidrValidate,
DiffSuppressFunc: cidrDiffSuppress, DiffSuppressFunc: cidrDiffSuppress,
}, },
"type": { "type": {
@@ -77,7 +79,7 @@ func resourceStaticRoute() *schema.Resource {
} }
func resourceStaticRouteCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceStaticRouteCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceStaticRouteGetResourceData(d) req, err := resourceStaticRouteGetResourceData(d)
if err != nil { if err != nil {
@@ -86,10 +88,10 @@ func resourceStaticRouteCreate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateRouting(ctx, site, req) resp, err := c.CreateRouting(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -107,7 +109,7 @@ func resourceStaticRouteGetResourceData(d *schema.ResourceData) (*unifi.Routing,
Type: "static-route", Type: "static-route",
Name: d.Get("name").(string), Name: d.Get("name").(string),
StaticRouteNetwork: cidrZeroBased(d.Get("network").(string)), StaticRouteNetwork: utils.CidrZeroBased(d.Get("network").(string)),
StaticRouteDistance: d.Get("distance").(int), StaticRouteDistance: d.Get("distance").(int),
StaticRouteType: t, StaticRouteType: t,
} }
@@ -128,7 +130,7 @@ func resourceStaticRouteGetResourceData(d *schema.ResourceData) (*unifi.Routing,
func resourceStaticRouteSetResourceData(resp *unifi.Routing, d *schema.ResourceData, site string) diag.Diagnostics { func resourceStaticRouteSetResourceData(resp *unifi.Routing, d *schema.ResourceData, site string) diag.Diagnostics {
d.Set("site", site) d.Set("site", site)
d.Set("name", resp.Name) d.Set("name", resp.Name)
d.Set("network", cidrZeroBased(resp.StaticRouteNetwork)) d.Set("network", utils.CidrZeroBased(resp.StaticRouteNetwork))
d.Set("distance", resp.StaticRouteDistance) d.Set("distance", resp.StaticRouteDistance)
t := resp.StaticRouteType t := resp.StaticRouteType
@@ -152,16 +154,16 @@ func resourceStaticRouteSetResourceData(resp *unifi.Routing, d *schema.ResourceD
} }
func resourceStaticRouteRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceStaticRouteRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetRouting(ctx, site, id) resp, err := c.GetRouting(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -174,7 +176,7 @@ func resourceStaticRouteRead(ctx context.Context, d *schema.ResourceData, meta i
} }
func resourceStaticRouteUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceStaticRouteUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceStaticRouteGetResourceData(d) req, err := resourceStaticRouteGetResourceData(d)
if err != nil { if err != nil {
@@ -185,11 +187,11 @@ func resourceStaticRouteUpdate(ctx context.Context, d *schema.ResourceData, meta
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateRouting(ctx, site, req) resp, err := c.UpdateRouting(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -198,15 +200,15 @@ func resourceStaticRouteUpdate(ctx context.Context, d *schema.ResourceData, meta
} }
func resourceStaticRouteDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceStaticRouteDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeleteRouting(ctx, site, id) err := c.DeleteRouting(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,9 +1,10 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -118,7 +119,7 @@ func resourceUser() *schema.Resource {
} }
func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceUserGetResourceData(d) req, err := resourceUserGetResourceData(d)
if err != nil { if err != nil {
@@ -129,18 +130,18 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateUser(ctx, site, req) resp, err := c.CreateUser(ctx, site, req)
if err != nil { if err != nil {
if !IsServerErrorContains(err, "api.err.MacUsed") || !allowExisting { if !provider.IsServerErrorContains(err, "api.err.MacUsed") || !allowExisting {
return diag.FromErr(err) return diag.FromErr(err)
} }
// mac in use, just absorb it // mac in use, just absorb it
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
existing, err := c.c.GetUserByMAC(ctx, site, mac) existing, err := c.GetUserByMAC(ctx, site, mac)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -148,7 +149,7 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
req.ID = existing.ID req.ID = existing.ID
req.SiteID = existing.SiteID req.SiteID = existing.SiteID
resp, err = c.c.UpdateUser(ctx, site, req) resp, err = c.UpdateUser(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -157,7 +158,7 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
d.SetId(resp.ID) d.SetId(resp.ID)
if d.Get("blocked").(bool) { if d.Get("blocked").(bool) {
err := c.c.BlockUserByMAC(ctx, site, d.Get("mac").(string)) err := c.BlockUserByMAC(ctx, site, d.Get("mac").(string))
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -167,7 +168,7 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, meta interf
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
device := d.Get("dev_id_override").(int) device := d.Get("dev_id_override").(int)
err := c.c.OverrideUserFingerprint(context.TODO(), site, mac, device) err := c.OverrideUserFingerprint(context.TODO(), site, mac, device)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -226,16 +227,16 @@ func resourceUserSetResourceData(resp *unifi.User, d *schema.ResourceData, site
} }
func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetUser(ctx, site, id) resp, err := c.GetUser(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -245,7 +246,7 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interfac
} }
// for some reason the IP address is only on this endpoint, so issue another request // for some reason the IP address is only on this endpoint, so issue another request
macResp, err := c.c.GetUserByMAC(ctx, site, resp.MAC) macResp, err := c.GetUserByMAC(ctx, site, resp.MAC)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -262,22 +263,22 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interfac
} }
func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
if d.HasChange("blocked") { if d.HasChange("blocked") {
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
if d.Get("blocked").(bool) { if d.Get("blocked").(bool) {
err := c.c.BlockUserByMAC(ctx, site, mac) err := c.BlockUserByMAC(ctx, site, mac)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
} else { } else {
err := c.c.UnblockUserByMAC(ctx, site, mac) err := c.UnblockUserByMAC(ctx, site, mac)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -288,7 +289,7 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interf
mac := d.Get("mac").(string) mac := d.Get("mac").(string)
device := d.Get("dev_id_override").(int) device := d.Get("dev_id_override").(int)
err := c.c.OverrideUserFingerprint(context.TODO(), site, mac, device) err := c.OverrideUserFingerprint(context.TODO(), site, mac, device)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -306,7 +307,7 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interf
req.ID = d.Id() req.ID = d.Id()
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateUser(ctx, site, req) resp, err := c.UpdateUser(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -315,7 +316,7 @@ func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, meta interf
} }
func resourceUserDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
@@ -325,11 +326,11 @@ func resourceUserDelete(ctx context.Context, d *schema.ResourceData, meta interf
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
// lookup MAC instead of trusting state // lookup MAC instead of trusting state
u, err := c.c.GetUser(ctx, site, id) u, err := c.GetUser(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }
@@ -337,6 +338,6 @@ func resourceUserDelete(ctx context.Context, d *schema.ResourceData, meta interf
return diag.FromErr(err) return diag.FromErr(err)
} }
err = c.c.DeleteUserByMAC(ctx, site, u.MAC) err = c.DeleteUserByMAC(ctx, site, u.MAC)
return diag.FromErr(err) return diag.FromErr(err)
} }

View File

@@ -1,8 +1,9 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -59,7 +60,7 @@ func resourceUserGroup() *schema.Resource {
} }
func resourceUserGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceUserGroupGetResourceData(d) req, err := resourceUserGroupGetResourceData(d)
if err != nil { if err != nil {
@@ -68,10 +69,10 @@ func resourceUserGroupCreate(ctx context.Context, d *schema.ResourceData, meta i
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateUserGroup(context.TODO(), site, req) resp, err := c.CreateUserGroup(context.TODO(), site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -98,16 +99,16 @@ func resourceUserGroupSetResourceData(resp *unifi.UserGroup, d *schema.ResourceD
} }
func resourceUserGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetUserGroup(context.TODO(), site, id) resp, err := c.GetUserGroup(context.TODO(), site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -120,7 +121,7 @@ func resourceUserGroupRead(ctx context.Context, d *schema.ResourceData, meta int
} }
func resourceUserGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceUserGroupGetResourceData(d) req, err := resourceUserGroupGetResourceData(d)
if err != nil { if err != nil {
@@ -131,11 +132,11 @@ func resourceUserGroupUpdate(ctx context.Context, d *schema.ResourceData, meta i
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateUserGroup(context.TODO(), site, req) resp, err := c.UpdateUserGroup(context.TODO(), site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -144,15 +145,15 @@ func resourceUserGroupUpdate(ctx context.Context, d *schema.ResourceData, meta i
} }
func resourceUserGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceUserGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeleteUserGroup(context.TODO(), site, id) err := c.DeleteUserGroup(context.TODO(), site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package provider package v1
import ( import (
"context" "context"

View File

@@ -1,9 +1,11 @@
package provider package v1
import ( import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/filipowm/go-unifi/unifi" "github.com/filipowm/go-unifi/unifi"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -203,7 +205,7 @@ func resourceWLAN() *schema.Resource {
"minimum_data_rate_2g_kbps": { "minimum_data_rate_2g_kbps": {
Description: "Set minimum data rate control for 2G devices, in Kbps. " + Description: "Set minimum data rate control for 2G devices, in Kbps. " +
"Use `0` to disable minimum data rates. " + "Use `0` to disable minimum data rates. " +
"Valid values are: " + markdownValueListInt(wlanValidMinimumDataRate2g) + ".", "Valid values are: " + utils.MarkdownValueListInt(wlanValidMinimumDataRate2g) + ".",
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
// TODO: this validation is from the UI, if other values work, perhaps remove this is set it to a range instead? // TODO: this validation is from the UI, if other values work, perhaps remove this is set it to a range instead?
@@ -212,7 +214,7 @@ func resourceWLAN() *schema.Resource {
"minimum_data_rate_5g_kbps": { "minimum_data_rate_5g_kbps": {
Description: "Set minimum data rate control for 5G devices, in Kbps. " + Description: "Set minimum data rate control for 5G devices, in Kbps. " +
"Use `0` to disable minimum data rates. " + "Use `0` to disable minimum data rates. " +
"Valid values are: " + markdownValueListInt(wlanValidMinimumDataRate5g) + ".", "Valid values are: " + utils.MarkdownValueListInt(wlanValidMinimumDataRate5g) + ".",
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
// TODO: this validation is from the UI, if other values work, perhaps remove this is set it to a range instead? // TODO: this validation is from the UI, if other values work, perhaps remove this is set it to a range instead?
@@ -243,7 +245,7 @@ func resourceWLAN() *schema.Resource {
} }
func resourceWLANGetResourceData(d *schema.ResourceData, meta interface{}) (*unifi.WLAN, error) { func resourceWLANGetResourceData(d *schema.ResourceData, meta interface{}) (*unifi.WLAN, error) {
c := meta.(*client) c := meta.(*provider.Client)
security := d.Get("security").(string) security := d.Get("security").(string)
passphrase := d.Get("passphrase").(string) passphrase := d.Get("passphrase").(string)
@@ -265,7 +267,7 @@ func resourceWLANGetResourceData(d *schema.ResourceData, meta interface{}) (*uni
} }
if !c.SupportsWPA3() { if !c.SupportsWPA3() {
if wpa3 || wpa3Transition { if wpa3 || wpa3Transition {
return nil, fmt.Errorf("WPA 3 support is not available on controller version %q, you must be on %q or higher", c.version, controllerVersionWPA3) return nil, fmt.Errorf("WPA 3 support is not available on controller version %q, you must be on %q or higher", c.Version, provider.ControllerVersionWPA3)
} }
} }
@@ -276,7 +278,7 @@ func resourceWLANGetResourceData(d *schema.ResourceData, meta interface{}) (*uni
} }
macFilterEnabled := d.Get("mac_filter_enabled").(bool) macFilterEnabled := d.Get("mac_filter_enabled").(bool)
macFilterList, err := setToStringSlice(d.Get("mac_filter_list").(*schema.Set)) macFilterList, err := utils.SetToStringSlice(d.Get("mac_filter_list").(*schema.Set))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -286,7 +288,7 @@ func resourceWLANGetResourceData(d *schema.ResourceData, meta interface{}) (*uni
// version specific fields and validation // version specific fields and validation
networkID := d.Get("network_id").(string) networkID := d.Get("network_id").(string)
apGroupIDs, err := setToStringSlice(d.Get("ap_group_ids").(*schema.Set)) apGroupIDs, err := utils.SetToStringSlice(d.Get("ap_group_ids").(*schema.Set))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -353,7 +355,7 @@ func resourceWLANGetResourceData(d *schema.ResourceData, meta interface{}) (*uni
} }
func resourceWLANCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceWLANCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceWLANGetResourceData(d, meta) req, err := resourceWLANGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -362,10 +364,10 @@ func resourceWLANCreate(ctx context.Context, d *schema.ResourceData, meta interf
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.CreateWLAN(ctx, site, req) resp, err := c.CreateWLAN(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -376,7 +378,7 @@ func resourceWLANCreate(ctx context.Context, d *schema.ResourceData, meta interf
} }
func resourceWLANSetResourceData(resp *unifi.WLAN, d *schema.ResourceData, meta interface{}, site string) diag.Diagnostics { func resourceWLANSetResourceData(resp *unifi.WLAN, d *schema.ResourceData, meta interface{}, site string) diag.Diagnostics {
// c := meta.(*client) // c := meta.(*provider.Client)
security := resp.Security security := resp.Security
passphrase := resp.XPassphrase passphrase := resp.XPassphrase
wpa3 := false wpa3 := false
@@ -393,11 +395,11 @@ func resourceWLANSetResourceData(resp *unifi.WLAN, d *schema.ResourceData, meta
var macFilterList *schema.Set var macFilterList *schema.Set
macFilterPolicy := "deny" macFilterPolicy := "deny"
if macFilterEnabled { if macFilterEnabled {
macFilterList = stringSliceToSet(resp.MACFilterList) macFilterList = utils.StringSliceToSet(resp.MACFilterList)
macFilterPolicy = resp.MACFilterPolicy macFilterPolicy = resp.MACFilterPolicy
} }
apGroupIDs := stringSliceToSet(resp.ApGroupIDs) apGroupIDs := utils.StringSliceToSet(resp.ApGroupIDs)
schedule := listFromSchedules(resp.ScheduleWithDuration) schedule := listFromSchedules(resp.ScheduleWithDuration)
@@ -441,15 +443,15 @@ func resourceWLANSetResourceData(resp *unifi.WLAN, d *schema.ResourceData, meta
} }
func resourceWLANRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceWLANRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
resp, err := c.c.GetWLAN(ctx, site, id) resp, err := c.GetWLAN(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
d.SetId("") d.SetId("")
return nil return nil
@@ -462,7 +464,7 @@ func resourceWLANRead(ctx context.Context, d *schema.ResourceData, meta interfac
} }
func resourceWLANUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceWLANUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
req, err := resourceWLANGetResourceData(d, meta) req, err := resourceWLANGetResourceData(d, meta)
if err != nil { if err != nil {
@@ -472,11 +474,11 @@ func resourceWLANUpdate(ctx context.Context, d *schema.ResourceData, meta interf
req.ID = d.Id() req.ID = d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
req.SiteID = site req.SiteID = site
resp, err := c.c.UpdateWLAN(ctx, site, req) resp, err := c.UpdateWLAN(ctx, site, req)
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)
} }
@@ -485,15 +487,15 @@ func resourceWLANUpdate(ctx context.Context, d *schema.ResourceData, meta interf
} }
func resourceWLANDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { func resourceWLANDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*client) c := meta.(*provider.Client)
id := d.Id() id := d.Id()
site := d.Get("site").(string) site := d.Get("site").(string)
if site == "" { if site == "" {
site = c.site site = c.Site
} }
err := c.c.DeleteWLAN(ctx, site, id) err := c.DeleteWLAN(ctx, site, id)
if errors.Is(err, unifi.ErrNotFound) { if errors.Is(err, unifi.ErrNotFound) {
return nil return nil
} }

View File

@@ -1,7 +1,8 @@
package provider package v1
import ( import (
"fmt" "fmt"
"github.com/filipowm/terraform-provider-unifi/internal/provider"
"net" "net"
"testing" "testing"
@@ -331,7 +332,7 @@ func TestAccWLAN_wpa3(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{ resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { PreCheck: func() {
preCheck(t) preCheck(t)
preCheckMinVersion(t, controllerVersionWPA3) preCheckMinVersion(t, provider.ControllerVersionWPA3)
}, },
ProviderFactories: providerFactories, ProviderFactories: providerFactories,
CheckDestroy: func(*terraform.State) error { CheckDestroy: func(*terraform.State) error {

View File

@@ -0,0 +1,172 @@
package v2
import (
"context"
up "github.com/filipowm/terraform-provider-unifi/internal/provider"
"github.com/filipowm/terraform-provider-unifi/internal/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &unifiProvider{
version: version,
}
}
}
type unifiProvider struct {
version string
}
type unifiProviderModel struct {
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
APIKey types.String `tfsdk:"api_key"`
APIUrl types.String `tfsdk:"api_url"`
Site types.String `tfsdk:"site"`
Insecure types.Bool `tfsdk:"allow_insecure"`
}
func (p *unifiProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "unifi"
resp.Version = p.version
}
func (p *unifiProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"username": schema.StringAttribute{
MarkdownDescription: up.ProviderUsernameDescription,
Optional: true,
},
"password": schema.StringAttribute{
MarkdownDescription: up.ProviderPasswordDescription,
Optional: true,
Sensitive: true,
},
"api_key": schema.StringAttribute{
MarkdownDescription: up.ProviderAPIKeyDescription,
Optional: true,
Sensitive: true,
},
"api_url": schema.StringAttribute{
MarkdownDescription: up.ProviderAPIURLDescription,
Validators: []validator.String{
stringvalidator.LengthAtLeast(1), // workaround for `required: true`, because it fails on doc generation due to incorrectly detected difference between v1 and v2
},
Optional: true,
},
"site": schema.StringAttribute{
MarkdownDescription: up.ProviderSiteDescription,
Optional: true,
},
"allow_insecure": schema.BoolAttribute{
MarkdownDescription: up.ProviderAllowInsecureDescription,
Optional: true,
},
},
}
}
func (p *unifiProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
tflog.Info(ctx, "Configuring Unifi provider...")
// Retrieve provider data from the configuration
var cfg unifiProviderModel
diags := req.Config.Get(ctx, &cfg)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// If practitioner provided a configuration value for any of the
// attributes, it must be a known value.
if cfg.APIUrl.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("api_url"),
"Unknown UniFi Controller API URL",
"The provider cannot create the UniFi Controller API client as there is an unknown configuration value "+
"for the API endpoint. Either target apply the source of the value first, set the value statically in "+
"the configuration, or use the UNIFI_API_URL environment variable.",
)
}
if resp.Diagnostics.HasError() {
return
}
// Default values to environment variables, but override
// with Terraform configuration value if set.
// Check environment variables
username := utils.GetAnyStringEnv("UNIFI_USERNAME")
password := utils.GetAnyStringEnv("UNIFI_PASSWORD")
apiKey := utils.GetAnyStringEnv("UNIFI_API_KEY")
apiUrl := utils.GetAnyStringEnv("UNIFI_API_URL")
site := utils.GetAnyStringEnv("UNIFI_SITE")
insecure := utils.GetAnyBoolEnv("UNIFI_INSECURE")
if !cfg.Username.IsNull() {
username = cfg.Username.ValueString()
}
if !cfg.Password.IsNull() {
password = cfg.Password.ValueString()
}
if !cfg.APIKey.IsNull() {
apiKey = cfg.APIKey.ValueString()
}
if !cfg.APIUrl.IsNull() {
apiUrl = cfg.APIUrl.ValueString()
}
if !cfg.Site.IsNull() {
site = cfg.Site.ValueString()
}
if !cfg.Insecure.IsNull() {
insecure = cfg.Insecure.ValueBool()
}
if apiKey != "" && (username != "" || password != "") {
resp.Diagnostics.AddAttributeError(path.Root("api_key"), "Two authentication methods configured", "Only one of `username`/`password` or `api_key` can be set")
} else if apiKey == "" && (username == "" || password == "") {
resp.Diagnostics.AddAttributeError(path.Root("api_key"), "Missing UniFi API credentials", "Either `username`/`password` or `api_key` must be set")
}
if apiUrl == "" {
resp.Diagnostics.AddAttributeError(path.Root("api_url"), "Missing UniFi API URL", "The `api_url` attribute must be set")
}
if resp.Diagnostics.HasError() {
return
}
if site == "" {
site = "default" // set default site if not provided
}
c, err := up.NewClient(&up.ClientConfig{
Username: username,
Password: password,
ApiKey: apiKey,
Url: apiUrl,
Site: site,
Insecure: insecure,
})
if err != nil {
resp.Diagnostics.AddError("Failed to create UniFi client", err.Error())
return
}
resp.ResourceData = c
resp.DataSourceData = c
}
func (p *unifiProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{}
}
func (p *unifiProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{}
}

40
internal/utils/cidr.go Normal file
View File

@@ -0,0 +1,40 @@
package utils
import (
"fmt"
"net"
)
func CidrValidate(raw interface{}, key string) ([]string, []error) {
v, ok := raw.(string)
if !ok {
return nil, []error{fmt.Errorf("expected string, got %T", raw)}
}
_, _, err := net.ParseCIDR(v)
if err != nil {
return nil, []error{err}
}
return nil, nil
}
func CidrZeroBased(cidr string) string {
_, cidrNet, err := net.ParseCIDR(cidr)
if err != nil {
return ""
}
return cidrNet.String()
}
func CidrOneBased(cidr string) string {
_, cidrNet, err := net.ParseCIDR(cidr)
if err != nil {
return ""
}
cidrNet.IP[3]++
return cidrNet.String()
}

View File

@@ -1,4 +1,4 @@
package provider package utils
import ( import (
"testing" "testing"
@@ -18,7 +18,7 @@ func TestCIDRValidate(t *testing.T) {
{"", "192.1.2.1/20"}, {"", "192.1.2.1/20"},
} { } {
t.Run(c.cidr, func(t *testing.T) { t.Run(c.cidr, func(t *testing.T) {
_, actualErrs := cidrValidate(c.cidr, "key") _, actualErrs := CidrValidate(c.cidr, "key")
switch len(actualErrs) { switch len(actualErrs) {
case 0: case 0:
if c.expectedError != "" { if c.expectedError != "" {

50
internal/utils/env.go Normal file
View File

@@ -0,0 +1,50 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package utils
import (
"os"
"strconv"
)
// GetAnyStringEnv returns the first non-empty string value from the environment variables.
func GetAnyStringEnv(ks ...string) string {
for _, k := range ks {
if v := os.Getenv(k); v != "" {
return v
}
}
return ""
}
// GetAnyBoolEnv returns the first non-empty boolean value from the environment variables.
func GetAnyBoolEnv(ks ...string) bool {
val := ""
for _, k := range ks {
if v := os.Getenv(k); v != "" {
val = v
break
}
}
return val == "true" || val == "1"
}
// GetAnyIntEnv returns the first non-empty integer value from the environment variables.
func GetAnyIntEnv(ks ...string) int {
for _, k := range ks {
if v := os.Getenv(k); v != "" {
if i, err := strconv.Atoi(v); err == nil {
return i
}
}
}
return 0
}

View File

@@ -1,8 +1,8 @@
package provider package utils
import "strconv" import "strconv"
func markdownValueListInt(values []int) string { func MarkdownValueListInt(values []int) string {
switch { switch {
case len(values) == 0: case len(values) == 0:
return "" return ""

View File

@@ -1,4 +1,4 @@
package provider package utils
import ( import (
"fmt" "fmt"
@@ -6,7 +6,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
) )
func listToStringSlice(src []interface{}) ([]string, error) { func ListToStringSlice(src []interface{}) ([]string, error) {
dst := make([]string, 0, len(src)) dst := make([]string, 0, len(src))
for _, s := range src { for _, s := range src {
d, ok := s.(string) d, ok := s.(string)
@@ -18,11 +18,11 @@ func listToStringSlice(src []interface{}) ([]string, error) {
return dst, nil return dst, nil
} }
func setToStringSlice(src *schema.Set) ([]string, error) { func SetToStringSlice(src *schema.Set) ([]string, error) {
return listToStringSlice(src.List()) return ListToStringSlice(src.List())
} }
func stringSliceToList(list []string) []interface{} { func StringSliceToList(list []string) []interface{} {
vs := make([]interface{}, 0, len(list)) vs := make([]interface{}, 0, len(list))
for _, v := range list { for _, v := range list {
vs = append(vs, v) vs = append(vs, v)
@@ -30,6 +30,6 @@ func stringSliceToList(list []string) []interface{} {
return vs return vs
} }
func stringSliceToSet(src []string) *schema.Set { func StringSliceToSet(src []string) *schema.Set {
return schema.NewSet(schema.HashString, stringSliceToList(src)) return schema.NewSet(schema.HashString, StringSliceToList(src))
} }

59
main.go
View File

@@ -1,16 +1,20 @@
package main // import "github.com/filipowm/terraform-provider-unifi" package main // import "github.com/filipowm/terraform-provider-unifi"
import ( import (
"context"
"flag" "flag"
v1 "github.com/filipowm/terraform-provider-unifi/internal/provider/v1"
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin" v2 "github.com/filipowm/terraform-provider-unifi/internal/provider/v2"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/filipowm/terraform-provider-unifi/internal/provider" "github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server"
"github.com/hashicorp/terraform-plugin-mux/tf5to6server"
"github.com/hashicorp/terraform-plugin-mux/tf6muxserver"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"log"
) )
// Generate docs for website
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
var ( var (
// these will be set by the goreleaser configuration // these will be set by the goreleaser configuration
// to appropriate values for the compiled binary // to appropriate values for the compiled binary
@@ -21,17 +25,50 @@ var (
) )
func main() { func main() {
ctx := context.Background()
var debugMode bool var debugMode bool
flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve") flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve")
flag.Parse() flag.Parse()
opts := &plugin.ServeOpts{ProviderFunc: provider.New(version)} p := v1.New(version)
upgradedSdkServer, err := tf5to6server.UpgradeServer(
ctx,
func() tfprotov5.ProviderServer {
return schema.NewGRPCProviderServer(p())
},
)
if err != nil {
log.Fatal(err)
}
providers := []func() tfprotov6.ProviderServer{
providerserver.NewProtocol6(v2.New(version)()),
func() tfprotov6.ProviderServer {
return upgradedSdkServer
},
}
muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
if err != nil {
log.Fatal(err)
}
var serveOpts []tf6server.ServeOpt
if debugMode { if debugMode {
opts.Debug = true serveOpts = append(serveOpts, tf6server.WithManagedDebug())
opts.ProviderAddr = "registry.terraform.io/filipowm/unifi"
} }
plugin.Serve(opts) // Remove any date and time prefix in log package function output to
// prevent duplicate timestamp and incorrect log level setting
log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime))
err = tf6server.Serve(
"registry.terraform.io/filipowm/unifi",
muxServer.ProviderServer,
serveOpts...,
)
if err != nil {
log.Fatal(err)
}
} }

View File

@@ -1,13 +1,13 @@
--- ---
layout: "" layout: ""
page_title: "Provider: Unifi" page_title: "Provider: UniFi"
description: |- description: |-
The Unifi provider provides resources to interact with a Unifi controller API. The Unifi provider provides resources to interact with a UniFi Controller API.
--- ---
# Unifi Provider # Unifi Provider
The Unifi provider provides resources to interact with a Unifi controller API. The Unifi provider provides resources to interact with a UniFi Controller API.
It is not recommended to use your own account for management of your controller. A user specific to It is not recommended to use your own account for management of your controller. A user specific to
Terraform is recommended. You can create a **Limited Admin** with **Local Access Only** and Terraform is recommended. You can create a **Limited Admin** with **Local Access Only** and

View File

@@ -7,3 +7,6 @@ import (
_ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/golangci/golangci-lint/cmd/golangci-lint"
_ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs" _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
) )
// Generate documentation.
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-dir ../ --rendered-website-dir ./docs --provider-name "terraform-provider-unifi" --rendered-provider-name "terraform-provider-unifi" //nolint:lll