Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for worker text bindings #710

Merged
merged 11 commits into from
Jun 21, 2020
133 changes: 103 additions & 30 deletions cloudflare/resource_cloudflare_worker_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,50 @@ import (
"strings"

cloudflare "github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/pkg/errors"
)

var kvNamespaceBindingResource = &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"namespace_id": {
Type: schema.TypeString,
Required: true,
},
},
}

var plainTextBindingResource = &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"text": {
Type: schema.TypeString,
Required: true,
},
},
}

var secretTextBindingResource = &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"text": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
},
}

func resourceCloudflareWorkerScript() *schema.Resource {
return &schema.Resource{
Create: resourceCloudflareWorkerScriptCreate,
Expand All @@ -27,38 +66,29 @@ func resourceCloudflareWorkerScript() *schema.Resource {
Required: true,
ForceNew: true,
},

"content": {
Type: schema.TypeString,
Required: true,
},
"plain_text_binding": {
Type: schema.TypeSet,
Optional: true,
Elem: plainTextBindingResource,
},
"secret_text_binding": {
Type: schema.TypeSet,
Optional: true,
Elem: secretTextBindingResource,
},
"kv_namespace_binding": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"namespace_id": {
Type: schema.TypeString,
Required: true,
},
},
},
Set: resourceCloudflareWorkerScriptKvNamespaceBindingHash,
Elem: kvNamespaceBindingResource,
},
},
}
}

func resourceCloudflareWorkerScriptKvNamespaceBindingHash(v interface{}) int {
m := v.(map[string]interface{})

return hashcode.String(fmt.Sprintf("%s-%s", m["name"].(string), m["namespace_id"].(string)))
}

type ScriptData struct {
// The script id will be the `name` for named script
// or the `zone_name` for zone-scoped scripts
Expand Down Expand Up @@ -96,13 +126,27 @@ func getWorkerScriptBindings(scriptName string, client *cloudflare.API) (ScriptB
return bindings, nil
}

func parseWorkerKvNamespaceBindings(d *schema.ResourceData, bindings ScriptBindings) {
func parseWorkerBindings(d *schema.ResourceData, bindings ScriptBindings) {
for _, rawData := range d.Get("kv_namespace_binding").(*schema.Set).List() {
data := rawData.(map[string]interface{})
bindings[data["name"].(string)] = cloudflare.WorkerKvNamespaceBinding{
NamespaceID: data["namespace_id"].(string),
}
}

for _, rawData := range d.Get("plain_text_binding").(*schema.Set).List() {
data := rawData.(map[string]interface{})
bindings[data["name"].(string)] = cloudflare.WorkerPlainTextBinding{
Text: data["text"].(string),
}
}

for _, rawData := range d.Get("secret_text_binding").(*schema.Set).List() {
data := rawData.(map[string]interface{})
bindings[data["name"].(string)] = cloudflare.WorkerSecretTextBinding{
Text: data["text"].(string),
}
}
}

func resourceCloudflareWorkerScriptCreate(d *schema.ResourceData, meta interface{}) error {
Expand All @@ -128,7 +172,7 @@ func resourceCloudflareWorkerScriptCreate(d *schema.ResourceData, meta interface

bindings := make(ScriptBindings)

parseWorkerKvNamespaceBindings(d, bindings)
parseWorkerBindings(d, bindings)

scriptParams := cloudflare.WorkerScriptParams{
Script: scriptBody,
Expand Down Expand Up @@ -163,17 +207,21 @@ func resourceCloudflareWorkerScriptRead(d *schema.ResourceData, meta interface{}
}

return errors.Wrap(err,
fmt.Sprintf("Error reading worker script from API for resouce %+v", &scriptData.Params))
fmt.Sprintf("Error reading worker script from API for resource %+v", &scriptData.Params))
}

existingBindings := make(ScriptBindings)

parseWorkerBindings(d, existingBindings)

bindings, err := getWorkerScriptBindings(d.Get("name").(string), client)
if err != nil {
return err
}

kvNamespaceBindings := &schema.Set{
F: resourceCloudflareWorkerScriptKvNamespaceBindingHash,
}
kvNamespaceBindings := &schema.Set{F: schema.HashResource(kvNamespaceBindingResource)}
plainTextBindings := &schema.Set{F: schema.HashResource(plainTextBindingResource)}
secretTextBindings := &schema.Set{F: schema.HashResource(secretTextBindingResource)}

for name, binding := range bindings {
switch v := binding.(type) {
Expand All @@ -182,13 +230,38 @@ func resourceCloudflareWorkerScriptRead(d *schema.ResourceData, meta interface{}
"name": name,
"namespace_id": v.NamespaceID,
})
case cloudflare.WorkerPlainTextBinding:
plainTextBindings.Add(map[string]interface{}{
"name": name,
"text": v.Text,
})
case cloudflare.WorkerSecretTextBinding:
value := v.Text
switch v := existingBindings[name].(type) {
case cloudflare.WorkerSecretTextBinding:
value = v.Text
}
secretTextBindings.Add(map[string]interface{}{
"name": name,
"text": value,
})
}
}

_ = d.Set("content", r.Script)
if err := d.Set("content", r.Script); err != nil {
return fmt.Errorf("cannot set content: %v", err)
}

if err := d.Set("kv_namespace_binding", kvNamespaceBindings); err != nil {
return fmt.Errorf("cannot set kv_namespace bindings for firewall policy (%s): %v", d.Id(), err)
return fmt.Errorf("cannot set kv namespace bindings (%s): %v", d.Id(), err)
}

if err := d.Set("plain_text_binding", plainTextBindings); err != nil {
return fmt.Errorf("cannot set plain text bindings (%s): %v", d.Id(), err)
}

if err := d.Set("secret_text_binding", secretTextBindings); err != nil {
return fmt.Errorf("cannot set secret text bindings (%s): %v", d.Id(), err)
}

return nil
Expand All @@ -211,7 +284,7 @@ func resourceCloudflareWorkerScriptUpdate(d *schema.ResourceData, meta interface

bindings := make(ScriptBindings)

parseWorkerKvNamespaceBindings(d, bindings)
parseWorkerBindings(d, bindings)

scriptParams := cloudflare.WorkerScriptParams{
Script: scriptBody,
Expand Down
25 changes: 15 additions & 10 deletions cloudflare/resource_cloudflare_worker_script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ func TestAccCloudflareWorkerScript_MultiScriptEnt(t *testing.T) {
),
},
{
Config: testAccCheckCloudflareWorkerScriptConfigMultiScriptUpdateKvNamespaceBinding(rnd),
Config: testAccCheckCloudflareWorkerScriptConfigMultiScriptUpdateBinding(rnd),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareWorkerScriptExists(name, &script, []string{rnd, fmt.Sprintf("%s-copy", rnd)}),
testAccCheckCloudflareWorkerScriptExists(name, &script, []string{"KV_NAMESPACE", "PLAIN_TEXT", "SECRET_TEXT"}),
resource.TestCheckResourceAttr(name, "name", rnd),
resource.TestCheckResourceAttr(name, "content", scriptContent2),
),
Expand All @@ -74,24 +74,29 @@ resource "cloudflare_worker_script" "%[1]s" {
}`, rnd, scriptContent2)
}

func testAccCheckCloudflareWorkerScriptConfigMultiScriptUpdateKvNamespaceBinding(rnd string) string {
func testAccCheckCloudflareWorkerScriptConfigMultiScriptUpdateBinding(rnd string) string {
return fmt.Sprintf(`
resource "cloudflare_workers_kv_namespace" "%[1]s" {
title = "%[1]s"
title = "%[1]s"
}

resource "cloudflare_worker_script" "%[1]s" {
name = "%[1]s"
name = "%[1]s"
content = "%[2]s"

kv_namespace_binding {
name = "%[1]s"
namespace_id = cloudflare_workers_kv_namespace.%[1]s.id
name = "KV_NAMESPACE"
namespace_id = cloudflare_workers_kv_namespace.%[1]s.id
}

kv_namespace_binding {
name = "%[1]s-copy"
namespace_id = cloudflare_workers_kv_namespace.%[1]s.id
plain_text_binding {
name = "PLAIN_TEXT"
jacobbednarz marked this conversation as resolved.
Show resolved Hide resolved
text = "%[1]s"
}

secret_text_binding {
name = "SECRET_TEXT"
text = "%[1]s"
}
}`, rnd, scriptContent2)
}
Expand Down
30 changes: 25 additions & 5 deletions website/docs/r/worker_script.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,21 @@ resource "cloudflare_workers_kv_namespace" "my_namespace" {
resource "cloudflare_worker_script" "my_script" {
name = "script_1"
content = file("script.js")

kv_namespace_binding {
name = "my_binding"
name = "DB"
blakeembrey marked this conversation as resolved.
Show resolved Hide resolved
namespace_id = cloudflare_workers_kv_namespace.my_namespace.id
}

plain_text_binding {
name = "ENVIRONMENT"
text = "staging"
blakeembrey marked this conversation as resolved.
Show resolved Hide resolved
}

secret_text_binding {
name = "SENTRY_DSN"
text = var.sentry_dsn
blakeembrey marked this conversation as resolved.
Show resolved Hide resolved
}
}
```

Expand All @@ -36,10 +46,20 @@ The following arguments are supported:
* `name` - (Required) The name for the script.
* `content` - (Required) The script content.

**kv_namespace_binding** (optional) block supports:
**kv_namespace_binding** supports:

* `name` - (Required) The global variable for the binding in your Worker code.
* `kv_namespace_id` - (Required) ID of the KV namespace you want to use.

**plain_text_binding** supports:

* `name` - (Required) The global variable for the binding in your Worker code.
* `text` - (Required) The plain text you want to store.

**secret_text_binding** supports:

* `name` - (Required) The name for the binding.
* `namespace_id` - (Required) ID of KV namespace.
* `name` - (Required) The global variable for the binding in your Worker code.
* `text` - (Required) The secret text you want to store.

## Import

Expand Down