diff --git a/go.mod b/go.mod index 8486fbb8..6c0962f5 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/stretchr/testify v1.9.0 github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a github.com/tailscale/tailscale-client-go v1.17.1-0.20240729175651-90a1e935cc19 + github.com/tailscale/tailscale-client-go/v2 v2.0.0-20240801195603-6096900af9df golang.org/x/tools v0.23.0 tailscale.com v1.70.0 ) diff --git a/go.sum b/go.sum index 0a2b6ace..083c5ff5 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,8 @@ github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29X github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8= github.com/tailscale/tailscale-client-go v1.17.1-0.20240729175651-90a1e935cc19 h1:fRLv1yZH1ueL1cnpLhOnOymoBfMCIviCn0e0VkAjkK4= github.com/tailscale/tailscale-client-go v1.17.1-0.20240729175651-90a1e935cc19/go.mod h1:jbwJyHniK3nyLttwcDTXnfdDQEnADvc4VMOP8hZWnR0= +github.com/tailscale/tailscale-client-go/v2 v2.0.0-20240801195603-6096900af9df h1:XjHI0pFxhttM1nEn/WNGOO3wrJYEJSZarJm0i5WFXgY= +github.com/tailscale/tailscale-client-go/v2 v2.0.0-20240801195603-6096900af9df/go.mod h1:i/MSgQ71kdyh1Wdp50XxrIgtsyO4uZ2SZSPd83lGKHM= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/tailscale/data_source_acl.go b/tailscale/data_source_acl.go index 3de8ac94..326a3ed6 100644 --- a/tailscale/data_source_acl.go +++ b/tailscale/data_source_acl.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/tailscale/hujson" - "github.com/tailscale/tailscale-client-go/tailscale" ) func dataSourceACL() *schema.Resource { @@ -30,7 +29,7 @@ func dataSourceACL() *schema.Resource { } func dataSourceACLRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 acl, err := client.RawACL(ctx) if err != nil { diff --git a/tailscale/data_source_device.go b/tailscale/data_source_device.go index aa9ada30..1b63a4cf 100644 --- a/tailscale/data_source_device.go +++ b/tailscale/data_source_device.go @@ -71,7 +71,7 @@ func dataSourceDevice() *schema.Resource { } func dataSourceDeviceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 var filter func(d tailscale.Device) bool var filterDesc string diff --git a/tailscale/data_source_devices.go b/tailscale/data_source_devices.go index 279d7593..f2574cf4 100644 --- a/tailscale/data_source_devices.go +++ b/tailscale/data_source_devices.go @@ -6,8 +6,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/tailscale/tailscale-client-go/tailscale" ) func dataSourceDevices() *schema.Resource { @@ -70,7 +68,7 @@ func dataSourceDevices() *schema.Resource { } func dataSourceDevicesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 devices, err := client.Devices(ctx) if err != nil { diff --git a/tailscale/provider.go b/tailscale/provider.go index 8b3f5c35..007237c5 100644 --- a/tailscale/provider.go +++ b/tailscale/provider.go @@ -5,6 +5,7 @@ package tailscale import ( "context" "fmt" + "net/url" "time" "github.com/hashicorp/go-cty/cty" @@ -13,6 +14,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/tailscale/tailscale-client-go/tailscale" + tailscalev2 "github.com/tailscale/tailscale-client-go/v2" ) // providerVersion is filled by goreleaser at build time. @@ -20,6 +22,12 @@ var providerVersion = "dev" type ProviderOption func(p *schema.Provider) +// Clients contains both v1 and v2 Tailscale Clients +type Clients struct { + V1 *tailscale.Client + V2 *tailscalev2.Client +} + // Provider returns the *schema.Provider instance that implements the terraform provider. func Provider(options ...ProviderOption) *schema.Provider { // Support both sets of OAuth Env vars for backwards compatibility @@ -107,6 +115,11 @@ func Provider(options ...ProviderOption) *schema.Provider { func providerConfigure(_ context.Context, provider *schema.Provider, d *schema.ResourceData) (interface{}, diag.Diagnostics) { baseURL := d.Get("base_url").(string) + parsedBaseURL, err := url.Parse(baseURL) + if err != nil { + return nil, diag.Errorf("could not parse baseURL %q: %s", baseURL, err) + } + tailnet := d.Get("tailnet").(string) if tailnet == "" { return nil, diag.Errorf("tailscale provider argument 'tailnet' is empty") @@ -152,7 +165,14 @@ func providerConfigure(_ context.Context, provider *schema.Provider, d *schema.R return nil, diagnosticsError(err, "failed to initialise client") } - return client, nil + clientV2 := &tailscalev2.Client{ + BaseURL: parsedBaseURL, + UserAgent: userAgent, + Tailnet: tailnet, + } + clientV2.UseOAuth(oauthClientID, oauthClientSecret, oauthScopes) + + return &Clients{client, clientV2}, nil } client, err := tailscale.NewClient( @@ -165,7 +185,14 @@ func providerConfigure(_ context.Context, provider *schema.Provider, d *schema.R return nil, diagnosticsError(err, "failed to initialise client") } - return client, nil + clientV2 := &tailscalev2.Client{ + BaseURL: parsedBaseURL, + UserAgent: userAgent, + APIKey: apiKey, + Tailnet: tailnet, + } + + return &Clients{client, clientV2}, nil } func diagnosticsError(err error, message string, args ...interface{}) diag.Diagnostics { diff --git a/tailscale/provider_test.go b/tailscale/provider_test.go index e2ad8785..608333fa 100644 --- a/tailscale/provider_test.go +++ b/tailscale/provider_test.go @@ -12,11 +12,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - ts "github.com/tailscale/tailscale-client-go/tailscale" "github.com/tailscale/terraform-provider-tailscale/tailscale" ) -var testClient *ts.Client +var testClients *tailscale.Clients var testServer *TestServer var testAccProvider = tailscale.Provider() @@ -66,13 +65,13 @@ func TestProvider_Implemented(t *testing.T) { func testProviderFactories(t *testing.T) map[string]func() (*schema.Provider, error) { t.Helper() - testClient, testServer = NewTestHarness(t) + testClients, testServer = NewTestHarness(t) return map[string]func() (*schema.Provider, error){ "tailscale": func() (*schema.Provider, error) { return tailscale.Provider(func(p *schema.Provider) { // Set up a test harness for the provider p.ConfigureContextFunc = func(ctx context.Context, data *schema.ResourceData) (interface{}, diag.Diagnostics) { - return testClient, nil + return testClients, nil } // Don't require any of the global configuration diff --git a/tailscale/resource_acl.go b/tailscale/resource_acl.go index 0ac00932..c3b5d015 100644 --- a/tailscale/resource_acl.go +++ b/tailscale/resource_acl.go @@ -35,7 +35,7 @@ func resourceACL() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, CustomizeDiff: func(ctx context.Context, rd *schema.ResourceDiff, m interface{}) error { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 //if the acl is only known after apply, then acl will be an empty string and validation will fail if rd.Get("acl").(string) == "" { @@ -97,7 +97,7 @@ func resourceACL() *schema.Resource { } func resourceACLRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 acl, err := client.RawACL(ctx) if err != nil { return diagnosticsError(err, "Failed to fetch ACL") @@ -110,7 +110,7 @@ func resourceACLRead(ctx context.Context, d *schema.ResourceData, m interface{}) } func resourceACLCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 acl := d.Get("acl").(string) // Setting the `ts-default` ETag will make this operation succeed only if @@ -136,7 +136,7 @@ func resourceACLCreate(ctx context.Context, d *schema.ResourceData, m interface{ } func resourceACLUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 if !d.HasChange("acl") { return nil diff --git a/tailscale/resource_contacts.go b/tailscale/resource_contacts.go index 1c6bbefd..3621a88e 100644 --- a/tailscale/resource_contacts.go +++ b/tailscale/resource_contacts.go @@ -75,7 +75,7 @@ func resourceContacts() *schema.Resource { } func resourceContactsCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 if diagErr := updateContact(ctx, client, d, tailscale.ContactAccount); diagErr != nil { return diagErr @@ -94,7 +94,7 @@ func resourceContactsCreate(ctx context.Context, d *schema.ResourceData, m inter } func resourceContactsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 contacts, err := client.Contacts(ctx) if err != nil { @@ -117,7 +117,7 @@ func resourceContactsRead(ctx context.Context, d *schema.ResourceData, m interfa } func resourceContactsUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 if d.HasChange("account") { if diagErr := updateContact(ctx, client, d, tailscale.ContactAccount); diagErr != nil { diff --git a/tailscale/resource_contacts_test.go b/tailscale/resource_contacts_test.go index 4ed40cb7..89d0b977 100644 --- a/tailscale/resource_contacts_test.go +++ b/tailscale/resource_contacts_test.go @@ -8,7 +8,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/tailscale/tailscale-client-go/tailscale" + ts "github.com/tailscale/tailscale-client-go/tailscale" + "github.com/tailscale/terraform-provider-tailscale/tailscale" ) const testContactsBasic = ` @@ -41,32 +42,32 @@ const testContactsUpdated = ` } }` -var expectedContactsBasic = &tailscale.Contacts{ - Account: tailscale.Contact{ +var expectedContactsBasic = &ts.Contacts{ + Account: ts.Contact{ Email: "account@example.com", }, - Support: tailscale.Contact{ + Support: ts.Contact{ Email: "support@example.com", }, - Security: tailscale.Contact{ + Security: ts.Contact{ Email: "security@example.com", }, } -var expectedContactsUpdated = &tailscale.Contacts{ - Account: tailscale.Contact{ +var expectedContactsUpdated = &ts.Contacts{ + Account: ts.Contact{ Email: "otheraccount@example.com", }, - Support: tailscale.Contact{ + Support: ts.Contact{ Email: "support@example.com", }, - Security: tailscale.Contact{ + Security: ts.Contact{ Email: "security2@example.com", }, } func TestAccTailscaleContacts_Basic(t *testing.T) { - contacts := &tailscale.Contacts{} + contacts := &ts.Contacts{} resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -93,7 +94,7 @@ func TestAccTailscaleContacts_Basic(t *testing.T) { } func TestAccTailscaleContacts_Update(t *testing.T) { - contacts := &tailscale.Contacts{} + contacts := &ts.Contacts{} resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -129,7 +130,7 @@ func TestAccTailscaleContacts_Update(t *testing.T) { }) } -func testAccCheckContactsExists(resourceName string, contacts *tailscale.Contacts) resource.TestCheckFunc { +func testAccCheckContactsExists(resourceName string, contacts *ts.Contacts) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -140,7 +141,7 @@ func testAccCheckContactsExists(resourceName string, contacts *tailscale.Contact return fmt.Errorf("resource has no ID set") } - client := testAccProvider.Meta().(*tailscale.Client) + client := testAccProvider.Meta().(*tailscale.Clients).V1 out, err := client.Contacts(context.Background()) if err != nil { return err @@ -151,7 +152,7 @@ func testAccCheckContactsExists(resourceName string, contacts *tailscale.Contact } } -func testAccCheckContactsPropertiesBasic(contacts *tailscale.Contacts) resource.TestCheckFunc { +func testAccCheckContactsPropertiesBasic(contacts *ts.Contacts) resource.TestCheckFunc { return func(s *terraform.State) error { if err := checkContacts(contacts, expectedContactsBasic); err != nil { return err @@ -161,7 +162,7 @@ func testAccCheckContactsPropertiesBasic(contacts *tailscale.Contacts) resource. } } -func testAccCheckContactsPropertiesUpdated(contacts *tailscale.Contacts) resource.TestCheckFunc { +func testAccCheckContactsPropertiesUpdated(contacts *ts.Contacts) resource.TestCheckFunc { return func(s *terraform.State) error { if err := checkContacts(contacts, expectedContactsUpdated); err != nil { return err @@ -179,8 +180,8 @@ func testAccCheckContactsDestroyUpdated(s *terraform.State) error { return testAccCheckContactsDestroy(s, expectedContactsUpdated) } -func testAccCheckContactsDestroy(s *terraform.State, expectedContacts *tailscale.Contacts) error { - client := testAccProvider.Meta().(*tailscale.Client) +func testAccCheckContactsDestroy(s *terraform.State, expectedContacts *ts.Contacts) error { + client := testAccProvider.Meta().(*tailscale.Clients).V1 for _, rs := range s.RootModule().Resources { if rs.Type != "tailscale_contacts" { @@ -203,7 +204,7 @@ func testAccCheckContactsDestroy(s *terraform.State, expectedContacts *tailscale return nil } -func checkContacts(contacts *tailscale.Contacts, expectedContacts *tailscale.Contacts) error { +func checkContacts(contacts *ts.Contacts, expectedContacts *ts.Contacts) error { if contacts.Account.Email != expectedContacts.Account.Email { return fmt.Errorf("bad account email, expected %q, got %q", expectedContacts.Account.Email, contacts.Account.Email) } diff --git a/tailscale/resource_device_authorization.go b/tailscale/resource_device_authorization.go index 8024211e..cf0d2c40 100644 --- a/tailscale/resource_device_authorization.go +++ b/tailscale/resource_device_authorization.go @@ -32,7 +32,7 @@ func resourceDeviceAuthorization() *schema.Resource { } func resourceDeviceAuthorizationRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) devices, err := client.Devices(ctx) @@ -60,7 +60,7 @@ func resourceDeviceAuthorizationRead(ctx context.Context, d *schema.ResourceData } func resourceDeviceAuthorizationCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) authorized := d.Get("authorized").(bool) @@ -75,7 +75,7 @@ func resourceDeviceAuthorizationCreate(ctx context.Context, d *schema.ResourceDa } func resourceDeviceAuthorizationUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) devices, err := client.Devices(ctx) diff --git a/tailscale/resource_device_key.go b/tailscale/resource_device_key.go index a6869305..e06d9cbd 100644 --- a/tailscale/resource_device_key.go +++ b/tailscale/resource_device_key.go @@ -32,7 +32,7 @@ func resourceDeviceKey() *schema.Resource { } func resourceDeviceKeyCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) keyExpiryDisabled := d.Get("key_expiry_disabled").(bool) @@ -50,7 +50,7 @@ func resourceDeviceKeyCreate(ctx context.Context, d *schema.ResourceData, m inte } func resourceDeviceKeyDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) key := tailscale.DeviceKey{} @@ -63,7 +63,7 @@ func resourceDeviceKeyDelete(ctx context.Context, d *schema.ResourceData, m inte } func resourceDeviceKeyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) devices, err := client.Devices(ctx) @@ -93,7 +93,7 @@ func resourceDeviceKeyRead(ctx context.Context, d *schema.ResourceData, m interf } func resourceDeviceKeyUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) keyExpiryDisabled := d.Get("key_expiry_disabled").(bool) diff --git a/tailscale/resource_device_subnet_routes.go b/tailscale/resource_device_subnet_routes.go index 4dbe125a..f0df2fb6 100644 --- a/tailscale/resource_device_subnet_routes.go +++ b/tailscale/resource_device_subnet_routes.go @@ -5,8 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/tailscale/tailscale-client-go/tailscale" ) func resourceDeviceSubnetRoutes() *schema.Resource { @@ -35,7 +33,7 @@ func resourceDeviceSubnetRoutes() *schema.Resource { } func resourceDeviceSubnetRoutesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) routes, err := client.DeviceSubnetRoutes(ctx, deviceID) @@ -51,7 +49,7 @@ func resourceDeviceSubnetRoutesRead(ctx context.Context, d *schema.ResourceData, } func resourceDeviceSubnetRoutesCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) routes := d.Get("routes").(*schema.Set).List() @@ -69,7 +67,7 @@ func resourceDeviceSubnetRoutesCreate(ctx context.Context, d *schema.ResourceDat } func resourceDeviceSubnetRoutesUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) routes := d.Get("routes").(*schema.Set).List() @@ -86,7 +84,7 @@ func resourceDeviceSubnetRoutesUpdate(ctx context.Context, d *schema.ResourceDat } func resourceDeviceSubnetRoutesDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) if err := client.SetDeviceSubnetRoutes(ctx, deviceID, []string{}); err != nil { diff --git a/tailscale/resource_device_tags.go b/tailscale/resource_device_tags.go index cdbdb65f..69eca865 100644 --- a/tailscale/resource_device_tags.go +++ b/tailscale/resource_device_tags.go @@ -35,7 +35,7 @@ func resourceDeviceTags() *schema.Resource { } func resourceDeviceTagsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) devices, err := client.Devices(ctx) @@ -63,7 +63,7 @@ func resourceDeviceTagsRead(ctx context.Context, d *schema.ResourceData, m inter } func resourceDeviceTagsCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) set := d.Get("tags").(*schema.Set) @@ -81,7 +81,7 @@ func resourceDeviceTagsCreate(ctx context.Context, d *schema.ResourceData, m int } func resourceDeviceTagsUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) set := d.Get("tags").(*schema.Set) @@ -99,7 +99,7 @@ func resourceDeviceTagsUpdate(ctx context.Context, d *schema.ResourceData, m int } func resourceDeviceTagsDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 deviceID := d.Get("device_id").(string) if err := client.SetDeviceTags(ctx, deviceID, []string{}); err != nil { diff --git a/tailscale/resource_dns_nameservers.go b/tailscale/resource_dns_nameservers.go index 956f999e..a376ec42 100644 --- a/tailscale/resource_dns_nameservers.go +++ b/tailscale/resource_dns_nameservers.go @@ -5,8 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/tailscale/tailscale-client-go/tailscale" ) func resourceDNSNameservers() *schema.Resource { @@ -31,7 +29,7 @@ func resourceDNSNameservers() *schema.Resource { } func resourceDNSNameserversRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 servers, err := client.DNSNameservers(ctx) if err != nil { return diagnosticsError(err, "Failed to fetch dns nameservers") @@ -45,7 +43,7 @@ func resourceDNSNameserversRead(ctx context.Context, d *schema.ResourceData, m i } func resourceDNSNameserversCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 nameservers := d.Get("nameservers").([]interface{}) servers := make([]string, len(nameservers)) @@ -66,7 +64,7 @@ func resourceDNSNameserversUpdate(ctx context.Context, d *schema.ResourceData, m return resourceDNSNameserversRead(ctx, d, m) } - client := m.(*tailscale.Client) + client := m.(*Clients).V1 nameservers := d.Get("nameservers").([]interface{}) servers := make([]string, len(nameservers)) @@ -82,7 +80,7 @@ func resourceDNSNameserversUpdate(ctx context.Context, d *schema.ResourceData, m } func resourceDNSNameserversDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 if err := client.SetDNSNameservers(ctx, []string{}); err != nil { return diagnosticsError(err, "Failed to set dns nameservers") diff --git a/tailscale/resource_dns_preferences.go b/tailscale/resource_dns_preferences.go index 605d2f44..e3b3f3a3 100644 --- a/tailscale/resource_dns_preferences.go +++ b/tailscale/resource_dns_preferences.go @@ -30,7 +30,7 @@ func resourceDNSPreferences() *schema.Resource { } func resourceDNSPreferencesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 preferences, err := client.DNSPreferences(ctx) if err != nil { @@ -45,7 +45,7 @@ func resourceDNSPreferencesRead(ctx context.Context, d *schema.ResourceData, m i } func resourceDNSPreferencesCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 magicDNS := d.Get("magic_dns").(bool) preferences := tailscale.DNSPreferences{ MagicDNS: magicDNS, @@ -64,7 +64,7 @@ func resourceDNSPreferencesUpdate(ctx context.Context, d *schema.ResourceData, m return resourceDNSPreferencesRead(ctx, d, m) } - client := m.(*tailscale.Client) + client := m.(*Clients).V1 magicDNS := d.Get("magic_dns").(bool) preferences := tailscale.DNSPreferences{ @@ -79,7 +79,7 @@ func resourceDNSPreferencesUpdate(ctx context.Context, d *schema.ResourceData, m } func resourceDNSPreferencesDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 if err := client.SetDNSPreferences(ctx, tailscale.DNSPreferences{}); err != nil { return diagnosticsError(err, "Failed to set dns preferences") diff --git a/tailscale/resource_dns_search_paths.go b/tailscale/resource_dns_search_paths.go index 8a6287d9..26aeb786 100644 --- a/tailscale/resource_dns_search_paths.go +++ b/tailscale/resource_dns_search_paths.go @@ -5,8 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/tailscale/tailscale-client-go/tailscale" ) func resourceDNSSearchPaths() *schema.Resource { @@ -30,7 +28,7 @@ func resourceDNSSearchPaths() *schema.Resource { } func resourceDNSSearchPathsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 paths, err := client.DNSSearchPaths(ctx) if err != nil { return diagnosticsError(err, "Failed to fetch dns search paths") @@ -44,7 +42,7 @@ func resourceDNSSearchPathsRead(ctx context.Context, d *schema.ResourceData, m i } func resourceDNSSearchPathsCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 paths := d.Get("search_paths").([]interface{}) searchPaths := make([]string, len(paths)) @@ -65,7 +63,7 @@ func resourceDNSSearchPathsUpdate(ctx context.Context, d *schema.ResourceData, m return resourceDNSSearchPathsRead(ctx, d, m) } - client := m.(*tailscale.Client) + client := m.(*Clients).V1 paths := d.Get("search_paths").([]interface{}) searchPaths := make([]string, len(paths)) @@ -81,7 +79,7 @@ func resourceDNSSearchPathsUpdate(ctx context.Context, d *schema.ResourceData, m } func resourceDNSSearchPathsDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 if err := client.SetDNSSearchPaths(ctx, []string{}); err != nil { return diagnosticsError(err, "Failed to fetch set search paths") diff --git a/tailscale/resource_dns_split_nameservers.go b/tailscale/resource_dns_split_nameservers.go index af948c08..8142b01a 100644 --- a/tailscale/resource_dns_split_nameservers.go +++ b/tailscale/resource_dns_split_nameservers.go @@ -38,7 +38,7 @@ func resourceDNSSplitNameservers() *schema.Resource { } func resourceSplitDNSNameserversRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 splitDNS, err := client.SplitDNS(ctx) if err != nil { return diagnosticsError(err, "Failed to fetch split DNS configs") @@ -60,7 +60,7 @@ func resourceSplitDNSNameserversRead(ctx context.Context, d *schema.ResourceData } func resourceSplitDNSNameserversCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 nameserversSet := d.Get("nameservers").(*schema.Set) domain := d.Get("domain").(string) @@ -91,7 +91,7 @@ func resourceSplitDNSNameserversUpdate(ctx context.Context, d *schema.ResourceDa } func resourceSplitDNSNameserversDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 domain := d.Get("domain").(string) req := make(tailscale.SplitDnsRequest) diff --git a/tailscale/resource_tailnet_key.go b/tailscale/resource_tailnet_key.go index b71eb0a9..24d8ea56 100644 --- a/tailscale/resource_tailnet_key.go +++ b/tailscale/resource_tailnet_key.go @@ -104,7 +104,7 @@ func resourceTailnetKey() *schema.Resource { } func resourceTailnetKeyCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 reusable := d.Get("reusable").(bool) ephemeral := d.Get("ephemeral").(bool) preauthorized := d.Get("preauthorized").(bool) @@ -157,7 +157,7 @@ func resourceTailnetKeyCreate(ctx context.Context, d *schema.ResourceData, m int } func resourceTailnetKeyDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 err := client.DeleteKey(ctx, d.Id()) switch { @@ -199,7 +199,7 @@ func resourceTailnetKeyDiff(ctx context.Context, d *schema.ResourceDiff, m inter return nil } - client := m.(*tailscale.Client) + client := m.(*Clients).V1 key, err := client.GetKey(ctx, d.Id()) if tailscale.IsNotFound(err) || (err == nil && key.Invalid) { d.ForceNew("recreate_if_invalid") @@ -210,7 +210,7 @@ func resourceTailnetKeyDiff(ctx context.Context, d *schema.ResourceDiff, m inter func resourceTailnetKeyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { recreateIfInvalid := shouldRecreateIfInvalid(d.Get("reusable").(bool), d.Get("recreate_if_invalid").(string)) - client := m.(*tailscale.Client) + client := m.(*Clients).V1 key, err := client.GetKey(ctx, d.Id()) switch { diff --git a/tailscale/resource_webhook.go b/tailscale/resource_webhook.go index 1bf00488..c77797b3 100644 --- a/tailscale/resource_webhook.go +++ b/tailscale/resource_webhook.go @@ -83,7 +83,7 @@ func resourceWebhook() *schema.Resource { } func resourceWebhookCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 endpointURL := d.Get("endpoint_url").(string) providerType := tailscale.WebhookProviderType(d.Get("provider_type").(string)) @@ -113,7 +113,7 @@ func resourceWebhookCreate(ctx context.Context, d *schema.ResourceData, m interf } func resourceWebhookRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 webhook, err := client.Webhook(ctx, d.Id()) if err != nil { @@ -144,7 +144,7 @@ func resourceWebhookUpdate(ctx context.Context, d *schema.ResourceData, m interf return resourceWebhookRead(ctx, d, m) } - client := m.(*tailscale.Client) + client := m.(*Clients).V1 subscriptions := d.Get("subscriptions").(*schema.Set).List() var requestSubscriptions []tailscale.WebhookSubscriptionType @@ -161,7 +161,7 @@ func resourceWebhookUpdate(ctx context.Context, d *schema.ResourceData, m interf } func resourceWebhookDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - client := m.(*tailscale.Client) + client := m.(*Clients).V1 err := client.DeleteWebhook(ctx, d.Id()) if err != nil { diff --git a/tailscale/resource_webhook_test.go b/tailscale/resource_webhook_test.go index 6b60856a..7fc77837 100644 --- a/tailscale/resource_webhook_test.go +++ b/tailscale/resource_webhook_test.go @@ -11,7 +11,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/tailscale/tailscale-client-go/tailscale" + ts "github.com/tailscale/tailscale-client-go/tailscale" + "github.com/tailscale/terraform-provider-tailscale/tailscale" ) const testWebhook = ` @@ -33,7 +34,7 @@ func TestProvider_TailscaleWebhook(t *testing.T) { IsUnitTest: true, PreCheck: func() { testServer.ResponseCode = http.StatusOK - testServer.ResponseBody = tailscale.Webhook{ + testServer.ResponseBody = ts.Webhook{ EndpointID: "12345", } }, @@ -46,7 +47,7 @@ func TestProvider_TailscaleWebhook(t *testing.T) { } func TestAccTailscaleWebhook_Basic(t *testing.T) { - webhook := &tailscale.Webhook{} + webhook := &ts.Webhook{} resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -76,7 +77,7 @@ func TestAccTailscaleWebhook_Basic(t *testing.T) { } func TestAccTailscaleWebhook_Update(t *testing.T) { - webhook := &tailscale.Webhook{} + webhook := &ts.Webhook{} resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -118,7 +119,7 @@ func TestAccTailscaleWebhook_Update(t *testing.T) { }) } -func testAccCheckWebhookExists(resourceName string, webhook *tailscale.Webhook) resource.TestCheckFunc { +func testAccCheckWebhookExists(resourceName string, webhook *ts.Webhook) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -129,7 +130,7 @@ func testAccCheckWebhookExists(resourceName string, webhook *tailscale.Webhook) return fmt.Errorf("resource has no ID set") } - client := testAccProvider.Meta().(*tailscale.Client) + client := testAccProvider.Meta().(*tailscale.Clients).V1 out, err := client.Webhook(context.Background(), rs.Primary.ID) if err != nil { return err @@ -141,7 +142,7 @@ func testAccCheckWebhookExists(resourceName string, webhook *tailscale.Webhook) } func testAccCheckWebhookDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*tailscale.Client) + client := testAccProvider.Meta().(*tailscale.Clients).V1 for _, rs := range s.RootModule().Resources { if rs.Type != "tailscale_webhook" { @@ -160,7 +161,7 @@ func testAccCheckWebhookDestroy(s *terraform.State) error { return nil } -func testAccCheckWebhookProperties(webhook *tailscale.Webhook) resource.TestCheckFunc { +func testAccCheckWebhookProperties(webhook *ts.Webhook) resource.TestCheckFunc { return func(s *terraform.State) error { if webhook.EndpointURL != "https://example.com/endpoint" { return fmt.Errorf("bad webhook.endpoint_url: %s", webhook.EndpointURL) @@ -169,9 +170,9 @@ func testAccCheckWebhookProperties(webhook *tailscale.Webhook) resource.TestChec return fmt.Errorf("bad webhook.provider_type: %s", webhook.ProviderType) } - expectedSubscriptions := []tailscale.WebhookSubscriptionType{ - tailscale.WebhookNodeCreated, - tailscale.WebhookUserNeedsApproval, + expectedSubscriptions := []ts.WebhookSubscriptionType{ + ts.WebhookNodeCreated, + ts.WebhookUserNeedsApproval, } slices.Sort(expectedSubscriptions) @@ -184,7 +185,7 @@ func testAccCheckWebhookProperties(webhook *tailscale.Webhook) resource.TestChec } } -func testAccCheckWebhookPropertiesUpdated(webhook *tailscale.Webhook) resource.TestCheckFunc { +func testAccCheckWebhookPropertiesUpdated(webhook *ts.Webhook) resource.TestCheckFunc { return func(s *terraform.State) error { if webhook.EndpointURL != "https://example.com/endpoint" { return fmt.Errorf("bad webhook.endpoint_url: %s", webhook.EndpointURL) @@ -193,10 +194,10 @@ func testAccCheckWebhookPropertiesUpdated(webhook *tailscale.Webhook) resource.T return fmt.Errorf("bad webhook.provider_type: %s", webhook.ProviderType) } - expectedSubscriptions := []tailscale.WebhookSubscriptionType{ - tailscale.WebhookNodeCreated, - tailscale.WebhookUserRoleUpdated, - tailscale.WebhookUserSuspended, + expectedSubscriptions := []ts.WebhookSubscriptionType{ + ts.WebhookNodeCreated, + ts.WebhookUserRoleUpdated, + ts.WebhookUserSuspended, } slices.Sort(expectedSubscriptions) diff --git a/tailscale/tailscale_test.go b/tailscale/tailscale_test.go index b101cf66..d247f14e 100644 --- a/tailscale/tailscale_test.go +++ b/tailscale/tailscale_test.go @@ -7,11 +7,15 @@ import ( "io" "net" "net/http" + "net/url" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" - "github.com/tailscale/tailscale-client-go/tailscale" + ts "github.com/tailscale/tailscale-client-go/tailscale" + tailscalev2 "github.com/tailscale/tailscale-client-go/v2" + "github.com/tailscale/terraform-provider-tailscale/tailscale" ) type TestServer struct { @@ -25,7 +29,7 @@ type TestServer struct { ResponseBody interface{} } -func NewTestHarness(t *testing.T) (*tailscale.Client, *TestServer) { +func NewTestHarness(t *testing.T) (*tailscale.Clients, *TestServer) { t.Helper() testServer := &TestServer{ @@ -52,12 +56,20 @@ func NewTestHarness(t *testing.T) (*tailscale.Client, *TestServer) { }) baseURL := fmt.Sprintf("http://localhost:%v", listener.Addr().(*net.TCPAddr).Port) - client, err := tailscale.NewClient("not-a-real-key", "example.com", tailscale.WithBaseURL(baseURL)) + client, err := ts.NewClient("not-a-real-key", "example.com", ts.WithBaseURL(baseURL)) if err != nil { assert.FailNow(t, "Client initialization failed", err.Error()) } - return client, testServer + parsedBaseURL, err := url.Parse(baseURL) + require.NoError(t, err) + clientV2 := &tailscalev2.Client{ + BaseURL: parsedBaseURL, + APIKey: "not-a-real-key", + Tailnet: "example.com", + } + + return &tailscale.Clients{client, clientV2}, testServer } func (t *TestServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {