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 8 months ago committed by Maisem Ali
parent d06b48dd0a
commit 4da0689c2c

@ -9,7 +9,6 @@ import (
"fmt"
"net"
"os"
"slices"
"strconv"
"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.
func (e *serveEnv) verifyFunnelEnabled(ctx context.Context, st *ipnstate.Status, port uint16) error {
hasFunnelAttrs := func(attrs []tailcfg.NodeCapability) bool {
hasHTTPS := slices.Contains(attrs, tailcfg.CapabilityHTTPS)
hasFunnel := slices.Contains(attrs, tailcfg.NodeAttrFunnel)
return hasHTTPS && hasFunnel
hasFunnelAttrs := func(selfNode *ipnstate.PeerStatus) bool {
return selfNode.HasCap(tailcfg.CapabilityHTTPS) && selfNode.HasCap(tailcfg.NodeAttrFunnel)
}
if hasFunnelAttrs(st.Self.Capabilities) {
if hasFunnelAttrs(st.Self) {
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
switch {
case statusErr != nil:

@ -18,7 +18,6 @@ import (
"path/filepath"
"reflect"
"runtime"
"slices"
"sort"
"strconv"
"strings"
@ -269,9 +268,7 @@ func (e *serveEnv) runServe(ctx context.Context, args []string) error {
// on, enableFeatureInteractive will error. For now, we hide that
// error and maintain the previous behavior (prior to 2023-08-15)
// of letting them edit the serve config before enabling certs.
e.enableFeatureInteractive(ctx, "serve", func(caps []tailcfg.NodeCapability) bool {
return slices.Contains(caps, tailcfg.CapabilityHTTPS)
})
e.enableFeatureInteractive(ctx, "serve", tailcfg.CapabilityHTTPS)
}
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".
// 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)
if err != nil {
return err
@ -875,7 +872,16 @@ func (e *serveEnv) enableFeatureInteractive(ctx context.Context, feature string,
return err
}
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)
fmt.Fprintln(os.Stdout, "Success.")
return nil

@ -15,7 +15,6 @@ import (
"os/signal"
"path"
"path/filepath"
"slices"
"sort"
"strconv"
"strings"
@ -233,9 +232,7 @@ func (e *serveEnv) runServeCombined(subcmd serveMode) execFunc {
// on, enableFeatureInteractive will error. For now, we hide that
// error and maintain the previous behavior (prior to 2023-08-15)
// of letting them edit the serve config before enabling certs.
if err := e.enableFeatureInteractive(ctx, "serve", func(caps []tailcfg.NodeCapability) bool {
return slices.Contains(caps, tailcfg.CapabilityHTTPS)
}); err != nil {
if err := e.enableFeatureInteractive(ctx, "serve", tailcfg.CapabilityHTTPS); err != nil {
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 {
if nm != nil && nm.SelfNode.Valid() {
return views.SliceContains(nm.SelfNode.Capabilities(), cap)
if nm != nil {
return nm.SelfNode.HasCap(cap)
}
return false
}

@ -1035,7 +1035,7 @@ func (h *peerAPIHandler) canPutFile() bool {
// canDebug reports whether h can debug this node (goroutines, metrics,
// magicsock internal state, etc).
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.
return false
}

@ -291,6 +291,11 @@ type PeerStatus struct {
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
// passed to various subsystems which then call methods on it to populate state.
// Call its Status method to return the final constructed Status.

@ -368,6 +368,16 @@ type Node struct {
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
// be shown in client UIs.
//

@ -15,7 +15,6 @@ import (
"tailscale.com/types/logger"
"tailscale.com/types/logid"
"tailscale.com/types/netmap"
"tailscale.com/types/views"
"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.
if nm.SelfNode.Valid() {
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 != "" {
nodeID, errNode := logid.ParsePrivateID(nm.SelfNode.DataPlaneAuditLogID())
if errNode != nil {

Loading…
Cancel
Save