all: update to Go 1.20, use strings.CutPrefix/Suffix instead of our fork

Updates #7123
Updates #5309

Change-Id: I90bcd87a2fb85a91834a0dd4be6e03db08438672
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/7155/head
Brad Fitzpatrick 1 year ago committed by Brad Fitzpatrick
parent 623176ebc9
commit b1248442c3

@ -45,14 +45,17 @@ jobs:
vet:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Check out code
uses: actions/checkout@v3
go-version-file: go.mod
- name: Run go vet
run: go vet ./...
- uses: k0kubun/action-slack@v2.0.0
with:
payload: |
@ -79,13 +82,14 @@ jobs:
goarch: 386
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
go-version-file: go.mod
- name: Check out code
uses: actions/checkout@v3
- name: Install staticcheck
run: "GOBIN=~/.local/bin go install honnef.co/go/tools/cmd/staticcheck"

@ -31,7 +31,7 @@
# $ docker exec tailscaled tailscale status
FROM golang:1.19-alpine AS build-env
FROM golang:1.20-alpine AS build-env
WORKDIR /go/src/tailscale

@ -12,13 +12,17 @@ tidy:
./tool/go mod tidy
updatedeps:
./tool/go run github.com/tailscale/depaware --update \
# depaware (via x/tools/go/packages) shells back to "go", so make sure the "go"
# it finds in its $$PATH is the right one.
PATH="$$(./tool/go env GOROOT)/bin:$$PATH" ./tool/go run github.com/tailscale/depaware --update \
tailscale.com/cmd/tailscaled \
tailscale.com/cmd/tailscale \
tailscale.com/cmd/derper
depaware:
./tool/go run github.com/tailscale/depaware --check \
# depaware (via x/tools/go/packages) shells back to "go", so make sure the "go"
# it finds in its $$PATH is the right one.
PATH="$$(./tool/go env GOROOT)/bin:$$PATH" ./tool/go run github.com/tailscale/depaware --check \
tailscale.com/cmd/tailscaled \
tailscale.com/cmd/tailscale \
tailscale.com/cmd/derper

@ -37,7 +37,7 @@ not open source.
## Building
We always require the latest Go release, currently Go 1.19. (While we build
We always require the latest Go release, currently Go 1.20. (While we build
releases with our [Go fork](https://github.com/tailscale/go/), its use is not
required.)

@ -1,10 +1,10 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !go1.19
//go:build !go1.20
package tailscale
func init() {
you_need_Go_1_19_to_compile_Tailscale()
you_need_Go_1_20_to_compile_Tailscale()
}

@ -82,7 +82,6 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
tailscale.com/util/lineread from tailscale.com/hostinfo+
tailscale.com/util/mak from tailscale.com/syncs
tailscale.com/util/singleflight from tailscale.com/net/dnscache
tailscale.com/util/strs from tailscale.com/hostinfo+
tailscale.com/util/vizerror from tailscale.com/tsweb
W 💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
tailscale.com/version from tailscale.com/derp+
@ -97,7 +96,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
golang.org/x/crypto/chacha20poly1305 from crypto/tls
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
golang.org/x/crypto/curve25519 from crypto/tls+
golang.org/x/crypto/curve25519 from golang.org/x/crypto/nacl/box+
golang.org/x/crypto/hkdf from crypto/tls
golang.org/x/crypto/nacl/box from tailscale.com/types/key
golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box
@ -135,6 +134,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
crypto/cipher from crypto/aes+
crypto/des from crypto/tls+
crypto/dsa from crypto/x509
crypto/ecdh from crypto/ecdsa+
crypto/ecdsa from crypto/tls+
crypto/ed25519 from crypto/tls+
crypto/elliptic from crypto/ecdsa+

@ -16,7 +16,6 @@ import (
"tailscale.com/derp/derphttp"
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/util/strs"
)
func startMesh(s *derp.Server) error {
@ -50,7 +49,7 @@ func startMeshWithHost(s *derp.Server, host string) error {
}
var d net.Dialer
var r net.Resolver
if base, ok := strs.CutSuffix(host, ".tailscale.com"); ok && port == "443" {
if base, ok := strings.CutSuffix(host, ".tailscale.com"); ok && port == "443" {
subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
vpcHost := base + "-vpc.tailscale.com"

@ -39,7 +39,6 @@ import (
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/util/must"
"tailscale.com/util/strs"
)
var debugCmd = &ffcli.Command{
@ -259,7 +258,7 @@ func runDebug(ctx context.Context, args []string) error {
e.Encode(wfs)
return nil
}
if name, ok := strs.CutPrefix(debugArgs.file, "delete:"); ok {
if name, ok := strings.CutPrefix(debugArgs.file, "delete:"); ok {
return localClient.DeleteWaitingFile(ctx, name)
}
rc, size, err := localClient.GetWaitingFile(ctx, debugArgs.file)

@ -31,7 +31,6 @@ import (
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/util/quarantine"
"tailscale.com/util/strs"
"tailscale.com/version"
)
@ -90,7 +89,7 @@ func runCp(ctx context.Context, args []string) error {
return errors.New("usage: tailscale file cp <files...> <target>:")
}
files, target := args[:len(args)-1], args[len(args)-1]
target, ok := strs.CutSuffix(target, ":")
target, ok := strings.CutSuffix(target, ":")
if !ok {
return fmt.Errorf("final argument to 'tailscale file cp' must end in colon")
}

@ -34,7 +34,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/types/preftype"
"tailscale.com/util/strs"
"tailscale.com/version"
"tailscale.com/version/distro"
)
@ -174,7 +173,7 @@ type upArgsT struct {
func (a upArgsT) getAuthKey() (string, error) {
v := a.authKeyOrFile
if file, ok := strs.CutPrefix(v, "file:"); ok {
if file, ok := strings.CutPrefix(v, "file:"); ok {
b, err := os.ReadFile(file)
if err != nil {
return "", err

@ -115,7 +115,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/util/must from tailscale.com/cmd/tailscale/cli
tailscale.com/util/quarantine from tailscale.com/cmd/tailscale/cli
tailscale.com/util/singleflight from tailscale.com/net/dnscache
tailscale.com/util/strs from tailscale.com/hostinfo+
💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
@ -127,7 +126,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
golang.org/x/crypto/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
golang.org/x/crypto/curve25519 from crypto/tls+
golang.org/x/crypto/curve25519 from golang.org/x/crypto/nacl/box+
golang.org/x/crypto/hkdf from crypto/tls+
golang.org/x/crypto/nacl/box from tailscale.com/types/key
golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box
@ -170,6 +169,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
crypto/cipher from crypto/aes+
crypto/des from crypto/tls+
crypto/dsa from crypto/x509
crypto/ecdh from crypto/ecdsa+
crypto/ecdsa from crypto/tls+
crypto/ed25519 from crypto/tls+
crypto/elliptic from crypto/ecdsa+

@ -302,7 +302,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/util/racebuild from tailscale.com/logpolicy
tailscale.com/util/set from tailscale.com/health+
tailscale.com/util/singleflight from tailscale.com/control/controlclient+
tailscale.com/util/strs from tailscale.com/hostinfo+
tailscale.com/util/systemd from tailscale.com/control/controlclient+
tailscale.com/util/uniq from tailscale.com/wgengine/magicsock+
tailscale.com/util/vizerror from tailscale.com/tsweb
@ -331,7 +330,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
golang.org/x/crypto/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
golang.org/x/crypto/curve25519 from crypto/tls+
golang.org/x/crypto/curve25519 from github.com/tailscale/golang-x-crypto/ssh+
LD golang.org/x/crypto/ed25519 from golang.org/x/crypto/ssh+
golang.org/x/crypto/hkdf from crypto/tls+
golang.org/x/crypto/nacl/box from tailscale.com/types/key
@ -381,6 +380,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
crypto/cipher from crypto/aes+
crypto/des from crypto/tls+
crypto/dsa from crypto/x509+
crypto/ecdh from crypto/ecdsa+
crypto/ecdsa from crypto/tls+
crypto/ed25519 from crypto/tls+
crypto/elliptic from crypto/ecdsa+

@ -1,10 +1,10 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !go1.19
//go:build !go1.20
package main
func init() {
you_need_Go_1_19_to_compile_Tailscale()
you_need_Go_1_20_to_compile_Tailscale()
}

@ -23,6 +23,7 @@ import (
"net/netip"
"net/url"
"runtime"
"strings"
"sync"
"time"
@ -37,7 +38,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/logger"
"tailscale.com/util/strs"
)
// Client is a DERP-over-HTTP client.
@ -1028,7 +1028,7 @@ var ErrClientClosed = errors.New("derphttp.Client closed")
func parseMetaCert(certs []*x509.Certificate) (serverPub key.NodePublic, serverProtoVersion int) {
for _, cert := range certs {
// Look for derpkey prefix added by initMetacert() on the server side.
if pubHex, ok := strs.CutPrefix(cert.Subject.CommonName, "derpkey"); ok {
if pubHex, ok := strings.CutPrefix(cert.Subject.CommonName, "derpkey"); ok {
var err error
serverPub, err = key.ParseNodePublicUntyped(mem.S(pubHex))
if err == nil && cert.SerialNumber.BitLen() <= 8 { // supports up to version 255

@ -7,11 +7,11 @@ import (
"fmt"
"net/netip"
"reflect"
"strings"
"testing"
"go4.org/mem"
"tailscale.com/types/key"
"tailscale.com/util/strs"
)
func TestMarshalAndParse(t *testing.T) {
@ -71,7 +71,7 @@ func TestMarshalAndParse(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
foo := []byte("foo")
got := string(tt.m.AppendMarshal(foo))
got, ok := strs.CutPrefix(got, "foo")
got, ok := strings.CutPrefix(got, "foo")
if !ok {
t.Fatalf("didn't start with foo: got %q", got)
}

@ -1,6 +1,6 @@
module tailscale.com
go 1.19
go 1.20
require (
filippo.io/mkcert v1.4.3
@ -77,11 +77,11 @@ require (
golang.org/x/sys v0.4.0
golang.org/x/term v0.4.0
golang.org/x/time v0.0.0-20220609170525-579cf78fd858
golang.org/x/tools v0.2.0
golang.org/x/tools v0.4.1-0.20221208213631-3f74d914ae6d
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
golang.zx2c4.com/wireguard/windows v0.5.3
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0
honnef.co/go/tools v0.4.0-0.dev.0.20220517111757-f4a2f64ce238
honnef.co/go/tools v0.4.0-0.dev.0.20230130122044-c30b15588105
inet.af/peercred v0.0.0-20210906144145-0893ea02156a
inet.af/wf v0.0.0-20220728202103-50d96caab2f6
k8s.io/api v0.25.0
@ -300,9 +300,9 @@ require (
github.com/yeya24/promlinter v0.1.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb // indirect
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/text v0.6.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect

@ -1348,8 +1348,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb h1:fP6C8Xutcp5AlakmT/SkQot0pMicROAsEX7OfNPuG10=
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE=
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
@ -1379,8 +1379,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1719,8 +1719,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8-0.20211102182255-bb4add04ddef/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.4.1-0.20221208213631-3f74d914ae6d h1:9ZNWAi4CYhNv60mXGgAncgq7SGc5qa7C8VZV8Tg7Ggs=
golang.org/x/tools v0.4.1-0.20221208213631-3f74d914ae6d/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1926,8 +1926,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
honnef.co/go/tools v0.4.0-0.dev.0.20220517111757-f4a2f64ce238 h1:8Vr1KP9OTjoKQSSeLefzibQgDV4s2ujJElKHqMi7nsA=
honnef.co/go/tools v0.4.0-0.dev.0.20220517111757-f4a2f64ce238/go.mod h1:DCQzo6aCmhYDJH+We7BIU38vNvVkaOKa6s57pewKdvI=
honnef.co/go/tools v0.4.0-0.dev.0.20230130122044-c30b15588105 h1:2OzOQ+1scFmv2dt7x+wNxgikA/Rn2qKrvc/CJCVuAJM=
honnef.co/go/tools v0.4.0-0.dev.0.20230130122044-c30b15588105/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=

@ -13,7 +13,6 @@ import (
"golang.org/x/sys/unix"
"tailscale.com/types/ptr"
"tailscale.com/util/lineread"
"tailscale.com/util/strs"
"tailscale.com/version/distro"
)
@ -73,7 +72,7 @@ func linuxDeviceModel() string {
func getQnapQtsVersion(versionInfo string) string {
for _, field := range strings.Fields(versionInfo) {
if suffix, ok := strs.CutPrefix(field, "QTSFW_"); ok {
if suffix, ok := strings.CutPrefix(field, "QTSFW_"); ok {
return suffix
}
}

@ -48,7 +48,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/util/clientmetric"
"tailscale.com/util/multierr"
"tailscale.com/util/strs"
"tailscale.com/wgengine"
"tailscale.com/wgengine/filter"
)
@ -164,7 +163,7 @@ func (s *peerAPIServer) hasFilesWaiting() bool {
if strings.HasSuffix(name, partialSuffix) {
continue
}
if name, ok := strs.CutSuffix(name, deletedSuffix); ok { // for Windows + tests
if name, ok := strings.CutSuffix(name, deletedSuffix); ok { // for Windows + tests
// After we're done looping over files, then try
// to delete this file. Don't do it proactively,
// as the OS may return "foo.jpg.deleted" before "foo.jpg"
@ -223,7 +222,7 @@ func (s *peerAPIServer) WaitingFiles() (ret []apitype.WaitingFile, err error) {
if strings.HasSuffix(name, partialSuffix) {
continue
}
if name, ok := strs.CutSuffix(name, deletedSuffix); ok { // for Windows + tests
if name, ok := strings.CutSuffix(name, deletedSuffix); ok { // for Windows + tests
if deleted == nil {
deleted = map[string]bool{}
}
@ -946,7 +945,7 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
return
}
rawPath := r.URL.EscapedPath()
suffix, ok := strs.CutPrefix(rawPath, "/v0/put/")
suffix, ok := strings.CutPrefix(rawPath, "/v0/put/")
if !ok {
http.Error(w, "misconfigured internals", 500)
return

@ -10,6 +10,7 @@ import (
"math/rand"
"net/netip"
"runtime"
"strings"
"time"
"golang.org/x/exp/slices"
@ -18,7 +19,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/util/clientmetric"
"tailscale.com/util/strs"
"tailscale.com/util/winutil"
"tailscale.com/version"
)
@ -520,7 +520,7 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, goos stri
}
}
if pm.currentProfile == nil {
if suf, ok := strs.CutPrefix(string(stateKey), "user-"); ok {
if suf, ok := strings.CutPrefix(string(stateKey), "user-"); ok {
pm.currentUserID = ipn.WindowsUserID(suf)
}
pm.NewProfile()

@ -31,7 +31,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/util/mak"
"tailscale.com/util/strs"
"tailscale.com/version"
)
@ -552,7 +551,7 @@ func expandProxyArg(s string) (targetURL string, insecureSkipVerify bool) {
if strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") {
return s, false
}
if rest, ok := strs.CutPrefix(s, "https+insecure://"); ok {
if rest, ok := strings.CutPrefix(s, "https+insecure://"); ok {
return "https://" + rest, true
}
if allNumeric(s) {

@ -8,9 +8,9 @@ package localapi
import (
"fmt"
"net/http"
"strings"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/util/strs"
)
func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) {
@ -18,7 +18,7 @@ func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) {
http.Error(w, "cert access denied", http.StatusForbidden)
return
}
domain, ok := strs.CutPrefix(r.URL.Path, "/localapi/v0/cert/")
domain, ok := strings.CutPrefix(r.URL.Path, "/localapi/v0/cert/")
if !ok {
http.Error(w, "internal handler config wired wrong", 500)
return

@ -43,7 +43,6 @@ import (
"tailscale.com/util/clientmetric"
"tailscale.com/util/httpm"
"tailscale.com/util/mak"
"tailscale.com/util/strs"
"tailscale.com/version"
)
@ -213,7 +212,7 @@ func handlerForPath(urlPath string) (h localAPIHandler, ok bool) {
if urlPath == "/" {
return (*Handler).serveLocalAPIRoot, true
}
suff, ok := strs.CutPrefix(urlPath, "/localapi/v0/")
suff, ok := strings.CutPrefix(urlPath, "/localapi/v0/")
if !ok {
// Currently all LocalAPI methods start with "/localapi/v0/" to signal
// to people that they're not necessarily stable APIs. In practice we'll
@ -886,7 +885,7 @@ func (h *Handler) serveFiles(w http.ResponseWriter, r *http.Request) {
http.Error(w, "file access denied", http.StatusForbidden)
return
}
suffix, ok := strs.CutPrefix(r.URL.EscapedPath(), "/localapi/v0/files/")
suffix, ok := strings.CutPrefix(r.URL.EscapedPath(), "/localapi/v0/files/")
if !ok {
http.Error(w, "misconfigured", http.StatusInternalServerError)
return
@ -1008,7 +1007,7 @@ func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
return
}
upath, ok := strs.CutPrefix(r.URL.EscapedPath(), "/localapi/v0/file-put/")
upath, ok := strings.CutPrefix(r.URL.EscapedPath(), "/localapi/v0/file-put/")
if !ok {
http.Error(w, "misconfigured", http.StatusInternalServerError)
return
@ -1448,7 +1447,7 @@ func (h *Handler) serveProfiles(w http.ResponseWriter, r *http.Request) {
http.Error(w, "profiles access denied", http.StatusForbidden)
return
}
suffix, ok := strs.CutPrefix(r.URL.EscapedPath(), "/localapi/v0/profiles/")
suffix, ok := strings.CutPrefix(r.URL.EscapedPath(), "/localapi/v0/profiles/")
if !ok {
http.Error(w, "misconfigured", http.StatusInternalServerError)
return

@ -13,8 +13,6 @@ import (
"sort"
"strings"
"sync"
"tailscale.com/util/strs"
)
// dohOfIP maps from public DNS IPs to their DoH base URL.
@ -82,7 +80,7 @@ func DoHIPsOfBase(dohBase string) []netip.Addr {
if s := dohIPsOfBase[dohBase]; len(s) > 0 {
return s
}
if hexStr, ok := strs.CutPrefix(dohBase, "https://dns.nextdns.io/"); ok {
if hexStr, ok := strings.CutPrefix(dohBase, "https://dns.nextdns.io/"); ok {
// The path is of the form /<profile-hex>[/<hostname>/<model>/<device id>...]
// or /<profile-hex>?<query params>
// but only the <profile-hex> is required. Ignore the rest:

@ -20,7 +20,6 @@ import (
"strings"
"tailscale.com/util/dnsname"
"tailscale.com/util/strs"
)
// Path is the canonical location of resolv.conf.
@ -69,7 +68,7 @@ func Parse(r io.Reader) (*Config, error) {
line, _, _ = strings.Cut(line, "#") // remove any comments
line = strings.TrimSpace(line)
if s, ok := strs.CutPrefix(line, "nameserver"); ok {
if s, ok := strings.CutPrefix(line, "nameserver"); ok {
nameserver := strings.TrimSpace(s)
if len(nameserver) == len(s) {
return nil, fmt.Errorf("missing space after \"nameserver\" in %q", line)
@ -82,7 +81,7 @@ func Parse(r io.Reader) (*Config, error) {
continue
}
if s, ok := strs.CutPrefix(line, "search"); ok {
if s, ok := strings.CutPrefix(line, "search"); ok {
domains := strings.TrimSpace(s)
if len(domains) == len(s) {
// No leading space?!

@ -33,7 +33,6 @@ import (
"tailscale.com/util/clientmetric"
"tailscale.com/util/cloudenv"
"tailscale.com/util/dnsname"
"tailscale.com/util/strs"
"tailscale.com/wgengine/monitor"
)
@ -1215,7 +1214,7 @@ func (r *Resolver) respond(query []byte) ([]byte, error) {
// unARPA maps from "4.4.8.8.in-addr.arpa." to "8.8.4.4", etc.
func unARPA(a string) (ipStr string, ok bool) {
const suf4 = ".in-addr.arpa."
if s, ok := strs.CutSuffix(a, suf4); ok {
if s, ok := strings.CutSuffix(a, suf4); ok {
// Parse and reverse octets.
ip, err := netip.ParseAddr(s)
if err != nil || !ip.Is4() {

@ -22,7 +22,6 @@ import (
"tailscale.com/net/stun"
"tailscale.com/net/stun/stuntest"
"tailscale.com/tailcfg"
"tailscale.com/util/strs"
)
func TestHairpinSTUN(t *testing.T) {
@ -621,7 +620,7 @@ func TestLogConciseReport(t *testing.T) {
var buf bytes.Buffer
c := &Client{Logf: func(f string, a ...any) { fmt.Fprintf(&buf, f, a...) }}
c.logConciseReport(tt.r, dm)
if got, ok := strs.CutPrefix(buf.String(), "[v1] report: "); !ok {
if got, ok := strings.CutPrefix(buf.String(), "[v1] report: "); !ok {
t.Errorf("unexpected result.\n got: %#q\nwant: %#q\n", got, tt.want)
}
})

@ -41,7 +41,6 @@ import (
"tailscale.com/tempfork/gliderlabs/ssh"
"tailscale.com/types/logger"
"tailscale.com/util/lineread"
"tailscale.com/util/strs"
"tailscale.com/version/distro"
)
@ -653,7 +652,7 @@ func pathFromPAMEnvLine(line []byte, u *user.User) (path string) {
return ""
}
rest := strings.TrimSpace(strings.TrimPrefix(string(line), "PATH"))
if quoted, ok := strs.CutPrefix(rest, "DEFAULT="); ok {
if quoted, ok := strings.CutPrefix(rest, "DEFAULT="); ok {
if path, err := strconv.Unquote(quoted); err == nil {
return expandDefaultPathTmpl(path, u)
}

@ -42,7 +42,6 @@ import (
"tailscale.com/util/cibuild"
"tailscale.com/util/lineread"
"tailscale.com/util/must"
"tailscale.com/util/strs"
"tailscale.com/version/distro"
"tailscale.com/wgengine"
)
@ -286,7 +285,7 @@ func (ts *localState) WhoIs(ipp netip.AddrPort) (n *tailcfg.Node, u tailcfg.User
func (ts *localState) DoNoiseRequest(req *http.Request) (*http.Response, error) {
rec := httptest.NewRecorder()
k, ok := strs.CutPrefix(req.URL.Path, "/ssh-action/")
k, ok := strings.CutPrefix(req.URL.Path, "/ssh-action/")
if !ok {
rec.WriteHeader(http.StatusNotFound)
}

@ -31,7 +31,6 @@ import (
"tailscale.com/metrics"
"tailscale.com/net/tsaddr"
"tailscale.com/types/logger"
"tailscale.com/util/strs"
"tailscale.com/util/vizerror"
"tailscale.com/version"
)
@ -753,7 +752,7 @@ func structTypeSortedFields(t reflect.Type) []sortedStructField {
// removed.
func removeTypePrefixes(s string) string {
for _, prefix := range prefixesToTrim {
if trimmed, ok := strs.CutPrefix(s, prefix); ok {
if trimmed, ok := strings.CutPrefix(s, prefix); ok {
return trimmed
}
}

@ -1001,11 +1001,11 @@ func FuzzTime(f *testing.F) {
) {
t1 := time.Unix(s1, ns1)
if loc1 {
t1.In(time.FixedZone(name1, off1))
_ = t1.In(time.FixedZone(name1, off1))
}
t2 := time.Unix(s2, ns2)
if loc2 {
t2.In(time.FixedZone(name2, off2))
_ = t2.In(time.FixedZone(name2, off2))
}
got := Hash(&t1) == Hash(&t2)
want := t1.Format(time.RFC3339Nano) == t2.Format(time.RFC3339Nano)

@ -1,35 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package strs contains string-related utility funcs.
package strs
import "strings"
// CutPrefix returns s without the provided leading prefix string
// and reports whether it found the prefix.
// If s doesn't start with prefix, CutPrefix returns s, false.
// If prefix is the empty string, CutPrefix returns s, true.
//
// TODO: remove this once Go 1.20 is out with it.
// See https://github.com/tailscale/tailscale/issues/5309
func CutPrefix(s, prefix string) (after string, found bool) {
if !strings.HasPrefix(s, prefix) {
return s, false
}
return s[len(prefix):], true
}
// CutSuffix returns s without the provided ending suffix string
// and reports whether it found the suffix.
// If s doesn't end with suffix, CutSuffix returns s, false.
// If suffix is the empty string, CutSuffix returns s, true.
//
// See https://github.com/tailscale/tailscale/issues/5309
// TODO: remove this once Go 1.20 is out with it.
func CutSuffix(s, suffix string) (before string, found bool) {
if !strings.HasSuffix(s, suffix) {
return s, false
}
return s[:len(s)-len(suffix)], true
}

@ -1,29 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package strs
import "testing"
func TestCut(t *testing.T) {
tests := []struct {
fn func(string, string) (string, bool)
in1, in2 string
want string
wantOK bool
}{
{CutPrefix, "foo", "fo", "o", true},
{CutPrefix, "bar", "fo", "bar", false},
{CutSuffix, "foo", "o", "fo", true},
{CutSuffix, "bar", "fo", "bar", false},
}
for i, tt := range tests {
got, gotOK := tt.fn(tt.in1, tt.in2)
if got != tt.want {
t.Errorf("%d. got %q; want %q", i, got, tt.want)
}
if gotOK != tt.wantOK {
t.Errorf("%d. got %v; want %v", i, gotOK, tt.wantOK)
}
}
}

@ -14,8 +14,6 @@ import (
"path"
"path/filepath"
"strings"
"tailscale.com/util/strs"
)
// CmdName returns either the base name of the current binary
@ -42,7 +40,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") {
if goPkg, ok := strs.CutPrefix(line, "path\t"); ok { // like "tailscale.com/cmd/tailscale"
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
}

Loading…
Cancel
Save