Skip to content
This repository has been archived by the owner on Jul 27, 2023. It is now read-only.

feat(bunny_pullzone): add a datasource for pull zones #98

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/data-sources/bunny_pullzone/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
data "bunny_pullzone" "example" {
pull_zone_id = 12345
}
89 changes: 89 additions & 0 deletions internal/provider/data_source_pullzone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package provider

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
keyPullZoneID = "pull_zone_id"
)

func dataSourcePullZone() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourcePullZoneRead,

Schema: map[string]*schema.Schema{
keyPullZoneID: {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
keyAWSSigningKey: {
Type: schema.TypeString,
Description: "AWS Signing Key",
Computed: true,
},
keyAWSSigningRegionName: {
Type: schema.TypeString,
Computed: true,
Description: "The AWS Signing region name.",
},
keyAWSSigningSecret: {
Type: schema.TypeString,
Sensitive: true,
Computed: true,
Description: "The AWS Signing region secret.",
},
keyCnameDomain: {
Type: schema.TypeString,
Description: "The CNAME domain of the Pull Zone for setting up custom hostnames.",
Computed: true,
},
keyName: {
Type: schema.TypeString,
Computed: true,
Description: "The name of the Pull Zone.",
},
keyStorageZoneID: {
Type: schema.TypeInt,
Computed: true,
Description: "The ID of the storage zone that the Pull Zone is linked to.",
},
keyZoneSecurityEnabled: {
Type: schema.TypeBool,
Computed: true,
Description: "True if the URL secure token authentication security is enabled.",
},
keyZoneSecurityIncludeHashRemoteIP: {
Type: schema.TypeBool,
Computed: true,
Description: "True if the zone security hash should include the remote IP.",
Comment on lines +55 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the usecase for keyZoneSecurityEnabled and keyZoneSecurityIncludeHashRemoteIP, when managing a videolib?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are properties that, while exposed via the Video Library update endpoint, are not fetchable as part of the Video Library, as they reside exclusively on the Pull Zone. I put them in here as I think they'll be helpful in certain cases to determine the full state of a video library.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that the EnableTokenIPVerification and PlayerTokenAuthenticationEnabled fields in the videolib update call change the keyZoneSecurityEnabled and keyZoneSecurityIncludeHashRemoteIP settings in the pull-zone? 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, those are pass throughs that bunny internally sets on the managed pull-zone. The unfortunate part is that they don't also return them in the video library API. I asked the Bunny team about this in a support request and they said you have to query the Pull Zone directly to get them. The other alternative to this is to do a special PullZone.Get as part of the VideoLibrary update process, which doesn't feel great to me. Exposing them in this way seemed fairly simple, although it has the downside of your bunny_videolibrary resource state potentially being incorrect if something weird happens.

I do like bunny a lot, but when they do stuff like this I get a bit frustrated with them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hum, I see, that makes it complicated.

Having to use fields in a pullzone datasource as workaround for the issue makes the Terraform provider difficult to use.
Users would need to know these API internals, that a videolib has an underlying pullzone, that some videolib attributes are stored in the pullzone and also the different attribute names.

A simple solution in other cases were a field was not retrievable via the Get endpoint was to mark the resource attribute as ForceNew: true.
I don't know if that is feasible for those videolib attributes.
Would be having to recreated the videolib when these attributes are changed an option?

Otherwise I think it is better to do the additional pullzone get call in the ReadContext function of the videolib.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ForceNew isn't really an option. It would delete all of your video content. As of the current state of the videolibrary resource I did something similar to what we did on the custom_404 property for storage zones if you recall that. Effectively, if the API call succeeds we just update the state to whatever value was sent to bunny. Which seems finish... until I found a bug in the bunny API where they were silently failing to set those properties in certain circumstances (I filed a bug report with them and they're going to fix it).

But still, stuff like that can happen unless you do a hard read on them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if we're going to read the pullzone in the bunny videolibrary resource, then it seems like we might as well return the entire data set that we care about from it instead of doing a datasource. The data source seemed like a cleaner way at first, but maybe that should be considered instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if we're going to read the pullzone in the bunny videolibrary resource, then it seems like we might as well return the entire data set that we care about from it instead of doing a datasource.

Let's go for that solution.
Hiding these ugly internals to make the provider easier to use will be worth the additional api requests and code.

},
keyZoneSecurityKey: {
Type: schema.TypeString,
Computed: true,
Sensitive: true,
Description: "The security key used for secure URL token authentication.",
},
},
}
}

func dataSourcePullZoneRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
id := d.Get(keyPullZoneID).(int)
d.SetId(fmt.Sprint(id))

pz, err := readPullZone(ctx, d, meta)
if err != nil {
return err
}

if err := pullZoneToResourceShared(pz, d); err != nil {
return diagsErrFromErr("converting api type to datasource after successful read failed", err)
}

return nil
}
37 changes: 37 additions & 0 deletions internal/provider/data_source_pullzone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package provider

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourcePullZone(t *testing.T) {
resourceName := randResourceName()
resource.Test(t, resource.TestCase{
Providers: testProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourcePullZone(resourceName),
Check: resource.TestCheckResourceAttr(
"data.bunny_pullzone.dpz1",
"name",
resourceName,
),
},
},
})
}

func testAccDataSourcePullZone(rName string) string {
return fmt.Sprintf(`
resource "bunny_pullzone" "pz1" {
name = %q
origin_url = "https://terraform.io"
}

data "bunny_pullzone" "dpz1" {
pull_zone_id = bunny_pullzone.pz1.id
}`, rName)
}
3 changes: 3 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ func New() *schema.Provider {
Description: "The bunny.net API Key.",
},
},
DataSourcesMap: map[string]*schema.Resource{
"bunny_pullzone": dataSourcePullZone(),
},
ResourcesMap: map[string]*schema.Resource{
"bunny_pullzone": resourcePullZone(),
"bunny_edgerule": resourceEdgeRule(),
Expand Down
93 changes: 60 additions & 33 deletions internal/provider/resource_pullzone.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,24 +531,41 @@ func resourcePullZoneUpdate(ctx context.Context, d *schema.ResourceData, meta in
return nil
}

func resourcePullZoneRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
func resourcePullZoneRead(
ctx context.Context,
d *schema.ResourceData,
meta interface{},
) diag.Diagnostics {
pz, err := readPullZone(ctx, d, meta)
if err != nil {
return err
}

if err := pullZoneToResource(pz, d); err != nil {
return diagsErrFromErr("converting api type to resource data after successful read failed", err)
}

return nil
}

func readPullZone(
ctx context.Context,
d *schema.ResourceData,
meta interface{},
) (*bunny.PullZone, diag.Diagnostics) {
clt := meta.(*bunny.Client)

id, err := getIDAsInt64(d)
if err != nil {
return diag.FromErr(err)
return nil, diag.FromErr(err)
}

pz, err := clt.PullZone.Get(ctx, id)
if err != nil {
return diagsErrFromErr("could not retrieve pull zone", err)
return nil, diagsErrFromErr("could not retrieve pull zone", err)
}

if err := pullZoneToResource(pz, d); err != nil {
return diagsErrFromErr("converting api type to resource data after successful read failed", err)
}

return nil
return pz, nil
}

func resourcePullZoneDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
Expand All @@ -569,15 +586,12 @@ func resourcePullZoneDelete(ctx context.Context, d *schema.ResourceData, meta in
return nil
}

// pullZoneToResource sets fields in d to the values in pz.
func pullZoneToResource(pz *bunny.PullZone, d *schema.ResourceData) error {
// pullZoneToResource sets shared fields in d to the values in pz.
func pullZoneToResourceShared(pz *bunny.PullZone, d *schema.ResourceData) error {
// shared
if pz.ID != nil {
d.SetId(strconv.FormatInt(*pz.ID, 10))
}

if err := d.Set(keyAWSSigningEnabled, pz.AWSSigningEnabled); err != nil {
return err
}
if err := d.Set(keyAWSSigningKey, pz.AWSSigningKey); err != nil {
return err
}
Expand All @@ -587,6 +601,38 @@ func pullZoneToResource(pz *bunny.PullZone, d *schema.ResourceData) error {
if err := d.Set(keyAWSSigningSecret, pz.AWSSigningSecret); err != nil {
return err
}
if err := d.Set(keyCnameDomain, pz.CnameDomain); err != nil {
return err
}
if err := d.Set(keyName, pz.Name); err != nil {
return err
}
if err := d.Set(keyStorageZoneID, pz.StorageZoneID); err != nil {
return err
}
if err := d.Set(keyZoneSecurityEnabled, pz.ZoneSecurityEnabled); err != nil {
return err
}
if err := d.Set(keyZoneSecurityIncludeHashRemoteIP, pz.ZoneSecurityIncludeHashRemoteIP); err != nil {
return err
}
if err := d.Set(keyZoneSecurityKey, pz.ZoneSecurityKey); err != nil {
return err
}

return nil
}

// pullZoneToResource sets fields in d to the values in pz.
func pullZoneToResource(pz *bunny.PullZone, d *schema.ResourceData) error {
// map shared values
if err := pullZoneToResourceShared(pz, d); err != nil {
return err
}

if err := d.Set(keyAWSSigningEnabled, pz.AWSSigningEnabled); err != nil {
return err
}
if err := setStrSet(d, keyAllowedReferrers, pz.AllowedReferrers, ignoreOrderOpt, caseInsensitiveOpt); err != nil {
return err
}
Expand Down Expand Up @@ -644,9 +690,6 @@ func pullZoneToResource(pz *bunny.PullZone, d *schema.ResourceData) error {
if err := d.Set(keyEnableHostnameVary, pz.EnableHostnameVary); err != nil {
return err
}
if err := d.Set(keyCnameDomain, pz.CnameDomain); err != nil {
return err
}
if err := d.Set(keyEnableLogging, pz.EnableLogging); err != nil {
return err
}
Expand Down Expand Up @@ -722,34 +765,18 @@ func pullZoneToResource(pz *bunny.PullZone, d *schema.ResourceData) error {
if err := d.Set(keyPermaCacheStorageZoneID, pz.PermaCacheStorageZoneID); err != nil {
return err
}
if err := d.Set(keyStorageZoneID, pz.StorageZoneID); err != nil {
return err
}
if err := d.Set(keyType, pz.Type); err != nil {
return err
}
if err := d.Set(keyVerifyOriginSSL, pz.VerifyOriginSSL); err != nil {
return err
}
if err := d.Set(keyZoneSecurityEnabled, pz.ZoneSecurityEnabled); err != nil {
return err
}
if err := d.Set(keyZoneSecurityIncludeHashRemoteIP, pz.ZoneSecurityIncludeHashRemoteIP); err != nil {
return err
}
if err := setStrSet(d, keyBlockedReferrers, pz.BlockedReferrers, ignoreOrderOpt, caseInsensitiveOpt); err != nil {
return err
}
if err := d.Set(keyEnabled, pz.Enabled); err != nil {
return err
}
if err := d.Set(keyName, pz.Name); err != nil {
return err
}
if err := d.Set(keyZoneSecurityKey, pz.ZoneSecurityKey); err != nil {
return err
}

if err := safeHopToResource(pz, d); err != nil {
return err
}
Expand Down