Compare commits
1 Commits
jonathan/n
...
irbekrm/de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8dda0048e |
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -49,13 +49,13 @@ jobs:
|
||||
|
||||
# Install a more recent Go that understands modern go.mod content.
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1
|
||||
uses: github/codeql-action/init@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1
|
||||
uses: github/codeql-action/autobuild@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -80,4 +80,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1
|
||||
uses: github/codeql-action/analyze@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11
|
||||
|
||||
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
|
||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@@ -80,7 +80,7 @@ jobs:
|
||||
- name: checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Restore Cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
|
||||
with:
|
||||
# Note: unlike the other setups, this is only grabbing the mod download
|
||||
# cache, rather than the whole mod directory, as the download cache
|
||||
@@ -153,13 +153,13 @@ jobs:
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
|
||||
- name: Restore Cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
|
||||
with:
|
||||
# Note: unlike the other setups, this is only grabbing the mod download
|
||||
# cache, rather than the whole mod directory, as the download cache
|
||||
@@ -260,7 +260,7 @@ jobs:
|
||||
- name: checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Restore Cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
|
||||
with:
|
||||
# Note: unlike the other setups, this is only grabbing the mod download
|
||||
# cache, rather than the whole mod directory, as the download cache
|
||||
@@ -319,7 +319,7 @@ jobs:
|
||||
- name: checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Restore Cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
|
||||
with:
|
||||
# Note: unlike the other setups, this is only grabbing the mod download
|
||||
# cache, rather than the whole mod directory, as the download cache
|
||||
@@ -367,7 +367,7 @@ jobs:
|
||||
- name: checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Restore Cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
|
||||
with:
|
||||
# Note: unlike the other setups, this is only grabbing the mod download
|
||||
# cache, rather than the whole mod directory, as the download cache
|
||||
@@ -461,7 +461,7 @@ jobs:
|
||||
run: |
|
||||
echo "artifacts_path=$(realpath .)" >> $GITHUB_ENV
|
||||
- name: upload crash
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
if: steps.run.outcome != 'success' && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
|
||||
@@ -102,6 +102,7 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -730,6 +731,7 @@ func tailscaledConfigFilePath() string {
|
||||
}
|
||||
cv, err := kubeutils.CapVerFromFileName(e.Name())
|
||||
if err != nil {
|
||||
log.Printf("skipping file %q in tailscaled config directory %q: %v", e.Name(), dir, err)
|
||||
continue
|
||||
}
|
||||
if cv > maxCompatVer && cv <= tailcfg.CurrentCapabilityVersion {
|
||||
@@ -737,9 +739,8 @@ func tailscaledConfigFilePath() string {
|
||||
}
|
||||
}
|
||||
if maxCompatVer == -1 {
|
||||
log.Fatalf("no tailscaled config file found in %q for current capability version %d", dir, tailcfg.CurrentCapabilityVersion)
|
||||
log.Fatalf("no tailscaled config file found in %q for current capability version %q", dir, tailcfg.CurrentCapabilityVersion)
|
||||
}
|
||||
filePath := filepath.Join(dir, kubeutils.TailscaledConfigFileName(maxCompatVer))
|
||||
log.Printf("Using tailscaled config file %q to match current capability version %d", filePath, tailcfg.CurrentCapabilityVersion)
|
||||
return filePath
|
||||
log.Printf("Using tailscaled config file %q for capability version %q", maxCompatVer, tailcfg.CurrentCapabilityVersion)
|
||||
return path.Join(dir, kubeutils.TailscaledConfigFileName(maxCompatVer))
|
||||
}
|
||||
|
||||
@@ -1388,7 +1388,7 @@ func TestTailscaledConfigfileHash(t *testing.T) {
|
||||
parentType: "svc",
|
||||
hostname: "default-test",
|
||||
clusterTargetIP: "10.20.30.40",
|
||||
confFileHash: "a67b5ad3ff605531c822327e8f1a23dd0846e1075b722c13402f7d5d0ba32ba2",
|
||||
confFileHash: "362360188dac62bca8013c8134929fed8efd84b1f410c00873d14a05709b5647",
|
||||
app: kubetypes.AppIngressProxy,
|
||||
}
|
||||
expectEqual(t, fc, expectedSTS(t, fc, o), nil)
|
||||
@@ -1399,7 +1399,7 @@ func TestTailscaledConfigfileHash(t *testing.T) {
|
||||
mak.Set(&svc.Annotations, AnnotationHostname, "another-test")
|
||||
})
|
||||
o.hostname = "another-test"
|
||||
o.confFileHash = "888a993ebee20ad6be99623b45015339de117946850cf1252bede0b570e04293"
|
||||
o.confFileHash = "20db57cfabc3fc6490f6bb1dc85994e61d255cdfa2a56abb0141736e59f263ef"
|
||||
expectReconciled(t, sr, "default", "test")
|
||||
expectEqual(t, fc, expectedSTS(t, fc, o), nil)
|
||||
}
|
||||
|
||||
@@ -521,6 +521,11 @@ func (a *tailscaleSTSReconciler) reconcileSTS(ctx context.Context, logger *zap.S
|
||||
Name: "TS_KUBE_SECRET",
|
||||
Value: proxySecret,
|
||||
},
|
||||
corev1.EnvVar{
|
||||
// Old tailscaled config key is still used for backwards compatibility.
|
||||
Name: "EXPERIMENTAL_TS_CONFIGFILE_PATH",
|
||||
Value: "/etc/tsconfig/tailscaled",
|
||||
},
|
||||
corev1.EnvVar{
|
||||
// New style is in the form of cap-<capability-version>.hujson.
|
||||
Name: "TS_EXPERIMENTAL_VERSIONED_CONFIG_DIR",
|
||||
@@ -784,9 +789,15 @@ func readAuthKey(secret *corev1.Secret, key string) (*string, error) {
|
||||
return origConf.AuthKey, nil
|
||||
}
|
||||
|
||||
// tailscaledConfig takes a proxy config, a newly generated auth key if generated and a Secret with the previous proxy
|
||||
// state and auth key and returns tailscaled config files for currently supported proxy versions and a hash of that
|
||||
// configuration.
|
||||
// tailscaledConfig takes a proxy config, a newly generated auth key if
|
||||
// generated and a Secret with the previous proxy state and auth key and
|
||||
// returns tailscaled configuration and a hash of that configuration.
|
||||
//
|
||||
// As of 2024-05-09 it also returns legacy tailscaled config without the
|
||||
// later added NoStatefulFilter field to support proxies older than cap95.
|
||||
// TODO (irbekrm): remove the legacy config once we no longer need to support
|
||||
// versions older than cap94,
|
||||
// https://tailscale.com/kb/1236/kubernetes-operator#operator-and-proxies
|
||||
func tailscaledConfig(stsC *tailscaleSTSConfig, newAuthkey string, oldSecret *corev1.Secret) (tailscaledConfigs, error) {
|
||||
conf := &ipn.ConfigVAlpha{
|
||||
Version: "alpha0",
|
||||
@@ -835,6 +846,10 @@ func tailscaledConfig(stsC *tailscaleSTSConfig, newAuthkey string, oldSecret *co
|
||||
// AppConnector config option is only understood by clients of capver 107 and newer.
|
||||
conf.AppConnector = nil
|
||||
capVerConfigs[95] = *conf
|
||||
|
||||
// StatefulFiltering is only understood by clients of capver 95 and newer.
|
||||
conf.NoStatefulFiltering.Clear()
|
||||
capVerConfigs[94] = *conf
|
||||
return capVerConfigs, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ func expectedSTS(t *testing.T, cl client.Client, opts configOpts) *appsv1.Statef
|
||||
{Name: "TS_USERSPACE", Value: "false"},
|
||||
{Name: "POD_IP", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "", FieldPath: "status.podIP"}, ResourceFieldRef: nil, ConfigMapKeyRef: nil, SecretKeyRef: nil}},
|
||||
{Name: "TS_KUBE_SECRET", Value: opts.secretName},
|
||||
{Name: "EXPERIMENTAL_TS_CONFIGFILE_PATH", Value: "/etc/tsconfig/tailscaled"},
|
||||
{Name: "TS_EXPERIMENTAL_VERSIONED_CONFIG_DIR", Value: "/etc/tsconfig"},
|
||||
},
|
||||
SecurityContext: &corev1.SecurityContext{
|
||||
@@ -229,6 +230,7 @@ func expectedSTSUserspace(t *testing.T, cl client.Client, opts configOpts) *apps
|
||||
{Name: "TS_USERSPACE", Value: "true"},
|
||||
{Name: "POD_IP", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{APIVersion: "", FieldPath: "status.podIP"}, ResourceFieldRef: nil, ConfigMapKeyRef: nil, SecretKeyRef: nil}},
|
||||
{Name: "TS_KUBE_SECRET", Value: opts.secretName},
|
||||
{Name: "EXPERIMENTAL_TS_CONFIGFILE_PATH", Value: "/etc/tsconfig/tailscaled"},
|
||||
{Name: "TS_EXPERIMENTAL_VERSIONED_CONFIG_DIR", Value: "/etc/tsconfig"},
|
||||
{Name: "TS_SERVE_CONFIG", Value: "/etc/tailscaled/serve-config"},
|
||||
{Name: "TS_INTERNAL_APP", Value: opts.app},
|
||||
@@ -402,6 +404,12 @@ func expectedSecret(t *testing.T, cl client.Client, opts configOpts) *corev1.Sec
|
||||
if err != nil {
|
||||
t.Fatalf("error marshalling tailscaled config")
|
||||
}
|
||||
conf.NoStatefulFiltering.Clear()
|
||||
b, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("error marshalling tailscaled config")
|
||||
}
|
||||
mak.Set(&s.StringData, "tailscaled", string(b))
|
||||
mak.Set(&s.StringData, "cap-95.hujson", string(bn))
|
||||
mak.Set(&s.StringData, "cap-107.hujson", string(bnn))
|
||||
labels := map[string]string{
|
||||
@@ -654,6 +662,18 @@ func removeTargetPortsFromSvc(svc *corev1.Service) {
|
||||
func removeAuthKeyIfExistsModifier(t *testing.T) func(s *corev1.Secret) {
|
||||
return func(secret *corev1.Secret) {
|
||||
t.Helper()
|
||||
if len(secret.StringData["tailscaled"]) != 0 {
|
||||
conf := &ipn.ConfigVAlpha{}
|
||||
if err := json.Unmarshal([]byte(secret.StringData["tailscaled"]), conf); err != nil {
|
||||
t.Fatalf("error unmarshalling 'tailscaled' contents: %v", err)
|
||||
}
|
||||
conf.AuthKey = nil
|
||||
b, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("error marshalling updated 'tailscaled' config: %v", err)
|
||||
}
|
||||
mak.Set(&secret.StringData, "tailscaled", string(b))
|
||||
}
|
||||
if len(secret.StringData["cap-95.hujson"]) != 0 {
|
||||
conf := &ipn.ConfigVAlpha{}
|
||||
if err := json.Unmarshal([]byte(secret.StringData["cap-95.hujson"]), conf); err != nil {
|
||||
|
||||
@@ -93,13 +93,8 @@ func Run(args []string) (err error) {
|
||||
|
||||
args = CleanUpArgs(args)
|
||||
|
||||
if len(args) == 1 {
|
||||
switch args[0] {
|
||||
case "-V", "--version":
|
||||
args = []string{"version"}
|
||||
case "help":
|
||||
args = []string{"--help"}
|
||||
}
|
||||
if len(args) == 1 && (args[0] == "-V" || args[0] == "--version") {
|
||||
args = []string{"version"}
|
||||
}
|
||||
|
||||
var warnOnce sync.Once
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -1481,33 +1480,3 @@ func TestParseNLArgs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelpAlias(t *testing.T) {
|
||||
var stdout, stderr bytes.Buffer
|
||||
tstest.Replace[io.Writer](t, &Stdout, &stdout)
|
||||
tstest.Replace[io.Writer](t, &Stderr, &stderr)
|
||||
|
||||
gotExit0 := false
|
||||
defer func() {
|
||||
if !gotExit0 {
|
||||
t.Error("expected os.Exit(0) to be called")
|
||||
return
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "SUBCOMMANDS") {
|
||||
t.Errorf("expected help output to contain SUBCOMMANDS; got stderr=%q; stdout=%q", stderr.String(), stdout.String())
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
if strings.Contains(fmt.Sprint(e), "unexpected call to os.Exit(0)") {
|
||||
gotExit0 = true
|
||||
} else {
|
||||
t.Errorf("unexpected panic: %v", e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
err := Run([]string{"help"})
|
||||
if err != nil {
|
||||
t.Fatalf("Run: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,6 +564,12 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
||||
case opt.URL != "":
|
||||
// Nothing.
|
||||
case regen || persist.PrivateNodeKey.IsZero():
|
||||
if regen {
|
||||
c.logf("TEST: need to regenerate")
|
||||
} else {
|
||||
c.logf("TEST: private node key is zero, persist is %v", persist)
|
||||
c.logf("TEST: private node key is zero, persist is %v", persist)
|
||||
}
|
||||
c.logf("Generating a new nodekey.")
|
||||
persist.OldPrivateNodeKey = persist.PrivateNodeKey
|
||||
tryingNewKey = key.NewNode()
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"tailscale.com/control/controlhttp"
|
||||
"tailscale.com/envknob"
|
||||
"tailscale.com/health"
|
||||
"tailscale.com/internal/noiseconn"
|
||||
"tailscale.com/net/dnscache"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
"tailscale.com/util/mak"
|
||||
"tailscale.com/util/multierr"
|
||||
"tailscale.com/util/singleflight"
|
||||
"tailscale.com/util/testenv"
|
||||
)
|
||||
|
||||
// NoiseClient provides a http.Client to connect to tailcontrol over
|
||||
@@ -105,6 +107,11 @@ type NoiseOpts struct {
|
||||
DialPlan func() *tailcfg.ControlDialPlan
|
||||
}
|
||||
|
||||
// controlIsPlaintext is whether we should assume that the controlplane is only accessible
|
||||
// over plaintext HTTP (as the first hop, before the ts2021 encryption begins).
|
||||
// This is used by some tests which don't have a real TLS certificate.
|
||||
var controlIsPlaintext = envknob.RegisterBool("TS_CONTROL_IS_PLAINTEXT_HTTP")
|
||||
|
||||
// NewNoiseClient returns a new noiseClient for the provided server and machine key.
|
||||
// serverURL is of the form https://<host>:<port> (no trailing slash).
|
||||
//
|
||||
@@ -122,7 +129,7 @@ func NewNoiseClient(opts NoiseOpts) (*NoiseClient, error) {
|
||||
if u.Scheme == "http" {
|
||||
httpPort = port
|
||||
httpsPort = "443"
|
||||
if u.Hostname() == "127.0.0.1" || u.Hostname() == "localhost" {
|
||||
if (testenv.InTest() || controlIsPlaintext()) && (u.Hostname() == "127.0.0.1" || u.Hostname() == "localhost") {
|
||||
httpsPort = ""
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1 +1 @@
|
||||
96578f73d04e1a231fa2a495ad3fa97747785bc6
|
||||
bf15628b759344c6fc7763795a405ba65b8be5d7
|
||||
|
||||
@@ -73,15 +73,6 @@ const (
|
||||
NotifyInitialOutgoingFiles // if set, the first Notify message (sent immediately) will contain the current Taildrop OutgoingFiles
|
||||
|
||||
NotifyInitialHealthState // if set, the first Notify message (sent immediately) will contain the current health.State of the client
|
||||
|
||||
NotifyRateLimitNetmaps // if set, rate limit netmap updates to once every DefaultNetmapRateLimit seconds
|
||||
)
|
||||
|
||||
const (
|
||||
// This is the minimum time between netmap updates when NotifyRateLimitNetmaps is included in the Notify opts.
|
||||
// 3 seconds should be sufficient to avoid flooding the UI with netmap updates on large/chatty tailnets without
|
||||
// causing noticable issues with the UI being out of date.
|
||||
DefaultNetmapRateLimit = time.Duration(3 * time.Second)
|
||||
)
|
||||
|
||||
// Notify is a communication from a backend (e.g. tailscaled) to a frontend
|
||||
|
||||
@@ -82,7 +82,6 @@ import (
|
||||
"tailscale.com/tka"
|
||||
"tailscale.com/tsd"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/tstime/rate"
|
||||
"tailscale.com/types/appctype"
|
||||
"tailscale.com/types/dnstype"
|
||||
"tailscale.com/types/empty"
|
||||
@@ -371,16 +370,6 @@ type LocalBackend struct {
|
||||
// backend is healthy and captive portal detection is not required
|
||||
// (sending false).
|
||||
needsCaptiveDetection chan bool
|
||||
|
||||
// netmapRateLimiter rate limits netmap updates to to the IPN bus.
|
||||
// It should be nil if the ipn bus options do not include the rate limiting flag.
|
||||
// It is automatically created via setNetmapRateLimit.
|
||||
netmapRateLimiter *rate.Limiter
|
||||
|
||||
// deferredNetmapCancel is used to cancel deferred netmap updates which
|
||||
// were initially blocked due to rate limiting. We always attempt to send the latest
|
||||
// netmap once the rate limiter allows it, discarding any pending netmaps.
|
||||
deferredNetmapCancel context.CancelFunc
|
||||
}
|
||||
|
||||
// HealthTracker returns the health tracker for the backend.
|
||||
@@ -486,7 +475,6 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
||||
captiveCtx: captiveCtx,
|
||||
captiveCancel: nil, // so that we start checkCaptivePortalLoop when Running
|
||||
needsCaptiveDetection: make(chan bool),
|
||||
deferredNetmapCancel: nil,
|
||||
}
|
||||
mConn.SetNetInfoCallback(b.setNetInfo)
|
||||
|
||||
@@ -977,11 +965,6 @@ func (b *LocalBackend) Shutdown() {
|
||||
if b.notifyCancel != nil {
|
||||
b.notifyCancel()
|
||||
}
|
||||
|
||||
if b.deferredNetmapCancel != nil {
|
||||
b.deferredNetmapCancel()
|
||||
}
|
||||
|
||||
b.mu.Unlock()
|
||||
b.webClientShutdown()
|
||||
|
||||
@@ -1608,7 +1591,8 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
|
||||
|
||||
// Update the DERP map in the health package, which uses it for health notifications
|
||||
b.health.SetDERPMap(st.NetMap.DERPMap)
|
||||
b.sendNetmap(st.NetMap)
|
||||
|
||||
b.send(ipn.Notify{NetMap: st.NetMap})
|
||||
}
|
||||
if st.URL != "" {
|
||||
b.logf("Received auth URL: %.20v...", st.URL)
|
||||
@@ -1693,91 +1677,20 @@ func applySysPolicy(prefs *ipn.Prefs) (anyChange bool) {
|
||||
return anyChange
|
||||
}
|
||||
|
||||
// setNetmapRateLimit Sets the minimum interval between netmap updates on the IPN Bus (in seconds)
|
||||
// If interval is 0 or negative, the rate limiter is disabled. Netmap rate limiting is
|
||||
// disabled by default
|
||||
// b.mu must be held
|
||||
func (b *LocalBackend) setNetmapRateLimit(interval time.Duration) {
|
||||
if interval > 0 {
|
||||
b.netmapRateLimiter = rate.NewLimiter(rate.Every(interval), 1)
|
||||
} else {
|
||||
b.netmapRateLimiter = nil
|
||||
}
|
||||
}
|
||||
|
||||
// sendNetmap sends a netmap update to the IPN bus respecting the rate limiter. This function
|
||||
// should be used for all netmap updates on the IPN bus unless there is some critical reason that
|
||||
// a netmap update be sent immediately.
|
||||
//
|
||||
// A non-nil channel will be returned if the netmap update was deferred due to rate limiting. The channel will be closed
|
||||
// when the netmap update is handled. true or false will be sent to the channel to indicate whether
|
||||
// or not the netmap was sent or cancelled respectively. A nil return value indicates that the netmap
|
||||
// was sent immediately. The returned value is primarily useful for testing and you can safely ignore
|
||||
// it and just call this method at will.
|
||||
func (b *LocalBackend) sendNetmap(nm *netmap.NetworkMap) chan bool {
|
||||
notify := ipn.Notify{NetMap: nm}
|
||||
|
||||
b.mu.Lock()
|
||||
|
||||
// Cancel all pending netmap updates, they're stale and we have something newer
|
||||
if b.deferredNetmapCancel != nil {
|
||||
b.deferredNetmapCancel()
|
||||
}
|
||||
|
||||
// No rate limiter? Send it.
|
||||
// Rate limiter allows the send? Send it.
|
||||
if b.netmapRateLimiter == nil || b.netmapRateLimiter.Allow() {
|
||||
b.mu.Unlock()
|
||||
b.send(notify)
|
||||
return nil
|
||||
}
|
||||
|
||||
// We're rate limited. Defer the netmap update
|
||||
var ctx context.Context
|
||||
ctx, cancel := context.WithCancel(b.ctx)
|
||||
b.deferredNetmapCancel = cancel
|
||||
// The rate limiter is set to Limit() events per second. Convert that back to
|
||||
// the time interval we need to wait
|
||||
delay := b.netmapRateLimiter.Delay()
|
||||
b.mu.Unlock()
|
||||
|
||||
c := make(chan bool)
|
||||
|
||||
// Send the netmap update once the rate limiter allows it
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(delay):
|
||||
b.send(notify)
|
||||
c <- true
|
||||
case <-ctx.Done():
|
||||
c <- false
|
||||
}
|
||||
close(c)
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
var _ controlclient.NetmapDeltaUpdater = (*LocalBackend)(nil)
|
||||
|
||||
// UpdateNetmapDelta implements controlclient.NetmapDeltaUpdater.
|
||||
func (b *LocalBackend) UpdateNetmapDelta(muts []netmap.NodeMutation) (handled bool) {
|
||||
if !b.MagicConn().UpdateNetmapDelta(muts) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Will be sent if non nil
|
||||
var netmap *netmap.NetworkMap
|
||||
// Will send an empty notify if true and netmap is nil (for tests - see below)
|
||||
var sendEmpty = false
|
||||
|
||||
var notify *ipn.Notify // non-nil if we need to send a Notify
|
||||
defer func() {
|
||||
if netmap != nil {
|
||||
b.sendNetmap(netmap)
|
||||
} else if sendEmpty {
|
||||
notify := new(ipn.Notify)
|
||||
if notify != nil {
|
||||
b.send(*notify)
|
||||
}
|
||||
}()
|
||||
|
||||
unlock := b.lockAndGetUnlock()
|
||||
defer unlock()
|
||||
if !b.updateNetmapDeltaLocked(muts) {
|
||||
@@ -1799,14 +1712,13 @@ func (b *LocalBackend) UpdateNetmapDelta(muts []netmap.NodeMutation) (handled bo
|
||||
slices.SortFunc(nm.Peers, func(a, b tailcfg.NodeView) int {
|
||||
return cmp.Compare(a.ID(), b.ID())
|
||||
})
|
||||
netmap = nm
|
||||
notify = &ipn.Notify{NetMap: nm}
|
||||
} else if testenv.InTest() {
|
||||
// In tests, send an empty Notify as a wake-up so end-to-end
|
||||
// integration tests in another repo can check on the status of
|
||||
// LocalBackend after processing deltas.
|
||||
sendEmpty = true
|
||||
notify = new(ipn.Notify)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -2083,6 +1995,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||
defer unlock()
|
||||
|
||||
if opts.UpdatePrefs != nil {
|
||||
log.Printf("TESTPREFS: update prefs non-nil")
|
||||
if err := b.checkPrefsLocked(opts.UpdatePrefs); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2149,6 +2062,10 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
||||
}
|
||||
|
||||
prefs := b.pm.CurrentPrefs()
|
||||
log.Printf("TESTPREFS persistent prefs: %v", prefs.Persist())
|
||||
if s := prefs.Persist().AsStruct(); s != nil {
|
||||
log.Printf("TESTPREFS persistent prefs private key is %v", s.PrivateNodeKey)
|
||||
}
|
||||
wantRunning := prefs.WantRunning()
|
||||
if wantRunning {
|
||||
if err := b.initMachineKeyLocked(); err != nil {
|
||||
@@ -2837,12 +2754,6 @@ func (b *LocalBackend) WatchNotificationsAs(ctx context.Context, actor ipnauth.A
|
||||
cancel: cancel,
|
||||
}
|
||||
mak.Set(&b.notifyWatchers, sessionID, session)
|
||||
if mask&ipn.NotifyRateLimitNetmaps != 0 {
|
||||
b.setNetmapRateLimit(ipn.DefaultNetmapRateLimit)
|
||||
} else {
|
||||
b.setNetmapRateLimit(0)
|
||||
}
|
||||
|
||||
b.mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
@@ -5083,9 +4994,6 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
|
||||
if authURL == "" {
|
||||
systemd.Status("Stopped; run 'tailscale up' to log in")
|
||||
}
|
||||
if b.deferredNetmapCancel != nil {
|
||||
b.deferredNetmapCancel()
|
||||
}
|
||||
case ipn.Starting, ipn.NeedsMachineAuth:
|
||||
b.authReconfig()
|
||||
// Needed so that UpdateEndpoints can run
|
||||
@@ -5098,9 +5006,7 @@ func (b *LocalBackend) enterStateLockedOnEntry(newState ipn.State, unlock unlock
|
||||
}
|
||||
systemd.Status("Connected; %s; %s", activeLogin, strings.Join(addrStrs, " "))
|
||||
case ipn.NoState:
|
||||
if b.deferredNetmapCancel != nil {
|
||||
b.deferredNetmapCancel()
|
||||
}
|
||||
// Do nothing.
|
||||
default:
|
||||
b.logf("[unexpected] unknown newState %#v", newState)
|
||||
}
|
||||
@@ -6877,6 +6783,7 @@ func (b *LocalBackend) CurrentProfile() ipn.LoginProfile {
|
||||
|
||||
// NewProfile creates and switches to the new profile.
|
||||
func (b *LocalBackend) NewProfile() error {
|
||||
log.Printf("TESTPREFS: NewProfile LB")
|
||||
unlock := b.lockAndGetUnlock()
|
||||
defer unlock()
|
||||
|
||||
|
||||
@@ -572,164 +572,6 @@ func TestSetUseExitNodeEnabled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetmapRateLimiting(t *testing.T) {
|
||||
b := new(LocalBackend)
|
||||
var cancel context.CancelFunc
|
||||
b.ctx, cancel = context.WithCancel(context.Background())
|
||||
b.logf = t.Logf
|
||||
b.setNetmapRateLimit(time.Duration(100 * time.Millisecond))
|
||||
|
||||
if b.netmapRateLimiter == nil {
|
||||
t.Fatalf("no netmapRateLimiter")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
b.netMap = new(netmap.NetworkMap)
|
||||
if g := b.sendNetmap(b.netMap); g != nil {
|
||||
t.Errorf("First should be immediately sent immediately")
|
||||
}
|
||||
|
||||
// We just sent a netmap, so these should all be rate limited. c1 should get cancelled.
|
||||
// c2 should be cancelled. c3 should be sent after 100ms.
|
||||
c1 := b.sendNetmap(b.netMap)
|
||||
c2 := b.sendNetmap(b.netMap)
|
||||
|
||||
// Let's spam a bunch more we won't track, just for fun
|
||||
for i := 0; i < 10; i++ {
|
||||
if g := b.sendNetmap(b.netMap); g == nil {
|
||||
t.Errorf("should have been deferred")
|
||||
}
|
||||
}
|
||||
|
||||
// This is our last netmap send. It should be deferred and sent after 100ms.
|
||||
c3 := b.sendNetmap(b.netMap)
|
||||
|
||||
// The first onnetmape should be cancelled
|
||||
select {
|
||||
case sent := <-c1:
|
||||
if sent {
|
||||
t.Errorf("Second netmap update was not cacncelled; sent got %v, want %v", sent, false)
|
||||
}
|
||||
}
|
||||
|
||||
// The second netmap should be cancelled
|
||||
select {
|
||||
case sent := <-c2:
|
||||
if sent {
|
||||
t.Errorf("Second netmap update was not cacncelled; got %v, sent want %v", sent, false)
|
||||
}
|
||||
}
|
||||
|
||||
// The last netmap should be sent after about 100ms
|
||||
select {
|
||||
case sent := <-c3:
|
||||
if !sent {
|
||||
t.Errorf("Fourth netmap update was deferred but not sent; sent got %v, want %v", sent, true)
|
||||
}
|
||||
}
|
||||
|
||||
elapsed := time.Since(now)
|
||||
if elapsed < 90*time.Millisecond {
|
||||
t.Errorf("elapsed time %v is too short", elapsed)
|
||||
}
|
||||
if elapsed > 110*time.Millisecond {
|
||||
t.Errorf("elapsed time %v is too long", elapsed)
|
||||
}
|
||||
|
||||
// The rate limiter should be reset at this point and the next netmap should be sent immediately.
|
||||
if g := b.sendNetmap(b.netMap); g != nil {
|
||||
t.Errorf("netmap should be immediately sent immediately")
|
||||
}
|
||||
|
||||
// We're rate limited - becuase we just sent a netmap.
|
||||
// Lower the rate limit and make sure we can send again once the rate limit is up.
|
||||
b.setNetmapRateLimit(time.Duration(10 * time.Millisecond))
|
||||
time.Sleep(12 * time.Millisecond)
|
||||
if g := b.sendNetmap(b.netMap); g != nil {
|
||||
t.Errorf("netmap should be immediately sent immediately")
|
||||
}
|
||||
|
||||
// Check to make sure the cancellation function is properly set and does what it's
|
||||
// supposed to do.
|
||||
c4 := b.sendNetmap(b.netMap)
|
||||
b.deferredNetmapCancel()
|
||||
select {
|
||||
case sent := <-c4:
|
||||
if sent {
|
||||
t.Errorf("Fourth netmap should have been cancelled; sent got %v, want %v", sent, true)
|
||||
}
|
||||
}
|
||||
|
||||
cancel()
|
||||
}
|
||||
|
||||
func TestNetmapDeferral(t *testing.T) {
|
||||
b := new(LocalBackend)
|
||||
var cancel context.CancelFunc
|
||||
b.ctx, cancel = context.WithCancel(context.Background())
|
||||
b.logf = t.Logf
|
||||
|
||||
w := 40 * time.Millisecond
|
||||
|
||||
// Ensure that a deferred netmap gets sent with the correct delay
|
||||
b.setNetmapRateLimit(time.Duration(w))
|
||||
start := time.Now()
|
||||
b.sendNetmap(b.netMap)
|
||||
|
||||
// Snooze for 20ms
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// This one should be deferred and sent after ~40-20ms
|
||||
c := b.sendNetmap(b.netMap)
|
||||
select {
|
||||
case sent := <-c:
|
||||
if !sent {
|
||||
t.Errorf("Fourth netmap update was deferred but not sent; sent got %v, want %v", sent, true)
|
||||
}
|
||||
}
|
||||
|
||||
slop := 5 * time.Millisecond
|
||||
g := time.Since(start) * time.Millisecond
|
||||
|
||||
// The difference between the elapsed time and the expected time should be within the slop
|
||||
// and our total time should always be slightly greater than the expected time.
|
||||
if w-g > slop || w > g {
|
||||
t.Errorf("elapsed time is too incorrect w:%v g:%v", w, g)
|
||||
}
|
||||
|
||||
cancel()
|
||||
}
|
||||
|
||||
func TestNetmapNoRateLimiting(t *testing.T) {
|
||||
b := new(LocalBackend)
|
||||
var cancel context.CancelFunc
|
||||
b.ctx, cancel = context.WithCancel(context.Background())
|
||||
b.logf = t.Logf
|
||||
|
||||
// A zero rate limit means send-at-will
|
||||
b.setNetmapRateLimit(0)
|
||||
|
||||
b.netMap = new(netmap.NetworkMap)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
if g := b.sendNetmap(b.netMap); g != nil {
|
||||
t.Errorf("should be immediately sent immediately")
|
||||
}
|
||||
}
|
||||
|
||||
// A negative rate limit also means send-at-will
|
||||
b.setNetmapRateLimit(-1)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
if g := b.sendNetmap(b.netMap); g != nil {
|
||||
t.Errorf("should be immediately sent immediately")
|
||||
}
|
||||
}
|
||||
|
||||
cancel()
|
||||
}
|
||||
|
||||
func TestFileTargets(t *testing.T) {
|
||||
b := new(LocalBackend)
|
||||
_, err := b.FileTargets()
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -203,6 +204,7 @@ func (pm *profileManager) setUnattendedModeAsConfigured() error {
|
||||
|
||||
// Reset unloads the current profile, if any.
|
||||
func (pm *profileManager) Reset() {
|
||||
log.Printf("TESTPREFS: Reset")
|
||||
pm.currentUserID = ""
|
||||
pm.NewProfile()
|
||||
}
|
||||
@@ -215,6 +217,7 @@ func (pm *profileManager) Reset() {
|
||||
// is logged into so that we can keep track of things like their domain name
|
||||
// across user switches to disambiguate the same account but a different tailnet.
|
||||
func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView, np ipn.NetworkProfile) error {
|
||||
log.Printf("TESTPREFS: SetPrefs with prefs %v", prefsIn)
|
||||
cp := pm.currentProfile
|
||||
if persist := prefsIn.Persist(); !persist.Valid() || persist.NodeID() == "" || persist.UserProfile().LoginName == "" {
|
||||
// We don't know anything about this profile, so ignore it for now.
|
||||
@@ -223,6 +226,7 @@ func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView, np ipn.NetworkProfile)
|
||||
|
||||
// Check if we already have an existing profile that matches the user/node.
|
||||
if existing := pm.findMatchingProfiles(prefsIn); len(existing) > 0 {
|
||||
log.Printf("TESTPREFS: SetPrefs found existing profile")
|
||||
// We already have a profile for this user/node we should reuse it. Also
|
||||
// cleanup any other duplicate profiles.
|
||||
cp = existing[0]
|
||||
@@ -230,6 +234,7 @@ func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView, np ipn.NetworkProfile)
|
||||
for _, p := range existing {
|
||||
// Clear the state.
|
||||
if err := pm.store.WriteState(p.Key, nil); err != nil {
|
||||
log.Printf("TESTPREFS: SetPrefs found existing profile, error writing state: %v", err)
|
||||
// We couldn't delete the state, so keep the profile around.
|
||||
continue
|
||||
}
|
||||
@@ -237,6 +242,8 @@ func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView, np ipn.NetworkProfile)
|
||||
// in [profileManager.setProfilePrefs] below.
|
||||
delete(pm.knownProfiles, p.ID)
|
||||
}
|
||||
} else {
|
||||
log.Printf("TESTPREFS: SetPrefs not found existing profile")
|
||||
}
|
||||
pm.currentProfile = cp
|
||||
if err := pm.SetProfilePrefs(cp, prefsIn, np); err != nil {
|
||||
@@ -327,6 +334,7 @@ func newUnusedID(knownProfiles map[ipn.ProfileID]*ipn.LoginProfile) (ipn.Profile
|
||||
// profile, such as verifying the caller's access rights or checking
|
||||
// if another profile for the same node already exists.
|
||||
func (pm *profileManager) setProfilePrefsNoPermCheck(profile *ipn.LoginProfile, clonedPrefs ipn.PrefsView) error {
|
||||
log.Printf("TESTPREFS: setProfilePrefsNoPerm")
|
||||
isCurrentProfile := pm.currentProfile == profile
|
||||
if isCurrentProfile {
|
||||
pm.prefs = clonedPrefs
|
||||
@@ -423,6 +431,7 @@ func (pm *profileManager) profilePrefs(p *ipn.LoginProfile) (ipn.PrefsView, erro
|
||||
// If the profile exists but is not accessible to the current user, it returns an [errProfileAccessDenied].
|
||||
// If the profile does not exist, it returns an [errProfileNotFound].
|
||||
func (pm *profileManager) SwitchProfile(id ipn.ProfileID) error {
|
||||
log.Printf("TESTPREFS: SwitchProfile")
|
||||
metricSwitchProfile.Add(1)
|
||||
|
||||
kp, ok := pm.knownProfiles[id]
|
||||
@@ -450,6 +459,7 @@ func (pm *profileManager) SwitchProfile(id ipn.ProfileID) error {
|
||||
// It creates a new one and switches to it if the current user does not have a default profile,
|
||||
// or returns an error if the default profile is inaccessible or could not be loaded.
|
||||
func (pm *profileManager) SwitchToDefaultProfile() error {
|
||||
log.Printf("TESTPREFS: SwitchToDefault")
|
||||
if id := pm.DefaultUserProfileID(pm.currentUserID); id != "" {
|
||||
return pm.SwitchProfile(id)
|
||||
}
|
||||
@@ -547,6 +557,7 @@ func (pm *profileManager) DeleteProfile(id ipn.ProfileID) error {
|
||||
}
|
||||
|
||||
func (pm *profileManager) deleteCurrentProfile() error {
|
||||
log.Printf("TESTPREFS: deleteCurrent")
|
||||
if err := pm.checkProfileAccess(pm.currentProfile); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -627,6 +638,7 @@ func (pm *profileManager) NewProfile() {
|
||||
// NewProfileForUser is like [profileManager.NewProfile], but it switches to the
|
||||
// specified user and sets that user as the profile owner for the new profile.
|
||||
func (pm *profileManager) NewProfileForUser(uid ipn.WindowsUserID) {
|
||||
log.Printf("TESTPREFS: NewProfileForUser")
|
||||
pm.currentUserID = uid
|
||||
|
||||
metricNewProfile.Add(1)
|
||||
@@ -641,6 +653,7 @@ func (pm *profileManager) NewProfileForUser(uid ipn.WindowsUserID) {
|
||||
// newly created profile immediately. It returns the newly created profile on success,
|
||||
// or an error on failure.
|
||||
func (pm *profileManager) newProfileWithPrefs(uid ipn.WindowsUserID, prefs ipn.PrefsView, switchNow bool) (*ipn.LoginProfile, error) {
|
||||
log.Printf("TESTPREFS: newProfileWithPrefs")
|
||||
metricNewProfile.Add(1)
|
||||
|
||||
profile := &ipn.LoginProfile{LocalUserID: uid}
|
||||
@@ -733,6 +746,7 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, ht *healt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("TESTPREFS: newProfileWithGOOS with state key %v", stateKey)
|
||||
|
||||
knownProfiles, err := readKnownProfiles(store)
|
||||
if err != nil {
|
||||
@@ -748,12 +762,15 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, ht *healt
|
||||
}
|
||||
|
||||
if stateKey != "" {
|
||||
log.Printf("TESTPREFS: state key %v exists", stateKey)
|
||||
for _, v := range knownProfiles {
|
||||
log.Printf("TESTPREFS: state key %v exists looking at matching profile %s", stateKey, v)
|
||||
if v.Key == stateKey {
|
||||
pm.currentProfile = v
|
||||
}
|
||||
}
|
||||
if pm.currentProfile == nil {
|
||||
log.Printf("TESTPREFS: current profile is nil")
|
||||
if suf, ok := strings.CutPrefix(string(stateKey), "user-"); ok {
|
||||
pm.currentUserID = ipn.WindowsUserID(suf)
|
||||
}
|
||||
@@ -776,12 +793,14 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, ht *healt
|
||||
// uid passed in from the unix tests. The uid's used for Windows tests
|
||||
// and runtime must be valid Windows security identifier structures.
|
||||
} else if len(knownProfiles) == 0 && goos != "windows" && runtime.GOOS != "windows" {
|
||||
log.Printf("TESTPREFS: no known profiles")
|
||||
// No known profiles, try a migration.
|
||||
pm.dlogf("no known profiles; trying to migrate from legacy prefs")
|
||||
if _, err := pm.migrateFromLegacyPrefs(pm.currentUserID, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
log.Printf("TESTPREFS: newProfileWithGOOS new profile")
|
||||
pm.NewProfile()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ package kubestore
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -142,6 +143,7 @@ func (s *Store) loadState() error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
log.Printf("TEST: kube store: got secret: %#+v", secret.Data)
|
||||
s.memory.LoadFromMap(secret.Data)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package mem
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
xmaps "golang.org/x/exp/maps"
|
||||
@@ -32,18 +33,21 @@ func (s *Store) String() string { return "mem.Store" }
|
||||
// ReadState implements the StateStore interface.
|
||||
// It returns ipn.ErrStateNotExist if the state does not exist.
|
||||
func (s *Store) ReadState(id ipn.StateKey) ([]byte, error) {
|
||||
log.Printf("TEST: ReadState key %v ", id)
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
bs, ok := s.cache[id]
|
||||
if !ok {
|
||||
return nil, ipn.ErrStateNotExist
|
||||
}
|
||||
log.Printf("TEST: ReadState key %v val %v", id, string(bs))
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
// WriteState implements the StateStore interface.
|
||||
// It never returns an error.
|
||||
func (s *Store) WriteState(id ipn.StateKey, bs []byte) error {
|
||||
log.Printf("TEST: WriteState key %v ", id)
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.cache == nil {
|
||||
@@ -57,10 +61,12 @@ func (s *Store) WriteState(id ipn.StateKey, bs []byte) error {
|
||||
// Any existing content is cleared, and the provided map is
|
||||
// copied into the cache.
|
||||
func (s *Store) LoadFromMap(m map[string][]byte) {
|
||||
log.Printf("Store: LoadFromMap")
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
xmaps.Clear(s.cache)
|
||||
for k, v := range m {
|
||||
log.Printf("TEST: setting state key %v %+#v", k, string(v))
|
||||
mak.Set(&s.cache, ipn.StateKey(k), v)
|
||||
}
|
||||
return
|
||||
|
||||
@@ -32,6 +32,9 @@ type Records struct {
|
||||
// TailscaledConfigFileName returns a tailscaled config file name in
|
||||
// format expected by containerboot for the given CapVer.
|
||||
func TailscaledConfigFileName(cap tailcfg.CapabilityVersion) string {
|
||||
if cap < 95 {
|
||||
return "tailscaled"
|
||||
}
|
||||
return fmt.Sprintf("cap-%v.hujson", cap)
|
||||
}
|
||||
|
||||
|
||||
@@ -56,29 +56,6 @@ func NewLimiter(r Limit, b int) *Limiter {
|
||||
return &Limiter{limit: r, burst: float64(b)}
|
||||
}
|
||||
|
||||
// Limit returns the maximum overall event rate.
|
||||
func (lim *Limiter) Limit() Limit {
|
||||
return lim.limit
|
||||
}
|
||||
|
||||
// Delay returns the approximate minimum duration before sufficient tokens
|
||||
// will be available to permit another event.
|
||||
func (lim *Limiter) Delay() time.Duration {
|
||||
lim.mu.Lock()
|
||||
defer lim.mu.Unlock()
|
||||
|
||||
// Calculate the new number of tokens available due to the passage of time.
|
||||
elapsed := mono.Now().Sub(lim.last)
|
||||
tokens := lim.tokens + float64(lim.limit)*elapsed.Seconds()
|
||||
if tokens > lim.burst {
|
||||
tokens = lim.burst
|
||||
}
|
||||
|
||||
// Calculate the time until the next token is available.
|
||||
wait := time.Duration((1-tokens)/float64(lim.limit)*1e9) * time.Nanosecond
|
||||
return wait
|
||||
}
|
||||
|
||||
// Allow reports whether an event may happen now.
|
||||
func (lim *Limiter) Allow() bool {
|
||||
return lim.allow(mono.Now())
|
||||
|
||||
@@ -145,31 +145,6 @@ func TestSimultaneousRequests(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelay(t *testing.T) {
|
||||
lim := NewLimiter(Every(1*time.Second), 1)
|
||||
// We'll allow for 10 ms of slop to avoid flakiness.
|
||||
// w should always be just slightly greater than d
|
||||
slop := int64(10)
|
||||
|
||||
lim.Allow()
|
||||
|
||||
d := lim.Delay().Milliseconds()
|
||||
w := int64(1000)
|
||||
|
||||
if w-d > slop || d > w {
|
||||
t.Errorf("Delay() = %v want 1000", d)
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
w = 950
|
||||
d = lim.Delay().Milliseconds()
|
||||
|
||||
// ~50 milliseconds will have passed,
|
||||
if w-d > slop || d > w {
|
||||
t.Errorf("Delay() = %v want 950", d)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAllowN(b *testing.B) {
|
||||
lim := NewLimiter(Every(1*time.Second), 1)
|
||||
now := mono.Now()
|
||||
|
||||
@@ -391,11 +391,3 @@ godzilla
|
||||
sirius
|
||||
vector
|
||||
cherimoya
|
||||
shilling
|
||||
kettle
|
||||
kitchen
|
||||
fahrenheit
|
||||
rankine
|
||||
piano
|
||||
ruler
|
||||
scoville
|
||||
|
||||
Reference in New Issue
Block a user