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

avoid unnecessary diffs of members #33

Merged
merged 6 commits into from
Oct 25, 2022
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ HOSTNAME=registry.terraform.io
NAMESPACE=zerotier
NAME=zerotier
BINARY=terraform-provider-${NAME}
VERSION=0.2.0
VERSION=0.2.2
OS_ARCH=$(shell go env GOOS)_$(shell go env GOARCH)
GOLANGCI_LINT_VERSION=1.34.1
BUILD=go build -ldflags "-X github.com/zerotier/terraform-provider-zerotier/pkg/zerotier.Version=${VERSION}" -o
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,15 @@ Included here is a description of the Make tasks and environment variables you n
- set in env or write to `test-token.txt` at the root.
- env is preferred but the token from file is just propagated to env and gitignored. No different, just easier to use.

## Cleanup commands you may find useful
Sometimes tests fail and resources get left behind

- `docker ps -a -f name=zerotier --format {{.ID}} | xargs docker rm -f` removed orphaned containers with "zerotier" in the name.
- `docker network prune` removed orphaned networks
- `rm /tmp/tftest` if problems downloading kreuzwerker/docker.
- Delete the "hello-word" auth token from your central account.
- Don't use your main central account for tests. Use an alternate. Delete it and recreate when too many networks to clean up.

# License

- BSD 3-Clause
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.8.0
github.com/zclconf/go-cty v1.9.0 // indirect
github.com/zerotier/go-ztcentral v0.5.3
github.com/zerotier/go-ztidentity v1.0.0
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -391,13 +391,16 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
Expand Down Expand Up @@ -749,8 +752,9 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
Expand Down
8 changes: 8 additions & 0 deletions pkg/zerotier/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func fetchStringList(d *schema.ResourceData, attr string) *[]string {
return toStringList(d.Get(attr).([]interface{})).(*[]string)
}

func fetchStringSet(d *schema.ResourceData, attr string) *[]string {
return toStringList(d.Get(attr).(*schema.Set).List()).(*[]string)
}

func toStringList(i interface{}) interface{} {
ray := &[]string{}
for _, x := range i.([]interface{}) {
Expand All @@ -56,6 +60,10 @@ func fetchIntList(d *schema.ResourceData, attr string) *[]int {
return toIntList(d.Get(attr).([]interface{})).(*[]int)
}

func fetchIntSet(d *schema.ResourceData, attr string) *[]int {
return toIntList(d.Get(attr).(*schema.Set).List()).(*[]int)
}

func fetchTags(d []interface{}) *[][]int {
tags := [][]int{}

Expand Down
48 changes: 48 additions & 0 deletions pkg/zerotier/converters_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package zerotier

import (
// "log"
"testing"

"github.com/stretchr/testify/assert"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/zerotier/go-ztcentral/pkg/spec"
)

func TestZeroTier_ToMember(t *testing.T) {

d := schema.TestResourceDataRaw(t, resourceMember().Schema, map[string]interface{}{
"network_id": "11122334455aabbccdd",
"ip_assignments": []interface{}{"10.10.10.10", "1.2.3.4"},
"capabilities": []interface{}{1, 2, 3},
"tags": []interface{}{[]interface{}{1, 2}, []interface{}{3, 4}},
"member_id": "2468012345",
// "hidden": false,
// "name": "a name",
// "description": "a description",
// "authorized": true,
// "allow_ethernet_bridging": false,
// "no_auto_assign_ips": false,
})

expectedNetworkId := "11122334455aabbccdd"
expectedTags := [][]int{{1, 2}, {3, 4}}
expectedCaps := []int{1, 2, 3}
expectedIps := []string{"10.10.10.10", "1.2.3.4"}
expected := &spec.Member{
NetworkId: &expectedNetworkId,
Config: &spec.MemberConfig{
Tags: &expectedTags,
Capabilities: &expectedCaps,
IpAssignments: &expectedIps,
},
}
out := toMember(d)

assert.Equal(t, *expected.NetworkId, *out.NetworkId)
assert.Equal(t, "2468012345", *out.NodeId)
assert.ElementsMatch(t, *expected.Config.IpAssignments, *out.Config.IpAssignments)
assert.ElementsMatch(t, *expected.Config.Tags, *out.Config.Tags)
assert.ElementsMatch(t, *expected.Config.Capabilities, *out.Config.Capabilities)
}
87 changes: 81 additions & 6 deletions pkg/zerotier/member.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package zerotier

import (
"strconv"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand Down Expand Up @@ -59,7 +60,7 @@ var MemberSchema = map[string]*schema.Schema{
Description: "Exempt this member from the IP auto assignment pool on a Network",
},
"ip_assignments": {
Type: schema.TypeList,
Type: schema.TypeSet,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Expand All @@ -68,7 +69,7 @@ var MemberSchema = map[string]*schema.Schema{
Description: "List of IP address assignments",
},
"capabilities": {
Type: schema.TypeList,
Type: schema.TypeSet,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Expand All @@ -77,7 +78,7 @@ var MemberSchema = map[string]*schema.Schema{
Description: "List of network capabilities",
},
"tags": {
Type: schema.TypeList,
Type: schema.TypeSet,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Expand All @@ -88,6 +89,36 @@ var MemberSchema = map[string]*schema.Schema{
},
Description: "List of network tags",
},
"ipv4_assignments": {
Type: schema.TypeSet,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "ZeroTier managed IPv4 addresses.",
},
"ipv6_assignments": {
Type: schema.TypeSet,
Computed: true,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "ZeroTier managed IPv6 addresses.",
},
"sixplane": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Computed 6PLANE address. assign_ipv6.sixplane must be enabled on the network resource.",
},
"rfc4193": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Computed RFC4193 address. assign_ipv6.rfc4193 must be enabled on the network resource.",
},
}

func toMember(d *schema.ResourceData) *spec.Member {
Expand All @@ -102,9 +133,9 @@ func toMember(d *schema.ResourceData) *spec.Member {
Authorized: boolPtr(d.Get("authorized").(bool)),
ActiveBridge: boolPtr(d.Get("allow_ethernet_bridging").(bool)),
NoAutoAssignIps: boolPtr(d.Get("no_auto_assign_ips").(bool)),
Capabilities: fetchIntList(d, "capabilities"),
IpAssignments: fetchStringList(d, "ip_assignments"),
Tags: fetchTags(d.Get("tags").([]interface{})),
Capabilities: fetchIntSet(d, "capabilities"),
IpAssignments: fetchStringSet(d, "ip_assignments"),
Tags: fetchTags(d.Get("tags").(*schema.Set).List()),
},
}
}
Expand All @@ -124,5 +155,49 @@ func memberToTerraform(d *schema.ResourceData, m *spec.Member) diag.Diagnostics
d.Set("capabilities", *m.Config.Capabilities)
d.Set("tags", *m.Config.Tags)

ipv4Assignments, ipv6Assignments := assignedIpsGrouping(*m.Config.IpAssignments)
d.Set("ipv4_assignments", ipv4Assignments)
d.Set("ipv6_assignments", ipv6Assignments)
d.Set("rfc4193", rfc4193Address(d))
d.Set("sixplane", sixPlaneAddress(d))

return nil
}

func sixPlaneAddress(d *schema.ResourceData) string {
nwid, nodeID := resourceNetworkAndNodeIdentifiers(d)
return buildIPV6("fd" + nwid + "9993" + nodeID)
}

func rfc4193Address(d *schema.ResourceData) string {
nwid, nodeID := resourceNetworkAndNodeIdentifiers(d)
nwidInt, _ := strconv.ParseUint(nwid, 16, 64)
networkMask := uint32((nwidInt >> 32) ^ nwidInt)
networkPrefix := strconv.FormatUint(uint64(networkMask), 16)
return buildIPV6("fc" + networkPrefix + nodeID + "000000000001")
}

// Receive a string and format every 4th element with a ":"
func buildIPV6(data string) (result string) {
s := strings.SplitAfter(data, "")
end := len(s) - 1
result = ""
for i, s := range s {
result += s
if (i+1)%4 == 0 && i != end {
result += ":"
}
}
return
}

func assignedIpsGrouping(ipAssignments []string) (ipv4s []string, ipv6s []string) {
for _, element := range ipAssignments {
if strings.Contains(element, ":") {
ipv6s = append(ipv6s, element)
} else {
ipv4s = append(ipv4s, element)
}
}
return
}