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

Commit

Permalink
Treat tags as an unordered set
Browse files Browse the repository at this point in the history
Breaking change.  The `tags` attribute will change from string-type to
set-of-string type.  Users will need to make the following syntactic
change to their Terraform configuration files:

    // before:
    resource "pingdom_check" "example" {
      // ...
      tags = "foo,bar"
    }

    // after:
    resource "pingdom_check" "example" {
      // ...
      tags = ["foo", "bar"]  // order no longer relevant
    }

This commit includes a schema migration for existing user state.

---

The Pingdom API returns tag elements in an unpredictable order.
Ordering for this type is not documented in the Pingdom API spec.
Current plugin behaviour often leads to spurious diffs against state.

https://www.pingdom.com/api/2.1/#MethodGet+Check+List

Notionally, tags should be considered to be sets in the Pingdom data
model.  It does not make much practical sense for us to enforce strict
ordering on them.  Any implied ordering is likely an inadvertent
side-effect of JSON's lack of an unordered set type.

This change will hash tag names using the FNV-1a alternate algorithm.
(Tag names are the only labels that users see in the Pingdom UI and API.
Pingdom do not expose tag IDs in their API.)

http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a

As far as I know, this algorithm is a good fit for set element hash
computation:  it is said to be fast, said to provide reasonably good
dispersion, and is a part of the Go standard library.
  • Loading branch information
saj committed Nov 13, 2019
1 parent 1beece6 commit ee86b12
Showing 1 changed file with 213 additions and 5 deletions.
218 changes: 213 additions & 5 deletions pingdom/resource_pingdom_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pingdom

import (
"fmt"
"hash/fnv"
"log"
"strconv"
"strings"
Expand Down Expand Up @@ -146,10 +147,12 @@ func resourcePingdomCheck() *schema.Resource {
Optional: true,
ForceNew: false,
},

"tags": {
Type: schema.TypeString,
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
},

"probefilters": {
Expand Down Expand Up @@ -184,6 +187,15 @@ func resourcePingdomCheck() *schema.Resource {
ForceNew: false,
},
},

SchemaVersion: 1,
StateUpgraders: []schema.StateUpgrader{
{
Version: 0,
Type: resourcePingdomCheckResourceV0().CoreConfigSchema().ImpliedType(),
Upgrade: resourcePingdomCheckStateUpgradeV0,
},
},
}
}

Expand Down Expand Up @@ -315,7 +327,12 @@ func checkForResource(d *schema.ResourceData) (pingdom.Check, error) {
}
}
if v, ok := d.GetOk("tags"); ok {
checkParams.Tags = v.(string)
interfaceSlice := v.(*schema.Set).List()
tags := make([]string, len(interfaceSlice))
for i := range interfaceSlice {
tags[i] = interfaceSlice[i].(string)
}
checkParams.Tags = strings.Join(tags, ",")
}

if v, ok := d.GetOk("probefilters"); ok {
Expand Down Expand Up @@ -467,11 +484,18 @@ func resourcePingdomCheckRead(d *schema.ResourceData, meta interface{}) error {
d.Set("notifywhenbackup", ck.NotifyWhenBackup)
d.Set("publicreport", inPublicReport)

tags := []string{}
tags := schema.NewSet(
func(tagName interface{}) int {
h := fnv.New32a()
h.Write([]byte(tagName.(string)))
return int(h.Sum32())
},
[]interface{}{},
)
for _, tag := range ck.Tags {
tags = append(tags, tag.Name)
tags.Add(tag.Name)
}
d.Set("tags", strings.Join(tags, ","))
d.Set("tags", tags)

if ck.Status == "paused" {
d.Set("paused", true)
Expand Down Expand Up @@ -580,3 +604,187 @@ func resourcePingdomCheckDelete(d *schema.ResourceData, meta interface{}) error

return nil
}

func resourcePingdomCheckResourceV0() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: false,
},

"host": {
Type: schema.TypeString,
Required: true,
ForceNew: false,
},

"type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"paused": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
},

"responsetime_threshold": {
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
Computed: true,
},

"publicreport": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
},

"resolution": {
Type: schema.TypeInt,
Required: true,
ForceNew: false,
},

"sendnotificationwhendown": {
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
Computed: true,
},

"notifyagainevery": {
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
},

"notifywhenbackup": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
Computed: true,
},

"integrationids": {
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeInt},
},

"encryption": {
Type: schema.TypeBool,
Optional: true,
ForceNew: false,
},

"url": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
Default: "/",
},

"port": {
Type: schema.TypeInt,
Optional: true,
ForceNew: false,
Computed: true,
},

"username": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"password": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"shouldcontain": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"shouldnotcontain": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"postdata": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"requestheaders": {
Type: schema.TypeMap,
Optional: true,
ForceNew: false,
},

"tags": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"probefilters": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"userids": {
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeInt},
},

"teamids": {
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeInt},
},

"stringtosend": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},

"stringtoexpect": {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
},
}
}

func resourcePingdomCheckStateUpgradeV0(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
oldTags, ok := rawState["tags"]
if !ok {
return rawState, nil
}
newTags := []string{}
tags := strings.Split(oldTags.(string), ",")
for _, t := range tags {
newTags = append(newTags, t)
}
rawState["tags"] = newTags
return rawState, nil
}

0 comments on commit ee86b12

Please sign in to comment.