Skip to content
This repository was archived by the owner on Mar 21, 2025. It is now read-only.

Commit 331f75a

Browse files
authored
Enable toggling of 6PLANE and RFC4193 NDP modes (#164)
* Enable toggling of 6PLANE and RFC4193 NDP modes * Fix criterion for detecting member revision change for device PUB
1 parent 43738a9 commit 331f75a

File tree

18 files changed

+784
-231
lines changed

18 files changed

+784
-231
lines changed

internal/app/fluitans/routes/networks/devices.go

Lines changed: 240 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import (
1111
"github.com/pkg/errors"
1212
"github.com/sargassum-world/godest/handling"
1313
"github.com/sargassum-world/godest/turbostreams"
14+
"golang.org/x/sync/errgroup"
1415

1516
"github.com/sargassum-world/fluitans/internal/app/fluitans/auth"
16-
"github.com/sargassum-world/fluitans/internal/clients/desec"
17+
desecc "github.com/sargassum-world/fluitans/internal/clients/desec"
1718
ztc "github.com/sargassum-world/fluitans/internal/clients/zerotier"
1819
"github.com/sargassum-world/fluitans/internal/clients/ztcontrollers"
20+
"github.com/sargassum-world/fluitans/pkg/desec"
1921
"github.com/sargassum-world/fluitans/pkg/zerotier"
2022
)
2123

@@ -59,13 +61,13 @@ func (ss StringSet) Equals(set StringSet) bool {
5961
return ss.Contains(set) && set.Contains(ss)
6062
}
6163

62-
// Authorization
64+
// Devices
6365

6466
const devicesListPartial = "networks/devices-list.partial.tmpl"
6567

6668
func replaceDevicesListStream(
6769
ctx context.Context, controllerAddress, networkID string, a auth.Auth,
68-
c *ztc.Client, cc *ztcontrollers.Client, dc *desec.Client,
70+
c *ztc.Client, cc *ztcontrollers.Client, dc *desecc.Client,
6971
) (turbostreams.Message, error) {
7072
networkViewData, err := getNetworkViewData(ctx, controllerAddress, networkID, c, cc, dc)
7173
if err != nil {
@@ -134,33 +136,32 @@ func (h *Handlers) HandleDevicesPub() turbostreams.HandlerFunc {
134136
return func(c *turbostreams.Context) error {
135137
// Make change trackers
136138
initialized := false
137-
var prevDevices StringSet
139+
var devices StringSet
138140

139141
// Parse params
140142
networkID := c.Param("id")
141143
controllerAddress := ztc.GetControllerAddress(networkID)
142144

143145
// Publish periodically
144-
const pubInterval = 2 * time.Second
145-
return handling.Repeat(c.Context(), pubInterval, func() (done bool, err error) {
146+
const pubInterval = 5 * time.Second
147+
return handling.RepeatImmediate(c.Context(), pubInterval, func() (done bool, err error) {
146148
// Check for changes
147-
changed, devices, err := checkDevicesList(
148-
c.Context(), controllerAddress, networkID, prevDevices, h.ztc, h.ztcc,
149+
changed, updatedDevices, err := checkDevicesList(
150+
c.Context(), controllerAddress, networkID, devices, h.ztc, h.ztcc,
149151
)
150152
if err != nil {
151153
return false, err
152154
}
155+
devices = updatedDevices
153156
if !changed {
154157
return false, nil
155158
}
156159
if !initialized {
157160
// We just started publishing because a page added a subscription, so there's no need to
158161
// send the devices list again - that page already has the latest version
159-
prevDevices = devices
160162
initialized = true
161163
return false, nil
162164
}
163-
prevDevices = devices
164165

165166
// Publish changes
166167
message, err := replaceDevicesListStream(
@@ -211,15 +212,234 @@ func (h *Handlers) HandleDevicesPost() auth.HTTPHandlerFunc {
211212
}
212213

213214
// Redirect user
214-
return c.Redirect(
215-
http.StatusSeeOther, fmt.Sprintf("/networks/%s#device-%s", networkID, memberAddress),
215+
return c.Redirect(http.StatusSeeOther, fmt.Sprintf(
216+
"/networks/%s#/networks/%s/devices/%s", networkID, networkID, memberAddress,
217+
))
218+
}
219+
}
220+
221+
// Device
222+
223+
const devicePartial = "networks/device.partial.tmpl"
224+
225+
type DeviceViewData struct {
226+
Member Member
227+
DomainNames []string
228+
Network zerotier.ControllerNetwork
229+
NetworkDNSNamed bool
230+
}
231+
232+
func getDeviceViewData(
233+
ctx context.Context, address, networkID, memberAddress string,
234+
c *ztc.Client, cc *ztcontrollers.Client, dc *desecc.Client,
235+
) (vd DeviceViewData, err error) {
236+
controller, err := cc.FindControllerByAddress(ctx, address)
237+
if err != nil {
238+
return DeviceViewData{}, err
239+
}
240+
if controller == nil {
241+
return DeviceViewData{}, echo.NewHTTPError(http.StatusNotFound, "controller not found")
242+
}
243+
244+
eg, egctx := errgroup.WithContext(ctx)
245+
var network *zerotier.ControllerNetwork
246+
var subnameRRsets map[string][]desec.RRset
247+
eg.Go(func() (err error) {
248+
network, err = c.GetNetwork(ctx, *controller, networkID)
249+
return err
250+
})
251+
eg.Go(func() (err error) {
252+
subnameRRsets, err = dc.GetRRsets(ctx)
253+
return err
254+
})
255+
if err = eg.Wait(); err != nil {
256+
return DeviceViewData{}, err
257+
}
258+
if network == nil {
259+
return DeviceViewData{}, echo.NewHTTPError(http.StatusNotFound, "zerotier network not found")
260+
}
261+
vd.Network = *network
262+
263+
members, err := getMemberRecords(
264+
ctx, dc.Config.DomainName, *controller, *network, []string{memberAddress}, subnameRRsets, c,
265+
)
266+
if err != nil {
267+
return DeviceViewData{}, err
268+
}
269+
var ok bool
270+
if vd.Member, ok = members[memberAddress]; !ok {
271+
return DeviceViewData{}, echo.NewHTTPError(
272+
http.StatusNotFound, "zerotier network member not found",
216273
)
217274
}
275+
276+
if vd.NetworkDNSNamed, err = checkNamedByDNS(egctx, *network.Name, *network.Id, dc); err != nil {
277+
return DeviceViewData{}, err
278+
}
279+
280+
return vd, nil
281+
}
282+
283+
func replaceDeviceStream(
284+
ctx context.Context, controllerAddress, networkID, memberAddress string, a auth.Auth,
285+
c *ztc.Client, cc *ztcontrollers.Client, dc *desecc.Client,
286+
) (turbostreams.Message, error) {
287+
deviceViewData, err := getDeviceViewData(
288+
ctx, controllerAddress, networkID, memberAddress, c, cc, dc,
289+
)
290+
if err != nil {
291+
return turbostreams.Message{}, err
292+
}
293+
return turbostreams.Message{
294+
Action: turbostreams.ActionReplace,
295+
Target: "/networks/" + networkID + "/devices/" + memberAddress,
296+
Template: devicePartial,
297+
Data: map[string]interface{}{
298+
"Member": deviceViewData.Member,
299+
"DomainNames": deviceViewData.DomainNames,
300+
"Network": deviceViewData.Network,
301+
"NetworkDNSNamed": deviceViewData.NetworkDNSNamed,
302+
"Auth": a,
303+
},
304+
}, nil
305+
}
306+
307+
func (h *Handlers) HandleDeviceSub() turbostreams.HandlerFunc {
308+
return func(c *turbostreams.Context) error {
309+
// Parse params
310+
networkID := c.Param("id")
311+
controllerAddress := ztc.GetControllerAddress(networkID)
312+
memberAddress := c.Param("address")
313+
314+
// Run queries
315+
ctx := c.Context()
316+
controller, err := h.ztcc.FindControllerByAddress(ctx, controllerAddress)
317+
if err != nil {
318+
return err
319+
}
320+
member, err := h.ztc.GetNetworkMember(ctx, *controller, networkID, memberAddress)
321+
if err != nil {
322+
return err
323+
}
324+
if member == nil {
325+
return errors.Errorf("network %s member %s not found", networkID, memberAddress)
326+
}
327+
328+
// Allow subscription
329+
return nil
330+
}
331+
}
332+
333+
func checkNetwork(
334+
ctx context.Context, controllerAddress, networkID string, prevNetwork zerotier.ControllerNetwork,
335+
c *ztc.Client, cc *ztcontrollers.Client,
336+
) (changed bool, updatedNetwork zerotier.ControllerNetwork, err error) {
337+
controller, err := cc.FindControllerByAddress(ctx, controllerAddress)
338+
if err != nil {
339+
return false, updatedNetwork, err
340+
}
341+
member, err := c.GetNetwork(ctx, *controller, networkID)
342+
if err != nil {
343+
return false, updatedNetwork, err
344+
}
345+
updatedNetwork = *member
346+
sixplaneChanged := prevNetwork.V6AssignMode == nil ||
347+
prevNetwork.V6AssignMode.N6plane == nil ||
348+
*updatedNetwork.V6AssignMode.N6plane != *prevNetwork.V6AssignMode.N6plane
349+
rfc4193Changed := prevNetwork.V6AssignMode == nil ||
350+
prevNetwork.V6AssignMode.Rfc4193 == nil ||
351+
*updatedNetwork.V6AssignMode.Rfc4193 != *prevNetwork.V6AssignMode.Rfc4193
352+
if !sixplaneChanged && !rfc4193Changed {
353+
prevNetwork.V6AssignMode = updatedNetwork.V6AssignMode
354+
return false, prevNetwork, nil
355+
}
356+
return true, updatedNetwork, nil
357+
}
358+
359+
func checkDevice(
360+
ctx context.Context, controllerAddress, networkID, memberAddress string,
361+
prevDevice zerotier.ControllerNetworkMember,
362+
c *ztc.Client, cc *ztcontrollers.Client,
363+
) (changed bool, updatedDevice zerotier.ControllerNetworkMember, err error) {
364+
controller, err := cc.FindControllerByAddress(ctx, controllerAddress)
365+
if err != nil {
366+
return false, updatedDevice, err
367+
}
368+
member, err := c.GetNetworkMember(ctx, *controller, networkID, memberAddress)
369+
if err != nil {
370+
return false, updatedDevice, err
371+
}
372+
updatedDevice = *member
373+
revisionChanged := prevDevice.Revision == nil ||
374+
*updatedDevice.Revision != *prevDevice.Revision
375+
// TODO: do we need to check whether the IP assignments list has changed?
376+
if !revisionChanged {
377+
return false, prevDevice, nil
378+
}
379+
return true, updatedDevice, nil
218380
}
219381

382+
func (h *Handlers) HandleDevicePub() turbostreams.HandlerFunc {
383+
t := devicesListPartial
384+
h.r.MustHave(t)
385+
return func(c *turbostreams.Context) error {
386+
// Make change trackers
387+
initialized := false
388+
var device zerotier.ControllerNetworkMember
389+
var network zerotier.ControllerNetwork
390+
391+
// Parse params
392+
networkID := c.Param("id")
393+
controllerAddress := ztc.GetControllerAddress(networkID)
394+
memberAddress := c.Param("address")
395+
396+
// Publish periodically
397+
const pubInterval = 5 * time.Second
398+
return handling.RepeatImmediate(c.Context(), pubInterval, func() (done bool, err error) {
399+
// Check for changes
400+
networkChanged, updatedNetwork, err := checkNetwork(
401+
c.Context(), controllerAddress, networkID, network, h.ztc, h.ztcc,
402+
)
403+
if err != nil {
404+
return false, err
405+
}
406+
network = updatedNetwork
407+
deviceChanged, updatedDevice, err := checkDevice(
408+
c.Context(), controllerAddress, networkID, memberAddress, device, h.ztc, h.ztcc,
409+
)
410+
if err != nil {
411+
return false, err
412+
}
413+
device = updatedDevice
414+
415+
if !deviceChanged && !networkChanged {
416+
return false, nil
417+
}
418+
if !initialized {
419+
// We just started publishing because a page added a subscription, so there's no need to
420+
// send the devices list again - that page already has the latest version
421+
initialized = true
422+
return false, nil
423+
}
424+
425+
// Publish changes
426+
message, err := replaceDeviceStream(
427+
c.Context(), controllerAddress, networkID, memberAddress, auth.Auth{}, h.ztc, h.ztcc, h.dc,
428+
)
429+
if err != nil {
430+
return false, err
431+
}
432+
c.Publish(message)
433+
return false, nil
434+
})
435+
}
436+
}
437+
438+
// Authorization
439+
220440
func setMemberAuthorization(
221-
ctx context.Context, controller ztcontrollers.Controller, networkID string,
222-
memberAddress string, authorized bool, c *ztc.Client,
441+
ctx context.Context, controller ztcontrollers.Controller, networkID, memberAddress string,
442+
authorized bool, c *ztc.Client,
223443
) error {
224444
auth := authorized
225445
if err := c.UpdateMember(
@@ -243,7 +463,7 @@ func (h *Handlers) HandleDeviceAuthorizationPost() auth.HTTPHandlerFunc {
243463
networkID := c.Param("id")
244464
controllerAddress := ztc.GetControllerAddress(networkID)
245465
memberAddress := c.Param("address")
246-
authorization := strings.ToLower(c.FormValue("authorization")) == "true"
466+
authorization := strings.ToLower(c.FormValue("authorization")) == checkboxTrueValue
247467

248468
// Run queries
249469
ctx := c.Request().Context()
@@ -274,7 +494,7 @@ func (h *Handlers) HandleDeviceAuthorizationPost() auth.HTTPHandlerFunc {
274494

275495
// Redirect user
276496
return c.Redirect(http.StatusSeeOther, fmt.Sprintf(
277-
"/networks/%s#device-%s", networkID, memberAddress,
497+
"/networks/%s#/networks/%s/devices/%s", networkID, networkID, memberAddress,
278498
))
279499
}
280500
}
@@ -283,7 +503,7 @@ func (h *Handlers) HandleDeviceAuthorizationPost() auth.HTTPHandlerFunc {
283503

284504
func confirmMemberNameManageable(
285505
ctx context.Context, controller ztcontrollers.Controller, networkID string,
286-
memberAddress, memberName string, c *ztc.Client, dc *desec.Client,
506+
memberAddress, memberName string, c *ztc.Client, dc *desecc.Client,
287507
) (memberSubname string, err error) {
288508
network, memberAddresses, err := c.GetNetworkInfo(ctx, controller, networkID)
289509
if err != nil {
@@ -317,7 +537,7 @@ func confirmMemberNameManageable(
317537

318538
func setMemberName(
319539
ctx context.Context, controller ztcontrollers.Controller, networkID string,
320-
memberAddress, memberName string, c *ztc.Client, dc *desec.Client,
540+
memberAddress, memberName string, c *ztc.Client, dc *desecc.Client,
321541
) error {
322542
memberSubname, err := confirmMemberNameManageable(
323543
ctx, controller, networkID, memberAddress, memberName, c, dc,
@@ -341,7 +561,7 @@ func setMemberName(
341561

342562
func unsetMemberName(
343563
ctx context.Context, controller ztcontrollers.Controller, networkID string,
344-
memberAddress, memberName string, c *ztc.Client, dc *desec.Client,
564+
memberAddress, memberName string, c *ztc.Client, dc *desecc.Client,
345565
) error {
346566
memberSubname, err := confirmMemberNameManageable(
347567
ctx, controller, networkID, memberAddress, memberName, c, dc,
@@ -405,7 +625,7 @@ func (h *Handlers) HandleDeviceNamePost() auth.HTTPHandlerFunc {
405625

406626
// Redirect user
407627
return c.Redirect(http.StatusSeeOther, fmt.Sprintf(
408-
"/networks/%s#device-%s", networkID, memberAddress,
628+
"/networks/%s#/networks/%s/device/%s", networkID, networkID, memberAddress,
409629
))
410630
}
411631
}

0 commit comments

Comments
 (0)