diff --git a/appc/appconnector_test.go b/appc/appconnector_test.go
index a860da6a7..d14ef68fc 100644
--- a/appc/appconnector_test.go
+++ b/appc/appconnector_test.go
@@ -698,7 +698,7 @@ func TestRateLogger(t *testing.T) {
wasCalled = true
})
- for i := 0; i < 3; i++ {
+ for range 3 {
clock.Advance(1 * time.Millisecond)
rl.update(0)
if wasCalled {
@@ -720,7 +720,7 @@ func TestRateLogger(t *testing.T) {
wasCalled = true
})
- for i := 0; i < 3; i++ {
+ for range 3 {
clock.Advance(1 * time.Minute)
rl.update(0)
if wasCalled {
diff --git a/client/local/local.go b/client/local/local.go
index a7b8b83b1..e72589306 100644
--- a/client/local/local.go
+++ b/client/local/local.go
@@ -192,8 +192,8 @@ func (e *AccessDeniedError) Unwrap() error { return e.err }
// IsAccessDeniedError reports whether err is or wraps an AccessDeniedError.
func IsAccessDeniedError(err error) bool {
- var ae *AccessDeniedError
- return errors.As(err, &ae)
+ _, ok := errors.AsType[*AccessDeniedError](err)
+ return ok
}
// PreconditionsFailedError is returned when the server responds
@@ -210,8 +210,8 @@ func (e *PreconditionsFailedError) Unwrap() error { return e.err }
// IsPreconditionsFailedError reports whether err is or wraps an PreconditionsFailedError.
func IsPreconditionsFailedError(err error) bool {
- var ae *PreconditionsFailedError
- return errors.As(err, &ae)
+ _, ok := errors.AsType[*PreconditionsFailedError](err)
+ return ok
}
// bestError returns either err, or if body contains a valid JSON
@@ -1071,7 +1071,7 @@ func tailscaledConnectHint() string {
// ActiveState=inactive
// SubState=dead
st := map[string]string{}
- for _, line := range strings.Split(string(out), "\n") {
+ for line := range strings.SplitSeq(string(out), "\n") {
if k, v, ok := strings.Cut(line, "="); ok {
st[k] = strings.TrimSpace(v)
}
diff --git a/client/systray/logo.go b/client/systray/logo.go
index 4cd19778d..a0f8bf7d0 100644
--- a/client/systray/logo.go
+++ b/client/systray/logo.go
@@ -233,8 +233,8 @@ func (logo tsLogo) renderWithBorder(borderUnits int) *bytes.Buffer {
dc.InvertMask()
}
- for y := 0; y < 3; y++ {
- for x := 0; x < 3; x++ {
+ for y := range 3 {
+ for x := range 3 {
px := (borderUnits + 1 + 3*x) * radius
py := (borderUnits + 1 + 3*y) * radius
col := fg
diff --git a/clientupdate/clientupdate.go b/clientupdate/clientupdate.go
index d52241483..6d034b342 100644
--- a/clientupdate/clientupdate.go
+++ b/clientupdate/clientupdate.go
@@ -1292,6 +1292,6 @@ func requireRoot() error {
}
func isExitError(err error) bool {
- var exitErr *exec.ExitError
- return errors.As(err, &exitErr)
+ _, ok := errors.AsType[*exec.ExitError](err)
+ return ok
}
diff --git a/clientupdate/clientupdate_test.go b/clientupdate/clientupdate_test.go
index 13fc8f08a..100339ce7 100644
--- a/clientupdate/clientupdate_test.go
+++ b/clientupdate/clientupdate_test.go
@@ -451,7 +451,7 @@ func TestSynoArch(t *testing.T) {
synoinfoConfPath := filepath.Join(t.TempDir(), "synoinfo.conf")
if err := os.WriteFile(
synoinfoConfPath,
- []byte(fmt.Sprintf("unique=%q\n", tt.synoinfoUnique)),
+ fmt.Appendf(nil, "unique=%q\n", tt.synoinfoUnique),
0600,
); err != nil {
t.Fatal(err)
diff --git a/cmd/containerboot/forwarding.go b/cmd/containerboot/forwarding.go
index 0ec9c36c0..6d90fbaaa 100644
--- a/cmd/containerboot/forwarding.go
+++ b/cmd/containerboot/forwarding.go
@@ -51,7 +51,7 @@ func ensureIPForwarding(root, clusterProxyTargetIP, tailnetTargetIP, tailnetTarg
v4Forwarding = true
}
if routes != nil && *routes != "" {
- for _, route := range strings.Split(*routes, ",") {
+ for route := range strings.SplitSeq(*routes, ",") {
cidr, err := netip.ParsePrefix(route)
if err != nil {
return fmt.Errorf("invalid subnet route: %v", err)
diff --git a/cmd/containerboot/main_test.go b/cmd/containerboot/main_test.go
index cc5629f99..365cf2184 100644
--- a/cmd/containerboot/main_test.go
+++ b/cmd/containerboot/main_test.go
@@ -15,6 +15,7 @@
"fmt"
"io"
"io/fs"
+ "maps"
"net"
"net/http"
"net/http/httptest"
@@ -1249,7 +1250,7 @@ func (b *lockingBuffer) String() string {
func waitLogLine(t *testing.T, timeout time.Duration, b *lockingBuffer, want string) {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
- for _, line := range strings.Split(b.String(), "\n") {
+ for line := range strings.SplitSeq(b.String(), "\n") {
if !strings.HasPrefix(line, "boot: ") {
continue
}
@@ -1438,9 +1439,7 @@ func (k *kubeServer) Secret() map[string]string {
k.Lock()
defer k.Unlock()
ret := map[string]string{}
- for k, v := range k.secret {
- ret[k] = v
- }
+ maps.Copy(ret, k.secret)
return ret
}
diff --git a/cmd/derper/mesh.go b/cmd/derper/mesh.go
index 34ea7da85..c07cfe969 100644
--- a/cmd/derper/mesh.go
+++ b/cmd/derper/mesh.go
@@ -25,7 +25,7 @@ func startMesh(s *derpserver.Server) error {
if !s.HasMeshKey() {
return errors.New("--mesh-with requires --mesh-psk-file")
}
- for _, hostTuple := range strings.Split(*meshWith, ",") {
+ for hostTuple := range strings.SplitSeq(*meshWith, ",") {
if err := startMeshWithHost(s, hostTuple); err != nil {
return err
}
diff --git a/cmd/k8s-operator/egress-eps_test.go b/cmd/k8s-operator/egress-eps_test.go
index 47acb64f2..6335b4eb8 100644
--- a/cmd/k8s-operator/egress-eps_test.go
+++ b/cmd/k8s-operator/egress-eps_test.go
@@ -11,7 +11,6 @@
"math/rand/v2"
"testing"
- "github.com/AlekSi/pointer"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
@@ -106,11 +105,11 @@ func TestTailscaleEgressEndpointSlices(t *testing.T) {
expectReconciled(t, er, "operator-ns", "foo")
eps.Endpoints = append(eps.Endpoints, discoveryv1.Endpoint{
Addresses: []string{"10.0.0.1"},
- Hostname: pointer.To("foo"),
+ Hostname: new("foo"),
Conditions: discoveryv1.EndpointConditions{
- Serving: pointer.ToBool(true),
- Ready: pointer.ToBool(true),
- Terminating: pointer.ToBool(false),
+ Serving: new(true),
+ Ready: new(true),
+ Terminating: new(false),
},
})
expectEqual(t, fc, eps)
diff --git a/cmd/k8s-operator/egress-services-readiness_test.go b/cmd/k8s-operator/egress-services-readiness_test.go
index ba89903df..96d76cc4e 100644
--- a/cmd/k8s-operator/egress-services-readiness_test.go
+++ b/cmd/k8s-operator/egress-services-readiness_test.go
@@ -9,7 +9,6 @@
"fmt"
"testing"
- "github.com/AlekSi/pointer"
"go.uber.org/zap"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@@ -145,9 +144,9 @@ func setEndpointForReplica(pg *tsapi.ProxyGroup, ordinal int32, eps *discoveryv1
eps.Endpoints = append(eps.Endpoints, discoveryv1.Endpoint{
Addresses: []string{p.Status.PodIPs[0].IP},
Conditions: discoveryv1.EndpointConditions{
- Ready: pointer.ToBool(true),
- Serving: pointer.ToBool(true),
- Terminating: pointer.ToBool(false),
+ Ready: new(true),
+ Serving: new(true),
+ Terminating: new(false),
},
})
}
diff --git a/cmd/k8s-operator/egress-services_test.go b/cmd/k8s-operator/egress-services_test.go
index 458614491..d38284690 100644
--- a/cmd/k8s-operator/egress-services_test.go
+++ b/cmd/k8s-operator/egress-services_test.go
@@ -243,7 +243,7 @@ func portsForEndpointSlice(svc *corev1.Service) []discoveryv1.EndpointPort {
ports = append(ports, discoveryv1.EndpointPort{
Name: &p.Name,
Protocol: &p.Protocol,
- Port: pointer.ToInt32(p.TargetPort.IntVal),
+ Port: new(p.TargetPort.IntVal),
})
}
return ports
diff --git a/cmd/k8s-operator/ingress-for-pg.go b/cmd/k8s-operator/ingress-for-pg.go
index 5966ace3c..4b140a8ae 100644
--- a/cmd/k8s-operator/ingress-for-pg.go
+++ b/cmd/k8s-operator/ingress-for-pg.go
@@ -10,6 +10,7 @@
"encoding/json"
"errors"
"fmt"
+ "maps"
"math/rand/v2"
"net/http"
"reflect"
@@ -914,9 +915,7 @@ func ownerAnnotations(operatorID string, svc *tailscale.VIPService) (map[string]
}
newAnnots := make(map[string]string, len(svc.Annotations)+1)
- for k, v := range svc.Annotations {
- newAnnots[k] = v
- }
+ maps.Copy(newAnnots, svc.Annotations)
newAnnots[ownerAnnotation] = string(json)
return newAnnots, nil
}
@@ -1129,8 +1128,7 @@ func hasCerts(ctx context.Context, cl client.Client, lc localClient, ns string,
}
func isErrorTailscaleServiceNotFound(err error) bool {
- var errResp tailscale.ErrResponse
- ok := errors.As(err, &errResp)
+ errResp, ok := errors.AsType[tailscale.ErrResponse](err)
return ok && errResp.Status == http.StatusNotFound
}
@@ -1144,7 +1142,7 @@ func tagViolations(obj client.Object) []string {
return nil
}
- for _, tag := range strings.Split(tags, ",") {
+ for tag := range strings.SplitSeq(tags, ",") {
tag = strings.TrimSpace(tag)
if err := tailcfg.CheckTag(tag); err != nil {
violations = append(violations, fmt.Sprintf("invalid tag %q: %v", tag, err))
diff --git a/cmd/k8s-operator/ingress-for-pg_test.go b/cmd/k8s-operator/ingress-for-pg_test.go
index e93d0184e..480e6a26e 100644
--- a/cmd/k8s-operator/ingress-for-pg_test.go
+++ b/cmd/k8s-operator/ingress-for-pg_test.go
@@ -1102,7 +1102,7 @@ func verifyTailscaledConfig(t *testing.T, fc client.Client, pgName string, expec
Labels: pgSecretLabels(pgName, kubetypes.LabelSecretTypeConfig),
},
Data: map[string][]byte{
- tsoperator.TailscaledConfigFileName(pgMinCapabilityVersion): []byte(fmt.Sprintf(`{"Version":""%s}`, expected)),
+ tsoperator.TailscaledConfigFileName(pgMinCapabilityVersion): fmt.Appendf(nil, `{"Version":""%s}`, expected),
},
})
}
diff --git a/cmd/k8s-operator/metrics_resources.go b/cmd/k8s-operator/metrics_resources.go
index afb055018..c7c329a7e 100644
--- a/cmd/k8s-operator/metrics_resources.go
+++ b/cmd/k8s-operator/metrics_resources.go
@@ -8,6 +8,7 @@
import (
"context"
"fmt"
+ "maps"
"reflect"
"go.uber.org/zap"
@@ -286,11 +287,7 @@ func isNamespacedProxyType(typ string) bool {
func mergeMapKeys(a, b map[string]string) map[string]string {
m := make(map[string]string, len(a)+len(b))
- for key, val := range b {
- m[key] = val
- }
- for key, val := range a {
- m[key] = val
- }
+ maps.Copy(m, b)
+ maps.Copy(m, a)
return m
}
diff --git a/cmd/k8s-operator/proxygroup.go b/cmd/k8s-operator/proxygroup.go
index db8733f90..200782498 100644
--- a/cmd/k8s-operator/proxygroup.go
+++ b/cmd/k8s-operator/proxygroup.go
@@ -308,8 +308,7 @@ func (r *ProxyGroupReconciler) maybeProvision(ctx context.Context, tailscaleClie
var err error
svcToNodePorts, tailscaledPort, err = r.ensureNodePortServiceCreated(ctx, pg, proxyClass)
if err != nil {
- var allocatePortErr *allocatePortsErr
- if errors.As(err, &allocatePortErr) {
+ if _, ok := errors.AsType[*allocatePortsErr](err); ok {
reason := reasonProxyGroupCreationFailed
msg := fmt.Sprintf("error provisioning NodePort Services for static endpoints: %v", err)
r.recorder.Event(pg, corev1.EventTypeWarning, reason, msg)
@@ -321,8 +320,7 @@ func (r *ProxyGroupReconciler) maybeProvision(ctx context.Context, tailscaleClie
staticEndpoints, err := r.ensureConfigSecretsCreated(ctx, tailscaleClient, pg, proxyClass, svcToNodePorts)
if err != nil {
- var selectorErr *FindStaticEndpointErr
- if errors.As(err, &selectorErr) {
+ if _, ok := errors.AsType[*FindStaticEndpointErr](err); ok {
reason := reasonProxyGroupCreationFailed
msg := fmt.Sprintf("error provisioning config Secrets: %v", err)
r.recorder.Event(pg, corev1.EventTypeWarning, reason, msg)
@@ -718,8 +716,7 @@ func (r *ProxyGroupReconciler) maybeCleanup(ctx context.Context, tailscaleClient
func (r *ProxyGroupReconciler) deleteTailnetDevice(ctx context.Context, tailscaleClient tsClient, id tailcfg.StableNodeID, logger *zap.SugaredLogger) error {
logger.Debugf("deleting device %s from control", string(id))
if err := tailscaleClient.DeleteDevice(ctx, string(id)); err != nil {
- errResp := &tailscale.ErrResponse{}
- if ok := errors.As(err, errResp); ok && errResp.Status == http.StatusNotFound {
+ if errResp, ok := errors.AsType[tailscale.ErrResponse](err); ok && errResp.Status == http.StatusNotFound {
logger.Debugf("device %s not found, likely because it has already been deleted from control", string(id))
} else {
return fmt.Errorf("error deleting device: %w", err)
diff --git a/cmd/k8s-operator/proxygroup_specs.go b/cmd/k8s-operator/proxygroup_specs.go
index 69b5b109a..60b4bddd5 100644
--- a/cmd/k8s-operator/proxygroup_specs.go
+++ b/cmd/k8s-operator/proxygroup_specs.go
@@ -7,6 +7,7 @@
import (
"fmt"
+ "maps"
"slices"
"strconv"
"strings"
@@ -544,9 +545,7 @@ func pgSecretLabels(pgName, secretType string) map[string]string {
func pgLabels(pgName string, customLabels map[string]string) map[string]string {
labels := make(map[string]string, len(customLabels)+3)
- for k, v := range customLabels {
- labels[k] = v
- }
+ maps.Copy(labels, customLabels)
labels[kubetypes.LabelManaged] = "true"
labels[LabelParentType] = "proxygroup"
diff --git a/cmd/k8s-operator/proxygroup_test.go b/cmd/k8s-operator/proxygroup_test.go
index 2d46e3d5b..9b3ee0e0f 100644
--- a/cmd/k8s-operator/proxygroup_test.go
+++ b/cmd/k8s-operator/proxygroup_test.go
@@ -1826,10 +1826,10 @@ func addNodeIDToStateSecrets(t *testing.T, fc client.WithWatch, pg *tsapi.ProxyG
currentProfileKey: []byte(key),
key: bytes,
kubetypes.KeyDeviceIPs: []byte(`["1.2.3.4", "::1"]`),
- kubetypes.KeyDeviceFQDN: []byte(fmt.Sprintf("hostname-nodeid-%d.tails-scales.ts.net", i)),
+ kubetypes.KeyDeviceFQDN: fmt.Appendf(nil, "hostname-nodeid-%d.tails-scales.ts.net", i),
// TODO(tomhjp): We have two different mechanisms to retrieve device IDs.
// Consolidate on this one.
- kubetypes.KeyDeviceID: []byte(fmt.Sprintf("nodeid-%d", i)),
+ kubetypes.KeyDeviceID: fmt.Appendf(nil, "nodeid-%d", i),
kubetypes.KeyPodUID: []byte(podUID),
}
})
diff --git a/cmd/k8s-operator/sts.go b/cmd/k8s-operator/sts.go
index 2a63ede4e..c88a6df17 100644
--- a/cmd/k8s-operator/sts.go
+++ b/cmd/k8s-operator/sts.go
@@ -11,6 +11,7 @@
"encoding/json"
"errors"
"fmt"
+ "maps"
"net/http"
"os"
"path"
@@ -304,8 +305,7 @@ func (a *tailscaleSTSReconciler) Cleanup(ctx context.Context, tailnet string, lo
if dev.id != "" {
logger.Debugf("deleting device %s from control", string(dev.id))
if err = tailscaleClient.DeleteDevice(ctx, string(dev.id)); err != nil {
- errResp := &tailscale.ErrResponse{}
- if ok := errors.As(err, errResp); ok && errResp.Status == http.StatusNotFound {
+ if errResp, ok := errors.AsType[tailscale.ErrResponse](err); ok && errResp.Status == http.StatusNotFound {
logger.Debugf("device %s not found, likely because it has already been deleted from control", string(dev.id))
} else {
return false, fmt.Errorf("deleting device: %w", err)
@@ -499,14 +499,11 @@ func (a *tailscaleSTSReconciler) provisionSecrets(ctx context.Context, tailscale
}
if dev != nil && dev.id != "" {
- var errResp *tailscale.ErrResponse
-
err = tailscaleClient.DeleteDevice(ctx, string(dev.id))
- switch {
- case errors.As(err, &errResp) && errResp.Status == http.StatusNotFound:
+ if errResp, ok := errors.AsType[*tailscale.ErrResponse](err); ok && errResp.Status == http.StatusNotFound {
// This device has possibly already been deleted in the admin console. So we can ignore this
// and move on to removing the secret.
- case err != nil:
+ } else if err != nil {
return nil, err
}
}
@@ -677,9 +674,8 @@ func (a *tailscaleSTSReconciler) reconcileSTS(ctx context.Context, logger *zap.S
},
}
mak.Set(&pod.Labels, "app", sts.ParentResourceUID)
- for key, val := range sts.ChildResourceLabels {
- pod.Labels[key] = val // sync StatefulSet labels to Pod to make it easier for users to select the Pod
- }
+ // sync StatefulSet labels to Pod to make it easier for users to select the Pod
+ maps.Copy(pod.Labels, sts.ChildResourceLabels)
if sts.Replicas > 0 {
ss.Spec.Replicas = new(sts.Replicas)
diff --git a/cmd/k8s-operator/sts_test.go b/cmd/k8s-operator/sts_test.go
index f44de8481..0ceec5791 100644
--- a/cmd/k8s-operator/sts_test.go
+++ b/cmd/k8s-operator/sts_test.go
@@ -8,6 +8,7 @@
import (
_ "embed"
"fmt"
+ "maps"
"reflect"
"regexp"
"strings"
@@ -408,7 +409,5 @@ func Test_mergeStatefulSetLabelsOrAnnots(t *testing.T) {
// updateMap updates map a with the values from map b.
func updateMap(a, b map[string]string) {
- for key, val := range b {
- a[key] = val
- }
+ maps.Copy(a, b)
}
diff --git a/cmd/k8s-operator/svc-for-pg.go b/cmd/k8s-operator/svc-for-pg.go
index e0383824a..7cbbaebaa 100644
--- a/cmd/k8s-operator/svc-for-pg.go
+++ b/cmd/k8s-operator/svc-for-pg.go
@@ -526,8 +526,7 @@ func (r *HAServiceReconciler) tailnetCertDomain(ctx context.Context) (string, er
func cleanupTailscaleService(ctx context.Context, tsClient tsClient, name tailcfg.ServiceName, operatorID string, logger *zap.SugaredLogger) (updated bool, err error) {
svc, err := tsClient.GetVIPService(ctx, name)
if err != nil {
- errResp := &tailscale.ErrResponse{}
- ok := errors.As(err, errResp)
+ errResp, ok := errors.AsType[tailscale.ErrResponse](err)
if ok && errResp.Status == http.StatusNotFound {
return false, nil
}
diff --git a/cmd/k8s-operator/testutils_test.go b/cmd/k8s-operator/testutils_test.go
index e13478d71..191a31723 100644
--- a/cmd/k8s-operator/testutils_test.go
+++ b/cmd/k8s-operator/testutils_test.go
@@ -13,6 +13,7 @@
"net/netip"
"path"
"reflect"
+ "slices"
"strings"
"sync"
"testing"
@@ -555,7 +556,7 @@ func expectedSecret(t *testing.T, cl client.Client, opts configOpts) *corev1.Sec
if opts.isExitNode {
r = "0.0.0.0/0,::/0," + r
}
- for _, rr := range strings.Split(r, ",") {
+ for rr := range strings.SplitSeq(r, ",") {
prefix, err := netip.ParsePrefix(rr)
if err != nil {
t.Fatal(err)
@@ -822,12 +823,9 @@ func expectEvents(t *testing.T, rec *record.FakeRecorder, wantsEvents []string)
select {
case gotEvent := <-rec.Events:
found := false
- for _, wantEvent := range wantsEvents {
- if wantEvent == gotEvent {
- found = true
- seenEvents = append(seenEvents, gotEvent)
- break
- }
+ if slices.Contains(wantsEvents, gotEvent) {
+ found = true
+ seenEvents = append(seenEvents, gotEvent)
}
if !found {
t.Errorf("got unexpected event %q, expected events: %+#v", gotEvent, wantsEvents)
diff --git a/cmd/k8s-operator/tsrecorder.go b/cmd/k8s-operator/tsrecorder.go
index 60ed24a70..0a497a46e 100644
--- a/cmd/k8s-operator/tsrecorder.go
+++ b/cmd/k8s-operator/tsrecorder.go
@@ -363,15 +363,12 @@ func (r *RecorderReconciler) maybeCleanupSecrets(ctx context.Context, tailscaleC
}
if ok {
- var errResp *tailscale.ErrResponse
-
r.log.Debugf("deleting device %s", devicePrefs.Config.NodeID)
err = tailscaleClient.DeleteDevice(ctx, string(devicePrefs.Config.NodeID))
- switch {
- case errors.As(err, &errResp) && errResp.Status == http.StatusNotFound:
+ if errResp, ok := errors.AsType[*tailscale.ErrResponse](err); ok && errResp.Status == http.StatusNotFound {
// This device has possibly already been deleted in the admin console. So we can ignore this
// and move on to removing the secret.
- case err != nil:
+ } else if err != nil {
return err
}
}
@@ -412,8 +409,7 @@ func (r *RecorderReconciler) maybeCleanup(ctx context.Context, tsr *tsapi.Record
nodeID := string(devicePrefs.Config.NodeID)
logger.Debugf("deleting device %s from control", nodeID)
if err = tailscaleClient.DeleteDevice(ctx, nodeID); err != nil {
- errResp := &tailscale.ErrResponse{}
- if errors.As(err, errResp) && errResp.Status == http.StatusNotFound {
+ if errResp, ok := errors.AsType[tailscale.ErrResponse](err); ok && errResp.Status == http.StatusNotFound {
logger.Debugf("device %s not found, likely because it has already been deleted from control", nodeID)
continue
}
diff --git a/cmd/k8s-operator/tsrecorder_specs.go b/cmd/k8s-operator/tsrecorder_specs.go
index 101f68405..5a93bc22b 100644
--- a/cmd/k8s-operator/tsrecorder_specs.go
+++ b/cmd/k8s-operator/tsrecorder_specs.go
@@ -7,6 +7,7 @@
import (
"fmt"
+ "maps"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@@ -312,9 +313,7 @@ func tsrEnv(tsr *tsapi.Recorder, loginServer string) []corev1.EnvVar {
func tsrLabels(app, instance string, customLabels map[string]string) map[string]string {
labels := make(map[string]string, len(customLabels)+3)
- for k, v := range customLabels {
- labels[k] = v
- }
+ maps.Copy(labels, customLabels)
// ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/
labels["app.kubernetes.io/name"] = app
diff --git a/cmd/mkpkg/main.go b/cmd/mkpkg/main.go
index 6f4de7e29..ecf108c2e 100644
--- a/cmd/mkpkg/main.go
+++ b/cmd/mkpkg/main.go
@@ -24,7 +24,7 @@ func parseFiles(s string, typ string) (files.Contents, error) {
return nil, nil
}
var contents files.Contents
- for _, f := range strings.Split(s, ",") {
+ for f := range strings.SplitSeq(s, ",") {
fs := strings.Split(f, ":")
if len(fs) != 2 {
return nil, fmt.Errorf("unparseable file field %q", f)
@@ -41,7 +41,7 @@ func parseEmptyDirs(s string) files.Contents {
return nil
}
var contents files.Contents
- for _, d := range strings.Split(s, ",") {
+ for d := range strings.SplitSeq(s, ",") {
contents = append(contents, &files.Content{Type: files.TypeDir, Destination: d})
}
return contents
diff --git a/cmd/natc/ippool/ippool_test.go b/cmd/natc/ippool/ippool_test.go
index 405ec6156..af0053c2f 100644
--- a/cmd/natc/ippool/ippool_test.go
+++ b/cmd/natc/ippool/ippool_test.go
@@ -30,7 +30,7 @@ func TestIPPoolExhaustion(t *testing.T) {
from := tailcfg.NodeID(12345)
- for i := 0; i < 5; i++ {
+ for range 5 {
for _, domain := range domains {
addr, err := pool.IPForDomain(from, domain)
if err != nil {
diff --git a/cmd/natc/natc.go b/cmd/natc/natc.go
index 11975b7d2..339c42ccd 100644
--- a/cmd/natc/natc.go
+++ b/cmd/natc/natc.go
@@ -149,7 +149,7 @@ func main() {
}
var prefixes []netip.Prefix
- for _, s := range strings.Split(*v4PfxStr, ",") {
+ for s := range strings.SplitSeq(*v4PfxStr, ",") {
p := netip.MustParsePrefix(strings.TrimSpace(s))
if p.Masked() != p {
log.Fatalf("v4 prefix %v is not a masked prefix", p)
@@ -372,8 +372,7 @@ func (c *connector) handleDNS(pc net.PacketConn, buf []byte, remoteAddr *net.UDP
addrQCount++
if _, ok := resolves[q.Name.String()]; !ok {
addrs, err := c.resolver.LookupNetIP(ctx, "ip", q.Name.String())
- var dnsErr *net.DNSError
- if errors.As(err, &dnsErr) && dnsErr.IsNotFound {
+ if dnsErr, ok := errors.AsType[*net.DNSError](err); ok && dnsErr.IsNotFound {
continue
}
if err != nil {
diff --git a/cmd/sniproxy/sniproxy.go b/cmd/sniproxy/sniproxy.go
index 45503feca..bd95cc113 100644
--- a/cmd/sniproxy/sniproxy.go
+++ b/cmd/sniproxy/sniproxy.go
@@ -225,7 +225,7 @@ func (s *sniproxy) mergeConfigFromFlags(out *appctype.AppConnectorConfig, ports,
Addrs: []netip.Addr{ip4, ip6},
}
if ports != "" {
- for _, portStr := range strings.Split(ports, ",") {
+ for portStr := range strings.SplitSeq(ports, ",") {
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
log.Fatalf("invalid port: %s", portStr)
@@ -238,7 +238,7 @@ func (s *sniproxy) mergeConfigFromFlags(out *appctype.AppConnectorConfig, ports,
}
var forwardConfigFromFlags []appctype.DNATConfig
- for _, forwStr := range strings.Split(forwards, ",") {
+ for forwStr := range strings.SplitSeq(forwards, ",") {
if forwStr == "" {
continue
}
diff --git a/cmd/speedtest/speedtest.go b/cmd/speedtest/speedtest.go
index 2cea97b1e..e11c4ad1d 100644
--- a/cmd/speedtest/speedtest.go
+++ b/cmd/speedtest/speedtest.go
@@ -72,8 +72,7 @@ func main() {
func runSpeedtest(ctx context.Context, args []string) error {
if _, _, err := net.SplitHostPort(speedtestArgs.host); err != nil {
- var addrErr *net.AddrError
- if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
+ if addrErr, ok := errors.AsType[*net.AddrError](err); ok && addrErr.Err == "missing port in address" {
// if no port is provided, append the default port
speedtestArgs.host = net.JoinHostPort(speedtestArgs.host, strconv.Itoa(speedtest.DefaultPort))
}
diff --git a/cmd/stunstamp/stunstamp.go b/cmd/stunstamp/stunstamp.go
index cfedd82bd..743d6aec3 100644
--- a/cmd/stunstamp/stunstamp.go
+++ b/cmd/stunstamp/stunstamp.go
@@ -889,8 +889,7 @@ func remoteWriteTimeSeries(client *remoteWriteClient, tsCh chan []prompb.TimeSer
reqCtx, cancel := context.WithTimeout(context.Background(), time.Second*30)
writeErr = client.write(reqCtx, ts)
cancel()
- var re recoverableErr
- recoverable := errors.As(writeErr, &re)
+ _, recoverable := errors.AsType[recoverableErr](writeErr)
if writeErr != nil {
log.Printf("remote write error(recoverable=%v): %v", recoverable, writeErr)
}
diff --git a/cmd/tailscale/cli/appcroutes.go b/cmd/tailscale/cli/appcroutes.go
index 2ea001aec..04cbcdd83 100644
--- a/cmd/tailscale/cli/appcroutes.go
+++ b/cmd/tailscale/cli/appcroutes.go
@@ -102,12 +102,12 @@ func getSummarizeLearnedOutput(ri *appctype.RouteInfo) string {
}
return 0
})
- s := ""
+ var s strings.Builder
fmtString := fmt.Sprintf("%%-%ds %%d\n", maxDomainWidth) // eg "%-10s %d\n"
for _, dc := range x {
- s += fmt.Sprintf(fmtString, dc.domain, dc.count)
+ s.WriteString(fmt.Sprintf(fmtString, dc.domain, dc.count))
}
- return s
+ return s.String()
}
func runAppcRoutesInfo(ctx context.Context, args []string) error {
diff --git a/cmd/tailscale/cli/cli.go b/cmd/tailscale/cli/cli.go
index fda6b4546..8a2c2b9ef 100644
--- a/cmd/tailscale/cli/cli.go
+++ b/cmd/tailscale/cli/cli.go
@@ -124,7 +124,7 @@ func Run(args []string) (err error) {
if errors.Is(err, flag.ErrHelp) {
return nil
}
- if noexec := (ffcli.NoExecError{}); errors.As(err, &noexec) {
+ if noexec, ok := errors.AsType[ffcli.NoExecError](err); ok {
// When the user enters an unknown subcommand, ffcli tries to run
// the closest valid parent subcommand with everything else as args,
// returning NoExecError if it doesn't have an Exec function.
diff --git a/cmd/tailscale/cli/cli_test.go b/cmd/tailscale/cli/cli_test.go
index 537e641fc..bdf9116a0 100644
--- a/cmd/tailscale/cli/cli_test.go
+++ b/cmd/tailscale/cli/cli_test.go
@@ -962,8 +962,8 @@ func TestPrefFlagMapping(t *testing.T) {
}
prefType := reflect.TypeFor[ipn.Prefs]()
- for i := range prefType.NumField() {
- prefName := prefType.Field(i).Name
+ for field := range prefType.Fields() {
+ prefName := field.Name
if prefHasFlag[prefName] {
continue
}
diff --git a/cmd/tailscale/cli/configure-synology-cert.go b/cmd/tailscale/cli/configure-synology-cert.go
index 0f38f2df2..32f5bbd70 100644
--- a/cmd/tailscale/cli/configure-synology-cert.go
+++ b/cmd/tailscale/cli/configure-synology-cert.go
@@ -16,6 +16,7 @@
"os/exec"
"path"
"runtime"
+ "slices"
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
@@ -85,11 +86,8 @@ func runConfigureSynologyCert(ctx context.Context, args []string) error {
domain = st.CertDomains[0]
} else {
var found bool
- for _, d := range st.CertDomains {
- if d == domain {
- found = true
- break
- }
+ if slices.Contains(st.CertDomains, domain) {
+ found = true
}
if !found {
return fmt.Errorf("Domain %q was not one of the valid domain options: %q.", domain, st.CertDomains)
diff --git a/cmd/tailscale/cli/file.go b/cmd/tailscale/cli/file.go
index 94b36f535..e7406bee3 100644
--- a/cmd/tailscale/cli/file.go
+++ b/cmd/tailscale/cli/file.go
@@ -19,6 +19,7 @@
"os"
"path"
"path/filepath"
+ "slices"
"strings"
"sync"
"sync/atomic"
@@ -126,10 +127,8 @@ func runCp(ctx context.Context, args []string) error {
if cpArgs.name != "" {
return errors.New("can't use --name= with multiple files")
}
- for _, fileArg := range files {
- if fileArg == "-" {
- return errors.New("can't use '-' as STDIN file when providing filename arguments")
- }
+ if slices.Contains(files, "-") {
+ return errors.New("can't use '-' as STDIN file when providing filename arguments")
}
}
diff --git a/cmd/tailscale/cli/ip.go b/cmd/tailscale/cli/ip.go
index 7159904c7..b76ef0a70 100644
--- a/cmd/tailscale/cli/ip.go
+++ b/cmd/tailscale/cli/ip.go
@@ -9,6 +9,7 @@
"flag"
"fmt"
"net/netip"
+ "slices"
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/ipn/ipnstate"
@@ -114,17 +115,13 @@ func peerMatchingIP(st *ipnstate.Status, ipStr string) (ps *ipnstate.PeerStatus,
return
}
for _, ps = range st.Peer {
- for _, pip := range ps.TailscaleIPs {
- if ip == pip {
- return ps, true
- }
+ if slices.Contains(ps.TailscaleIPs, ip) {
+ return ps, true
}
}
if ps := st.Self; ps != nil {
- for _, pip := range ps.TailscaleIPs {
- if ip == pip {
- return ps, true
- }
+ if slices.Contains(ps.TailscaleIPs, ip) {
+ return ps, true
}
}
return nil, false
diff --git a/cmd/tailscale/cli/risks.go b/cmd/tailscale/cli/risks.go
index 1bd128d56..6f3ebf37b 100644
--- a/cmd/tailscale/cli/risks.go
+++ b/cmd/tailscale/cli/risks.go
@@ -39,7 +39,7 @@ func registerAcceptRiskFlag(f *flag.FlagSet, acceptedRisks *string) {
// isRiskAccepted reports whether riskType is in the comma-separated list of
// risks in acceptedRisks.
func isRiskAccepted(riskType, acceptedRisks string) bool {
- for _, r := range strings.Split(acceptedRisks, ",") {
+ for r := range strings.SplitSeq(acceptedRisks, ",") {
if r == riskType || r == riskAll {
return true
}
diff --git a/cmd/tailscale/cli/serve_v2.go b/cmd/tailscale/cli/serve_v2.go
index 9ac303c79..13f5c09b8 100644
--- a/cmd/tailscale/cli/serve_v2.go
+++ b/cmd/tailscale/cli/serve_v2.go
@@ -114,8 +114,8 @@ func (u *acceptAppCapsFlag) Set(s string) error {
if s == "" {
return nil
}
- appCaps := strings.Split(s, ",")
- for _, appCap := range appCaps {
+ appCaps := strings.SplitSeq(s, ",")
+ for appCap := range appCaps {
appCap = strings.TrimSpace(appCap)
if !validAppCap.MatchString(appCap) {
return fmt.Errorf("%q does not match the form {domain}/{name}, where domain must be a fully qualified domain name", appCap)
diff --git a/cmd/tailscale/cli/set.go b/cmd/tailscale/cli/set.go
index feccf6d12..b12b7de49 100644
--- a/cmd/tailscale/cli/set.go
+++ b/cmd/tailscale/cli/set.go
@@ -183,8 +183,7 @@ func runSet(ctx context.Context, args []string) (retErr error) {
maskedPrefs.AutoExitNode = expr
maskedPrefs.AutoExitNodeSet = true
} else if err := maskedPrefs.Prefs.SetExitNodeIP(setArgs.exitNodeIP, st); err != nil {
- var e ipn.ExitNodeLocalIPError
- if errors.As(err, &e) {
+ if _, ok := errors.AsType[ipn.ExitNodeLocalIPError](err); ok {
return fmt.Errorf("%w; did you mean --advertise-exit-node?", err)
}
return err
@@ -251,8 +250,8 @@ func runSet(ctx context.Context, args []string) (retErr error) {
if setArgs.relayServerStaticEndpoints != "" {
endpointsSet := make(set.Set[netip.AddrPort])
- endpointsSplit := strings.Split(setArgs.relayServerStaticEndpoints, ",")
- for _, s := range endpointsSplit {
+ endpointsSplit := strings.SplitSeq(setArgs.relayServerStaticEndpoints, ",")
+ for s := range endpointsSplit {
ap, err := netip.ParseAddrPort(s)
if err != nil {
return fmt.Errorf("failed to set relay server static endpoints: %q is not a valid IP:port", s)
diff --git a/cmd/tailscale/cli/ssh.go b/cmd/tailscale/cli/ssh.go
index bea18f7ab..9efab8cf7 100644
--- a/cmd/tailscale/cli/ssh.go
+++ b/cmd/tailscale/cli/ssh.go
@@ -14,6 +14,7 @@
"os/user"
"path/filepath"
"runtime"
+ "slices"
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
@@ -202,10 +203,8 @@ func peerStatusFromArg(st *ipnstate.Status, arg string) (*ipnstate.PeerStatus, b
argIP, _ := netip.ParseAddr(arg)
for _, ps := range st.Peer {
if argIP.IsValid() {
- for _, ip := range ps.TailscaleIPs {
- if ip == argIP {
- return ps, true
- }
+ if slices.Contains(ps.TailscaleIPs, argIP) {
+ return ps, true
}
continue
}
@@ -230,10 +229,8 @@ func nodeDNSNameFromArg(st *ipnstate.Status, arg string) (dnsName string, ok boo
for _, ps := range st.Peer {
dnsName = ps.DNSName
if argIP.IsValid() {
- for _, ip := range ps.TailscaleIPs {
- if ip == argIP {
- return dnsName, true
- }
+ if slices.Contains(ps.TailscaleIPs, argIP) {
+ return dnsName, true
}
continue
}
diff --git a/cmd/tailscale/cli/ssh_exec_windows.go b/cmd/tailscale/cli/ssh_exec_windows.go
index 85e151817..f9d306463 100644
--- a/cmd/tailscale/cli/ssh_exec_windows.go
+++ b/cmd/tailscale/cli/ssh_exec_windows.go
@@ -28,9 +28,8 @@ func execSSH(ssh string, argv []string) error {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
- var ee *exec.ExitError
err := cmd.Run()
- if errors.As(err, &ee) {
+ if ee, ok := errors.AsType[*exec.ExitError](err); ok {
os.Exit(ee.ExitCode())
}
return err
diff --git a/cmd/tailscale/cli/ssh_unix.go b/cmd/tailscale/cli/ssh_unix.go
index 768d71116..1cc3ccbe8 100644
--- a/cmd/tailscale/cli/ssh_unix.go
+++ b/cmd/tailscale/cli/ssh_unix.go
@@ -39,7 +39,7 @@ func init() {
return ""
}
prefix := []byte("SSH_CLIENT=")
- for _, env := range bytes.Split(b, []byte{0}) {
+ for env := range bytes.SplitSeq(b, []byte{0}) {
if bytes.HasPrefix(env, prefix) {
return string(env[len(prefix):])
}
diff --git a/cmd/tailscale/cli/up.go b/cmd/tailscale/cli/up.go
index 79cc60ca2..81c67d662 100644
--- a/cmd/tailscale/cli/up.go
+++ b/cmd/tailscale/cli/up.go
@@ -334,8 +334,7 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
if expr, useAutoExitNode := ipn.ParseAutoExitNodeString(upArgs.exitNodeIP); useAutoExitNode {
prefs.AutoExitNode = expr
} else if err := prefs.SetExitNodeIP(upArgs.exitNodeIP, st); err != nil {
- var e ipn.ExitNodeLocalIPError
- if errors.As(err, &e) {
+ if _, ok := errors.AsType[ipn.ExitNodeLocalIPError](err); ok {
return nil, fmt.Errorf("%w; did you mean --advertise-exit-node?", err)
}
return nil, err
@@ -912,7 +911,7 @@ func addPrefFlagMapping(flagName string, prefNames ...string) {
prefType := reflect.TypeFor[ipn.Prefs]()
for _, pref := range prefNames {
t := prefType
- for _, name := range strings.Split(pref, ".") {
+ for name := range strings.SplitSeq(pref, ".") {
// Crash at runtime if there's a typo in the prefName.
f, ok := t.FieldByName(name)
if !ok {
diff --git a/cmd/testwrapper/testwrapper.go b/cmd/testwrapper/testwrapper.go
index e35b83407..204409a63 100644
--- a/cmd/testwrapper/testwrapper.go
+++ b/cmd/testwrapper/testwrapper.go
@@ -352,8 +352,7 @@ type nextRun struct {
// If there's nothing to retry and no non-retryable tests have
// failed then we've probably hit a build error.
if err := <-runErr; len(toRetry) == 0 && err != nil {
- var exit *exec.ExitError
- if errors.As(err, &exit) {
+ if exit, ok := errors.AsType[*exec.ExitError](err); ok {
if code := exit.ExitCode(); code > -1 {
os.Exit(exit.ExitCode())
}
diff --git a/cmd/testwrapper/testwrapper_test.go b/cmd/testwrapper/testwrapper_test.go
index cf023f436..7ad78a3d0 100644
--- a/cmd/testwrapper/testwrapper_test.go
+++ b/cmd/testwrapper/testwrapper_test.go
@@ -273,8 +273,7 @@ func TestCached(t *testing.T) {}
}
func errExitCode(err error) (int, bool) {
- var exit *exec.ExitError
- if errors.As(err, &exit) {
+ if exit, ok := errors.AsType[*exec.ExitError](err); ok {
return exit.ExitCode(), true
}
return 0, false
diff --git a/cmd/tta/tta.go b/cmd/tta/tta.go
index 377d01c94..dbdbf5ddf 100644
--- a/cmd/tta/tta.go
+++ b/cmd/tta/tta.go
@@ -91,7 +91,7 @@ func main() {
if distro.Get() == distro.Gokrazy {
cmdLine, _ := os.ReadFile("/proc/cmdline")
explicitNS := false
- for _, s := range strings.Fields(string(cmdLine)) {
+ for s := range strings.FieldsSeq(string(cmdLine)) {
if ns, ok := strings.CutPrefix(s, "tta.nameserver="); ok {
err := atomicfile.WriteFile("/tmp/resolv.conf", []byte("nameserver "+ns+"\n"), 0644)
log.Printf("Wrote /tmp/resolv.conf: %v", err)
diff --git a/cmd/viewer/viewer.go b/cmd/viewer/viewer.go
index 56b999f5f..1c04dbb2b 100644
--- a/cmd/viewer/viewer.go
+++ b/cmd/viewer/viewer.go
@@ -500,8 +500,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, fie
}
writeTemplateWithComment("unsupportedField", fname)
}
- for i := range typ.NumMethods() {
- f := typ.Method(i)
+ for f := range typ.Methods() {
if !f.Exported() {
continue
}
@@ -720,7 +719,7 @@ func main() {
fieldComments := getFieldComments(pkg.Syntax)
cloneOnlyType := map[string]bool{}
- for _, t := range strings.Split(*flagCloneOnlyTypes, ",") {
+ for t := range strings.SplitSeq(*flagCloneOnlyTypes, ",") {
cloneOnlyType[t] = true
}
diff --git a/control/controlclient/controlclient_test.go b/control/controlclient/controlclient_test.go
index dca1d8ddf..2205a0eb3 100644
--- a/control/controlclient/controlclient_test.go
+++ b/control/controlclient/controlclient_test.go
@@ -38,8 +38,8 @@
)
func fieldsOf(t reflect.Type) (fields []string) {
- for i := range t.NumField() {
- if name := t.Field(i).Name; name != "_" {
+ for field := range t.Fields() {
+ if name := field.Name; name != "_" {
fields = append(fields, name)
}
}
@@ -214,12 +214,12 @@ func TestRetryableErrors(t *testing.T) {
}
type retryableForTest interface {
+ error
Retryable() bool
}
func isRetryableErrorForTest(err error) bool {
- var ae retryableForTest
- if errors.As(err, &ae) {
+ if ae, ok := errors.AsType[retryableForTest](err); ok {
return ae.Retryable()
}
return false
diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go
index 965523f95..db46a956f 100644
--- a/control/controlclient/direct.go
+++ b/control/controlclient/direct.go
@@ -1484,7 +1484,7 @@ func (c *Direct) answerPing(pr *tailcfg.PingRequest) {
}
return
}
- for _, t := range strings.Split(pr.Types, ",") {
+ for t := range strings.SplitSeq(pr.Types, ",") {
switch pt := tailcfg.PingType(t); pt {
case tailcfg.PingTSMP, tailcfg.PingDisco, tailcfg.PingICMP, tailcfg.PingPeerAPI:
go doPingerPing(c.logf, httpc, pr, c.pinger, pt)
diff --git a/control/controlclient/map.go b/control/controlclient/map.go
index 29b0a0348..f33620edd 100644
--- a/control/controlclient/map.go
+++ b/control/controlclient/map.go
@@ -617,12 +617,12 @@ func (ms *mapSession) patchifyPeersChanged(resp *tailcfg.MapResponse) {
var nodeFields = sync.OnceValue(getNodeFields)
-// getNodeFields returns the fails of tailcfg.Node.
+// getNodeFields returns the fields of tailcfg.Node.
func getNodeFields() []string {
rt := reflect.TypeFor[tailcfg.Node]()
- ret := make([]string, rt.NumField())
- for i := range rt.NumField() {
- ret[i] = rt.Field(i).Name
+ ret := make([]string, 0, rt.NumField())
+ for f := range rt.Fields() {
+ ret = append(ret, f.Name)
}
return ret
}
diff --git a/control/controlhttp/http_test.go b/control/controlhttp/http_test.go
index c02ac758e..7f0203cd0 100644
--- a/control/controlhttp/http_test.go
+++ b/control/controlhttp/http_test.go
@@ -814,8 +814,8 @@ func runDialPlanTest(t *testing.T, plan *tailcfg.ControlDialPlan, want []netip.A
// split on "|" first to remove memnet pipe suffix
addrPart := raddrStr
- if idx := strings.Index(raddrStr, "|"); idx >= 0 {
- addrPart = raddrStr[:idx]
+ if before, _, ok := strings.Cut(raddrStr, "|"); ok {
+ addrPart = before
}
host, _, err2 := net.SplitHostPort(addrPart)
diff --git a/control/controlknobs/controlknobs.go b/control/controlknobs/controlknobs.go
index 1861a122e..14f30d9ce 100644
--- a/control/controlknobs/controlknobs.go
+++ b/control/controlknobs/controlknobs.go
@@ -205,17 +205,15 @@ func (k *Knobs) AsDebugJSON() map[string]any {
return nil
}
ret := map[string]any{}
- rt := reflect.TypeFor[Knobs]()
rv := reflect.ValueOf(k).Elem() // of *k
- for i := 0; i < rt.NumField(); i++ {
- name := rt.Field(i).Name
- switch v := rv.Field(i).Addr().Interface().(type) {
+ for sf, fv := range rv.Fields() {
+ switch v := fv.Addr().Interface().(type) {
case *atomic.Bool:
- ret[name] = v.Load()
+ ret[sf.Name] = v.Load()
case *syncs.AtomicValue[opt.Bool]:
- ret[name] = v.Load()
+ ret[sf.Name] = v.Load()
default:
- panic(fmt.Sprintf("unknown field type %T for %v", v, name))
+ panic(fmt.Sprintf("unknown field type %T for %v", v, sf.Name))
}
}
return ret
diff --git a/derp/derp_test.go b/derp/derp_test.go
index cff069dd4..f2ccefc9f 100644
--- a/derp/derp_test.go
+++ b/derp/derp_test.go
@@ -121,8 +121,7 @@ func TestSendRecv(t *testing.T) {
}
defer cin.Close()
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
brwServer := bufio.NewReadWriter(bufio.NewReader(cin), bufio.NewWriter(cin))
go s.Accept(ctx, cin, brwServer, fmt.Sprintf("[abc::def]:%v", i))
@@ -331,8 +330,7 @@ func TestSendFreeze(t *testing.T) {
return c, c2
}
- ctx, clientCtxCancel := context.WithCancel(context.Background())
- defer clientCtxCancel()
+ ctx := t.Context()
aliceKey := key.NewNode()
aliceClient, aliceConn := newClient(ctx, "alice", aliceKey)
@@ -716,8 +714,7 @@ func (c *testClient) close(t *testing.T) {
// TestWatch tests the connection watcher mechanism used by regional
// DERP nodes to mesh up with each other.
func TestWatch(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
ts := newTestServer(t, ctx)
defer ts.close(t)
@@ -764,8 +761,7 @@ func waitConnect(t testing.TB, c *Client) {
}
func TestServerRepliesToPing(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
ts := newTestServer(t, ctx)
defer ts.close(t)
diff --git a/derp/derphttp/derphttp_test.go b/derp/derphttp/derphttp_test.go
index ae530c93a..304906b74 100644
--- a/derp/derphttp/derphttp_test.go
+++ b/derp/derphttp/derphttp_test.go
@@ -299,9 +299,7 @@ func TestBreakWatcherConnRecv(t *testing.T) {
errChan := make(chan error, 1)
// Start the watcher thread (which connects to the watched server)
- wg.Add(1) // To avoid using t.Logf after the test ends. See https://golang.org/issue/40343
- go func() {
- defer wg.Done()
+ wg.Go(func() {
var peers int
add := func(m derp.PeerPresentMessage) {
t.Logf("add: %v", m.Key.ShortString())
@@ -318,7 +316,7 @@ func TestBreakWatcherConnRecv(t *testing.T) {
}
watcher.RunWatchConnectionLoop(ctx, serverPrivateKey1.Public(), t.Logf, add, remove, notifyErr)
- }()
+ })
synctest.Wait()
@@ -381,9 +379,7 @@ func TestBreakWatcherConn(t *testing.T) {
errorChan := make(chan error, 1)
// Start the watcher thread (which connects to the watched server)
- wg.Add(1) // To avoid using t.Logf after the test ends. See https://golang.org/issue/40343
- go func() {
- defer wg.Done()
+ wg.Go(func() {
var peers int
add := func(m derp.PeerPresentMessage) {
t.Logf("add: %v", m.Key.ShortString())
@@ -403,7 +399,7 @@ func TestBreakWatcherConn(t *testing.T) {
}
watcher1.RunWatchConnectionLoop(ctx, serverPrivateKey1.Public(), t.Logf, add, remove, notifyError)
- }()
+ })
synctest.Wait()
diff --git a/derp/derpserver/derpserver.go b/derp/derpserver/derpserver.go
index f311eb25d..343543c55 100644
--- a/derp/derpserver/derpserver.go
+++ b/derp/derpserver/derpserver.go
@@ -30,6 +30,7 @@
"os"
"os/exec"
"runtime"
+ "slices"
"strconv"
"strings"
"sync"
@@ -71,7 +72,7 @@ func init() {
if keys == "" {
return
}
- for _, keyStr := range strings.Split(keys, ",") {
+ for keyStr := range strings.SplitSeq(keys, ",") {
k, err := key.ParseNodePublicUntyped(mem.S(keyStr))
if err != nil {
log.Printf("ignoring invalid debug key %q: %v", keyStr, err)
@@ -1287,7 +1288,7 @@ func (c *sclient) sendPkt(dst *sclient, p pkt) error {
if disco.LooksLikeDiscoWrapper(p.bs) {
sendQueue = dst.discoSendQueue
}
- for attempt := 0; attempt < 3; attempt++ {
+ for attempt := range 3 {
select {
case <-dst.done:
s.recordDrop(p.bs, c.key, dstKey, dropReasonGoneDisconnected)
@@ -1484,16 +1485,13 @@ func (s *Server) noteClientActivity(c *sclient) {
// If we saw this connection send previously, then consider
// the group fighting and disable them all.
if s.dupPolicy == disableFighters {
- for _, prior := range dup.sendHistory {
- if prior == c {
- cs.ForeachClient(func(c *sclient) {
- c.isDisabled.Store(true)
- if cs.activeClient.Load() == c {
- cs.activeClient.Store(nil)
- }
- })
- break
- }
+ if slices.Contains(dup.sendHistory, c) {
+ cs.ForeachClient(func(c *sclient) {
+ c.isDisabled.Store(true)
+ if cs.activeClient.Load() == c {
+ cs.activeClient.Store(nil)
+ }
+ })
}
}
diff --git a/derp/derpserver/derpserver_test.go b/derp/derpserver/derpserver_test.go
index 3a778d59f..7f956ba78 100644
--- a/derp/derpserver/derpserver_test.go
+++ b/derp/derpserver/derpserver_test.go
@@ -627,22 +627,17 @@ func BenchmarkConcurrentStreams(b *testing.B) {
if err != nil {
b.Fatal(err)
}
- defer ln.Close()
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := b.Context()
+ acceptDone := make(chan struct{})
go func() {
- for ctx.Err() == nil {
+ defer close(acceptDone)
+ for {
connIn, err := ln.Accept()
if err != nil {
- if ctx.Err() != nil {
- return
- }
- b.Error(err)
return
}
-
brwServer := bufio.NewReadWriter(bufio.NewReader(connIn), bufio.NewWriter(connIn))
go s.Accept(ctx, connIn, brwServer, "test-client")
}
@@ -680,6 +675,9 @@ func BenchmarkConcurrentStreams(b *testing.B) {
}
}
})
+
+ ln.Close()
+ <-acceptDone
}
func BenchmarkSendRecv(b *testing.B) {
@@ -769,7 +767,7 @@ func TestServeDebugTrafficUniqueSenders(t *testing.T) {
senderCardinality: hyperloglog.New(),
}
- for i := 0; i < 5; i++ {
+ for range 5 {
c.senderCardinality.Insert(key.NewNode().Public().AppendTo(nil))
}
@@ -845,7 +843,7 @@ func TestSenderCardinality(t *testing.T) {
t.Errorf("EstimatedUniqueSenders() = %d, want ~10 (8-12 range)", estimate)
}
- for i := 0; i < 5; i++ {
+ for i := range 5 {
c.senderCardinality.Insert(senders[i].AppendTo(nil))
}
@@ -869,7 +867,7 @@ func TestSenderCardinality100(t *testing.T) {
}
numSenders := 100
- for i := 0; i < numSenders; i++ {
+ for range numSenders {
c.senderCardinality.Insert(key.NewNode().Public().AppendTo(nil))
}
@@ -945,7 +943,7 @@ func BenchmarkHyperLogLogInsertUnique(b *testing.B) {
func BenchmarkHyperLogLogEstimate(b *testing.B) {
hll := hyperloglog.New()
- for i := 0; i < 100; i++ {
+ for range 100 {
hll.Insert(key.NewNode().Public().AppendTo(nil))
}
diff --git a/derp/xdp/xdp_linux.go b/derp/xdp/xdp_linux.go
index 5d22716be..7ab23bd2e 100644
--- a/derp/xdp/xdp_linux.go
+++ b/derp/xdp/xdp_linux.go
@@ -62,8 +62,7 @@ func NewSTUNServer(config *STUNServerConfig, opts ...STUNServerOption) (*STUNSer
objs := new(bpfObjects)
err = loadBpfObjects(objs, nil)
if err != nil {
- var ve *ebpf.VerifierError
- if config.FullVerifierErr && errors.As(err, &ve) {
+ if ve, ok := errors.AsType[*ebpf.VerifierError](err); config.FullVerifierErr && ok {
err = fmt.Errorf("verifier error: %+v", ve)
}
return nil, fmt.Errorf("error loading XDP program: %w", err)
diff --git a/disco/disco.go b/disco/disco.go
index 2147529d1..8f667b262 100644
--- a/disco/disco.go
+++ b/disco/disco.go
@@ -475,7 +475,7 @@ type AllocateUDPRelayEndpointRequest struct {
func (m *AllocateUDPRelayEndpointRequest) AppendMarshal(b []byte) []byte {
ret, p := appendMsgHeader(b, TypeAllocateUDPRelayEndpointRequest, v0, allocateUDPRelayEndpointRequestLen)
- for i := 0; i < len(m.ClientDisco); i++ {
+ for i := range len(m.ClientDisco) {
disco := m.ClientDisco[i].AppendTo(nil)
copy(p, disco)
p = p[key.DiscoPublicRawLen:]
@@ -492,7 +492,7 @@ func parseAllocateUDPRelayEndpointRequest(ver uint8, p []byte) (m *AllocateUDPRe
if len(p) < allocateUDPRelayEndpointRequestLen {
return m, errShort
}
- for i := 0; i < len(m.ClientDisco); i++ {
+ for i := range len(m.ClientDisco) {
m.ClientDisco[i] = key.DiscoPublicFromRaw32(mem.B(p[:key.DiscoPublicRawLen]))
p = p[key.DiscoPublicRawLen:]
}
@@ -565,7 +565,7 @@ func (m *UDPRelayEndpoint) encode(b []byte) {
disco := m.ServerDisco.AppendTo(nil)
copy(b, disco)
b = b[key.DiscoPublicRawLen:]
- for i := 0; i < len(m.ClientDisco); i++ {
+ for i := range len(m.ClientDisco) {
disco = m.ClientDisco[i].AppendTo(nil)
copy(b, disco)
b = b[key.DiscoPublicRawLen:]
@@ -594,7 +594,7 @@ func (m *UDPRelayEndpoint) decode(b []byte) error {
}
m.ServerDisco = key.DiscoPublicFromRaw32(mem.B(b[:key.DiscoPublicRawLen]))
b = b[key.DiscoPublicRawLen:]
- for i := 0; i < len(m.ClientDisco); i++ {
+ for i := range len(m.ClientDisco) {
m.ClientDisco[i] = key.DiscoPublicFromRaw32(mem.B(b[:key.DiscoPublicRawLen]))
b = b[key.DiscoPublicRawLen:]
}
diff --git a/docs/webhooks/example.go b/docs/webhooks/example.go
index 53ec1c8b7..d93c425d2 100644
--- a/docs/webhooks/example.go
+++ b/docs/webhooks/example.go
@@ -87,7 +87,7 @@ func verifyWebhookSignature(req *http.Request, secret string) (events []event, e
return nil, err
}
mac := hmac.New(sha256.New, []byte(secret))
- mac.Write([]byte(fmt.Sprint(timestamp.Unix())))
+ mac.Write(fmt.Append(nil, timestamp.Unix()))
mac.Write([]byte("."))
mac.Write(b)
want := hex.EncodeToString(mac.Sum(nil))
@@ -120,8 +120,8 @@ func parseSignatureHeader(header string) (timestamp time.Time, signatures map[st
}
signatures = make(map[string][]string)
- pairs := strings.Split(header, ",")
- for _, pair := range pairs {
+ pairs := strings.SplitSeq(header, ",")
+ for pair := range pairs {
parts := strings.Split(pair, "=")
if len(parts) != 2 {
return time.Time{}, nil, errNotSigned
diff --git a/drive/driveimpl/compositedav/rewriting.go b/drive/driveimpl/compositedav/rewriting.go
index 47f020461..1f0a69d75 100644
--- a/drive/driveimpl/compositedav/rewriting.go
+++ b/drive/driveimpl/compositedav/rewriting.go
@@ -63,7 +63,7 @@ func (h *Handler) delegateRewriting(w http.ResponseWriter, r *http.Request, path
// Fixup paths to add the requested path as a prefix, escaped for inclusion in XML.
pp := shared.EscapeForXML(shared.Join(pathComponents[0:mpl]...))
- b := responseHrefRegex.ReplaceAll(bw.buf.Bytes(), []byte(fmt.Sprintf("$1%s/$3", pp)))
+ b := responseHrefRegex.ReplaceAll(bw.buf.Bytes(), fmt.Appendf(nil, "$1%s/$3", pp))
return bw.status, b
}
diff --git a/feature/conn25/conn25.go b/feature/conn25/conn25.go
index 05f087e21..64fa93394 100644
--- a/feature/conn25/conn25.go
+++ b/feature/conn25/conn25.go
@@ -12,6 +12,7 @@
"errors"
"net/http"
"net/netip"
+ "slices"
"sync"
"go4.org/netipx"
@@ -329,13 +330,7 @@ func configFromNodeView(n tailcfg.NodeView) (config, error) {
selfRoutedDomains: set.Set[dnsname.FQDN]{},
}
for _, app := range apps {
- selfMatchesTags := false
- for _, tag := range app.Connectors {
- if selfTags.Contains(tag) {
- selfMatchesTags = true
- break
- }
- }
+ selfMatchesTags := slices.ContainsFunc(app.Connectors, selfTags.Contains)
for _, d := range app.Domains {
fqdn, err := dnsname.ToFQDN(d)
if err != nil {
diff --git a/feature/conn25/conn25_test.go b/feature/conn25/conn25_test.go
index d63e84e02..7ed5c13b2 100644
--- a/feature/conn25/conn25_test.go
+++ b/feature/conn25/conn25_test.go
@@ -115,7 +115,7 @@ func TestHandleConnectorTransitIPRequestMultipleTIP(t *testing.T) {
t.Fatalf("n TransitIPs in response: %d, want 3", len(resp.TransitIPs))
}
- for i := 0; i < 3; i++ {
+ for i := range 3 {
got := resp.TransitIPs[i].Code
if got != TransitIPResponseCode(0) {
t.Fatalf("i=%d TransitIP Code: %d, want 0", i, got)
diff --git a/feature/identityfederation/identityfederation.go b/feature/identityfederation/identityfederation.go
index 4b96fd6a2..51a8018d8 100644
--- a/feature/identityfederation/identityfederation.go
+++ b/feature/identityfederation/identityfederation.go
@@ -128,8 +128,7 @@ func exchangeJWTForToken(ctx context.Context, baseURL, clientID, idToken string)
}).Exchange(ctx, "", oauth2.SetAuthURLParam("client_id", clientID), oauth2.SetAuthURLParam("jwt", idToken))
if err != nil {
// Try to extract more detailed error message
- var retrieveErr *oauth2.RetrieveError
- if errors.As(err, &retrieveErr) {
+ if retrieveErr, ok := errors.AsType[*oauth2.RetrieveError](err); ok {
return "", fmt.Errorf("token exchange failed with status %d: %s", retrieveErr.Response.StatusCode, string(retrieveErr.Body))
}
return "", fmt.Errorf("unexpected token exchange request error: %w", err)
diff --git a/feature/linuxdnsfight/linuxdnsfight_test.go b/feature/linuxdnsfight/linuxdnsfight_test.go
index 661ba7f6f..ce67353db 100644
--- a/feature/linuxdnsfight/linuxdnsfight_test.go
+++ b/feature/linuxdnsfight/linuxdnsfight_test.go
@@ -42,7 +42,7 @@ func TestWatchFile(t *testing.T) {
// Keep writing until we get a callback.
func() {
for i := range 10000 {
- if err := os.WriteFile(filepath, []byte(fmt.Sprintf("write%d", i)), 0644); err != nil {
+ if err := os.WriteFile(filepath, fmt.Appendf(nil, "write%d", i), 0644); err != nil {
t.Fatal(err)
}
select {
diff --git a/feature/taildrop/fileops_fs.go b/feature/taildrop/fileops_fs.go
index 4a5b3e71a..3ddf95d03 100644
--- a/feature/taildrop/fileops_fs.go
+++ b/feature/taildrop/fileops_fs.go
@@ -101,7 +101,7 @@ func (f fsFileOps) Rename(oldPath, newName string) (newPath string, err error) {
wantSize := st.Size()
const maxRetries = 10
- for i := 0; i < maxRetries; i++ {
+ for range maxRetries {
renameMu.Lock()
fi, statErr := os.Stat(dst)
// Atomically rename the partial file as the destination file if it doesn't exist.
diff --git a/health/health_test.go b/health/health_test.go
index 953c4dca2..824d53f7a 100644
--- a/health/health_test.go
+++ b/health/health_test.go
@@ -82,8 +82,7 @@ func TestAppendWarnableDebugFlags(t *testing.T) {
func TestNilMethodsDontCrash(t *testing.T) {
var nilt *Tracker
rv := reflect.ValueOf(nilt)
- for i := 0; i < rv.NumMethod(); i++ {
- mt := rv.Type().Method(i)
+ for mt, method := range rv.Methods() {
t.Logf("calling Tracker.%s ...", mt.Name)
var args []reflect.Value
for j := 0; j < mt.Type.NumIn(); j++ {
@@ -92,7 +91,7 @@ func TestNilMethodsDontCrash(t *testing.T) {
}
args = append(args, reflect.Zero(mt.Type.In(j)))
}
- rv.Method(i).Call(args)
+ method.Call(args)
}
}
diff --git a/hostinfo/hostinfo_linux.go b/hostinfo/hostinfo_linux.go
index 77f47ffe2..6b21d8152 100644
--- a/hostinfo/hostinfo_linux.go
+++ b/hostinfo/hostinfo_linux.go
@@ -68,7 +68,7 @@ func deviceModelLinux() string {
}
func getQnapQtsVersion(versionInfo string) string {
- for _, field := range strings.Fields(versionInfo) {
+ for field := range strings.FieldsSeq(versionInfo) {
if suffix, ok := strings.CutPrefix(field, "QTSFW_"); ok {
return suffix
}
@@ -110,11 +110,11 @@ func linuxVersionMeta() (meta versionMeta) {
if err != nil {
break
}
- eq := bytes.IndexByte(line, '=')
- if eq == -1 {
+ before, after, ok := bytes.Cut(line, []byte{'='})
+ if !ok {
continue
}
- k, v := string(line[:eq]), strings.Trim(string(line[eq+1:]), `"'`)
+ k, v := string(before), strings.Trim(string(after), `"'`)
m[k] = v
}
diff --git a/ipn/auditlog/auditlog.go b/ipn/auditlog/auditlog.go
index cc6b43cbd..0d6bd278d 100644
--- a/ipn/auditlog/auditlog.go
+++ b/ipn/auditlog/auditlog.go
@@ -69,8 +69,11 @@ type Opts struct {
// IsRetryableError returns true if the given error is retryable
// See [controlclient.apiResponseError]. Potentially retryable errors implement the Retryable() method.
func IsRetryableError(err error) bool {
- var retryable interface{ Retryable() bool }
- return errors.As(err, &retryable) && retryable.Retryable()
+ retryable, ok := errors.AsType[interface {
+ error
+ Retryable() bool
+ }](err)
+ return ok && retryable.Retryable()
}
type backoffOpts struct {
diff --git a/ipn/ipnlocal/breaktcp_linux.go b/ipn/ipnlocal/breaktcp_linux.go
index 0ba9ed6d7..1d7ea0f31 100644
--- a/ipn/ipnlocal/breaktcp_linux.go
+++ b/ipn/ipnlocal/breaktcp_linux.go
@@ -15,7 +15,7 @@ func init() {
func breakTCPConnsLinux() error {
var matched int
- for fd := 0; fd < 1000; fd++ {
+ for fd := range 1000 {
_, err := unix.GetsockoptTCPInfo(fd, unix.IPPROTO_TCP, unix.TCP_INFO)
if err == nil {
matched++
diff --git a/ipn/ipnlocal/bus_test.go b/ipn/ipnlocal/bus_test.go
index 27ffebcdd..47d13f305 100644
--- a/ipn/ipnlocal/bus_test.go
+++ b/ipn/ipnlocal/bus_test.go
@@ -36,9 +36,8 @@ func TestIsNotableNotify(t *testing.T) {
// We use reflect to catch fields that might be added in the future without
// remembering to update the [isNotableNotify] function.
rt := reflect.TypeFor[ipn.Notify]()
- for i := range rt.NumField() {
+ for sf := range rt.Fields() {
n := &ipn.Notify{}
- sf := rt.Field(i)
switch sf.Name {
case "_", "NetMap", "Engine", "Version":
// Already covered above or not applicable.
@@ -46,7 +45,7 @@ func TestIsNotableNotify(t *testing.T) {
case "DriveShares":
n.DriveShares = views.SliceOfViews[*drive.Share, drive.ShareView](make([]*drive.Share, 1))
default:
- rf := reflect.ValueOf(n).Elem().Field(i)
+ rf := reflect.ValueOf(n).Elem().FieldByIndex(sf.Index)
switch rf.Kind() {
case reflect.Pointer:
rf.Set(reflect.New(rf.Type().Elem()))
@@ -64,7 +63,7 @@ func TestIsNotableNotify(t *testing.T) {
notify *ipn.Notify
want bool
}{
- name: "field-" + rt.Field(i).Name,
+ name: "field-" + sf.Name,
notify: n,
want: true,
})
diff --git a/ipn/ipnlocal/extension_host.go b/ipn/ipnlocal/extension_host.go
index 125a23294..7264d7407 100644
--- a/ipn/ipnlocal/extension_host.go
+++ b/ipn/ipnlocal/extension_host.go
@@ -339,7 +339,7 @@ func (h *ExtensionHost) FindMatchingExtension(target any) bool {
val := reflect.ValueOf(target)
typ := val.Type()
- if typ.Kind() != reflect.Ptr || val.IsNil() {
+ if typ.Kind() != reflect.Pointer || val.IsNil() {
panic("ipnext: target must be a non-nil pointer")
}
targetType := typ.Elem()
diff --git a/ipn/ipnlocal/extension_host_test.go b/ipn/ipnlocal/extension_host_test.go
index 3bd302aea..a22c5156c 100644
--- a/ipn/ipnlocal/extension_host_test.go
+++ b/ipn/ipnlocal/extension_host_test.go
@@ -1010,9 +1010,8 @@ func TestNilExtensionHostMethodCall(t *testing.T) {
t.Parallel()
var h *ExtensionHost
- typ := reflect.TypeOf(h)
- for i := range typ.NumMethod() {
- m := typ.Method(i)
+ typ := reflect.TypeFor[*ExtensionHost]()
+ for m := range typ.Methods() {
if strings.HasSuffix(m.Name, "ForTest") {
// Skip methods that are only for testing.
continue
diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go
index b8f355039..5f694e915 100644
--- a/ipn/ipnlocal/local.go
+++ b/ipn/ipnlocal/local.go
@@ -3687,12 +3687,7 @@ func generateInterceptTCPPortFunc(ports []uint16) func(uint16) bool {
f = func(p uint16) bool { return m[p] }
} else {
f = func(p uint16) bool {
- for _, x := range ports {
- if p == x {
- return true
- }
- }
- return false
+ return slices.Contains(ports, p)
}
}
}
@@ -7387,10 +7382,8 @@ func (b *LocalBackend) seamlessRenewalEnabled() bool {
// allowedAutoRoute determines if the route being added via AdvertiseRoute (the app connector featuge) should be allowed.
func allowedAutoRoute(ipp netip.Prefix) bool {
// Note: blocking the addrs for globals, not solely the prefixes.
- for _, addr := range disallowedAddrs {
- if ipp.Addr() == addr {
- return false
- }
+ if slices.Contains(disallowedAddrs, ipp.Addr()) {
+ return false
}
for _, pfx := range disallowedRanges {
if pfx.Overlaps(ipp) {
diff --git a/ipn/ipnlocal/netmapcache/netmapcache_test.go b/ipn/ipnlocal/netmapcache/netmapcache_test.go
index b5a46d298..ca66a1713 100644
--- a/ipn/ipnlocal/netmapcache/netmapcache_test.go
+++ b/ipn/ipnlocal/netmapcache/netmapcache_test.go
@@ -275,7 +275,7 @@ func TestInvalidCache(t *testing.T) {
func checkFieldCoverage(t *testing.T, nm *netmap.NetworkMap) {
t.Helper()
- mt := reflect.TypeOf(nm).Elem()
+ mt := reflect.TypeFor[netmap.NetworkMap]()
mv := reflect.ValueOf(nm).Elem()
for i := 0; i < mt.NumField(); i++ {
f := mt.Field(i)
diff --git a/ipn/ipnlocal/peerapi.go b/ipn/ipnlocal/peerapi.go
index aa4c1ef52..322884fc7 100644
--- a/ipn/ipnlocal/peerapi.go
+++ b/ipn/ipnlocal/peerapi.go
@@ -103,7 +103,7 @@ func (s *peerAPIServer) listen(ip netip.Addr, tunIfIndex int) (ln net.Listener,
// deterministic that people will bake this into clients.
// We try a few times just in case something's already
// listening on that port (on all interfaces, probably).
- for try := uint8(0); try < 5; try++ {
+ for try := range uint8(5) {
a16 := ip.As16()
hashData := a16[len(a16)-3:]
hashData[0] += try
diff --git a/ipn/ipnlocal/serve.go b/ipn/ipnlocal/serve.go
index d25251acc..9460896ad 100644
--- a/ipn/ipnlocal/serve.go
+++ b/ipn/ipnlocal/serve.go
@@ -835,8 +835,8 @@ func (b *LocalBackend) proxyHandlerForBackend(backend string) (http.Handler, err
targetURL, insecure := expandProxyArg(backend)
// Handle unix: scheme specially
- if strings.HasPrefix(targetURL, "unix:") {
- socketPath := strings.TrimPrefix(targetURL, "unix:")
+ if after, ok := strings.CutPrefix(targetURL, "unix:"); ok {
+ socketPath := after
if socketPath == "" {
return nil, fmt.Errorf("empty unix socket path")
}
diff --git a/ipn/ipnlocal/ssh.go b/ipn/ipnlocal/ssh.go
index 52b306658..56a6d60cc 100644
--- a/ipn/ipnlocal/ssh.go
+++ b/ipn/ipnlocal/ssh.go
@@ -101,9 +101,9 @@ func (b *LocalBackend) getSSHUsernames(req *tailcfg.C2NSSHUsernamesRequest) (*ta
mem.HasSuffix(mem.B(line), mem.S("/false")) {
continue
}
- colon := bytes.IndexByte(line, ':')
- if colon != -1 {
- add(string(line[:colon]))
+ before, _, ok := bytes.Cut(line, []byte{':'})
+ if ok {
+ add(string(before))
}
}
}
diff --git a/ipn/localapi/debug.go b/ipn/localapi/debug.go
index d1348abaa..36fce16ac 100644
--- a/ipn/localapi/debug.go
+++ b/ipn/localapi/debug.go
@@ -142,14 +142,11 @@ type result struct {
var wg sync.WaitGroup
for _, dialer := range dialers {
- dialer := dialer // loop capture
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
conn, err := dialer.dial(ctx, network, addr)
results <- result{dialer.name, conn, err}
- }()
+ })
}
wg.Wait()
diff --git a/ipn/prefs.go b/ipn/prefs.go
index 72e0cf8b7..1492bae38 100644
--- a/ipn/prefs.go
+++ b/ipn/prefs.go
@@ -439,12 +439,11 @@ func applyPrefsEdits(src, dst reflect.Value, mask map[string]reflect.Value) {
func maskFields(v reflect.Value) map[string]reflect.Value {
mask := make(map[string]reflect.Value)
- for i := range v.NumField() {
- f := v.Type().Field(i).Name
- if !strings.HasSuffix(f, "Set") {
+ for sf, fv := range v.Fields() {
+ if !strings.HasSuffix(sf.Name, "Set") {
continue
}
- mask[strings.TrimSuffix(f, "Set")] = v.Field(i)
+ mask[strings.TrimSuffix(sf.Name, "Set")] = fv
}
return mask
}
@@ -845,22 +844,15 @@ func (p *Prefs) SetAdvertiseExitNode(runExit bool) {
// Tailscale IP.
func peerWithTailscaleIP(st *ipnstate.Status, ip netip.Addr) (ps *ipnstate.PeerStatus, ok bool) {
for _, ps := range st.Peer {
- for _, ip2 := range ps.TailscaleIPs {
- if ip == ip2 {
- return ps, true
- }
+ if slices.Contains(ps.TailscaleIPs, ip) {
+ return ps, true
}
}
return nil, false
}
func isRemoteIP(st *ipnstate.Status, ip netip.Addr) bool {
- for _, selfIP := range st.TailscaleIPs {
- if ip == selfIP {
- return false
- }
- }
- return true
+ return !slices.Contains(st.TailscaleIPs, ip)
}
// ClearExitNode sets the ExitNodeID and ExitNodeIP to their zero values.
diff --git a/ipn/prefs_test.go b/ipn/prefs_test.go
index 347a91e50..24c8f194e 100644
--- a/ipn/prefs_test.go
+++ b/ipn/prefs_test.go
@@ -27,8 +27,8 @@
)
func fieldsOf(t reflect.Type) (fields []string) {
- for i := range t.NumField() {
- fields = append(fields, t.Field(i).Name)
+ for field := range t.Fields() {
+ fields = append(fields, field.Name)
}
return
}
diff --git a/ipn/serve.go b/ipn/serve.go
index 911b408b6..21d15ab81 100644
--- a/ipn/serve.go
+++ b/ipn/serve.go
@@ -673,7 +673,7 @@ func CheckFunnelPort(wantedPort uint16, node *ipnstate.PeerStatus) error {
return deny("")
}
wantedPortString := strconv.Itoa(int(wantedPort))
- for _, ps := range strings.Split(portsStr, ",") {
+ for ps := range strings.SplitSeq(portsStr, ",") {
if ps == "" {
continue
}
diff --git a/ipn/store/awsstore/store_aws.go b/ipn/store/awsstore/store_aws.go
index e06e00eb3..feb86e457 100644
--- a/ipn/store/awsstore/store_aws.go
+++ b/ipn/store/awsstore/store_aws.go
@@ -189,8 +189,7 @@ func (s *awsStore) LoadState() error {
)
if err != nil {
- var pnf *ssmTypes.ParameterNotFound
- if errors.As(err, &pnf) {
+ if _, ok := errors.AsType[*ssmTypes.ParameterNotFound](err); ok {
// Create the parameter as it does not exist yet
// and return directly as it is defacto empty
return s.persistState()
diff --git a/k8s-operator/sessionrecording/spdy/frame.go b/k8s-operator/sessionrecording/spdy/frame.go
index 7087db3c3..3ca661e0b 100644
--- a/k8s-operator/sessionrecording/spdy/frame.go
+++ b/k8s-operator/sessionrecording/spdy/frame.go
@@ -211,7 +211,7 @@ func parseHeaders(decompressor io.Reader, log *zap.SugaredLogger) (http.Header,
return nil, fmt.Errorf("error determining num headers: %v", err)
}
h := make(http.Header, numHeaders)
- for i := uint32(0); i < numHeaders; i++ {
+ for range numHeaders {
name, err := readLenBytes()
if err != nil {
return nil, err
@@ -224,7 +224,7 @@ func parseHeaders(decompressor io.Reader, log *zap.SugaredLogger) (http.Header,
if err != nil {
return nil, fmt.Errorf("error reading header data: %w", err)
}
- for _, v := range bytes.Split(val, headerSep) {
+ for v := range bytes.SplitSeq(val, headerSep) {
h.Add(ns, string(v))
}
}
diff --git a/logtail/logtail.go b/logtail/logtail.go
index ef296568d..ed3872e79 100644
--- a/logtail/logtail.go
+++ b/logtail/logtail.go
@@ -902,8 +902,8 @@ func parseAndRemoveLogLevel(buf []byte) (level int, cleanBuf []byte) {
if bytes.Contains(buf, v2) {
return 2, bytes.ReplaceAll(buf, v2, nil)
}
- if i := bytes.Index(buf, vJSON); i != -1 {
- rest := buf[i+len(vJSON):]
+ if _, after, ok := bytes.Cut(buf, vJSON); ok {
+ rest := after
if len(rest) >= 2 {
v := rest[0]
if v >= '0' && v <= '9' {
diff --git a/logtail/logtail_test.go b/logtail/logtail_test.go
index 67250ae0d..19e1eeb7a 100644
--- a/logtail/logtail_test.go
+++ b/logtail/logtail_test.go
@@ -86,10 +86,10 @@ func TestDrainPendingMessages(t *testing.T) {
}
// all of the "log line" messages usually arrive at once, but poll if needed.
- body := ""
+ var body strings.Builder
for i := 0; i <= logLines; i++ {
- body += string(<-ts.uploaded)
- count := strings.Count(body, "log line")
+ body.WriteString(string(<-ts.uploaded))
+ count := strings.Count(body.String(), "log line")
if count == logLines {
break
}
diff --git a/metrics/multilabelmap.go b/metrics/multilabelmap.go
index 54d41bbae..fa31819d9 100644
--- a/metrics/multilabelmap.go
+++ b/metrics/multilabelmap.go
@@ -63,16 +63,16 @@ func LabelString(k any) string {
var sb strings.Builder
sb.WriteString("{")
- for i := range t.NumField() {
- if i > 0 {
+ first := true
+ for ft, fv := range rv.Fields() {
+ if !first {
sb.WriteString(",")
}
- ft := t.Field(i)
+ first = false
label := ft.Tag.Get("prom")
if label == "" {
label = strings.ToLower(ft.Name)
}
- fv := rv.Field(i)
switch fv.Kind() {
case reflect.String:
fmt.Fprintf(&sb, "%s=%q", label, fv.String())
diff --git a/net/art/stride_table_test.go b/net/art/stride_table_test.go
index e797f40ee..8279a545e 100644
--- a/net/art/stride_table_test.go
+++ b/net/art/stride_table_test.go
@@ -19,7 +19,7 @@
func TestInversePrefix(t *testing.T) {
t.Parallel()
for i := range 256 {
- for len := 0; len < 9; len++ {
+ for len := range 9 {
addr := i & (0xFF << (8 - len))
idx := prefixIndex(uint8(addr), len)
addr2, len2 := inversePrefixIndex(idx)
diff --git a/net/captivedetection/captivedetection_test.go b/net/captivedetection/captivedetection_test.go
index 2aa660d88..6b09ca0cc 100644
--- a/net/captivedetection/captivedetection_test.go
+++ b/net/captivedetection/captivedetection_test.go
@@ -94,8 +94,7 @@ func TestCaptivePortalRequest(t *testing.T) {
now := time.Now()
d.clock = func() time.Time { return now }
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
@@ -133,8 +132,7 @@ func TestCaptivePortalRequest(t *testing.T) {
func TestAgainstDERPHandler(t *testing.T) {
d := NewDetector(t.Logf)
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
s := httptest.NewServer(http.HandlerFunc(derpserver.ServeNoContent))
defer s.Close()
diff --git a/net/dns/manager_linux.go b/net/dns/manager_linux.go
index e68b2e7f9..392b64ba9 100644
--- a/net/dns/manager_linux.go
+++ b/net/dns/manager_linux.go
@@ -380,7 +380,7 @@ func isLibnssResolveUsed(env newOSConfigEnv) error {
if err != nil {
return fmt.Errorf("reading /etc/resolv.conf: %w", err)
}
- for _, line := range strings.Split(string(bs), "\n") {
+ for line := range strings.SplitSeq(string(bs), "\n") {
fields := strings.Fields(line)
if len(fields) < 2 || fields[0] != "hosts:" {
continue
diff --git a/net/dns/openresolv.go b/net/dns/openresolv.go
index c3aaf3a69..2a4ed174e 100644
--- a/net/dns/openresolv.go
+++ b/net/dns/openresolv.go
@@ -82,7 +82,7 @@ func (m openresolvManager) GetBaseConfig() (OSConfig, error) {
// Remove the "tailscale" snippet from the list.
args := []string{"-l"}
- for _, f := range strings.Split(strings.TrimSpace(string(bs)), " ") {
+ for f := range strings.SplitSeq(strings.TrimSpace(string(bs)), " ") {
if f == "tailscale" {
continue
}
diff --git a/net/dns/resolver/forwarder.go b/net/dns/resolver/forwarder.go
index 6fec32d6a..ca1599589 100644
--- a/net/dns/resolver/forwarder.go
+++ b/net/dns/resolver/forwarder.go
@@ -727,8 +727,7 @@ func (f *forwarder) send(ctx context.Context, fq *forwardQuery, rr resolverAndDe
}
// If we got a truncated UDP response, return that instead of an error.
- var trErr truncatedResponseError
- if errors.As(err, &trErr) {
+ if trErr, ok := errors.AsType[truncatedResponseError](err); ok {
return trErr.res, nil
}
return nil, err
diff --git a/net/dns/resolver/forwarder_test.go b/net/dns/resolver/forwarder_test.go
index 6fd186c25..3ddb47433 100644
--- a/net/dns/resolver/forwarder_test.go
+++ b/net/dns/resolver/forwarder_test.go
@@ -328,7 +328,7 @@ func runDNSServer(tb testing.TB, opts *testDNSServerOptions, response []byte, on
udpLn *net.UDPConn
err error
)
- for try := 0; try < tries; try++ {
+ for range tries {
if tcpLn != nil {
tcpLn.Close()
tcpLn = nil
@@ -392,9 +392,7 @@ func runDNSServer(tb testing.TB, opts *testDNSServerOptions, response []byte, on
var wg sync.WaitGroup
if opts == nil || !opts.SkipTCP {
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
for {
conn, err := tcpLn.Accept()
if err != nil {
@@ -402,7 +400,7 @@ func runDNSServer(tb testing.TB, opts *testDNSServerOptions, response []byte, on
}
go handleConn(conn)
}
- }()
+ })
}
handleUDP := func(addr netip.AddrPort, req []byte) {
@@ -413,9 +411,7 @@ func runDNSServer(tb testing.TB, opts *testDNSServerOptions, response []byte, on
}
if opts == nil || !opts.SkipUDP {
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
for {
buf := make([]byte, 65535)
n, addr, err := udpLn.ReadFromUDPAddrPort(buf)
@@ -425,7 +421,7 @@ func runDNSServer(tb testing.TB, opts *testDNSServerOptions, response []byte, on
buf = buf[:n]
go handleUDP(addr, buf)
}
- }()
+ })
}
tb.Cleanup(func() {
@@ -684,7 +680,7 @@ func makeResponseOfSize(tb testing.TB, domain string, targetSize int, includeOPT
var response []byte
var err error
- for attempt := 0; attempt < 10; attempt++ {
+ for range 10 {
testBuilder := dns.NewBuilder(nil, dns.Header{
Response: true,
Authoritative: true,
diff --git a/net/dns/resolver/tsdns.go b/net/dns/resolver/tsdns.go
index d0601de7b..53f130a8a 100644
--- a/net/dns/resolver/tsdns.go
+++ b/net/dns/resolver/tsdns.go
@@ -16,7 +16,7 @@
"net/netip"
"os"
"runtime"
- "sort"
+ "slices"
"strconv"
"strings"
"sync"
@@ -172,7 +172,7 @@ func WriteRoutes(w *bufio.Writer, routes map[dnsname.FQDN][]*dnstype.Resolver) {
}
kk = append(kk, k)
}
- sort.Slice(kk, func(i, j int) bool { return kk[i] < kk[j] })
+ slices.Sort(kk)
w.WriteByte('{')
for i, k := range kk {
if i > 0 {
diff --git a/net/dns/wsl_windows.go b/net/dns/wsl_windows.go
index c2400746b..1b93142f5 100644
--- a/net/dns/wsl_windows.go
+++ b/net/dns/wsl_windows.go
@@ -172,8 +172,7 @@ func (fs wslFS) Truncate(name string) error { return fs.WriteFile(name, nil, 064
func (fs wslFS) ReadFile(name string) ([]byte, error) {
b, err := wslCombinedOutput(fs.cmd("cat", "--", name))
- var ee *exec.ExitError
- if errors.As(err, &ee) && ee.ExitCode() == 1 {
+ if ee, ok := errors.AsType[*exec.ExitError](err); ok && ee.ExitCode() == 1 {
return nil, os.ErrNotExist
}
return b, err
diff --git a/net/netcheck/netcheck.go b/net/netcheck/netcheck.go
index ebcdc4eac..a64c358c5 100644
--- a/net/netcheck/netcheck.go
+++ b/net/netcheck/netcheck.go
@@ -545,7 +545,7 @@ func makeProbePlanInitial(dm *tailcfg.DERPMap, ifState *netmon.State) (plan prob
var p4 []probe
var p6 []probe
- for try := 0; try < 3; try++ {
+ for try := range 3 {
n := reg.Nodes[try%len(reg.Nodes)]
delay := time.Duration(try) * defaultInitialRetransmitTime
if n.IPv4 != "none" && ((ifState.HaveV4 && nodeMight4(n)) || n.IsTestNode()) {
@@ -975,13 +975,11 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap, opts *GetRe
// need to close the underlying Pinger after a timeout
// or when all ICMP probes are done, regardless of
// whether the HTTPS probes have finished.
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
if err := c.measureAllICMPLatency(ctx, rs, need); err != nil {
c.logf("[v1] measureAllICMPLatency: %v", err)
}
- }()
+ })
}
wg.Add(len(need))
c.logf("netcheck: UDP is blocked, trying HTTPS")
@@ -1072,9 +1070,7 @@ func (c *Client) runHTTPOnlyChecks(ctx context.Context, last *Report, rs *report
if len(rg.Nodes) == 0 {
continue
}
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
node := rg.Nodes[0]
req, _ := http.NewRequestWithContext(ctx, "HEAD", "https://"+node.HostName+"/derp/probe", nil)
// One warm-up one to get HTTP connection set
@@ -1099,7 +1095,7 @@ func (c *Client) runHTTPOnlyChecks(ctx context.Context, last *Report, rs *report
}
d := c.timeNow().Sub(t0)
rs.addNodeLatency(node, netip.AddrPort{}, d)
- }()
+ })
}
wg.Wait()
return nil
diff --git a/net/netcheck/netcheck_test.go b/net/netcheck/netcheck_test.go
index ab7f58feb..bc8f4a744 100644
--- a/net/netcheck/netcheck_test.go
+++ b/net/netcheck/netcheck_test.go
@@ -42,8 +42,7 @@ func TestBasic(t *testing.T) {
c := newTestClient(t)
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
if err := c.Standalone(ctx, "127.0.0.1:0"); err != nil {
t.Fatal(err)
@@ -124,8 +123,7 @@ func TestWorksWhenUDPBlocked(t *testing.T) {
c := newTestClient(t)
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
r, err := c.GetReport(ctx, dm, nil)
if err != nil {
@@ -1038,8 +1036,7 @@ func TestNoUDPNilGetReportOpts(t *testing.T) {
}
c := newTestClient(t)
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
r, err := c.GetReport(ctx, dm, nil)
if err != nil {
diff --git a/net/neterror/neterror_linux.go b/net/neterror/neterror_linux.go
index 9add4fd1d..a99452de5 100644
--- a/net/neterror/neterror_linux.go
+++ b/net/neterror/neterror_linux.go
@@ -12,8 +12,7 @@
func init() {
shouldDisableUDPGSO = func(err error) bool {
- var serr *os.SyscallError
- if errors.As(err, &serr) {
+ if serr, ok := errors.AsType[*os.SyscallError](err); ok {
// EIO is returned by udp_send_skb() if the device driver does not
// have tx checksumming enabled, which is a hard requirement of
// UDP_SEGMENT. See:
diff --git a/net/netmon/state.go b/net/netmon/state.go
index cdfa1d0fb..98ed52e5e 100644
--- a/net/netmon/state.go
+++ b/net/netmon/state.go
@@ -812,11 +812,8 @@ func (m *Monitor) HasCGNATInterface() (bool, error) {
if hasCGNATInterface || !i.IsUp() || isTailscaleInterface(i.Name, pfxs) {
return
}
- for _, pfx := range pfxs {
- if cgnatRange.Overlaps(pfx) {
- hasCGNATInterface = true
- break
- }
+ if slices.ContainsFunc(pfxs, cgnatRange.Overlaps) {
+ hasCGNATInterface = true
}
})
if err != nil {
diff --git a/net/netutil/routes.go b/net/netutil/routes.go
index c8212b9af..26f2de97c 100644
--- a/net/netutil/routes.go
+++ b/net/netutil/routes.go
@@ -41,8 +41,8 @@ func CalcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
routeMap := map[netip.Prefix]bool{}
if advertiseRoutes != "" {
var default4, default6 bool
- advroutes := strings.Split(advertiseRoutes, ",")
- for _, s := range advroutes {
+ advroutes := strings.SplitSeq(advertiseRoutes, ",")
+ for s := range advroutes {
ipp, err := netip.ParsePrefix(s)
if err != nil {
return nil, fmt.Errorf("%q is not a valid IP address or CIDR prefix", s)
diff --git a/net/socks5/socks5.go b/net/socks5/socks5.go
index 729fc8e88..f67dc1ecc 100644
--- a/net/socks5/socks5.go
+++ b/net/socks5/socks5.go
@@ -21,6 +21,7 @@
"io"
"log"
"net"
+ "slices"
"strconv"
"time"
@@ -488,10 +489,8 @@ func parseClientGreeting(r io.Reader, authMethod byte) error {
if err != nil {
return fmt.Errorf("could not read methods")
}
- for _, m := range methods {
- if m == authMethod {
- return nil
- }
+ if slices.Contains(methods, authMethod) {
+ return nil
}
return fmt.Errorf("no acceptable auth methods")
}
diff --git a/net/socks5/socks5_test.go b/net/socks5/socks5_test.go
index e6ca4b68e..84ef4be7b 100644
--- a/net/socks5/socks5_test.go
+++ b/net/socks5/socks5_test.go
@@ -180,11 +180,11 @@ func TestUDP(t *testing.T) {
const echoServerNumber = 3
echoServerListener := make([]net.PacketConn, echoServerNumber)
- for i := 0; i < echoServerNumber; i++ {
+ for i := range echoServerNumber {
echoServerListener[i] = newUDPEchoServer()
}
defer func() {
- for i := 0; i < echoServerNumber; i++ {
+ for i := range echoServerNumber {
_ = echoServerListener[i].Close()
}
}()
@@ -277,10 +277,10 @@ func TestUDP(t *testing.T) {
}
defer socks5UDPConn.Close()
- for i := 0; i < echoServerNumber; i++ {
+ for i := range echoServerNumber {
port := echoServerListener[i].LocalAddr().(*net.UDPAddr).Port
addr := socksAddr{addrType: ipv4, addr: "127.0.0.1", port: uint16(port)}
- requestBody := []byte(fmt.Sprintf("Test %d", i))
+ requestBody := fmt.Appendf(nil, "Test %d", i)
responseBody := sendUDPAndWaitResponse(socks5UDPConn, addr, requestBody)
if !bytes.Equal(requestBody, responseBody) {
t.Fatalf("got: %q want: %q", responseBody, requestBody)
diff --git a/net/stunserver/stunserver_test.go b/net/stunserver/stunserver_test.go
index c96aea4d1..f9efe21f3 100644
--- a/net/stunserver/stunserver_test.go
+++ b/net/stunserver/stunserver_test.go
@@ -60,8 +60,7 @@ func TestSTUNServer(t *testing.T) {
func BenchmarkServerSTUN(b *testing.B) {
b.ReportAllocs()
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := b.Context()
s := New(ctx)
s.Listen("localhost:0")
diff --git a/net/tstun/wrap.go b/net/tstun/wrap.go
index 2f5d8c1d1..6fe992575 100644
--- a/net/tstun/wrap.go
+++ b/net/tstun/wrap.go
@@ -1406,11 +1406,11 @@ func (t *Wrapper) InjectInboundPacketBuffer(pkt *netstack_PacketBuffer, buffs []
return err
}
}
- for i := 0; i < n; i++ {
+ for i := range n {
buffs[i] = buffs[i][:PacketStartOffset+sizes[i]]
}
defer func() {
- for i := 0; i < n; i++ {
+ for i := range n {
buffs[i] = buffs[i][:cap(buffs[i])]
}
}()
diff --git a/net/tstun/wrap_test.go b/net/tstun/wrap_test.go
index bd29489a8..57b300513 100644
--- a/net/tstun/wrap_test.go
+++ b/net/tstun/wrap_test.go
@@ -95,7 +95,7 @@ func tcp4syn(src, dst string, sport, dport uint16) []byte {
func nets(nets ...string) (ret []netip.Prefix) {
for _, s := range nets {
- if i := strings.IndexByte(s, '/'); i == -1 {
+ if found := strings.Contains(s, "/"); !found {
ip, err := netip.ParseAddr(s)
if err != nil {
panic(err)
@@ -122,13 +122,13 @@ func ports(s string) filter.PortRange {
}
var fs, ls string
- i := strings.IndexByte(s, '-')
- if i == -1 {
+ before, after, ok := strings.Cut(s, "-")
+ if !ok {
fs = s
ls = fs
} else {
- fs = s[:i]
- ls = s[i+1:]
+ fs = before
+ ls = after
}
first, err := strconv.ParseInt(fs, 10, 16)
if err != nil {
diff --git a/net/udprelay/server.go b/net/udprelay/server.go
index 03d8e3dc3..7dd89920e 100644
--- a/net/udprelay/server.go
+++ b/net/udprelay/server.go
@@ -977,7 +977,7 @@ func (e ErrServerNotReady) Error() string {
// For now, we favor simplicity and reducing VNI re-use over more complex
// ephemeral port (VNI) selection algorithms.
func (s *Server) getNextVNILocked() (uint32, error) {
- for i := uint32(0); i < totalPossibleVNI; i++ {
+ for range totalPossibleVNI {
vni := s.nextVNI
if vni == maxVNI {
s.nextVNI = minVNI
diff --git a/net/udprelay/server_test.go b/net/udprelay/server_test.go
index 66de0d88a..204e365bc 100644
--- a/net/udprelay/server_test.go
+++ b/net/udprelay/server_test.go
@@ -265,7 +265,7 @@ func TestServer(t *testing.T) {
tcB := newTestClient(t, endpoint.VNI, tcBServerEndpointAddr, discoB, discoA.Public(), endpoint.ServerDisco)
defer tcB.close()
- for i := 0; i < 2; i++ {
+ for range 2 {
// We handshake both clients twice to guarantee server-side
// packet reading goroutines, which are independent across
// address families, have seen an answer from both clients
@@ -345,7 +345,7 @@ func TestServer_getNextVNILocked(t *testing.T) {
s := &Server{
nextVNI: minVNI,
}
- for i := uint64(0); i < uint64(totalPossibleVNI); i++ {
+ for range uint64(totalPossibleVNI) {
vni, err := s.getNextVNILocked()
if err != nil { // using quicktest here triples test time
t.Fatal(err)
diff --git a/prober/prober.go b/prober/prober.go
index 3a43401a1..40eef2faf 100644
--- a/prober/prober.go
+++ b/prober/prober.go
@@ -122,12 +122,8 @@ func (p *Prober) Run(name string, interval time.Duration, labels Labels, pc Prob
"name": name,
"class": pc.Class,
}
- for k, v := range pc.Labels {
- lb[k] = v
- }
- for k, v := range labels {
- lb[k] = v
- }
+ maps.Copy(lb, pc.Labels)
+ maps.Copy(lb, labels)
probe := newProbe(p, name, interval, lb, pc)
p.probes[name] = probe
diff --git a/ssh/tailssh/incubator.go b/ssh/tailssh/incubator.go
index b414ce3fb..28316b04d 100644
--- a/ssh/tailssh/incubator.go
+++ b/ssh/tailssh/incubator.go
@@ -158,8 +158,7 @@ func (ss *sshSession) newIncubatorCommand(logf logger.Logf) (cmd *exec.Cmd, err
cmd.Dir = "/"
case errors.Is(err, fs.ErrPermission) || errors.Is(err, fs.ErrNotExist):
// Ensure that cmd.Dir is the source of the error.
- var pathErr *fs.PathError
- if errors.As(err, &pathErr) && pathErr.Path == cmd.Dir {
+ if pathErr, ok := errors.AsType[*fs.PathError](err); ok && pathErr.Path == cmd.Dir {
// If we cannot run loginShell in localUser.HomeDir,
// we will try to run this command in the root directory.
cmd.Dir = "/"
@@ -312,7 +311,7 @@ func parseIncubatorArgs(args []string) (incubatorArgs, error) {
flags.StringVar(&ia.encodedEnv, "encoded-env", "", "JSON encoded array of environment variables in '['key=value']' format")
flags.Parse(args)
- for _, g := range strings.Split(groups, ",") {
+ for g := range strings.SplitSeq(groups, ",") {
gid, err := strconv.Atoi(g)
if err != nil {
return ia, fmt.Errorf("unable to parse group id %q: %w", g, err)
diff --git a/ssh/tailssh/privs_test.go b/ssh/tailssh/privs_test.go
index f0ec66c64..7ddc9c861 100644
--- a/ssh/tailssh/privs_test.go
+++ b/ssh/tailssh/privs_test.go
@@ -262,12 +262,10 @@ func maybeValidUID(id int) bool {
return true
}
- var u1 user.UnknownUserIdError
- if errors.As(err, &u1) {
+ if _, ok := errors.AsType[user.UnknownUserIdError](err); ok {
return false
}
- var u2 user.UnknownUserError
- if errors.As(err, &u2) {
+ if _, ok := errors.AsType[user.UnknownUserError](err); ok {
return false
}
@@ -281,12 +279,10 @@ func maybeValidGID(id int) bool {
return true
}
- var u1 user.UnknownGroupIdError
- if errors.As(err, &u1) {
+ if _, ok := errors.AsType[user.UnknownGroupIdError](err); ok {
return false
}
- var u2 user.UnknownGroupError
- if errors.As(err, &u2) {
+ if _, ok := errors.AsType[user.UnknownGroupError](err); ok {
return false
}
diff --git a/ssh/tailssh/tailssh.go b/ssh/tailssh/tailssh.go
index cb56f701b..debad2b5c 100644
--- a/ssh/tailssh/tailssh.go
+++ b/ssh/tailssh/tailssh.go
@@ -14,6 +14,7 @@
"errors"
"fmt"
"io"
+ "maps"
"net"
"net/http"
"net/netip"
@@ -500,15 +501,9 @@ func (srv *server) newConn() (*conn, error) {
},
}
ss := c.Server
- for k, v := range ssh.DefaultRequestHandlers {
- ss.RequestHandlers[k] = v
- }
- for k, v := range ssh.DefaultChannelHandlers {
- ss.ChannelHandlers[k] = v
- }
- for k, v := range ssh.DefaultSubsystemHandlers {
- ss.SubsystemHandlers[k] = v
- }
+ maps.Copy(ss.RequestHandlers, ssh.DefaultRequestHandlers)
+ maps.Copy(ss.ChannelHandlers, ssh.DefaultChannelHandlers)
+ maps.Copy(ss.SubsystemHandlers, ssh.DefaultSubsystemHandlers)
keys, err := srv.lb.GetSSH_HostKeys()
if err != nil {
return nil, err
@@ -964,8 +959,7 @@ func (ss *sshSession) run() {
var err error
rec, err = ss.startNewRecording()
if err != nil {
- var uve userVisibleError
- if errors.As(err, &uve) {
+ if uve, ok := errors.AsType[userVisibleError](err); ok {
fmt.Fprintf(ss, "%s\r\n", uve.SSHTerminationMessage())
} else {
fmt.Fprintf(ss, "can't start new recording\r\n")
@@ -986,8 +980,7 @@ func (ss *sshSession) run() {
logf("start failed: %v", err.Error())
if errors.Is(err, context.Canceled) {
err := context.Cause(ss.ctx)
- var uve userVisibleError
- if errors.As(err, &uve) {
+ if uve, ok := errors.AsType[userVisibleError](err); ok {
fmt.Fprintf(ss, "%s\r\n", uve)
}
}
diff --git a/ssh/tailssh/tailssh_test.go b/ssh/tailssh/tailssh_test.go
index df8023500..4d6f2172d 100644
--- a/ssh/tailssh/tailssh_test.go
+++ b/ssh/tailssh/tailssh_test.go
@@ -571,9 +571,7 @@ func TestSSHRecordingCancelsSessionsOnUploadFailure(t *testing.T) {
tstest.Replace(t, &handler, tt.handler)
sc, dc := memnet.NewTCPConn(src, dst, 1024)
var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
c, chans, reqs, err := testssh.NewClientConn(sc, sc.RemoteAddr().String(), cfg)
if err != nil {
t.Errorf("client: %v", err)
@@ -603,7 +601,7 @@ func TestSSHRecordingCancelsSessionsOnUploadFailure(t *testing.T) {
t.Errorf("client output must not contain %q", x)
}
}
- }()
+ })
if err := s.HandleSSHConn(dc); err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -666,9 +664,7 @@ func TestMultipleRecorders(t *testing.T) {
}
var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
c, chans, reqs, err := testssh.NewClientConn(sc, sc.RemoteAddr().String(), cfg)
if err != nil {
t.Errorf("client: %v", err)
@@ -690,7 +686,7 @@ func TestMultipleRecorders(t *testing.T) {
if string(out) != "Ran echo!\n" {
t.Errorf("client: unexpected output: %q", out)
}
- }()
+ })
if err := s.HandleSSHConn(dc); err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -757,9 +753,7 @@ func TestSSHRecordingNonInteractive(t *testing.T) {
}
var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
c, chans, reqs, err := testssh.NewClientConn(sc, sc.RemoteAddr().String(), cfg)
if err != nil {
t.Errorf("client: %v", err)
@@ -778,7 +772,7 @@ func TestSSHRecordingNonInteractive(t *testing.T) {
if err != nil {
t.Errorf("client: %v", err)
}
- }()
+ })
if err := s.HandleSSHConn(dc); err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -988,9 +982,7 @@ func TestSSHAuthFlow(t *testing.T) {
}
var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
c, chans, reqs, err := testssh.NewClientConn(sc, sc.RemoteAddr().String(), cfg)
if err != nil {
if !tc.authErr {
@@ -1014,7 +1006,7 @@ func TestSSHAuthFlow(t *testing.T) {
if err != nil {
t.Errorf("client: %v", err)
}
- }()
+ })
if err := s.HandleSSHConn(dc); err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -1228,8 +1220,8 @@ func TestSSH(t *testing.T) {
func parseEnv(out []byte) map[string]string {
e := map[string]string{}
for line := range lineiter.Bytes(out) {
- if i := bytes.IndexByte(line, '='); i != -1 {
- e[string(line[:i])] = string(line[i+1:])
+ if before, after, ok := bytes.Cut(line, []byte{'='}); ok {
+ e[string(before)] = string(after)
}
}
return e
diff --git a/syncs/shardedint_test.go b/syncs/shardedint_test.go
index 8c3f7ef7b..ac298e626 100644
--- a/syncs/shardedint_test.go
+++ b/syncs/shardedint_test.go
@@ -66,10 +66,10 @@ func TestShardedInt(t *testing.T) {
numWorkers := 1000
numIncrements := 1000
wg.Add(numWorkers)
- for i := 0; i < numWorkers; i++ {
+ for range numWorkers {
go func() {
defer wg.Done()
- for i := 0; i < numIncrements; i++ {
+ for range numIncrements {
m.Add(1)
}
}()
diff --git a/syncs/shardvalue_test.go b/syncs/shardvalue_test.go
index 1dd0a542e..ab34527ab 100644
--- a/syncs/shardvalue_test.go
+++ b/syncs/shardvalue_test.go
@@ -66,10 +66,10 @@ type intVal struct {
iterations := 10000
var wg sync.WaitGroup
wg.Add(goroutines)
- for i := 0; i < goroutines; i++ {
+ for range goroutines {
go func() {
defer wg.Done()
- for i := 0; i < iterations; i++ {
+ for range iterations {
sv.One(func(v *intVal) {
v.Add(1)
})
diff --git a/syncs/syncs_test.go b/syncs/syncs_test.go
index 81fcccbf6..1e79448ad 100644
--- a/syncs/syncs_test.go
+++ b/syncs/syncs_test.go
@@ -6,6 +6,7 @@
import (
"context"
"io"
+ "maps"
"os"
"sync"
"testing"
@@ -226,9 +227,7 @@ func TestMap(t *testing.T) {
}
got := map[string]int{}
want := map[string]int{"one": 1, "two": 2, "three": 3}
- for k, v := range m.All() {
- got[k] = v
- }
+ maps.Insert(got, m.All())
if d := cmp.Diff(got, want); d != "" {
t.Errorf("Range mismatch (-got +want):\n%s", d)
}
@@ -243,9 +242,7 @@ func TestMap(t *testing.T) {
m.Delete("noexist")
got = map[string]int{}
want = map[string]int{}
- for k, v := range m.All() {
- got[k] = v
- }
+ maps.Insert(got, m.All())
if d := cmp.Diff(got, want); d != "" {
t.Errorf("Range mismatch (-got +want):\n%s", d)
}
diff --git a/tailcfg/tailcfg_test.go b/tailcfg/tailcfg_test.go
index 9ed7c1e14..6d4d4e8e9 100644
--- a/tailcfg/tailcfg_test.go
+++ b/tailcfg/tailcfg_test.go
@@ -21,8 +21,8 @@
)
func fieldsOf(t reflect.Type) (fields []string) {
- for i := range t.NumField() {
- fields = append(fields, t.Field(i).Name)
+ for field := range t.Fields() {
+ fields = append(fields, field.Name)
}
return
}
diff --git a/tka/chaintest_test.go b/tka/chaintest_test.go
index 5ca68afa8..467880e2c 100644
--- a/tka/chaintest_test.go
+++ b/tka/chaintest_test.go
@@ -7,6 +7,7 @@
"bytes"
"crypto/ed25519"
"fmt"
+ "maps"
"strconv"
"strings"
"testing"
@@ -198,9 +199,7 @@ func (c *testChain) recordParent(t *testing.T, child, parent string) {
// This method populates c.AUMs and c.AUMHashes.
func (c *testChain) buildChain() {
pending := make(map[string]*testchainNode, len(c.Nodes))
- for k, v := range c.Nodes {
- pending[k] = v
- }
+ maps.Copy(pending, c.Nodes)
// AUMs with a parent need to know their hash, so we
// only compute AUMs whose parents have been computed
diff --git a/tka/key.go b/tka/key.go
index bc946156e..005a10433 100644
--- a/tka/key.go
+++ b/tka/key.go
@@ -7,6 +7,7 @@
"crypto/ed25519"
"errors"
"fmt"
+ "maps"
"tailscale.com/types/tkatype"
)
@@ -64,9 +65,7 @@ func (k Key) Clone() Key {
if k.Meta != nil {
out.Meta = make(map[string]string, len(k.Meta))
- for k, v := range k.Meta {
- out.Meta[k] = v
- }
+ maps.Copy(out.Meta, k.Meta)
}
return out
diff --git a/tka/scenario_test.go b/tka/scenario_test.go
index cf4ee2d5b..ad3742dbf 100644
--- a/tka/scenario_test.go
+++ b/tka/scenario_test.go
@@ -5,6 +5,7 @@
import (
"crypto/ed25519"
+ "maps"
"sort"
"testing"
)
@@ -36,9 +37,7 @@ func (s *scenarioTest) mkNode(name string) *scenarioNode {
}
aums := make(map[string]AUM, len(s.initial.AUMs))
- for k, v := range s.initial.AUMs {
- aums[k] = v
- }
+ maps.Copy(aums, s.initial.AUMs)
n := &scenarioNode{
A: authority,
diff --git a/tka/sync.go b/tka/sync.go
index 27e1c0e63..18a991384 100644
--- a/tka/sync.go
+++ b/tka/sync.go
@@ -107,7 +107,7 @@ func (a *Authority) SyncOffer(storage Chonk) (SyncOffer, error) {
skipAmount uint64 = ancestorsSkipStart
curs AUMHash = a.Head()
)
- for i := uint64(0); i < maxSyncHeadIntersectionIter; i++ {
+ for i := range uint64(maxSyncHeadIntersectionIter) {
if i > 0 && (i%skipAmount) == 0 {
out.Ancestors = append(out.Ancestors, curs)
skipAmount = skipAmount << ancestorsSkipShift
diff --git a/tool/gocross/exec_other.go b/tool/gocross/exec_other.go
index 20e52aa8f..b9004b8d5 100644
--- a/tool/gocross/exec_other.go
+++ b/tool/gocross/exec_other.go
@@ -21,8 +21,7 @@ func doExec(cmd string, args []string, env []string) error {
// Propagate ExitErrors within this func to give us similar semantics to
// the Unix variant.
- var ee *exec.ExitError
- if errors.As(err, &ee) {
+ if ee, ok := errors.AsType[*exec.ExitError](err); ok {
os.Exit(ee.ExitCode())
}
diff --git a/tsconsensus/monitor.go b/tsconsensus/monitor.go
index cc5ac812c..b937926a6 100644
--- a/tsconsensus/monitor.go
+++ b/tsconsensus/monitor.go
@@ -85,7 +85,7 @@ func (m *monitor) handleSummaryStatus(w http.ResponseWriter, r *http.Request) {
lines = append(lines, fmt.Sprintf("%s\t\t%d\t%d\t%t", name, p.RxBytes, p.TxBytes, p.Active))
}
}
- _, err = w.Write([]byte(fmt.Sprintf("RaftState: %s\n", s.RaftState)))
+ _, err = w.Write(fmt.Appendf(nil, "RaftState: %s\n", s.RaftState))
if err != nil {
log.Printf("monitor: error writing status: %v", err)
return
@@ -93,7 +93,7 @@ func (m *monitor) handleSummaryStatus(w http.ResponseWriter, r *http.Request) {
slices.Sort(lines)
for _, ln := range lines {
- _, err = w.Write([]byte(fmt.Sprintf("%s\n", ln)))
+ _, err = w.Write(fmt.Appendf(nil, "%s\n", ln))
if err != nil {
log.Printf("monitor: error writing status: %v", err)
return
diff --git a/tsconsensus/tsconsensus_test.go b/tsconsensus/tsconsensus_test.go
index 8897db119..3236ef680 100644
--- a/tsconsensus/tsconsensus_test.go
+++ b/tsconsensus/tsconsensus_test.go
@@ -296,7 +296,7 @@ func startNodesAndWaitForPeerStatus(t testing.TB, ctx context.Context, clusterTa
keysToTag := make([]key.NodePublic, nNodes)
localClients := make([]*tailscale.LocalClient, nNodes)
control, controlURL := startControl(t)
- for i := 0; i < nNodes; i++ {
+ for i := range nNodes {
ts, key, _ := startNode(t, ctx, controlURL, fmt.Sprintf("node %d", i))
ps[i] = &participant{ts: ts, key: key}
keysToTag[i] = key
@@ -353,7 +353,7 @@ func createConsensusCluster(t testing.TB, ctx context.Context, clusterTag string
}
fxRaftConfigContainsAll := func() bool {
- for i := 0; i < len(participants); i++ {
+ for i := range participants {
fut := participants[i].c.raft.GetConfiguration()
err = fut.Error()
if err != nil {
@@ -618,8 +618,8 @@ func TestOnlyTaggedPeersCanDialRaftPort(t *testing.T) {
}
isNetErr := func(err error) bool {
- var netErr net.Error
- return errors.As(err, &netErr)
+ _, ok := errors.AsType[net.Error](err)
+ return ok
}
err := getErrorFromTryingToSend(untaggedNode)
diff --git a/tsd/tsd.go b/tsd/tsd.go
index 9d79334d6..57437ddcc 100644
--- a/tsd/tsd.go
+++ b/tsd/tsd.go
@@ -226,8 +226,7 @@ func (p *SubSystem[T]) Set(v T) {
return
}
- var z *T
- panic(fmt.Sprintf("%v is already set", reflect.TypeOf(z).Elem().String()))
+ panic(fmt.Sprintf("%v is already set", reflect.TypeFor[T]().String()))
}
p.v = v
p.set = true
@@ -236,8 +235,7 @@ func (p *SubSystem[T]) Set(v T) {
// Get returns the value of p, panicking if it hasn't been set.
func (p *SubSystem[T]) Get() T {
if !p.set {
- var z *T
- panic(fmt.Sprintf("%v is not set", reflect.TypeOf(z).Elem().String()))
+ panic(fmt.Sprintf("%v is not set", reflect.TypeFor[T]().String()))
}
return p.v
}
diff --git a/tsnet/tsnet.go b/tsnet/tsnet.go
index 776854e22..4a116cf34 100644
--- a/tsnet/tsnet.go
+++ b/tsnet/tsnet.go
@@ -466,9 +466,7 @@ func (s *Server) close() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
// Perform a best-effort final flush.
if s.logtail != nil {
s.logtail.Shutdown(ctx)
@@ -476,14 +474,12 @@ func (s *Server) close() {
if s.logbuffer != nil {
s.logbuffer.Close()
}
- }()
- wg.Add(1)
- go func() {
- defer wg.Done()
+ })
+ wg.Go(func() {
if s.localAPIServer != nil {
s.localAPIServer.Shutdown(ctx)
}
- }()
+ })
if s.shutdownCancel != nil {
s.shutdownCancel()
diff --git a/tsnet/tsnet_test.go b/tsnet/tsnet_test.go
index 1cf4bf48f..a2bf76e18 100644
--- a/tsnet/tsnet_test.go
+++ b/tsnet/tsnet_test.go
@@ -2598,7 +2598,7 @@ func buildDNSQuery(name string, srcIP netip.Addr) []byte {
0x00, 0x01, // QDCOUNT: 1
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ANCOUNT, NSCOUNT, ARCOUNT
}
- for _, label := range strings.Split(name, ".") {
+ for label := range strings.SplitSeq(name, ".") {
dns = append(dns, byte(len(label)))
dns = append(dns, label...)
}
diff --git a/tstest/deptest/deptest.go b/tstest/deptest/deptest.go
index 3117af2ff..59672761e 100644
--- a/tstest/deptest/deptest.go
+++ b/tstest/deptest/deptest.go
@@ -124,7 +124,7 @@ func ImportAliasCheck(t testing.TB, relDir string) {
}
badRx := regexp.MustCompile(`^([^:]+:\d+):\s+"golang\.org/x/exp/(slices|maps)"`)
if s := strings.TrimSpace(string(matches)); s != "" {
- for _, line := range strings.Split(s, "\n") {
+ for line := range strings.SplitSeq(s, "\n") {
if m := badRx.FindStringSubmatch(line); m != nil {
t.Errorf("%s: the x/exp/%s package should be imported as x%s", m[1], m[2], m[2])
}
diff --git a/tstest/integration/integration_test.go b/tstest/integration/integration_test.go
index 2d2194278..0482e4b53 100644
--- a/tstest/integration/integration_test.go
+++ b/tstest/integration/integration_test.go
@@ -1673,7 +1673,7 @@ func TestNetstackTCPLoopback(t *testing.T) {
defer lis.Close()
writeFn := func(conn net.Conn) error {
- for i := 0; i < writeBufIterations; i++ {
+ for range writeBufIterations {
toWrite := make([]byte, writeBufSize)
var wrote int
for {
diff --git a/tstest/integration/nat/nat_test.go b/tstest/integration/nat/nat_test.go
index 1f62436ff..2ac16bf58 100644
--- a/tstest/integration/nat/nat_test.go
+++ b/tstest/integration/nat/nat_test.go
@@ -317,9 +317,7 @@ func (nt *natTest) runTest(addNode ...addNodeFunc) pingRoute {
}
defer srv.Close()
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
for {
c, err := srv.Accept()
if err != nil {
@@ -327,7 +325,7 @@ func (nt *natTest) runTest(addNode ...addNodeFunc) pingRoute {
}
go nt.vnet.ServeUnixConn(c.(*net.UnixConn), vnet.ProtocolQEMU)
}
- }()
+ })
for i, node := range nodes {
disk := fmt.Sprintf("%s/node-%d.qcow2", nt.tempDir, i)
@@ -391,7 +389,6 @@ func (nt *natTest) runTest(addNode ...addNodeFunc) pingRoute {
var eg errgroup.Group
for i, c := range clients {
- i, c := i, c
eg.Go(func() error {
node := nodes[i]
t.Logf("%v calling Status...", node)
diff --git a/tstest/integration/vms/vms_test.go b/tstest/integration/vms/vms_test.go
index 5ebb12b71..bdfba1e27 100644
--- a/tstest/integration/vms/vms_test.go
+++ b/tstest/integration/vms/vms_test.go
@@ -364,7 +364,7 @@ func (h *Harness) testDistro(t *testing.T, d Distro, ipm ipMapping) {
// starts with testcontrol sometimes there can be up to a few seconds where
// tailscaled is in an unknown state on these virtual machines. This exponential
// delay loop should delay long enough for tailscaled to be ready.
- for count := 0; count < 10; count++ {
+ for range 10 {
sess := getSession(t, cli)
outp, err = sess.CombinedOutput("tailscale status")
diff --git a/tstest/natlab/natlab.go b/tstest/natlab/natlab.go
index add812d8f..b66779eeb 100644
--- a/tstest/natlab/natlab.go
+++ b/tstest/natlab/natlab.go
@@ -18,6 +18,7 @@
"net"
"net/netip"
"os"
+ "slices"
"sort"
"strconv"
"sync"
@@ -247,12 +248,7 @@ func (f *Interface) String() string {
// Contains reports whether f contains ip as an IP.
func (f *Interface) Contains(ip netip.Addr) bool {
- for _, v := range f.ips {
- if ip == v {
- return true
- }
- }
- return false
+ return slices.Contains(f.ips, ip)
}
type routeEntry struct {
@@ -348,10 +344,8 @@ func (m *Machine) isLocalIP(ip netip.Addr) bool {
m.mu.Lock()
defer m.mu.Unlock()
for _, intf := range m.interfaces {
- for _, iip := range intf.ips {
- if ip == iip {
- return true
- }
+ if slices.Contains(intf.ips, ip) {
+ return true
}
}
return false
@@ -565,7 +559,7 @@ func (m *Machine) interfaceForIP(ip netip.Addr) (*Interface, error) {
func (m *Machine) pickEphemPort() (port uint16, err error) {
m.mu.Lock()
defer m.mu.Unlock()
- for tries := 0; tries < 500; tries++ {
+ for range 500 {
port := uint16(rand.IntN(32<<10) + 32<<10)
if !m.portInUseLocked(port) {
return port, nil
diff --git a/tstest/natlab/vnet/vnet.go b/tstest/natlab/vnet/vnet.go
index ea119bad7..9eb81520c 100644
--- a/tstest/natlab/vnet/vnet.go
+++ b/tstest/natlab/vnet/vnet.go
@@ -1917,7 +1917,7 @@ func (n *network) doPortMap(src netip.Addr, dstLANPort, wantExtPort uint16, sec
}
}
- for try := 0; try < 20_000; try++ {
+ for range 20_000 {
if wanAP.Port() > 0 && !n.natTable.IsPublicPortUsed(wanAP) {
mak.Set(&n.portMap, wanAP, portMapping{
dst: dst,
diff --git a/tstest/resource_test.go b/tstest/resource_test.go
index ecef91cf6..fc868d5f5 100644
--- a/tstest/resource_test.go
+++ b/tstest/resource_test.go
@@ -245,7 +245,7 @@ func TestParseGoroutines(t *testing.T) {
t.Errorf("sort field has different number of words: got %d, want %d", len(sorted), len(original))
continue
}
- for i := 0; i < len(original); i++ {
+ for i := range original {
if original[i] != sorted[len(sorted)-1-i] {
t.Errorf("sort field word mismatch at position %d: got %q, want %q", i, sorted[len(sorted)-1-i], original[i])
}
diff --git a/tstest/typewalk/typewalk.go b/tstest/typewalk/typewalk.go
index f989b4c18..dea87a8e9 100644
--- a/tstest/typewalk/typewalk.go
+++ b/tstest/typewalk/typewalk.go
@@ -54,14 +54,13 @@ func MatchingPaths(rt reflect.Type, match func(reflect.Type) bool) iter.Seq[Path
return
}
switch t.Kind() {
- case reflect.Ptr, reflect.Slice, reflect.Array:
+ case reflect.Pointer, reflect.Slice, reflect.Array:
walk(t.Elem(), func(root reflect.Value) reflect.Value {
v := getV(root)
return v.Elem()
})
case reflect.Struct:
- for i := range t.NumField() {
- sf := t.Field(i)
+ for sf := range t.Fields() {
fieldName := sf.Name
if fieldName == "_" {
continue
diff --git a/tsweb/tsweb.go b/tsweb/tsweb.go
index f464e7af2..c73010783 100644
--- a/tsweb/tsweb.go
+++ b/tsweb/tsweb.go
@@ -13,6 +13,7 @@
"expvar"
"fmt"
"io"
+ "maps"
"net"
"net/http"
"net/netip"
@@ -734,8 +735,8 @@ func (h errorHandler) handleError(w http.ResponseWriter, r *http.Request, lw *lo
// Extract a presentable, loggable error.
var hOK bool
- var hErr HTTPError
- if errors.As(err, &hErr) {
+ hErr, hAsOK := errors.AsType[HTTPError](err)
+ if hAsOK {
hOK = true
if hErr.Code == 0 {
lw.logf("[unexpected] HTTPError %v did not contain an HTTP status code, sending internal server error", hErr)
@@ -854,9 +855,7 @@ func WriteHTTPError(w http.ResponseWriter, r *http.Request, e HTTPError) {
h.Set("X-Content-Type-Options", "nosniff")
// Custom headers from the error.
- for k, vs := range e.Header {
- h[k] = vs
- }
+ maps.Copy(h, e.Header)
// Write the msg back to the user.
w.WriteHeader(e.Code)
diff --git a/tsweb/varz/varz.go b/tsweb/varz/varz.go
index a2286c760..0df6e5775 100644
--- a/tsweb/varz/varz.go
+++ b/tsweb/varz/varz.go
@@ -93,8 +93,8 @@ func prometheusMetric(prefix string, key string) (string, string, string) {
typ = "histogram"
key = strings.TrimPrefix(key, histogramPrefix)
}
- if strings.HasPrefix(key, labelMapPrefix) {
- key = strings.TrimPrefix(key, labelMapPrefix)
+ if after, ok := strings.CutPrefix(key, labelMapPrefix); ok {
+ key = after
if a, b, ok := strings.Cut(key, "_"); ok {
label, key = a, b
}
@@ -154,7 +154,7 @@ func writePromExpVar(w io.Writer, prefix string, kv expvar.KeyValue) {
case PrometheusMetricsReflectRooter:
root := v.PrometheusMetricsReflectRoot()
rv := reflect.ValueOf(root)
- if rv.Type().Kind() == reflect.Ptr {
+ if rv.Type().Kind() == reflect.Pointer {
if rv.IsNil() {
return
}
@@ -419,8 +419,7 @@ func structTypeSortedFields(t reflect.Type) []sortedStructField {
return v.([]sortedStructField)
}
fields := make([]sortedStructField, 0, t.NumField())
- for i, n := 0, t.NumField(); i < n; i++ {
- sf := t.Field(i)
+ for sf := range t.Fields() {
name := sf.Name
if v := sf.Tag.Get("json"); v != "" {
v, _, _ = strings.Cut(v, ",")
@@ -433,7 +432,7 @@ func structTypeSortedFields(t reflect.Type) []sortedStructField {
}
}
fields = append(fields, sortedStructField{
- Index: i,
+ Index: sf.Index[0],
Name: name,
SortName: removeTypePrefixes(name),
MetricType: sf.Tag.Get("metrictype"),
@@ -467,7 +466,7 @@ func foreachExportedStructField(rv reflect.Value, f func(fieldOrJSONName, metric
sf := ssf.StructFieldType
if ssf.MetricType != "" || sf.Type.Kind() == reflect.Struct {
f(ssf.Name, ssf.MetricType, rv.Field(ssf.Index))
- } else if sf.Type.Kind() == reflect.Ptr && sf.Type.Elem().Kind() == reflect.Struct {
+ } else if sf.Type.Kind() == reflect.Pointer && sf.Type.Elem().Kind() == reflect.Struct {
fv := rv.Field(ssf.Index)
if !fv.IsNil() {
f(ssf.Name, ssf.MetricType, fv.Elem())
diff --git a/types/ipproto/ipproto_test.go b/types/ipproto/ipproto_test.go
index 8bfeb13fa..6d8be47a9 100644
--- a/types/ipproto/ipproto_test.go
+++ b/types/ipproto/ipproto_test.go
@@ -69,7 +69,7 @@ func TestProtoUnmarshalText(t *testing.T) {
for i := range 256 {
var p Proto
- must.Do(p.UnmarshalText([]byte(fmt.Sprintf("%d", i))))
+ must.Do(p.UnmarshalText(fmt.Appendf(nil, "%d", i)))
if got, want := p, Proto(i); got != want {
t.Errorf("Proto(%d) = %v, want %v", i, got, want)
}
@@ -122,7 +122,7 @@ func TestProtoUnmarshalJSON(t *testing.T) {
var p Proto
for i := range 256 {
- j := []byte(fmt.Sprintf(`%d`, i))
+ j := fmt.Appendf(nil, `%d`, i)
must.Do(json.Unmarshal(j, &p))
if got, want := p, Proto(i); got != want {
t.Errorf("Proto(%d) = %v, want %v", i, got, want)
@@ -130,7 +130,7 @@ func TestProtoUnmarshalJSON(t *testing.T) {
}
for name, wantProto := range acceptedNames {
- must.Do(json.Unmarshal([]byte(fmt.Sprintf(`"%s"`, name)), &p))
+ must.Do(json.Unmarshal(fmt.Appendf(nil, `"%s"`, name), &p))
if got, want := p, wantProto; got != want {
t.Errorf("Proto(%q) = %v, want %v", name, got, want)
}
diff --git a/types/lazy/deferred_test.go b/types/lazy/deferred_test.go
index 61cc8f8ac..4b2bb07ee 100644
--- a/types/lazy/deferred_test.go
+++ b/types/lazy/deferred_test.go
@@ -145,13 +145,11 @@ func TestDeferredInit(t *testing.T) {
// Call [DeferredInit.Do] concurrently.
const N = 10000
for range N {
- wg.Add(1)
- go func() {
+ wg.Go(func() {
gotErr := di.Do()
checkError(t, gotErr, nil, false)
checkCalls()
- wg.Done()
- }()
+ })
}
wg.Wait()
})
@@ -193,12 +191,10 @@ func() error { return errors.New("unreachable") },
var wg sync.WaitGroup
N := 10000
for range N {
- wg.Add(1)
- go func() {
+ wg.Go(func() {
gotErr := di.Do()
checkError(t, gotErr, tt.wantErr, false)
- wg.Done()
- }()
+ })
}
wg.Wait()
})
@@ -254,11 +250,9 @@ func TestDeferAfterDo(t *testing.T) {
const N = 10000
var wg sync.WaitGroup
for range N {
- wg.Add(1)
- go func() {
+ wg.Go(func() {
deferOnce()
- wg.Done()
- }()
+ })
}
if err := di.Do(); err != nil {
diff --git a/types/netmap/nodemut_test.go b/types/netmap/nodemut_test.go
index a03dee49c..1ae2ab1f9 100644
--- a/types/netmap/nodemut_test.go
+++ b/types/netmap/nodemut_test.go
@@ -34,7 +34,7 @@ func TestMapResponseContainsNonPatchFields(t *testing.T) {
return reflect.ValueOf(int64(1)).Convert(t)
case reflect.Slice:
return reflect.MakeSlice(t, 1, 1)
- case reflect.Ptr:
+ case reflect.Pointer:
return reflect.New(t.Elem())
case reflect.Map:
return reflect.MakeMap(t)
@@ -43,8 +43,7 @@ func TestMapResponseContainsNonPatchFields(t *testing.T) {
}
rt := reflect.TypeFor[tailcfg.MapResponse]()
- for i := range rt.NumField() {
- f := rt.Field(i)
+ for f := range rt.Fields() {
var want bool
switch f.Name {
diff --git a/types/persist/persist_test.go b/types/persist/persist_test.go
index b25af5a0b..33773013d 100644
--- a/types/persist/persist_test.go
+++ b/types/persist/persist_test.go
@@ -12,8 +12,8 @@
)
func fieldsOf(t reflect.Type) (fields []string) {
- for i := range t.NumField() {
- if name := t.Field(i).Name; name != "_" {
+ for field := range t.Fields() {
+ if name := field.Name; name != "_" {
fields = append(fields, name)
}
}
diff --git a/types/views/views.go b/types/views/views.go
index 4e17ac952..fe70e227f 100644
--- a/types/views/views.go
+++ b/types/views/views.go
@@ -968,8 +968,8 @@ func containsPointers(typ reflect.Type) bool {
if isWellKnownImmutableStruct(typ) {
return false
}
- for i := range typ.NumField() {
- if containsPointers(typ.Field(i).Type) {
+ for field := range typ.Fields() {
+ if containsPointers(field.Type) {
return true
}
}
diff --git a/util/dnsname/dnsname.go b/util/dnsname/dnsname.go
index 263c376aa..cf1ae6200 100644
--- a/util/dnsname/dnsname.go
+++ b/util/dnsname/dnsname.go
@@ -234,7 +234,7 @@ func ValidHostname(hostname string) error {
return err
}
- for _, label := range strings.Split(fqdn.WithoutTrailingDot(), ".") {
+ for label := range strings.SplitSeq(fqdn.WithoutTrailingDot(), ".") {
if err := ValidLabel(label); err != nil {
return err
}
diff --git a/util/goroutines/goroutines.go b/util/goroutines/goroutines.go
index fd0a4dd7e..f184fcd6c 100644
--- a/util/goroutines/goroutines.go
+++ b/util/goroutines/goroutines.go
@@ -52,7 +52,7 @@ func scrubHex(buf []byte) []byte {
in[0] = '?'
return
}
- v := []byte(fmt.Sprintf("v%d%%%d", len(saw)+1, u64%8))
+ v := fmt.Appendf(nil, "v%d%%%d", len(saw)+1, u64%8)
saw[inStr] = v
copy(in, v)
})
diff --git a/util/hashx/block512_test.go b/util/hashx/block512_test.go
index 91d5d9ee6..03c77eabb 100644
--- a/util/hashx/block512_test.go
+++ b/util/hashx/block512_test.go
@@ -47,7 +47,7 @@ type hasher interface {
func hashSuite(h hasher) {
for i := range 10 {
- for j := 0; j < 10; j++ {
+ for range 10 {
h.HashUint8(0x01)
h.HashUint8(0x23)
h.HashUint32(0x456789ab)
diff --git a/util/httphdr/httphdr.go b/util/httphdr/httphdr.go
index 01e8eddc6..852b3f5c7 100644
--- a/util/httphdr/httphdr.go
+++ b/util/httphdr/httphdr.go
@@ -44,7 +44,7 @@ func ParseRange(hdr string) (ranges []Range, ok bool) {
hdr = strings.Trim(hdr, ows) // per RFC 7230, section 3.2
units, elems, hasUnits := strings.Cut(hdr, "=")
elems = strings.TrimLeft(elems, ","+ows)
- for _, elem := range strings.Split(elems, ",") {
+ for elem := range strings.SplitSeq(elems, ",") {
elem = strings.Trim(elem, ows) // per RFC 7230, section 7
switch {
case strings.HasPrefix(elem, "-"): // i.e., "-" suffix-length
diff --git a/util/httpm/httpm_test.go b/util/httpm/httpm_test.go
index 4e7f7b5ab..4a36a38e1 100644
--- a/util/httpm/httpm_test.go
+++ b/util/httpm/httpm_test.go
@@ -27,7 +27,7 @@ func TestUsedConsistently(t *testing.T) {
cmd := exec.Command("git", "grep", "-l", "-F", "http.Method")
cmd.Dir = rootDir
matches, _ := cmd.Output()
- for _, fn := range strings.Split(strings.TrimSpace(string(matches)), "\n") {
+ for fn := range strings.SplitSeq(strings.TrimSpace(string(matches)), "\n") {
switch fn {
case "util/httpm/httpm.go", "util/httpm/httpm_test.go":
continue
diff --git a/util/linuxfw/fake.go b/util/linuxfw/fake.go
index 1886e2542..166d80401 100644
--- a/util/linuxfw/fake.go
+++ b/util/linuxfw/fake.go
@@ -9,6 +9,7 @@
"errors"
"fmt"
"os"
+ "slices"
"strconv"
"strings"
)
@@ -60,10 +61,8 @@ func (n *fakeIPTables) Append(table, chain string, args ...string) error {
func (n *fakeIPTables) Exists(table, chain string, args ...string) (bool, error) {
k := table + "/" + chain
if rules, ok := n.n[k]; ok {
- for _, rule := range rules {
- if rule == strings.Join(args, " ") {
- return true, nil
- }
+ if slices.Contains(rules, strings.Join(args, " ")) {
+ return true, nil
}
return false, nil
} else {
diff --git a/util/linuxfw/iptables.go b/util/linuxfw/iptables.go
index f054e7abe..3bd2c2886 100644
--- a/util/linuxfw/iptables.go
+++ b/util/linuxfw/iptables.go
@@ -21,8 +21,8 @@
func init() {
isNotExistError = func(err error) bool {
- var e *iptables.Error
- return errors.As(err, &e) && e.IsNotExist()
+ e, ok := errors.AsType[*iptables.Error](err)
+ return ok && e.IsNotExist()
}
}
diff --git a/util/linuxfw/nftables_for_svcs.go b/util/linuxfw/nftables_for_svcs.go
index c2425e2ff..35764a2bd 100644
--- a/util/linuxfw/nftables_for_svcs.go
+++ b/util/linuxfw/nftables_for_svcs.go
@@ -236,7 +236,7 @@ func portMapRule(t *nftables.Table, ch *nftables.Chain, tun string, targetIP net
// This metadata can then be used to find the rule.
// https://github.com/google/nftables/issues/48
func svcPortMapRuleMeta(svcName string, targetIP netip.Addr, pm PortMap) []byte {
- return []byte(fmt.Sprintf("svc:%s,targetIP:%s:matchPort:%v,targetPort:%v,proto:%v", svcName, targetIP.String(), pm.MatchPort, pm.TargetPort, pm.Protocol))
+ return fmt.Appendf(nil, "svc:%s,targetIP:%s:matchPort:%v,targetPort:%v,proto:%v", svcName, targetIP.String(), pm.MatchPort, pm.TargetPort, pm.Protocol)
}
func (n *nftablesRunner) findRuleByMetadata(t *nftables.Table, ch *nftables.Chain, meta []byte) (*nftables.Rule, error) {
@@ -305,5 +305,5 @@ func protoFromString(s string) (uint8, error) {
// This metadata can then be used to find the rule.
// https://github.com/google/nftables/issues/48
func svcRuleMeta(svcName string, origDst, dst netip.Addr) []byte {
- return []byte(fmt.Sprintf("svc:%s,VIP:%s,ClusterIP:%s", svcName, origDst.String(), dst.String()))
+ return fmt.Appendf(nil, "svc:%s,VIP:%s,ClusterIP:%s", svcName, origDst.String(), dst.String())
}
diff --git a/util/linuxfw/nftables_runner_test.go b/util/linuxfw/nftables_runner_test.go
index 8299a9cbd..19e869a04 100644
--- a/util/linuxfw/nftables_runner_test.go
+++ b/util/linuxfw/nftables_runner_test.go
@@ -1066,7 +1066,7 @@ func checkSNATRule_nft(t *testing.T, runner *nftablesRunner, fam nftables.TableF
if chain == nil {
t.Fatal("POSTROUTING chain does not exist")
}
- meta := []byte(fmt.Sprintf("dst:%s,src:%s", dst.String(), src.String()))
+ meta := fmt.Appendf(nil, "dst:%s,src:%s", dst.String(), src.String())
wantsRule := snatRule(chain.Table, chain, src, dst, meta)
checkRule(t, wantsRule, runner.conn)
}
diff --git a/util/pool/pool_test.go b/util/pool/pool_test.go
index ac7cf86be..ad509a563 100644
--- a/util/pool/pool_test.go
+++ b/util/pool/pool_test.go
@@ -94,12 +94,12 @@ func TestPool(t *testing.T) {
func TestTakeRandom(t *testing.T) {
p := Pool[int]{}
- for i := 0; i < 10; i++ {
+ for i := range 10 {
p.Add(i + 100)
}
seen := make(map[int]bool)
- for i := 0; i < 10; i++ {
+ for range 10 {
item, ok := p.TakeRandom()
if !ok {
t.Errorf("unexpected empty pool")
@@ -116,7 +116,7 @@ func TestTakeRandom(t *testing.T) {
t.Errorf("expected empty pool")
}
- for i := 0; i < 10; i++ {
+ for i := range 10 {
want := 100 + i
if !seen[want] {
t.Errorf("item %v not seen", want)
diff --git a/util/set/intset.go b/util/set/intset.go
index 04f614742..29a634516 100644
--- a/util/set/intset.go
+++ b/util/set/intset.go
@@ -152,7 +152,7 @@ func (s bitSet) values() iter.Seq[uint64] {
return func(yield func(uint64) bool) {
// Hyrum-proofing: randomly iterate in forwards or reverse.
if rand.Uint64()%2 == 0 {
- for i := 0; i < bits.UintSize; i++ {
+ for i := range bits.UintSize {
if s.contains(uint64(i)) && !yield(uint64(i)) {
return
}
diff --git a/util/singleflight/singleflight.go b/util/singleflight/singleflight.go
index 23cf7e21f..e6d859178 100644
--- a/util/singleflight/singleflight.go
+++ b/util/singleflight/singleflight.go
@@ -36,7 +36,7 @@
// A panicError is an arbitrary value recovered from a panic
// with the stack trace during the execution of given function.
type panicError struct {
- value interface{}
+ value any
stack []byte
}
@@ -45,7 +45,7 @@ func (p *panicError) Error() string {
return fmt.Sprintf("%v\n\n%s", p.value, p.stack)
}
-func newPanicError(v interface{}) error {
+func newPanicError(v any) error {
stack := debug.Stack()
// The first line of the stack trace is of the form "goroutine N [status]:"
diff --git a/util/singleflight/singleflight_test.go b/util/singleflight/singleflight_test.go
index 9f0ca7f1d..4e8500cc3 100644
--- a/util/singleflight/singleflight_test.go
+++ b/util/singleflight/singleflight_test.go
@@ -25,7 +25,7 @@
func TestDo(t *testing.T) {
var g Group[string, any]
- v, err, _ := g.Do("key", func() (interface{}, error) {
+ v, err, _ := g.Do("key", func() (any, error) {
return "bar", nil
})
if got, want := fmt.Sprintf("%v (%T)", v, v), "bar (string)"; got != want {
@@ -39,7 +39,7 @@ func TestDo(t *testing.T) {
func TestDoErr(t *testing.T) {
var g Group[string, any]
someErr := errors.New("Some error")
- v, err, _ := g.Do("key", func() (interface{}, error) {
+ v, err, _ := g.Do("key", func() (any, error) {
return nil, someErr
})
if err != someErr {
@@ -55,7 +55,7 @@ func TestDoDupSuppress(t *testing.T) {
var wg1, wg2 sync.WaitGroup
c := make(chan string, 1)
var calls int32
- fn := func() (interface{}, error) {
+ fn := func() (any, error) {
if atomic.AddInt32(&calls, 1) == 1 {
// First invocation.
wg1.Done()
@@ -72,9 +72,7 @@ func TestDoDupSuppress(t *testing.T) {
wg1.Add(1)
for range n {
wg1.Add(1)
- wg2.Add(1)
- go func() {
- defer wg2.Done()
+ wg2.Go(func() {
wg1.Done()
v, err, _ := g.Do("key", fn)
if err != nil {
@@ -84,7 +82,7 @@ func TestDoDupSuppress(t *testing.T) {
if s, _ := v.(string); s != "bar" {
t.Errorf("Do = %T %v; want %q", v, v, "bar")
}
- }()
+ })
}
wg1.Wait()
// At least one goroutine is in fn now and all of them have at
@@ -108,7 +106,7 @@ func TestForget(t *testing.T) {
)
go func() {
- g.Do("key", func() (i interface{}, e error) {
+ g.Do("key", func() (i any, e error) {
close(firstStarted)
<-unblockFirst
close(firstFinished)
@@ -119,7 +117,7 @@ func TestForget(t *testing.T) {
g.Forget("key")
unblockSecond := make(chan struct{})
- secondResult := g.DoChan("key", func() (i interface{}, e error) {
+ secondResult := g.DoChan("key", func() (i any, e error) {
<-unblockSecond
return 2, nil
})
@@ -127,7 +125,7 @@ func TestForget(t *testing.T) {
close(unblockFirst)
<-firstFinished
- thirdResult := g.DoChan("key", func() (i interface{}, e error) {
+ thirdResult := g.DoChan("key", func() (i any, e error) {
return 3, nil
})
@@ -141,7 +139,7 @@ func TestForget(t *testing.T) {
func TestDoChan(t *testing.T) {
var g Group[string, any]
- ch := g.DoChan("key", func() (interface{}, error) {
+ ch := g.DoChan("key", func() (any, error) {
return "bar", nil
})
@@ -160,7 +158,7 @@ func TestDoChan(t *testing.T) {
// See https://github.com/golang/go/issues/41133
func TestPanicDo(t *testing.T) {
var g Group[string, any]
- fn := func() (interface{}, error) {
+ fn := func() (any, error) {
panic("invalid memory address or nil pointer dereference")
}
@@ -197,7 +195,7 @@ func TestPanicDo(t *testing.T) {
func TestGoexitDo(t *testing.T) {
var g Group[string, any]
- fn := func() (interface{}, error) {
+ fn := func() (any, error) {
runtime.Goexit()
return nil, nil
}
@@ -238,7 +236,7 @@ func TestPanicDoChan(t *testing.T) {
}()
g := new(Group[string, any])
- ch := g.DoChan("", func() (interface{}, error) {
+ ch := g.DoChan("", func() (any, error) {
panic("Panicking in DoChan")
})
<-ch
@@ -283,7 +281,7 @@ func TestPanicDoSharedByDoChan(t *testing.T) {
defer func() {
recover()
}()
- g.Do("", func() (interface{}, error) {
+ g.Do("", func() (any, error) {
close(blocked)
<-unblock
panic("Panicking in Do")
@@ -291,7 +289,7 @@ func TestPanicDoSharedByDoChan(t *testing.T) {
}()
<-blocked
- ch := g.DoChan("", func() (interface{}, error) {
+ ch := g.DoChan("", func() (any, error) {
panic("DoChan unexpectedly executed callback")
})
close(unblock)
@@ -325,8 +323,7 @@ func TestPanicDoSharedByDoChan(t *testing.T) {
func TestDoChanContext(t *testing.T) {
t.Run("Basic", func(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
var g Group[string, int]
ch := g.DoChanContext(ctx, "key", func(_ context.Context) (int, error) {
@@ -337,8 +334,7 @@ func TestDoChanContext(t *testing.T) {
})
t.Run("DoesNotPropagateValues", func(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
key := new(int)
const value = "hello world"
@@ -364,8 +360,7 @@ func TestDoChanContext(t *testing.T) {
ctx1, cancel1 := context.WithCancel(context.Background())
defer cancel1()
- ctx2, cancel2 := context.WithCancel(context.Background())
- defer cancel2()
+ ctx2 := t.Context()
fn := func(ctx context.Context) (int, error) {
select {
diff --git a/util/slicesx/slicesx_test.go b/util/slicesx/slicesx_test.go
index d5c87a372..6b28c29b4 100644
--- a/util/slicesx/slicesx_test.go
+++ b/util/slicesx/slicesx_test.go
@@ -53,7 +53,7 @@ func TestShuffle(t *testing.T) {
}
var wasShuffled bool
- for try := 0; try < 10; try++ {
+ for range 10 {
shuffled := slices.Clone(sl)
Shuffle(shuffled)
if !reflect.DeepEqual(shuffled, sl) {
diff --git a/util/syspolicy/policytest/policytest.go b/util/syspolicy/policytest/policytest.go
index ef5ce889d..9879a0fd3 100644
--- a/util/syspolicy/policytest/policytest.go
+++ b/util/syspolicy/policytest/policytest.go
@@ -89,12 +89,7 @@ func (pc policyChanges) HasChanged(v pkey.Key) bool {
return ok
}
func (pc policyChanges) HasChangedAnyOf(keys ...pkey.Key) bool {
- for _, k := range keys {
- if pc.HasChanged(k) {
- return true
- }
- }
- return false
+ return slices.ContainsFunc(keys, pc.HasChanged)
}
const watchersKey = "_policytest_watchers"
diff --git a/util/topk/topk_test.go b/util/topk/topk_test.go
index 06656c420..7679f59a3 100644
--- a/util/topk/topk_test.go
+++ b/util/topk/topk_test.go
@@ -43,7 +43,7 @@ func TestTopK(t *testing.T) {
got []int
want = []int{5, 6, 7, 8, 9}
)
- for try := 0; try < 10; try++ {
+ for range 10 {
topk := NewWithParams[int](5, func(in []byte, val int) []byte {
return binary.LittleEndian.AppendUint64(in, uint64(val))
}, 4, 1000)
diff --git a/util/vizerror/vizerror.go b/util/vizerror/vizerror.go
index 479bd2de9..e0abe8f97 100644
--- a/util/vizerror/vizerror.go
+++ b/util/vizerror/vizerror.go
@@ -77,6 +77,5 @@ func WrapWithMessage(wrapped error, publicMsg string) error {
// As returns the first vizerror.Error in err's chain.
func As(err error) (e Error, ok bool) {
- ok = errors.As(err, &e)
- return
+ return errors.AsType[Error](err)
}
diff --git a/util/zstdframe/zstd_test.go b/util/zstdframe/zstd_test.go
index 302090b99..c006a06fd 100644
--- a/util/zstdframe/zstd_test.go
+++ b/util/zstdframe/zstd_test.go
@@ -128,7 +128,7 @@ func BenchmarkEncode(b *testing.B) {
b.Run(bb.name, func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(len(src)))
- for range b.N {
+ for b.Loop() {
dst = AppendEncode(dst[:0], src, bb.opts...)
}
})
@@ -153,7 +153,7 @@ func BenchmarkDecode(b *testing.B) {
b.Run(bb.name, func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(len(src)))
- for range b.N {
+ for b.Loop() {
dst = must.Get(AppendDecode(dst[:0], src, bb.opts...))
}
})
@@ -169,16 +169,14 @@ func BenchmarkEncodeParallel(b *testing.B) {
}
b.Run(coder.name, func(b *testing.B) {
b.ReportAllocs()
- for range b.N {
- var group sync.WaitGroup
- for j := 0; j < numCPU; j++ {
- group.Add(1)
- go func(j int) {
- defer group.Done()
+ for b.Loop() {
+ var wg sync.WaitGroup
+ for j := range numCPU {
+ wg.Go(func() {
dsts[j] = coder.appendEncode(dsts[j][:0], src)
- }(j)
+ })
}
- group.Wait()
+ wg.Wait()
}
})
}
@@ -194,16 +192,14 @@ func BenchmarkDecodeParallel(b *testing.B) {
}
b.Run(coder.name, func(b *testing.B) {
b.ReportAllocs()
- for range b.N {
- var group sync.WaitGroup
- for j := 0; j < numCPU; j++ {
- group.Add(1)
- go func(j int) {
- defer group.Done()
+ for b.Loop() {
+ var wg sync.WaitGroup
+ for j := range numCPU {
+ wg.Go(func() {
dsts[j] = must.Get(coder.appendDecode(dsts[j][:0], src))
- }(j)
+ })
}
- group.Wait()
+ wg.Wait()
}
})
}
diff --git a/version/cmdname.go b/version/cmdname.go
index 8a4040f97..5a0b84875 100644
--- a/version/cmdname.go
+++ b/version/cmdname.go
@@ -39,7 +39,7 @@ func cmdName(exe string) string {
}
// v is like:
// "path\ttailscale.com/cmd/tailscale\nmod\ttailscale.com\t(devel)\t\ndep\tgithub.com/apenwarr/fixconsole\tv0.0.0-20191012055117-5a9f6489cc29\th1:muXWUcay7DDy1/hEQWrYlBy+g0EuwT70sBHg65SeUc4=\ndep\tgithub....
- for _, line := range strings.Split(info, "\n") {
+ for line := range strings.SplitSeq(info, "\n") {
if goPkg, ok := strings.CutPrefix(line, "path\t"); ok { // like "tailscale.com/cmd/tailscale"
ret = path.Base(goPkg) // goPkg is always forward slashes; use path, not filepath
break
diff --git a/version/version_test.go b/version/version_test.go
index ebae7f177..42bcf2163 100644
--- a/version/version_test.go
+++ b/version/version_test.go
@@ -30,7 +30,7 @@ func readAlpineTag(t *testing.T, file string) string {
if err != nil {
t.Fatal(err)
}
- for _, line := range bytes.Split(f, []byte{'\n'}) {
+ for line := range bytes.SplitSeq(f, []byte{'\n'}) {
line = bytes.TrimSpace(line)
_, suf, ok := bytes.Cut(line, []byte("FROM alpine:"))
if !ok {
diff --git a/wgengine/filter/filter_test.go b/wgengine/filter/filter_test.go
index c588a506e..a3b9a8e00 100644
--- a/wgengine/filter/filter_test.go
+++ b/wgengine/filter/filter_test.go
@@ -751,13 +751,13 @@ func ports(s string) PortRange {
}
var fs, ls string
- i := strings.IndexByte(s, '-')
- if i == -1 {
+ before, after, ok := strings.Cut(s, "-")
+ if !ok {
fs = s
ls = fs
} else {
- fs = s[:i]
- ls = s[i+1:]
+ fs = before
+ ls = after
}
first, err := strconv.ParseInt(fs, 10, 16)
if err != nil {
diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go
index 1f02d84c7..78ffd0cd0 100644
--- a/wgengine/magicsock/magicsock.go
+++ b/wgengine/magicsock/magicsock.go
@@ -1474,8 +1474,7 @@ func (c *Conn) sendUDPBatch(addr epAddr, buffs [][]byte, offset int) (sent bool,
err = c.pconn4.WriteWireGuardBatchTo(buffs, addr, offset)
}
if err != nil {
- var errGSO neterror.ErrUDPGSODisabled
- if errors.As(err, &errGSO) {
+ if errGSO, ok := errors.AsType[neterror.ErrUDPGSODisabled](err); ok {
c.logf("magicsock: %s", errGSO.Error())
err = errGSO.RetryErr
} else {
diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go
index dfd9d395d..7a8a6374c 100644
--- a/wgengine/magicsock/magicsock_test.go
+++ b/wgengine/magicsock/magicsock_test.go
@@ -2007,7 +2007,7 @@ func TestStressSetNetworkMap(t *testing.T) {
const iters = 1000 // approx 0.5s on an m1 mac
for range iters {
- for j := 0; j < npeers; j++ {
+ for j := range npeers {
// Randomize which peers are present.
if prng.Int()&1 == 0 {
present[j] = !present[j]
@@ -2196,7 +2196,7 @@ func newWireguard(t *testing.T, uapi string, aips []netip.Prefix) (*device.Devic
if err != nil {
t.Fatal(err)
}
- for _, line := range strings.Split(s, "\n") {
+ for line := range strings.SplitSeq(s, "\n") {
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
@@ -4311,7 +4311,7 @@ func TestRotateDiscoKeyMultipleTimes(t *testing.T) {
keys := make([]key.DiscoPublic, 0, 5)
keys = append(keys, c.discoAtomic.Public())
- for i := 0; i < 4; i++ {
+ for i := range 4 {
c.RotateDiscoKey()
newKey := c.discoAtomic.Public()
diff --git a/wgengine/pendopen.go b/wgengine/pendopen.go
index 77cb4a7b9..116deffa9 100644
--- a/wgengine/pendopen.go
+++ b/wgengine/pendopen.go
@@ -102,7 +102,7 @@ func (e *userspaceEngine) trackOpenPreFilterIn(pp *packet.Parsed, t *tstun.Wrapp
canonicalIPs = sync.OnceValue(func() (checkIPFunc func(netip.Addr) bool) {
// https://bgp.he.net/AS41231#_prefixes
t := &bart.Table[bool]{}
- for _, s := range strings.Fields(`
+ for s := range strings.FieldsSeq(`
91.189.89.0/24
91.189.91.0/24
91.189.92.0/24
diff --git a/wgengine/router/osrouter/router_linux_test.go b/wgengine/router/osrouter/router_linux_test.go
index bae997e33..8c2514eb5 100644
--- a/wgengine/router/osrouter/router_linux_test.go
+++ b/wgengine/router/osrouter/router_linux_test.go
@@ -1073,11 +1073,9 @@ func (o *fakeOS) run(args ...string) error {
switch args[2] {
case "add":
- for _, el := range *ls {
- if el == rest {
- o.t.Errorf("can't add %q, already present", rest)
- return errors.New("already exists")
- }
+ if slices.Contains(*ls, rest) {
+ o.t.Errorf("can't add %q, already present", rest)
+ return errors.New("already exists")
}
*ls = append(*ls, rest)
sort.Strings(*ls)
diff --git a/wgengine/router/osrouter/runner.go b/wgengine/router/osrouter/runner.go
index bdc710a8d..82b2680e6 100644
--- a/wgengine/router/osrouter/runner.go
+++ b/wgengine/router/osrouter/runner.go
@@ -10,6 +10,7 @@
"fmt"
"os"
"os/exec"
+ "slices"
"strconv"
"strings"
"syscall"
@@ -42,8 +43,7 @@ func errCode(err error) int {
if err == nil {
return 0
}
- var e *exec.ExitError
- if ok := errors.As(err, &e); ok {
+ if e, ok := errors.AsType[*exec.ExitError](err); ok {
return e.ExitCode()
}
s := err.Error()
@@ -96,12 +96,7 @@ func newRunGroup(okCode []int, runner commandRunner) *runGroup {
func (rg *runGroup) okCode(err error) bool {
got := errCode(err)
- for _, want := range rg.OkCode {
- if got == want {
- return true
- }
- }
- return false
+ return slices.Contains(rg.OkCode, got)
}
func (rg *runGroup) Output(args ...string) []byte {
diff --git a/wgengine/router/router_test.go b/wgengine/router/router_test.go
index 28750e115..f6176f1d0 100644
--- a/wgengine/router/router_test.go
+++ b/wgengine/router/router_test.go
@@ -19,8 +19,8 @@ func TestConfigEqual(t *testing.T) {
}
configType := reflect.TypeFor[Config]()
configFields := []string{}
- for i := range configType.NumField() {
- configFields = append(configFields, configType.Field(i).Name)
+ for field := range configType.Fields() {
+ configFields = append(configFields, field.Name)
}
if !reflect.DeepEqual(configFields, testedFields) {
t.Errorf("Config.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
diff --git a/wgengine/wgcfg/config_test.go b/wgengine/wgcfg/config_test.go
index b15b8cbf5..7059b17b2 100644
--- a/wgengine/wgcfg/config_test.go
+++ b/wgengine/wgcfg/config_test.go
@@ -12,8 +12,7 @@
// that might get added in the future.
func TestConfigEqual(t *testing.T) {
rt := reflect.TypeFor[Config]()
- for i := range rt.NumField() {
- sf := rt.Field(i)
+ for sf := range rt.Fields() {
switch sf.Name {
case "Name", "NodeID", "PrivateKey", "MTU", "Addresses", "DNS", "Peers",
"NetworkLogging":
@@ -28,8 +27,7 @@ func TestConfigEqual(t *testing.T) {
// that might get added in the future.
func TestPeerEqual(t *testing.T) {
rt := reflect.TypeFor[Peer]()
- for i := range rt.NumField() {
- sf := rt.Field(i)
+ for sf := range rt.Fields() {
switch sf.Name {
case "PublicKey", "DiscoKey", "AllowedIPs", "IsJailed",
"PersistentKeepalive", "V4MasqAddr", "V6MasqAddr", "WGEndpoint":
diff --git a/wif/wif.go b/wif/wif.go
index bb2e760f2..bc479fad1 100644
--- a/wif/wif.go
+++ b/wif/wif.go
@@ -190,8 +190,7 @@ func acquireAWSWebIdentityToken(ctx context.Context, audience string) (string, e
out, err := stsClient.GetWebIdentityToken(ctx, in)
if err != nil {
- var apiErr smithy.APIError
- if errors.As(err, &apiErr) {
+ if apiErr, ok := errors.AsType[smithy.APIError](err); ok {
return "", fmt.Errorf("aws sts:GetWebIdentityToken failed (%s): %w", apiErr.ErrorCode(), err)
}
return "", fmt.Errorf("aws sts:GetWebIdentityToken failed: %w", err)