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 worker kv #595

Merged
merged 7 commits into from
Feb 27, 2020
Merged
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
1 change: 1 addition & 0 deletions cloudflare/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ func Provider() terraform.ResourceProvider {
"cloudflare_waf_rule": resourceCloudflareWAFRule(),
"cloudflare_worker_route": resourceCloudflareWorkerRoute(),
"cloudflare_worker_script": resourceCloudflareWorkerScript(),
"cloudflare_workers_kv": resourceCloudflareWorkerKV(),
"cloudflare_workers_kv_namespace": resourceCloudflareWorkersKVNamespace(),
"cloudflare_zone_lockdown": resourceCloudflareZoneLockdown(),
"cloudflare_zone_settings_override": resourceCloudflareZoneSettingsOverride(),
Expand Down
106 changes: 106 additions & 0 deletions cloudflare/resource_cloudflare_worker_kv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package cloudflare

import (
"context"
"fmt"
"log"
"strings"

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

func resourceCloudflareWorkerKV() *schema.Resource {
return &schema.Resource{
Create: resourceCloudflareWorkersKVUpdate,
Read: resourceCloudflareWorkersKVRead,
Update: resourceCloudflareWorkersKVUpdate,
Copy link
Member

Choose a reason for hiding this comment

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

I see your create + update methods are pretty similar. In some resources where this is also the case, the Create and Update just share the one method. Up to you as I don't mind either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there any harm in calling d.SetId in an Update method? That's the main difference between them. I'm all for saving some code if that isn't an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These have been combined into one method.

Delete: resourceCloudflareWorkersKVDelete,
Importer: &schema.ResourceImporter{
State: resourceCloudflareWorkersKVImport,
},

Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Required: true,
},
"namespace_id": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
nrf110 marked this conversation as resolved.
Show resolved Hide resolved
},
"value": {
Type: schema.TypeString,
Required: true,
},
},
}
}

func resourceCloudflareWorkersKVRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
namespaceID, key := parseId(d.Id())

value, err := client.ReadWorkersKV(context.Background(), namespaceID, key)
Copy link
Member

Choose a reason for hiding this comment

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

All of the Worker KV methods require setting api.AccountID however we're not setting it or documenting it as a required configuration parameter. How does it work at the moment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

None of the methods have a parameter for the account ID, it is set on the cloudflare.API client when the provider configures it. This is identical to how the existing cloudflare_workers_kv_namespace resource works, but I can add it to the documentation if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a note in the docs that setting account_id on the provider, or the CLOUDFLARE_ACCOUNT_ID environment variable is necessary to use this. Does that cover the requirement?

if err != nil {
return errors.Wrap(err, "error reading workers kv")
}

if value == nil {
d.SetId("")
return nil
}

d.Set("value", string(value))
return nil
}

func resourceCloudflareWorkersKVUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
namespaceID := d.Get("namespace_id").(string)
key := d.Get("key").(string)
value := d.Get("value").(string)

_, err := client.WriteWorkersKV(context.Background(), namespaceID, key, []byte(value))
if err != nil {
return errors.Wrap(err, "error creating workers kv")
}

d.SetId(fmt.Sprintf("%s/%s", namespaceID, key))

log.Printf("[INFO] Cloudflare Workers KV Namespace ID: %s", d.Id())

return resourceCloudflareWorkersKVRead(d, meta)
}

func resourceCloudflareWorkersKVDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.API)
namespaceID, key := parseId(d.Id())

log.Printf("[INFO] Deleting Cloudflare Workers KV with id: %+v", d.Id())

_, err := client.DeleteWorkersKV(context.Background(), namespaceID, key)
if err != nil {
return errors.Wrap(err, "error deleting workers kv")
}

return nil
}

func resourceCloudflareWorkersKVImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
namespaceID, key := parseId(d.Id())

d.Set("namespace_id", namespaceID)
d.Set("key", key)

resourceCloudflareWorkersKVRead(d, meta)

return []*schema.ResourceData{d}, nil
}

func parseId(id string) (string, string) {
parts := strings.SplitN(id, "/", 2)
return parts[0], parts[1]
}
91 changes: 91 additions & 0 deletions cloudflare/resource_cloudflare_worker_kv_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package cloudflare

import (
"context"
"fmt"
"testing"

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

func TestAccCloudflareWorkersKV_Basic(t *testing.T) {
t.Parallel()
var kvPair cloudflare.WorkersKVPair
name := generateRandomResourceName()
key := generateRandomResourceName()
value := generateRandomResourceName()
resourceName := "cloudflare_workers_kv." + name

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheckAccount(t) },
Providers: testAccProviders,
CheckDestroy: testAccCloudflareWorkersKVDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckCloudflareWorkersKV(name, key, value),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareWorkersKVExists(key, &kvPair),
resource.TestCheckResourceAttr(
resourceName, "value", value,
),
),
},
},
})
}

func testAccCloudflareWorkersKVDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*cloudflare.API)

for _, rs := range s.RootModule().Resources {
if rs.Type != "cloudflare_workers_kv" {
continue
}

namespaceID := rs.Primary.Attributes["namespace_id"]
key := rs.Primary.Attributes["key"]

_, err := client.ReadWorkersKV(context.Background(), namespaceID, key)

if err == nil {
return fmt.Errorf("workers kv pair still exists")
}
}

return nil
}

func testAccCheckCloudflareWorkersKV(rName string, key string, value string) string {
return testAccCheckCloudflareWorkersKVNamespace(rName) + fmt.Sprintf(`
resource "cloudflare_workers_kv" "%[1]s" {
namespace_id = cloudflare_workers_kv_namespace.%[1]s.id
key = "%[2]s"
value = "%[3]s"
}`, rName, key, value)
}

func testAccCheckCloudflareWorkersKVExists(key string, kv *cloudflare.WorkersKVPair) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*cloudflare.API)

for _, rs := range s.RootModule().Resources {
if rs.Type != "cloudflare_workers_kv" {
continue
}

namespaceID := rs.Primary.Attributes["namespace_id"]
value, err := client.ReadWorkersKV(context.Background(), namespaceID, key)
if err != nil {
return err
}

if value == nil {
return fmt.Errorf("workers kv key %s not found in namespace %s", key, namespaceID)
}
}

return nil
}
}
3 changes: 3 additions & 0 deletions website/cloudflare.erb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@
<li<%= sidebar_current("docs-cloudflare-resource-worker-script") %>>
<a href="/docs/providers/cloudflare/r/worker_script.html">cloudflare_worker_script</a>
</li>
<li<%= sidebar_current("docs-cloudflare-resource-workers-kv") %>>
<a href="/docs/providers/cloudflare/r/workers_kv.html">cloudflare_workers_kv</a>
</li>
<li<%= sidebar_current("docs-cloudflare-resource-workers-kv-namespace") %>>
<a href="/docs/providers/cloudflare/r/workers_kv_namespace.html">cloudflare_workers_kv_namespace</a>
</li>
Expand Down
45 changes: 45 additions & 0 deletions website/docs/r/workers_kv.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
layout: "cloudflare"
page_title: "Cloudflare: cloudflare_workers_kv"
sidebar_current: "docs-cloudflare-resource-workers-kv"
description: |-
Provides the ability to manage Cloudflare Workers KV features.
---

# cloudflare_workers_kv

Provides a Workers KV Pair. *NOTE:* This resource uses the Cloudflare account APIs. This requires setting the `CLOUDFLARE_ACCOUNT_ID` environment variable or `account_id` provider argument.

## Example Usage

```hcl
resource "cloudflare_workers_kv_namespace" "example_ns" {
title = "test-namespace"
}

resource "cloudflare_workers_kv" "example" {
namespace_id = cloudflare_workers_kv_namespace.example_ns.id
key = "test-key"
value = "test value"
}
```

## Argument Reference

The following arguments are supported:

* `namespace_id` - (Required) The ID of the Workers KV namespace in which you want to create the KV pair
* `key` - (Required) The key name
* `value` - (Required) The string value to be stored in the key


## Import

Workers KV Namespace settings can be imported using it's ID. **Note:** Because the same key can exist in multiple namespaces, the ID is a composite key of the format <namespace_id>_<key>.

```
$ terraform import cloudflare_workers_kv_namespace.example beaeb6716c9443eaa4deef11763ccca6_test-key
```

where:
- `beaeb6716c9443eaa4deef11763ccca6` is the ID of the namespace and `test-key` is the key