Skip to content

Commit

Permalink
Support flow rules; make compatible with the latest API
Browse files Browse the repository at this point in the history
This required a number of changes:

- SchemaWrap required a copy constructor, it was doing really dumb stuff
  I'd rather not discuss :)
- Pointer-ize many fields and manage boolean pointers safely and
  properly.

The rest is fairly wrapped around supporting flow rules.

Signed-off-by: Erik Hollensbe <[email protected]>
  • Loading branch information
Erik Hollensbe committed Feb 13, 2021
1 parent 09a6a82 commit 0463efb
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 25 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require (
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/zclconf/go-cty v1.7.1 // indirect
github.com/zerotier/go-ztcentral v0.2.0
github.com/zerotier/go-ztcentral v0.2.1-0.20210213070016-be6aaad4acde
github.com/zerotier/go-ztidentity v1.0.0
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/text v0.3.5 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ github.com/zclconf/go-cty v1.7.1/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPB
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zerotier/go-ztcentral v0.2.0 h1:/kFGwrDZA2uMQZPNKXhZNidWrai3YydxXW03qd8k4tM=
github.com/zerotier/go-ztcentral v0.2.0/go.mod h1:90weKZT7Rdq02tdJskc+XR8W93bMgL/9uMCq4Txz0Hk=
github.com/zerotier/go-ztcentral v0.2.1-0.20210213070016-be6aaad4acde h1:RlNjDsKOiZO25gT2zYaUoxagUPtb7Jx7VPF5fpzW4Es=
github.com/zerotier/go-ztcentral v0.2.1-0.20210213070016-be6aaad4acde/go.mod h1:90weKZT7Rdq02tdJskc+XR8W93bMgL/9uMCq4Txz0Hk=
github.com/zerotier/go-ztidentity v1.0.0 h1:dgm1ChTxw1TXMrSJQ6VWK2RmHKVYqRd69h/Co/RG+xo=
github.com/zerotier/go-ztidentity v1.0.0/go.mod h1:zcOy+qXl5A01QhR6dfqOTPiHFMBEjgPlPpDPAO+2jg4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
Expand Down
44 changes: 37 additions & 7 deletions pkg/zerotier/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ import (
"github.com/zerotier/go-ztcentral"
)

func boolPtr(b bool) *bool {
return &b
}

func ptrBool(p *bool) bool {
if p != nil && *p {
return true
}

return false
}

func getMemberIDs(d *schema.ResourceData) (string, string) {
ztNetworkID := d.Get("network_id").(string)
memberID := d.Get("member_id").(string)
Expand Down Expand Up @@ -176,19 +188,33 @@ func mktfRanges(ranges interface{}) interface{} {
}

func mktfipv6assign(i interface{}) interface{} {
ipv6 := i.(ztcentral.IPV6AssignMode)
return map[string]interface{}{
ipv6 := i.(*ztcentral.IPV6AssignMode)

m := map[string]interface{}{}

iter := map[string]*bool{
"zerotier": ipv6.ZeroTier,
"sixplane": ipv6.ZT6Plane,
"rfc4193": ipv6.RFC4193,
}

for key, b := range iter {
if b != nil {
m[key] = ptrBool(b)
}
}

return m
}

func mktfipv4assign(i interface{}) interface{} {
ipv4 := i.(ztcentral.IPV4AssignMode)
return map[string]interface{}{
"zerotier": ipv4.ZeroTier,
ipv4 := i.(*ztcentral.IPV4AssignMode)
m := map[string]interface{}{}
if ipv4.ZeroTier != nil {
m["zerotier"] = ptrBool(ipv4.ZeroTier)
}

return m
}

func mkipv4assign(assignments interface{}) (interface{}, diag.Diagnostics) {
Expand All @@ -200,7 +226,7 @@ func mkipv4assign(assignments interface{}) (interface{}, diag.Diagnostics) {
zt = true // default
}

return ztcentral.IPV4AssignMode{ZeroTier: zt}, nil
return &ztcentral.IPV4AssignMode{ZeroTier: boolPtr(zt)}, nil
}

func mkipv6assign(assignments interface{}) (interface{}, diag.Diagnostics) {
Expand All @@ -222,5 +248,9 @@ func mkipv6assign(assignments interface{}) (interface{}, diag.Diagnostics) {
rfc4193 = r.(bool)
}

return ztcentral.IPV6AssignMode{ZeroTier: zt, ZT6Plane: sixPlane, RFC4193: rfc4193}, nil
return &ztcentral.IPV6AssignMode{
ZeroTier: boolPtr(zt),
ZT6Plane: boolPtr(sixPlane),
RFC4193: boolPtr(rfc4193),
}, nil
}
29 changes: 23 additions & 6 deletions pkg/zerotier/resource_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ func resourceNetwork() *schema.Resource {
}

func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
if err := ZTNetwork.CollectFromTerraform(d); err != nil {
ztn := ZTNetwork.Clone()
if err := ztn.CollectFromTerraform(d); err != nil {
return err
}

c := m.(*ztcentral.Client)
net := ZTNetwork.Yield().(*ztcentral.Network)
net := ztn.Yield().(*ztcentral.Network)
rules := net.RulesSource

n, err := c.NewNetwork(ctx, net.Config.Name, net)
if err != nil {
Expand All @@ -37,6 +39,10 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, m interf
}}
}

if _, err := c.UpdateNetworkRules(ctx, n.ID, rules); err != nil {
return diag.FromErr(err)
}

d.SetId(n.ID)
d.Set("tf_last_updated", time.Now().Unix())

Expand All @@ -58,7 +64,7 @@ func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, m interfac
return diags
}

return ZTNetwork.CollectFromObject(d, ztNetwork)
return ZTNetwork.Clone().CollectFromObject(d, ztNetwork)
}

func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
Expand All @@ -67,14 +73,25 @@ func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, m interf
}

c := m.(*ztcentral.Client)
ZTNetwork.CollectFromTerraform(d)
ztn := ZTNetwork.Clone()

ztn.CollectFromTerraform(d)

net := ztn.Yield().(*ztcentral.Network)
rules := net.RulesSource

if _, err := c.UpdateNetworkRules(ctx, net.ID, rules); err != nil {
return diag.FromErr(err)
}

net.RulesSource = ""

updated, err := c.UpdateNetwork(ctx, ZTNetwork.Yield().(*ztcentral.Network))
updated, err := c.UpdateNetwork(ctx, net)
if err != nil {
return diag.FromErr(err)
}

ZTNetwork.CollectFromObject(d, updated)
ztn.CollectFromObject(d, updated)

return nil
}
Expand Down
30 changes: 30 additions & 0 deletions pkg/zerotier/schemawrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,36 @@ type SchemaWrap struct {
Value interface{}
}

func (sw *SchemaWrap) Clone() *SchemaWrap {
val, err := sw.Schema.DefaultValue()
if err != nil {
panic(err)
}

return &SchemaWrap{
Value: val,
Schema: sw.Schema,
ValidatorFunc: sw.ValidatorFunc,
FromTerraformFunc: sw.FromTerraformFunc,
ToTerraformFunc: sw.ToTerraformFunc,
EqualFunc: sw.EqualFunc,
}
}

func (vs ValidatedSchema) Clone() ValidatedSchema {
vs2 := ValidatedSchema{
Schema: map[string]*SchemaWrap{},
YieldFunc: vs.YieldFunc,
CollectFunc: vs.CollectFunc,
}

for key, sw := range vs.Schema {
vs2.Schema[key] = sw.Clone()
}

return vs2
}

// TerraformSchema returns the unadulterated schema for use by terraform.
func (vs ValidatedSchema) TerraformSchema() map[string]*schema.Schema {
res := map[string]*schema.Schema{}
Expand Down
21 changes: 15 additions & 6 deletions pkg/zerotier/ztnetwork.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import (

func ztNetworkYield(vs ValidatedSchema) interface{} {
return &ztcentral.Network{
RulesSource: vs.Get("flow_rules").(string),
Config: ztcentral.NetworkConfig{
Name: vs.Get("name").(string),
IPAssignmentPool: vs.Get("assignment_pool").([]ztcentral.IPRange),
Routes: vs.Get("route").([]ztcentral.Route),
IPV4AssignMode: vs.Get("assign_ipv4").(ztcentral.IPV4AssignMode),
IPV6AssignMode: vs.Get("assign_ipv6").(ztcentral.IPV6AssignMode),
EnableBroadcast: vs.Get("enable_broadcast").(bool),
IPV4AssignMode: vs.Get("assign_ipv4").(*ztcentral.IPV4AssignMode),
IPV6AssignMode: vs.Get("assign_ipv6").(*ztcentral.IPV6AssignMode),
EnableBroadcast: boolPtr(vs.Get("enable_broadcast").(bool)),
MTU: vs.Get("mtu").(int),
MulticastLimit: vs.Get("multicast_limit").(int),
Private: vs.Get("private").(bool),
Private: boolPtr(vs.Get("private").(bool)),
},
}
}
Expand All @@ -27,14 +28,15 @@ func ztNetworkCollect(vs ValidatedSchema, d *schema.ResourceData, i interface{})

var diags diag.Diagnostics

diags = append(diags, vs.Set(d, "flow_rules", ztNetwork.RulesSource)...)
diags = append(diags, vs.Set(d, "name", ztNetwork.Config.Name)...)
diags = append(diags, vs.Set(d, "mtu", ztNetwork.Config.MTU)...)
diags = append(diags, vs.Set(d, "creation_time", ztNetwork.Config.CreationTime)...)
diags = append(diags, vs.Set(d, "route", ztNetwork.Config.Routes)...)
diags = append(diags, vs.Set(d, "assignment_pool", ztNetwork.Config.IPAssignmentPool)...)
diags = append(diags, vs.Set(d, "enable_broadcast", ztNetwork.Config.EnableBroadcast)...)
diags = append(diags, vs.Set(d, "enable_broadcast", ptrBool(ztNetwork.Config.EnableBroadcast))...)
diags = append(diags, vs.Set(d, "multicast_limit", ztNetwork.Config.MulticastLimit)...)
diags = append(diags, vs.Set(d, "private", ztNetwork.Config.Private)...)
diags = append(diags, vs.Set(d, "private", ptrBool(ztNetwork.Config.Private))...)
diags = append(diags, vs.Set(d, "assign_ipv4", ztNetwork.Config.IPV4AssignMode)...)
diags = append(diags, vs.Set(d, "assign_ipv6", ztNetwork.Config.IPV6AssignMode)...)

Expand Down Expand Up @@ -175,5 +177,12 @@ var ZTNetwork = ValidatedSchema{
},
},
},
"flow_rules": {
Schema: &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "accept;",
},
},
},
}
18 changes: 17 additions & 1 deletion provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ func s(m interface{}) string {
}

func isBool(t *testing.T, i interface{}, val bool, name string) {
if b, ok := i.(bool); !ok || b != val {
b, ok := i.(bool)
if ok && b != val {
t.Fatalf("%q was not set to %v", name, val)
} else if !ok {
b2, ok := i.(*bool)
if ok && (b2 == nil || *b2 != val) {
t.Fatalf("%q was not set to %v", name, val)
} else if !ok {
t.Fatalf("%q was not set properly", name)
}
}
}

Expand Down Expand Up @@ -167,6 +175,10 @@ func TestBasicNetworkSetup(t *testing.T) {
// if s != "My description is changed!" {
// t.Fatalf("description was improperly set")
// }
case "flow_rules":
if attrs["flow_rules"].(string) != "drop;" {
t.Fatal("flow_rules were not altered")
}
case "assign_off":
isBool(t, h(attrs["assign_ipv4"])["zerotier"], false, "assign_ipv4/zerotier")

Expand Down Expand Up @@ -213,6 +225,10 @@ func TestBasicNetworkSetup(t *testing.T) {
isBool(t, h(m)[name], val, "assign_ipv6/"+name)
}

if attrs["flow_rules"].(string) != "accept;" {
t.Fatal("flow_rules were not accept by default:", attrs["flow_rules"])
}

// FIXME needs patch to ztcentral
// if f, ok := attrs["last_modified"].(float64); !ok || f == 0 {
// t.Fatal("last modified (on zerotier) for alice network was 0")
Expand Down
5 changes: 5 additions & 0 deletions testdata/plans/basic-network.tf
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ resource "zerotier_network" "private" {
private = true
}

resource "zerotier_network" "flow_rules" {
name = "flow_rules"
flow_rules = "drop;"
}

resource "zerotier_identity" "bob" {}

resource "zerotier_member" "bob" {
Expand Down
26 changes: 22 additions & 4 deletions update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import (
"github.com/zerotier/go-ztcentral"
)

func boolPtr(b bool) *bool {
return &b
}

func modifyMember(ctx context.Context, networkID string, memberID string, updateFunc func(*ztcentral.Member)) error {
c := ztcentral.NewClient(controllerToken)
member, err := c.GetMember(ctx, networkID, memberID)
Expand Down Expand Up @@ -129,29 +133,39 @@ func TestNetworkUpdate(t *testing.T) {
// not updateable
case "assign_off":
err := modifyNetwork(ctx, attrs["id"].(string), func(net *ztcentral.Network) {
net.Config.IPV4AssignMode = ztcentral.IPV4AssignMode{ZeroTier: true}
net.Config.IPV6AssignMode = ztcentral.IPV6AssignMode{ZeroTier: true, ZT6Plane: false, RFC4193: false}
net.Config.IPV4AssignMode = &ztcentral.IPV4AssignMode{ZeroTier: boolPtr(true)}
net.Config.IPV6AssignMode = &ztcentral.IPV6AssignMode{ZeroTier: boolPtr(true), ZT6Plane: boolPtr(false), RFC4193: boolPtr(false)}
})

if err != nil {
t.Fatal(err)
}
case "private":
err := modifyNetwork(ctx, attrs["id"].(string), func(net *ztcentral.Network) {
net.Config.Private = false
net.Config.Private = boolPtr(false)
})

if err != nil {
t.Fatal(err)
}
case "no_broadcast":
err := modifyNetwork(ctx, attrs["id"].(string), func(net *ztcentral.Network) {
net.Config.EnableBroadcast = true
net.Config.EnableBroadcast = boolPtr(true)
})

if err != nil {
t.Fatal(err)
}
case "flow_rules":
c := ztcentral.NewClient(controllerToken)
rules, err := c.UpdateNetworkRules(ctx, attrs["id"].(string), "accept;")
if err != nil {
t.Fatal(err)
}

if rules != "accept;" {
t.Fatal("rules were not alterered on set")
}
case "alice", "bobs_garage":
// this is a collection of defaults; not sure testing this is really worth the effort.
default:
Expand All @@ -177,6 +191,10 @@ func TestNetworkUpdate(t *testing.T) {
// not updateable
case "description":
// not updateable
case "flow_rules":
if attrs["flow_rules"].(string) != "accept;" {
t.Fatal("flow_rules were not updated")
}
case "assign_off":
isBool(t, h(attrs["assign_ipv4"])["zerotier"], true, "assign_ipv4/zerotier")

Expand Down

0 comments on commit 0463efb

Please sign in to comment.