diff --git a/management/server/differs/nameserver.go b/management/server/differs/nameserver.go new file mode 100644 index 000000000..a190ddb5e --- /dev/null +++ b/management/server/differs/nameserver.go @@ -0,0 +1,64 @@ +package differs + +import ( + "fmt" + "reflect" + + nbdns "github.com/netbirdio/netbird/dns" + "github.com/r3labs/diff" +) + +type NameServerComparator struct{} + +func NewNameServerComparator() *NameServerComparator { + return &NameServerComparator{} +} + +func (d *NameServerComparator) Match(a, b reflect.Value) bool { + return diff.AreType(a, b, reflect.TypeOf(nbdns.NameServer{})) || + diff.AreType(a, b, reflect.TypeOf([]nbdns.NameServer{})) +} + +func (d *NameServerComparator) Diff(cl *diff.Changelog, path []string, a, b reflect.Value) error { + if a.Kind() == reflect.Invalid { + cl.Add(diff.CREATE, path, nil, b.Interface()) + return nil + } + if b.Kind() == reflect.Invalid { + cl.Add(diff.DELETE, path, a.Interface(), nil) + return nil + } + + if a.Kind() == reflect.Slice && b.Kind() == reflect.Slice { + if a.Len() != b.Len() { + cl.Add(diff.UPDATE, append(path, "length"), a.Len(), b.Len()) + return nil + } + + for i := 0; i < min(a.Len(), b.Len()); i++ { + err := d.Diff(cl, append(path, fmt.Sprintf("[%d]", i)), a.Index(i), b.Index(i)) + if err != nil { + return err + } + } + return nil + } + + ns1, ok1 := a.Interface().(nbdns.NameServer) + ns2, ok2 := b.Interface().(nbdns.NameServer) + if !ok1 || !ok2 { + return fmt.Errorf("invalid type for NameServer") + } + + if ns1.IP.String() != ns2.IP.String() { + cl.Add(diff.UPDATE, append(path, "IP"), ns1.IP.String(), ns2.IP.String()) + } + if ns1.NSType != ns2.NSType { + cl.Add(diff.UPDATE, append(path, "NSType"), ns1.NSType, ns2.NSType) + } + if ns1.Port != ns2.Port { + cl.Add(diff.UPDATE, append(path, "Port"), ns1.Port, ns2.Port) + } + + return nil +} diff --git a/management/server/differs/route.go b/management/server/differs/route.go new file mode 100644 index 000000000..7d6efaf64 --- /dev/null +++ b/management/server/differs/route.go @@ -0,0 +1,99 @@ +package differs + +import ( + "fmt" + "reflect" + "slices" + + nbroute "github.com/netbirdio/netbird/route" + "github.com/r3labs/diff" +) + +type RouteComparator struct{} + +func NewRouteComparator() *RouteComparator { + return &RouteComparator{} +} + +func (d *RouteComparator) Match(a, b reflect.Value) bool { + return diff.AreType(a, b, reflect.TypeOf(&nbroute.Route{})) || + diff.AreType(a, b, reflect.TypeOf([]*nbroute.Route{})) +} + +func (d *RouteComparator) Diff(cl *diff.Changelog, path []string, a, b reflect.Value) error { + if a.Kind() == reflect.Invalid { + cl.Add(diff.CREATE, path, nil, b.Interface()) + return nil + } + if b.Kind() == reflect.Invalid { + cl.Add(diff.DELETE, path, a.Interface(), nil) + return nil + } + + // Handle slice comparison + if a.Kind() == reflect.Slice && b.Kind() == reflect.Slice { + if a.Len() != b.Len() { + cl.Add(diff.UPDATE, append(path, "length"), a.Len(), b.Len()) + return nil + } + + for i := 0; i < min(a.Len(), b.Len()); i++ { + err := d.Diff(cl, append(path, fmt.Sprintf("[%d]", i)), a.Index(i), b.Index(i)) + if err != nil { + return err + } + } + return nil + } + + route1, ok1 := a.Interface().(*nbroute.Route) + route2, ok2 := b.Interface().(*nbroute.Route) + if !ok1 || !ok2 { + return fmt.Errorf("invalid type for Route") + } + + if route1.ID != route2.ID { + cl.Add(diff.UPDATE, append(path, "ID"), route1.ID, route2.ID) + } + if route1.AccountID != route2.AccountID { + cl.Add(diff.UPDATE, append(path, "AccountID"), route1.AccountID, route2.AccountID) + } + if route1.Network.String() != route2.Network.String() { + cl.Add(diff.UPDATE, append(path, "Network"), route1.Network.String(), route2.Network.String()) + } + if !slices.Equal(route1.Domains, route2.Domains) { + cl.Add(diff.UPDATE, append(path, "Domains"), route1.Domains, route2.Domains) + } + if route1.KeepRoute != route2.KeepRoute { + cl.Add(diff.UPDATE, append(path, "KeepRoute"), route1.KeepRoute, route2.KeepRoute) + } + if route1.NetID != route2.NetID { + cl.Add(diff.UPDATE, append(path, "NetID"), route1.NetID, route2.NetID) + } + if route1.Description != route2.Description { + cl.Add(diff.UPDATE, append(path, "Description"), route1.Description, route2.Description) + } + if route1.Peer != route2.Peer { + cl.Add(diff.UPDATE, append(path, "Peer"), route1.Peer, route2.Peer) + } + if !slices.Equal(route1.PeerGroups, route2.PeerGroups) { + cl.Add(diff.UPDATE, append(path, "PeerGroups"), route1.PeerGroups, route2.PeerGroups) + } + if route1.NetworkType != route2.NetworkType { + cl.Add(diff.UPDATE, append(path, "NetworkType"), route1.NetworkType, route2.NetworkType) + } + if route1.Masquerade != route2.Masquerade { + cl.Add(diff.UPDATE, append(path, "Masquerade"), route1.Masquerade, route2.Masquerade) + } + if route1.Metric != route2.Metric { + cl.Add(diff.UPDATE, append(path, "Metric"), route1.Metric, route2.Metric) + } + if route1.Enabled != route2.Enabled { + cl.Add(diff.UPDATE, append(path, "Enabled"), route1.Enabled, route2.Enabled) + } + if !slices.Equal(route1.Groups, route2.Groups) { + cl.Add(diff.UPDATE, append(path, "Groups"), route1.Groups, route2.Groups) + } + + return nil +}