tailcfg: add Node.HasCap helpers

This makes a follow up change less noisy.

Updates #cleanup

Signed-off-by: Maisem Ali <maisem@tailscale.com>
pull/9455/head
Maisem Ali 9 months ago committed by Maisem Ali
parent d06b48dd0a
commit 4da0689c2c

@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"net" "net"
"os" "os"
"slices"
"strconv" "strconv"
"strings" "strings"
@ -147,15 +146,13 @@ func (e *serveEnv) runFunnel(ctx context.Context, args []string) error {
// //
// verifyFunnelEnabled may refresh the local state and modify the st input. // verifyFunnelEnabled may refresh the local state and modify the st input.
func (e *serveEnv) verifyFunnelEnabled(ctx context.Context, st *ipnstate.Status, port uint16) error { func (e *serveEnv) verifyFunnelEnabled(ctx context.Context, st *ipnstate.Status, port uint16) error {
hasFunnelAttrs := func(attrs []tailcfg.NodeCapability) bool { hasFunnelAttrs := func(selfNode *ipnstate.PeerStatus) bool {
hasHTTPS := slices.Contains(attrs, tailcfg.CapabilityHTTPS) return selfNode.HasCap(tailcfg.CapabilityHTTPS) && selfNode.HasCap(tailcfg.NodeAttrFunnel)
hasFunnel := slices.Contains(attrs, tailcfg.NodeAttrFunnel)
return hasHTTPS && hasFunnel
} }
if hasFunnelAttrs(st.Self.Capabilities) { if hasFunnelAttrs(st.Self) {
return nil // already enabled return nil // already enabled
} }
enableErr := e.enableFeatureInteractive(ctx, "funnel", hasFunnelAttrs) enableErr := e.enableFeatureInteractive(ctx, "funnel", tailcfg.CapabilityHTTPS, tailcfg.NodeAttrFunnel)
st, statusErr := e.getLocalClientStatusWithoutPeers(ctx) // get updated status; interactive flow may block st, statusErr := e.getLocalClientStatusWithoutPeers(ctx) // get updated status; interactive flow may block
switch { switch {
case statusErr != nil: case statusErr != nil:

@ -18,7 +18,6 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -269,9 +268,7 @@ func (e *serveEnv) runServe(ctx context.Context, args []string) error {
// on, enableFeatureInteractive will error. For now, we hide that // on, enableFeatureInteractive will error. For now, we hide that
// error and maintain the previous behavior (prior to 2023-08-15) // error and maintain the previous behavior (prior to 2023-08-15)
// of letting them edit the serve config before enabling certs. // of letting them edit the serve config before enabling certs.
e.enableFeatureInteractive(ctx, "serve", func(caps []tailcfg.NodeCapability) bool { e.enableFeatureInteractive(ctx, "serve", tailcfg.CapabilityHTTPS)
return slices.Contains(caps, tailcfg.CapabilityHTTPS)
})
} }
srcPort, err := parseServePort(srcPortStr) srcPort, err := parseServePort(srcPortStr)
@ -829,7 +826,7 @@ func parseServePort(s string) (uint16, error) {
// //
// 2023-08-09: The only valid feature values are "serve" and "funnel". // 2023-08-09: The only valid feature values are "serve" and "funnel".
// This can be moved to some CLI lib when expanded past serve/funnel. // This can be moved to some CLI lib when expanded past serve/funnel.
func (e *serveEnv) enableFeatureInteractive(ctx context.Context, feature string, hasRequiredCapabilities func(caps []tailcfg.NodeCapability) bool) (err error) { func (e *serveEnv) enableFeatureInteractive(ctx context.Context, feature string, caps ...tailcfg.NodeCapability) (err error) {
info, err := e.lc.QueryFeature(ctx, feature) info, err := e.lc.QueryFeature(ctx, feature)
if err != nil { if err != nil {
return err return err
@ -875,7 +872,16 @@ func (e *serveEnv) enableFeatureInteractive(ctx context.Context, feature string,
return err return err
} }
if nm := n.NetMap; nm != nil && nm.SelfNode.Valid() { if nm := n.NetMap; nm != nil && nm.SelfNode.Valid() {
if hasRequiredCapabilities(nm.SelfNode.Capabilities().AsSlice()) { gotAll := true
for _, c := range caps {
if !nm.SelfNode.HasCap(c) {
// The feature is not yet enabled.
// Continue blocking until it is.
gotAll = false
break
}
}
if gotAll {
e.lc.IncrementCounter(ctx, fmt.Sprintf("%s_enabled", feature), 1) e.lc.IncrementCounter(ctx, fmt.Sprintf("%s_enabled", feature), 1)
fmt.Fprintln(os.Stdout, "Success.") fmt.Fprintln(os.Stdout, "Success.")
return nil return nil

@ -15,7 +15,6 @@ import (
"os/signal" "os/signal"
"path" "path"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -233,9 +232,7 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc {
// on, enableFeatureInteractive will error. For now, we hide that // on, enableFeatureInteractive will error. For now, we hide that
// error and maintain the previous behavior (prior to 2023-08-15) // error and maintain the previous behavior (prior to 2023-08-15)
// of letting them edit the serve config before enabling certs. // of letting them edit the serve config before enabling certs.
if err := e.enableFeatureInteractive(ctx, "serve", func(caps []tailcfg.NodeCapability) bool { if err := e.enableFeatureInteractive(ctx, "serve", tailcfg.CapabilityHTTPS); err != nil {
return slices.Contains(caps, tailcfg.CapabilityHTTPS)
}); err != nil {
return fmt.Errorf("error enabling https feature: %w", err) return fmt.Errorf("error enabling https feature: %w", err)
} }
} }

@ -4094,8 +4094,8 @@ func (b *LocalBackend) setNetInfo(ni *tailcfg.NetInfo) {
} }
func hasCapability(nm *netmap.NetworkMap, cap tailcfg.NodeCapability) bool { func hasCapability(nm *netmap.NetworkMap, cap tailcfg.NodeCapability) bool {
if nm != nil && nm.SelfNode.Valid() { if nm != nil {
return views.SliceContains(nm.SelfNode.Capabilities(), cap) return nm.SelfNode.HasCap(cap)
} }
return false return false
} }

@ -1035,7 +1035,7 @@ func (h *peerAPIHandler) canPutFile() bool {
// canDebug reports whether h can debug this node (goroutines, metrics, // canDebug reports whether h can debug this node (goroutines, metrics,
// magicsock internal state, etc). // magicsock internal state, etc).
func (h *peerAPIHandler) canDebug() bool { func (h *peerAPIHandler) canDebug() bool {
if !views.SliceContains(h.selfNode.Capabilities(), tailcfg.CapabilityDebug) { if !h.selfNode.HasCap(tailcfg.CapabilityDebug) {
// This node does not expose debug info. // This node does not expose debug info.
return false return false
} }

@ -291,6 +291,11 @@ type PeerStatus struct {
Location *tailcfg.Location `json:",omitempty"` Location *tailcfg.Location `json:",omitempty"`
} }
// HasCap reports whether ps has the given capability.
func (ps *PeerStatus) HasCap(cap tailcfg.NodeCapability) bool {
return slices.Contains(ps.Capabilities, cap)
}
// StatusBuilder is a request to construct a Status. A new StatusBuilder is // StatusBuilder is a request to construct a Status. A new StatusBuilder is
// passed to various subsystems which then call methods on it to populate state. // passed to various subsystems which then call methods on it to populate state.
// Call its Status method to return the final constructed Status. // Call its Status method to return the final constructed Status.

@ -368,6 +368,16 @@ type Node struct {
ExitNodeDNSResolvers []*dnstype.Resolver `json:",omitempty"` ExitNodeDNSResolvers []*dnstype.Resolver `json:",omitempty"`
} }
// HasCap reports whether the node has the given capability.
func (v NodeView) HasCap(cap NodeCapability) bool {
return v.ж.HasCap(cap)
}
// HasCap reports whether the node has the given capability.
func (v *Node) HasCap(cap NodeCapability) bool {
return v != nil && slices.Contains(v.Capabilities, cap)
}
// DisplayName returns the user-facing name for a node which should // DisplayName returns the user-facing name for a node which should
// be shown in client UIs. // be shown in client UIs.
// //

@ -15,7 +15,6 @@ import (
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/logid" "tailscale.com/types/logid"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/types/views"
"tailscale.com/wgengine/wgcfg" "tailscale.com/wgengine/wgcfg"
) )
@ -63,7 +62,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
// Setup log IDs for data plane audit logging. // Setup log IDs for data plane audit logging.
if nm.SelfNode.Valid() { if nm.SelfNode.Valid() {
cfg.NodeID = nm.SelfNode.StableID() cfg.NodeID = nm.SelfNode.StableID()
canNetworkLog := views.SliceContains(nm.SelfNode.Capabilities(), tailcfg.CapabilityDataPlaneAuditLogs) canNetworkLog := nm.SelfNode.HasCap(tailcfg.CapabilityDataPlaneAuditLogs)
if canNetworkLog && nm.SelfNode.DataPlaneAuditLogID() != "" && nm.DomainAuditLogID != "" { if canNetworkLog && nm.SelfNode.DataPlaneAuditLogID() != "" && nm.DomainAuditLogID != "" {
nodeID, errNode := logid.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID()) nodeID, errNode := logid.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID())
if errNode != nil { if errNode != nil {

Loading…
Cancel
Save