From 2594ccfe2ddf18a2db59c2072ab40a314ad0643e Mon Sep 17 00:00:00 2001 From: Ehab Younes Date: Wed, 14 Oct 2026 11:28:51 +0000 Subject: [PATCH] fix: repair fork CI and stop false-green test runs Upgrade AWS S3 dependencies and Staticcheck so the fork builds, and refresh generated files and depaware inventories. Make testwrapper fail on build, setup, and package failures, including tests that time out without a final event, while still retrying marked flaky tests. Print each package result once, and keep timeout panics that go test attributes to an already-passed test. Run the Windows tests through testwrapper so marked flaky tests are retried there too. Fuzz this fork's own targets with ClusterFuzzLite instead of building upstream through OSS-Fuzz. Fix license headers and the empty-interval connstats assertion, upgrade artifact uploads, restrict job permissions, and allow the upstream-only VM job to be skipped. --- .clusterfuzzlite/Dockerfile | 7 + .clusterfuzzlite/build.sh | 21 ++ .clusterfuzzlite/project.yaml | 1 + .github/workflows/test.yml | 90 +++----- cmd/derper/depaware.txt | 1 - cmd/tailscale/depaware.txt | 3 +- cmd/tailscaled/depaware.txt | 19 +- cmd/testwrapper/testwrapper.go | 76 +++++-- cmd/testwrapper/testwrapper_test.go | 213 ++++++++++++++++++ derp/dropreason_string.go | 5 +- go.mod | 19 +- go.sum | 45 ++-- net/connstats/stats_test.go | 5 + net/portmapper/pmpresultcode_string.go | 5 +- net/sockstats/label_string.go | 5 +- net/tshttpproxy/zsyscall_windows.go | 8 +- util/osdiag/zsyscall_windows.go | 2 +- util/winutil/authenticode/zsyscall_windows.go | 20 +- util/winutil/zsyscall_windows.go | 2 +- wgengine/magicsock/discopingpurpose_string.go | 5 +- wgengine/magicsock/magicsock_darwin.go | 3 + wgengine/magicsock/magicsock_windows.go | 3 + 22 files changed, 421 insertions(+), 137 deletions(-) create mode 100644 .clusterfuzzlite/Dockerfile create mode 100755 .clusterfuzzlite/build.sh create mode 100644 .clusterfuzzlite/project.yaml create mode 100644 cmd/testwrapper/testwrapper_test.go diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 0000000000000..25b655544265a --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,7 @@ +# Copyright (c) Tailscale Inc & AUTHORS +# SPDX-License-Identifier: BSD-3-Clause + +FROM gcr.io/oss-fuzz-base/base-builder-go +COPY . $SRC/tailscale +WORKDIR $SRC/tailscale +COPY .clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh new file mode 100755 index 0000000000000..299930fc3bfa8 --- /dev/null +++ b/.clusterfuzzlite/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash -eu +# Copyright (c) Tailscale Inc & AUTHORS +# SPDX-License-Identifier: BSD-3-Clause + +tool=github.com/AdamKorcz/go-118-fuzz-build +ref=fc5dc53b9db8a38c394c53d6e439a1410cf8fc19 +work=$(mktemp -d) +version=$(awk '/^go / { print $2 }' go.mod) +goroot=$(GOTOOLCHAIN="go$version" go env GOROOT) +# The v2 builder modifies testing internals and must match the Go toolchain. +cp -a "$goroot/." "$work/goroot" +chmod -R u+w "$work/goroot" +export GOROOT="$work/goroot" GOTOOLCHAIN=local +export PATH="$work/bin:$GOROOT/bin:$PATH" +GOBIN="$work/bin" go install "$tool@$ref" +mv "$work/bin/go-118-fuzz-build" "$work/bin/go-118-fuzz-build_v2" +go get "$tool/testing@$ref" +compile_native_go_fuzzer_v2 tailscale.com/net/dns/resolver FuzzClampEDNSSize clamp_edns_size +compile_native_go_fuzzer_v2 tailscale.com/util/hashx Fuzz hashx +compile_native_go_fuzzer_v2 tailscale.com/util/deephash FuzzTime deephash_time +compile_native_go_fuzzer_v2 tailscale.com/util/deephash FuzzAddr deephash_addr diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml new file mode 100644 index 0000000000000..4f2ee4d973389 --- /dev/null +++ b/.clusterfuzzlite/project.yaml @@ -0,0 +1 @@ +language: go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e3d8fa18a1f5..4a275b6e04d74 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,19 +2,7 @@ # both PRs and merged commits, and for the latter reports failures to slack. name: CI -env: - # Our fuzz job, powered by OSS-Fuzz, fails periodically because we upgrade to - # new Go versions very eagerly. OSS-Fuzz is a little more conservative, and - # ends up being unable to compile our code. - # - # When this happens, we want to disable the fuzz target until OSS-Fuzz catches - # up. However, we also don't want to forget to turn it back on when OSS-Fuzz - # can once again build our code. - # - # This variable toggles the fuzz job between two modes: - # - false: we expect fuzzing to be happy, and should report failure if it's not. - # - true: we expect fuzzing is broken, and should report failure if it start working. - TS_FUZZ_CURRENTLY_BROKEN: false +permissions: {} on: push: @@ -40,6 +28,8 @@ concurrency: jobs: test: + permissions: + contents: read strategy: fail-fast: false # don't abort the entire matrix if one element fails matrix: @@ -116,6 +106,8 @@ jobs: fi windows: + permissions: + contents: read runs-on: windows-2022 steps: - name: checkout @@ -148,9 +140,12 @@ jobs: # Don't use -bench=. -benchtime=1x. # Somewhere in the layers (powershell?) # the equals signs cause great confusion. - run: go test -bench . -benchtime 1x ./... + # testwrapper retries tests marked with flakytest.Mark. + run: go run ./cmd/testwrapper ./... -bench . -benchtime 1x vm: + permissions: + contents: read runs-on: ["self-hosted", "linux", "vm"] # VM tests run with some privileges, don't let them run on 3p PRs. if: github.repository == 'tailscale/tailscale' @@ -171,6 +166,8 @@ jobs: XDB_CACHE_HOME: "/var/lib/ghrunner/cache" cross: # cross-compile checks, build only. + permissions: + contents: read strategy: fail-fast: false # don't abort the entire matrix if one element fails matrix: @@ -248,6 +245,8 @@ jobs: ios: # similar to cross above, but iOS can't build most of the repo. So, just #make it build a few smoke packages. + permissions: + contents: read runs-on: ubuntu-22.04 steps: - name: checkout @@ -259,6 +258,8 @@ jobs: GOARCH: arm64 android: + permissions: + contents: read # similar to cross above, but android fails to build a few pieces of the # repo. We should fix those pieces, they're small, but as a stepping stone, # only test the subset of android that our past smoke test checked. @@ -281,65 +282,33 @@ jobs: GOARCH: arm64 fuzz: - # This target periodically breaks (see TS_FUZZ_CURRENTLY_BROKEN at the top - # of the file), so it's more complex than usual: the 'build fuzzers' step - # might fail, and depending on the value of 'TS_FUZZ_CURRENTLY_BROKEN', that - # might or might not be fine. The steps after the build figure out whether - # the success/failure is expected, and appropriately pass/fail the job - # overall accordingly. - # - # Practically, this means that all steps after 'build fuzzers' must have an - # explicit 'if' condition, because the default condition for steps is - # 'success()', meaning "only run this if no previous steps failed". + permissions: + contents: read if: github.event_name == 'pull_request' runs-on: ubuntu-22.04 steps: - name: build fuzzers - id: build - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master - # continue-on-error makes steps.build.conclusion be 'success' even if - # steps.build.outcome is 'failure'. This means this step does not - # contribute to the job's overall pass/fail evaluation. - continue-on-error: true + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: - oss-fuzz-project-name: "tailscale" - dry-run: false language: go - - name: report unexpectedly broken fuzz build - if: steps.build.outcome == 'failure' && env.TS_FUZZ_CURRENTLY_BROKEN != 'true' - run: | - echo "fuzzer build failed, see above for why" - echo "if the failure is due to OSS-Fuzz not being on the latest Go yet," - echo "set TS_FUZZ_CURRENTLY_BROKEN=true in .github/workflows/test.yml" - echo "to temporarily disable fuzzing until OSS-Fuzz works again." - exit 1 - - name: report unexpectedly working fuzz build - if: steps.build.outcome == 'success' && env.TS_FUZZ_CURRENTLY_BROKEN == 'true' - run: | - echo "fuzzer build succeeded, but we expect it to be broken" - echo "please set TS_FUZZ_CURRENTLY_BROKEN=false in .github/workflows/test.yml" - echo "to reenable fuzz testing" - exit 1 + keep-unaffected-fuzz-targets: true - name: run fuzzers id: run - # Run the fuzzers whenever they're able to build, even if we're going to - # report a failure because TS_FUZZ_CURRENTLY_BROKEN is set to the wrong - # value. - if: steps.build.outcome == 'success' - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: - oss-fuzz-project-name: "tailscale" fuzz-seconds: 300 - dry-run: false language: go + github-token: ${{ secrets.GITHUB_TOKEN }} - name: upload crash - uses: actions/upload-artifact@v3 - if: steps.run.outcome != 'success' && steps.build.outcome == 'success' + uses: actions/upload-artifact@v4 + if: failure() && steps.run.outcome == 'failure' with: name: artifacts path: ./out/artifacts depaware: + permissions: + contents: read runs-on: ubuntu-22.04 steps: - name: checkout @@ -354,6 +323,8 @@ jobs: find . -name 'depaware.txt' | xargs -n1 dirname | xargs ./tool/go run github.com/tailscale/depaware --check go_generate: + permissions: + contents: read runs-on: ubuntu-22.04 steps: - name: checkout @@ -371,6 +342,8 @@ jobs: git diff --name-only --exit-code || (echo "The files above need updating. Please run 'go generate'."; exit 1) go_mod_tidy: + permissions: + contents: read runs-on: ubuntu-22.04 steps: - name: checkout @@ -387,6 +360,8 @@ jobs: git diff --name-only --exit-code || (echo "Please run 'go mod tidy'."; exit 1) licenses: + permissions: + contents: read runs-on: ubuntu-22.04 steps: - name: checkout @@ -395,6 +370,8 @@ jobs: run: ./scripts/check_license_headers.sh . staticcheck: + permissions: + contents: read runs-on: ubuntu-22.04 strategy: fail-fast: false # don't abort the entire matrix if one element fails @@ -483,4 +460,5 @@ jobs: if: github.event_name != 'push' uses: re-actors/alls-green@release/v1 with: + allowed-skips: ${{ github.repository != 'tailscale/tailscale' && 'vm' || '' }} jobs: ${{ toJSON(needs) }} diff --git a/cmd/derper/depaware.txt b/cmd/derper/depaware.txt index 46fbbfdc9db01..f4aeffbdf2cb5 100644 --- a/cmd/derper/depaware.txt +++ b/cmd/derper/depaware.txt @@ -168,7 +168,6 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa golang.org/x/crypto/nacl/box from tailscale.com/types/key+ golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ - golang.org/x/exp/constraints from golang.org/x/exp/slices golang.org/x/exp/maps from tailscale.com/types/views golang.org/x/exp/slices from tailscale.com/net/tsaddr+ L golang.org/x/net/bpf from github.com/mdlayher/netlink+ diff --git a/cmd/tailscale/depaware.txt b/cmd/tailscale/depaware.txt index 0b9729a78d8b3..1f91a41c153a0 100644 --- a/cmd/tailscale/depaware.txt +++ b/cmd/tailscale/depaware.txt @@ -163,7 +163,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box golang.org/x/crypto/pbkdf2 from software.sslmate.com/src/go-pkcs12 golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ - golang.org/x/exp/constraints from golang.org/x/exp/slices+ + golang.org/x/exp/constraints from github.com/dblohm7/wingoes/pe+ golang.org/x/exp/maps from tailscale.com/types/views+ golang.org/x/exp/slices from tailscale.com/net/tsaddr+ golang.org/x/net/bpf from github.com/mdlayher/netlink+ @@ -213,6 +213,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep crypto/hkdf from crypto/internal/hpke+ crypto/hmac from crypto/tls+ crypto/md5 from crypto/tls+ + crypto/pbkdf2 from golang.org/x/crypto/pbkdf2 crypto/rand from crypto/ed25519+ crypto/rc4 from crypto/tls crypto/rsa from crypto/tls+ diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index c065d6129b9c6..787ea1689989c 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -11,7 +11,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy LD github.com/anmitsu/go-shlex from tailscale.com/tempfork/gliderlabs/ssh - L github.com/aws/aws-sdk-go-v2 from github.com/aws/aws-sdk-go-v2/internal/ini L github.com/aws/aws-sdk-go-v2/aws from github.com/aws/aws-sdk-go-v2/aws/middleware+ L github.com/aws/aws-sdk-go-v2/aws/arn from tailscale.com/ipn/store/awsstore L github.com/aws/aws-sdk-go-v2/aws/defaults from github.com/aws/aws-sdk-go-v2/service/ssm+ @@ -29,12 +28,18 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de L github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds from github.com/aws/aws-sdk-go-v2/config L github.com/aws/aws-sdk-go-v2/credentials/endpointcreds from github.com/aws/aws-sdk-go-v2/config L github.com/aws/aws-sdk-go-v2/credentials/endpointcreds/internal/client from github.com/aws/aws-sdk-go-v2/credentials/endpointcreds + L github.com/aws/aws-sdk-go-v2/credentials/logincreds from github.com/aws/aws-sdk-go-v2/config L github.com/aws/aws-sdk-go-v2/credentials/processcreds from github.com/aws/aws-sdk-go-v2/config L github.com/aws/aws-sdk-go-v2/credentials/ssocreds from github.com/aws/aws-sdk-go-v2/config L github.com/aws/aws-sdk-go-v2/credentials/stscreds from github.com/aws/aws-sdk-go-v2/config L github.com/aws/aws-sdk-go-v2/feature/ec2/imds from github.com/aws/aws-sdk-go-v2/config+ L github.com/aws/aws-sdk-go-v2/feature/ec2/imds/internal/config from github.com/aws/aws-sdk-go-v2/feature/ec2/imds + L github.com/aws/aws-sdk-go-v2/internal/auth from github.com/aws/aws-sdk-go-v2/aws/signer/v4+ + L github.com/aws/aws-sdk-go-v2/internal/auth/smithy from github.com/aws/aws-sdk-go-v2/service/signin+ L github.com/aws/aws-sdk-go-v2/internal/configsources from github.com/aws/aws-sdk-go-v2/service/ssm+ + L github.com/aws/aws-sdk-go-v2/internal/context from github.com/aws/aws-sdk-go-v2/aws/retry+ + L github.com/aws/aws-sdk-go-v2/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/signin+ + L github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn from github.com/aws/aws-sdk-go-v2/service/signin+ L github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 from github.com/aws/aws-sdk-go-v2/service/ssm/internal/endpoints+ L github.com/aws/aws-sdk-go-v2/internal/ini from github.com/aws/aws-sdk-go-v2/config L github.com/aws/aws-sdk-go-v2/internal/rand from github.com/aws/aws-sdk-go-v2/aws+ @@ -44,7 +49,11 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de L github.com/aws/aws-sdk-go-v2/internal/strings from github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4 L github.com/aws/aws-sdk-go-v2/internal/sync/singleflight from github.com/aws/aws-sdk-go-v2/aws L github.com/aws/aws-sdk-go-v2/internal/timeconv from github.com/aws/aws-sdk-go-v2/aws/retry + L github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding from github.com/aws/aws-sdk-go-v2/service/sts L github.com/aws/aws-sdk-go-v2/service/internal/presigned-url from github.com/aws/aws-sdk-go-v2/service/sts + L github.com/aws/aws-sdk-go-v2/service/signin from github.com/aws/aws-sdk-go-v2/config+ + L github.com/aws/aws-sdk-go-v2/service/signin/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/signin + L github.com/aws/aws-sdk-go-v2/service/signin/types from github.com/aws/aws-sdk-go-v2/credentials/logincreds+ L github.com/aws/aws-sdk-go-v2/service/ssm from tailscale.com/ipn/store/awsstore L github.com/aws/aws-sdk-go-v2/service/ssm/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/ssm L github.com/aws/aws-sdk-go-v2/service/ssm/types from github.com/aws/aws-sdk-go-v2/service/ssm+ @@ -58,6 +67,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de L github.com/aws/aws-sdk-go-v2/service/sts/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/sts L github.com/aws/aws-sdk-go-v2/service/sts/types from github.com/aws/aws-sdk-go-v2/credentials/stscreds+ L github.com/aws/smithy-go from github.com/aws/aws-sdk-go-v2/aws/protocol/restjson+ + L github.com/aws/smithy-go/auth from github.com/aws/aws-sdk-go-v2/internal/auth+ L github.com/aws/smithy-go/auth/bearer from github.com/aws/aws-sdk-go-v2/aws+ L github.com/aws/smithy-go/context from github.com/aws/smithy-go/auth/bearer L github.com/aws/smithy-go/document from github.com/aws/aws-sdk-go-v2/service/ssm+ @@ -65,13 +75,18 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de L github.com/aws/smithy-go/encoding/httpbinding from github.com/aws/aws-sdk-go-v2/aws/protocol/query+ L github.com/aws/smithy-go/encoding/json from github.com/aws/aws-sdk-go-v2/service/ssm+ L github.com/aws/smithy-go/encoding/xml from github.com/aws/aws-sdk-go-v2/service/sts + L github.com/aws/smithy-go/endpoints from github.com/aws/aws-sdk-go-v2/service/signin+ + L github.com/aws/smithy-go/endpoints/private/rulesfn from github.com/aws/aws-sdk-go-v2/service/signin+ L github.com/aws/smithy-go/internal/sync/singleflight from github.com/aws/smithy-go/auth/bearer L github.com/aws/smithy-go/io from github.com/aws/aws-sdk-go-v2/feature/ec2/imds+ L github.com/aws/smithy-go/logging from github.com/aws/aws-sdk-go-v2/aws+ + L github.com/aws/smithy-go/metrics from github.com/aws/aws-sdk-go-v2/aws/retry+ L github.com/aws/smithy-go/middleware from github.com/aws/aws-sdk-go-v2/aws+ + L github.com/aws/smithy-go/private/requestcompression from github.com/aws/aws-sdk-go-v2/config L github.com/aws/smithy-go/ptr from github.com/aws/aws-sdk-go-v2/aws+ L github.com/aws/smithy-go/rand from github.com/aws/aws-sdk-go-v2/aws/middleware+ L github.com/aws/smithy-go/time from github.com/aws/aws-sdk-go-v2/service/ssm+ + L github.com/aws/smithy-go/tracing from github.com/aws/aws-sdk-go-v2/aws/middleware+ L github.com/aws/smithy-go/transport/http from github.com/aws/aws-sdk-go-v2/aws/middleware+ L github.com/aws/smithy-go/transport/http/internal/io from github.com/aws/smithy-go/transport/http L github.com/aws/smithy-go/waiter from github.com/aws/aws-sdk-go-v2/service/ssm @@ -376,7 +391,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de golang.org/x/crypto/poly1305 from github.com/tailscale/golang-x-crypto/ssh+ golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ LD golang.org/x/crypto/ssh from tailscale.com/ssh/tailssh+ - golang.org/x/exp/constraints from golang.org/x/exp/slices+ + golang.org/x/exp/constraints from github.com/dblohm7/wingoes/pe+ golang.org/x/exp/maps from tailscale.com/wgengine+ golang.org/x/exp/slices from tailscale.com/ipn/ipnlocal+ golang.org/x/net/bpf from github.com/mdlayher/genetlink+ diff --git a/cmd/testwrapper/testwrapper.go b/cmd/testwrapper/testwrapper.go index c9790aafcd32b..5bfaea3512b43 100644 --- a/cmd/testwrapper/testwrapper.go +++ b/cmd/testwrapper/testwrapper.go @@ -33,6 +33,7 @@ type testAttempt struct { outcome string // "pass", "fail", "skip" logs bytes.Buffer isMarkedFlaky bool // set if the test is marked as flaky + ended bool // set after the test's "--- PASS" or "--- SKIP" line pkgFinished bool } @@ -61,11 +62,9 @@ type goTestOutput struct { var debug = os.Getenv("TS_TESTWRAPPER_DEBUG") != "" -// runTests runs the tests in pt and sends the results on ch. It sends a -// testAttempt for each test and a final testAttempt per pkg with pkgFinished -// set to true. -// It calls close(ch) when it's done. -func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []string, ch chan<- *testAttempt) { +// runTests sends per-test and package outcomes to ch, then closes ch. +// It returns startup errors or the go test subprocess's exit error. +func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []string, ch chan<- *testAttempt) error { defer close(ch) args := []string{"test", "-json", pt.pattern} args = append(args, otherArgs...) @@ -79,7 +78,7 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st cmd := exec.CommandContext(ctx, "go", args...) r, err := cmd.StdoutPipe() if err != nil { - log.Printf("error creating stdout pipe: %v", err) + return fmt.Errorf("creating stdout pipe: %w", err) } defer r.Close() cmd.Stderr = os.Stderr @@ -88,14 +87,8 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", flakytest.FlakeAttemptEnv, attempt)) if err := cmd.Start(); err != nil { - log.Printf("error starting test: %v", err) - os.Exit(1) + return fmt.Errorf("starting test: %w", err) } - done := make(chan struct{}) - go func() { - defer close(done) - cmd.Wait() - }() jd := json.NewDecoder(r) resultMap := make(map[testName]*testAttempt) @@ -118,7 +111,22 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st } if goOutput.Test == "" { switch goOutput.Action { + case "output": + if !isPkgSummary(goOutput.Output, goOutput.Package) { + fmt.Print(goOutput.Output) + } + case "build-output": + fmt.Print(goOutput.Output) case "fail", "pass", "skip": + if goOutput.Action == "fail" { + // Timeouts omit the final per-test event; report those tests as failed. + for name, test := range resultMap { + if name.pkg == goOutput.Package && test.outcome == "" { + test.outcome = "fail" + ch <- test + } + } + } ch <- &testAttempt{ name: testName{ pkg: goOutput.Package, @@ -151,14 +159,32 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st resultMap[name].outcome = goOutput.Action ch <- resultMap[name] case "output": - if strings.TrimSpace(goOutput.Output) == flakytest.FlakyTestLogMessage { - resultMap[name].isMarkedFlaky = true - } else { - resultMap[name].logs.WriteString(goOutput.Output) + test := resultMap[name] + switch { + case test.ended: + // go test attributes a timeout panic to the last resumed + // test, even if it already passed. Print it so it isn't lost. + fmt.Print(goOutput.Output) + case strings.TrimSpace(goOutput.Output) == flakytest.FlakyTestLogMessage: + test.isMarkedFlaky = true + default: + test.logs.WriteString(goOutput.Output) + test.ended = strings.HasPrefix(goOutput.Output, "--- PASS: "+name.name+" ") || + strings.HasPrefix(goOutput.Output, "--- SKIP: "+name.name+" ") } } } - <-done + return cmd.Wait() +} + +// isPkgSummary reports whether line is a go test result line for pkg, such as +// "ok \tpkg\t0.1s". main prints its own summary with the attempt number. +func isPkgSummary(line, pkg string) bool { + if line == "PASS\n" || line == "FAIL\n" { + return true + } + f := strings.Fields(line) + return len(f) >= 2 && f[1] == pkg && (f[0] == "ok" || f[0] == "FAIL" || f[0] == "?") } func main() { @@ -244,9 +270,16 @@ func main() { toRetry := make(map[string][]string) // pkg -> tests to retry for _, pt := range thisRun.tests { ch := make(chan *testAttempt) - go runTests(ctx, thisRun.attempt, pt, otherArgs, ch) + runErr := make(chan error, 1) + go func() { + runErr <- runTests(ctx, thisRun.attempt, pt, otherArgs, ch) + }() + retryable := false for tr := range ch { if tr.pkgFinished { + if tr.outcome == "fail" && len(toRetry[tr.name.pkg]) == 0 { + failed = true + } printPkgOutcome(tr.name.pkg, tr.outcome, thisRun.attempt) continue } @@ -257,11 +290,16 @@ func main() { continue } if tr.isMarkedFlaky { + retryable = true toRetry[tr.name.pkg] = append(toRetry[tr.name.pkg], tr.name.name) } else { failed = true } } + if err := <-runErr; err != nil && !retryable { + log.Printf("go test: %v", err) + failed = true + } } if failed { fmt.Println("\n\nNot retrying flaky tests because non-flaky tests failed.") diff --git a/cmd/testwrapper/testwrapper_test.go b/cmd/testwrapper/testwrapper_test.go new file mode 100644 index 0000000000000..a69d95ad23fa3 --- /dev/null +++ b/cmd/testwrapper/testwrapper_test.go @@ -0,0 +1,213 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package main_test + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + "time" + + "golang.org/x/mod/modfile" +) + +const ( + okTest = "package ok\n\nimport \"testing\"\n\nfunc TestOK(t *testing.T) {}\n" + + flakyTest = `package flaky + +import ( + "os" + "testing" + + "tailscale.com/cmd/testwrapper/flakytest" +) + +func TestOK(t *testing.T) {} + +func TestFlaky(t *testing.T) { + flakytest.Mark(t, "https://github.com/tailscale/tailscale/issues/0") + if os.Getenv(flakytest.FlakeAttemptEnv) == "1" { + t.Fatal("failing first attempt") + } +} +` + + flakyThenHangTest = `package flakyhang + +import ( + "os" + "testing" + + "tailscale.com/cmd/testwrapper/flakytest" +) + +func TestFlaky(t *testing.T) { + flakytest.Mark(t, "https://github.com/tailscale/tailscale/issues/0") + if os.Getenv(flakytest.FlakeAttemptEnv) == "1" { + t.Fatal("failing first attempt") + } +} + +func TestHang(t *testing.T) { <-make(chan struct{}) } +` + + compileErrorTest = "package broken\n\nimport \"testing\"\n\nfunc TestBroken(t *testing.T) { undefinedSymbol() }\n" +) + +func TestCLI(t *testing.T) { + t.Parallel() + repo, err := filepath.Abs(filepath.Join("..", "..")) + if err != nil { + t.Fatal(err) + } + repoMod, err := os.ReadFile(filepath.Join(repo, "go.mod")) + if err != nil { + t.Fatal(err) + } + mf, err := modfile.ParseLax("go.mod", repoMod, nil) + if err != nil || mf.Go == nil { + t.Fatalf("reading repo go version: %v", err) + } + bin := filepath.Join(t.TempDir(), "testwrapper") + if runtime.GOOS == "windows" { + bin += ".exe" + } + buildCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + if out, err := exec.CommandContext(buildCtx, "go", "build", "-o", bin, ".").CombinedOutput(); err != nil { + t.Fatalf("building testwrapper: %v\n%s", err, out) + } + + tests := []struct { + name string + files map[string]string + args []string + quiet bool // run without -v, as CI does + wantCode int + want []string + // wantCount maps output substrings to their exact number of occurrences. + wantCount map[string]int + }{ + {name: "Pass", files: map[string]string{"ok/ok_test.go": okTest}, wantCode: 0, wantCount: map[string]int{"fixture/ok": 1}}, + {name: "InvalidGoFlags", files: map[string]string{"ok/ok_test.go": okTest}, args: []string{"-p=invalid"}, wantCode: 1, want: []string{"invalid value"}}, + {name: "CompileError", files: map[string]string{"broken/broken_test.go": compileErrorTest}, wantCode: 1, want: []string{"undefinedSymbol"}, wantCount: map[string]int{"FAIL\tfixture/broken": 1}}, + { + name: "MarkedFlakeThenPass", + files: map[string]string{"flaky/flaky_test.go": flakyTest}, + wantCode: 0, + want: []string{"[attempt=2]"}, + wantCount: map[string]int{"=== RUN TestOK\n": 1, "=== RUN TestFlaky\n": 2}, + }, + { + name: "MixedFlakeAndCompileError", + files: map[string]string{ + "flaky/flaky_test.go": flakyTest, + "broken/broken_test.go": compileErrorTest, + }, + wantCode: 1, + want: []string{"undefinedSymbol"}, + }, + { + name: "NonFlakyFailure", + files: map[string]string{"fail/fail_test.go": "package fail\n\nimport \"testing\"\n\nfunc TestFail(t *testing.T) { t.Error(\"expected failure\") }\n"}, + wantCode: 1, + want: []string{"expected failure"}, + }, + { + name: "Timeout", + files: map[string]string{"hang/hang_test.go": "package hang\n\nimport \"testing\"\n\nfunc TestHang(t *testing.T) { <-make(chan struct{}) }\n"}, + args: []string{"-timeout=1s"}, + wantCode: 1, + want: []string{"timed out"}, + }, + { + name: "TimeoutAfterParallelPass", + files: map[string]string{"hang/hang_test.go": `package hang + +import ( + "testing" + "time" +) + +func TestHang(t *testing.T) { t.Parallel(); <-make(chan struct{}) } + +func TestQuick(t *testing.T) { t.Parallel(); time.Sleep(100 * time.Millisecond) } +`}, + args: []string{"-timeout=2s"}, + quiet: true, + wantCode: 1, + want: []string{"panic: test timed out", "running tests:"}, + }, + { + name: "FlakeThenTimeout", + files: map[string]string{"flakyhang/flakyhang_test.go": flakyThenHangTest}, + args: []string{"-timeout=1s"}, + wantCode: 1, + want: []string{"timed out", "Not retrying flaky tests"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + dir := t.TempDir() + gomod := fmt.Sprintf("module fixture\n\ngo %s\n\nrequire tailscale.com v0.0.0\n\nreplace tailscale.com => %q\n", mf.Go.Version, filepath.ToSlash(repo)) + if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte(gomod), 0o644); err != nil { + t.Fatal(err) + } + for name, content := range tt.files { + path := filepath.Join(dir, filepath.FromSlash(name)) + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, []byte(content), 0o644); err != nil { + t.Fatal(err) + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + args := []string{"./..."} + if !tt.quiet { + args = append([]string{"-v"}, args...) + } + cmd := exec.CommandContext(ctx, bin, append(args, tt.args...)...) + cmd.Dir = dir + cmd.Env = append(os.Environ(), "GOWORK=off", "GOFLAGS=-mod=mod", "GOPROXY=off") + outBytes, err := cmd.CombinedOutput() + out := string(outBytes) + if ctx.Err() != nil { + t.Fatalf("testwrapper did not finish: %v\n%s", ctx.Err(), out) + } + code := 0 + if exitErr := (*exec.ExitError)(nil); errors.As(err, &exitErr) { + code = exitErr.ExitCode() + } else if err != nil { + t.Fatalf("running testwrapper: %v", err) + } + if code != tt.wantCode { + t.Errorf("exit code = %d, want %d", code, tt.wantCode) + } + for _, want := range tt.want { + if !strings.Contains(out, want) { + t.Errorf("output missing %q", want) + } + } + for sub, want := range tt.wantCount { + if got := strings.Count(out, sub); got != want { + t.Errorf("output has %q %d times, want %d", sub, got, want) + } + } + if t.Failed() { + t.Logf("output:\n%s", out) + } + }) + } +} diff --git a/derp/dropreason_string.go b/derp/dropreason_string.go index 5ba500ab20530..6bfa6817d5d38 100644 --- a/derp/dropreason_string.go +++ b/derp/dropreason_string.go @@ -25,8 +25,9 @@ const _dropReason_name = "UnknownDestUnknownDestOnFwdGoneDisconnectedQueueHeadQu var _dropReason_index = [...]uint8{0, 11, 27, 43, 52, 61, 71, 80} func (i dropReason) String() string { - if i < 0 || i >= dropReason(len(_dropReason_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_dropReason_index)-1 { return "dropReason(" + strconv.FormatInt(int64(i), 10) + ")" } - return _dropReason_name[_dropReason_index[i]:_dropReason_index[i+1]] + return _dropReason_name[_dropReason_index[idx]:_dropReason_index[idx+1]] } diff --git a/go.mod b/go.mod index 6d37f6075e7f5..3464aa1ddca1c 100644 --- a/go.mod +++ b/go.mod @@ -15,8 +15,8 @@ require ( github.com/andybalholm/brotli v1.0.5 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be github.com/aws/aws-sdk-go-v2 v1.41.5 - github.com/aws/aws-sdk-go-v2/config v1.18.22 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.64 + github.com/aws/aws-sdk-go-v2/config v1.32.13 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.10 github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3 github.com/coder/websocket v1.8.12 @@ -95,7 +95,7 @@ require ( golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard/windows v0.5.3 gvisor.dev/gvisor v0.0.0-20240509041132-65b30f7869dc - honnef.co/go/tools v0.5.1 + honnef.co/go/tools v0.7.0 k8s.io/api v0.27.2 k8s.io/apimachinery v0.27.2 k8s.io/client-go v0.27.2 @@ -126,19 +126,20 @@ require ( github.com/ashanbrown/forbidigo v1.5.1 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.21 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.13 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect github.com/aws/smithy-go v1.24.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.0 // indirect diff --git a/go.sum b/go.sum index 681c57976b8d1..33f2874dd8bb1 100644 --- a/go.sum +++ b/go.sum @@ -124,51 +124,46 @@ github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvx github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= -github.com/aws/aws-sdk-go-v2/config v1.18.22 h1:7vkUEmjjv+giht4wIROqLs+49VWmiQMMHSduxmoNKLU= -github.com/aws/aws-sdk-go-v2/config v1.18.22/go.mod h1:mN7Li1wxaPxSSy4Xkr6stFuinJGf3VZW3ZSNvO0q6sI= -github.com/aws/aws-sdk-go-v2/credentials v1.13.21 h1:VRiXnPEaaPeGeoFcXvMZOB5K/yfIXOYE3q97Kgb0zbU= -github.com/aws/aws-sdk-go-v2/credentials v1.13.21/go.mod h1:90Dk1lJoMyspa/EDUrldTxsPns0wn6+KpRKpdAWc0uA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.64 h1:9QJQs36z61YB8nxGwRDfWXEDYbU6H7jdI6zFiAX1vag= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.64/go.mod h1:4Q7R9MFpXRdjO3YnAfUTdnuENs32WzBkASt6VxSYDYQ= +github.com/aws/aws-sdk-go-v2/config v1.32.13 h1:5KgbxMaS2coSWRrx9TX/QtWbqzgQkOdEa3sZPhBhCSg= +github.com/aws/aws-sdk-go-v2/config v1.32.13/go.mod h1:8zz7wedqtCbw5e9Mi2doEwDyEgHcEE9YOJp6a8jdSMY= +github.com/aws/aws-sdk-go-v2/credentials v1.19.13 h1:mA59E3fokBvyEGHKFdnpNNrvaR351cqiHgRg+JzOSRI= +github.com/aws/aws-sdk-go-v2/credentials v1.19.13/go.mod h1:yoTXOQKea18nrM69wGF9jBdG4WocSZA1h38A+t/MAsk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.10 h1:GHKiUsNpMVIrrf4v+IvC56VfCB0LeZ6FUFpMUDIckSI= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.10/go.mod h1:wGl2ts9ULQknI/BNi3VzcRFv3ebvOViQdtyxaMpBzzI= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25/go.mod h1:SUbB4wcbSEyCvqBxv/O/IBf93RbEze7U7OnoTlpPB+g= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28/go.mod h1:spfrICMD6wCAhjhzHuy6DOZZ+LAIY10UxhUmLzpJTTs= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2/go.mod h1:4tfW5l4IAB32VWCDEBxCRtR9T4BWy4I4kr1spr8NgZM= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ= -github.com/aws/aws-sdk-go-v2/service/s3 v1.33.0/go.mod h1:J9kLNzEiHSeGMyN7238EjJmBpCniVzFda75Gxl/NqB8= github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo= github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI= github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3 h1:TQZH0Djie8VVgTBDOQ02M4zVHJFrNzLMsYMbNfRitVM= github.com/aws/aws-sdk-go-v2/service/ssm v1.36.3/go.mod h1:p6MaesK9061w6NTiFmZpUzEkKUY5blKlwD2zYyErxKA= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.9 h1:GAiaQWuQhQQui76KjuXeShmyXqECwQ0mGRMc/rwsL+c= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.9/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9 h1:TraLwncRJkWqtIBVKI/UqBymq4+hL+3MzUOtUATuzkA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.9/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.10 h1:6UbNM/KJhMBfOI5+lpVcJ/8OA7cBSz0O6OX37SRKlSw= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.10/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 h1:GcLE9ba5ehAQma6wlopUesYg/hbcOhFNWTjELkiWkh4= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.14/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 h1:mP49nTpfKtpXLt5SLn8Uv8z6W+03jYVoOSAl/c02nog= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= @@ -1713,8 +1708,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 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.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= -honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= +honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= +honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= diff --git a/net/connstats/stats_test.go b/net/connstats/stats_test.go index 0db7996f4072b..20b019370786a 100644 --- a/net/connstats/stats_test.go +++ b/net/connstats/stats_test.go @@ -87,6 +87,11 @@ func TestConcurrent(t *testing.T) { const maxConns = 10 virtualAggregate := make(map[netlogtype.Connection]netlogtype.Counts) stats := NewStatistics(maxPeriod, maxConns, func(start, end time.Time, virtual, physical map[netlogtype.Connection]netlogtype.Counts) { + // Empty intervals have no timestamps. + if len(virtual)+len(physical) == 0 { + c.Assert(start.IsZero() && end.IsZero(), qt.IsTrue) + return + } c.Assert(start.IsZero(), qt.IsFalse) c.Assert(end.IsZero(), qt.IsFalse) c.Assert(end.Before(start), qt.IsFalse) diff --git a/net/portmapper/pmpresultcode_string.go b/net/portmapper/pmpresultcode_string.go index 603636adec044..18d911d944126 100644 --- a/net/portmapper/pmpresultcode_string.go +++ b/net/portmapper/pmpresultcode_string.go @@ -24,8 +24,9 @@ const _pmpResultCode_name = "OKUnsupportedVersionNotAuthorizedNetworkFailureOutO var _pmpResultCode_index = [...]uint8{0, 2, 20, 33, 47, 61, 78} func (i pmpResultCode) String() string { - if i >= pmpResultCode(len(_pmpResultCode_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_pmpResultCode_index)-1 { return "pmpResultCode(" + strconv.FormatInt(int64(i), 10) + ")" } - return _pmpResultCode_name[_pmpResultCode_index[i]:_pmpResultCode_index[i+1]] + return _pmpResultCode_name[_pmpResultCode_index[idx]:_pmpResultCode_index[idx+1]] } diff --git a/net/sockstats/label_string.go b/net/sockstats/label_string.go index 2c3fb6bd7b2e8..9a07718617079 100644 --- a/net/sockstats/label_string.go +++ b/net/sockstats/label_string.go @@ -27,8 +27,9 @@ const _Label_name = "ControlClientAutoControlClientDialerDERPHTTPClientLogtailLo var _Label_index = [...]uint8{0, 17, 36, 50, 63, 78, 93, 107, 123, 140, 157, 169, 186} func (i Label) String() string { - if i >= Label(len(_Label_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_Label_index)-1 { return "Label(" + strconv.FormatInt(int64(i), 10) + ")" } - return _Label_name[_Label_index[i]:_Label_index[i+1]] + return _Label_name[_Label_index[idx]:_Label_index[idx+1]] } diff --git a/net/tshttpproxy/zsyscall_windows.go b/net/tshttpproxy/zsyscall_windows.go index c07e9ee03a69e..5dcfae83ea1a4 100644 --- a/net/tshttpproxy/zsyscall_windows.go +++ b/net/tshttpproxy/zsyscall_windows.go @@ -48,7 +48,7 @@ var ( ) func globalFree(hglobal winHGlobal) (err error) { - r1, _, e1 := syscall.Syscall(procGlobalFree.Addr(), 1, uintptr(hglobal), 0, 0) + r1, _, e1 := syscall.SyscallN(procGlobalFree.Addr(), uintptr(hglobal)) if r1 == 0 { err = errnoErr(e1) } @@ -56,7 +56,7 @@ func globalFree(hglobal winHGlobal) (err error) { } func winHTTPCloseHandle(whi winHTTPInternet) (err error) { - r1, _, e1 := syscall.Syscall(procWinHttpCloseHandle.Addr(), 1, uintptr(whi), 0, 0) + r1, _, e1 := syscall.SyscallN(procWinHttpCloseHandle.Addr(), uintptr(whi)) if r1 == 0 { err = errnoErr(e1) } @@ -64,7 +64,7 @@ func winHTTPCloseHandle(whi winHTTPInternet) (err error) { } func winHTTPGetProxyForURL(whi winHTTPInternet, url *uint16, options *winHTTPAutoProxyOptions, proxyInfo *winHTTPProxyInfo) (err error) { - r1, _, e1 := syscall.Syscall6(procWinHttpGetProxyForUrl.Addr(), 4, uintptr(whi), uintptr(unsafe.Pointer(url)), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(proxyInfo)), 0, 0) + r1, _, e1 := syscall.SyscallN(procWinHttpGetProxyForUrl.Addr(), uintptr(whi), uintptr(unsafe.Pointer(url)), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(proxyInfo))) if r1 == 0 { err = errnoErr(e1) } @@ -72,7 +72,7 @@ func winHTTPGetProxyForURL(whi winHTTPInternet, url *uint16, options *winHTTPAut } func winHTTPOpen(agent *uint16, accessType uint32, proxy *uint16, proxyBypass *uint16, flags uint32) (whi winHTTPInternet, err error) { - r0, _, e1 := syscall.Syscall6(procWinHttpOpen.Addr(), 5, uintptr(unsafe.Pointer(agent)), uintptr(accessType), uintptr(unsafe.Pointer(proxy)), uintptr(unsafe.Pointer(proxyBypass)), uintptr(flags), 0) + r0, _, e1 := syscall.SyscallN(procWinHttpOpen.Addr(), uintptr(unsafe.Pointer(agent)), uintptr(accessType), uintptr(unsafe.Pointer(proxy)), uintptr(unsafe.Pointer(proxyBypass)), uintptr(flags)) whi = winHTTPInternet(r0) if whi == 0 { err = errnoErr(e1) diff --git a/util/osdiag/zsyscall_windows.go b/util/osdiag/zsyscall_windows.go index caeb245d2974f..570d298d9d469 100644 --- a/util/osdiag/zsyscall_windows.go +++ b/util/osdiag/zsyscall_windows.go @@ -45,7 +45,7 @@ var ( ) func regEnumValue(key registry.Key, index uint32, valueName *uint16, valueNameLen *uint32, reserved *uint32, valueType *uint32, pData *byte, cbData *uint32) (ret error) { - r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(valueName)), uintptr(unsafe.Pointer(valueNameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valueType)), uintptr(unsafe.Pointer(pData)), uintptr(unsafe.Pointer(cbData)), 0) + r0, _, _ := syscall.SyscallN(procRegEnumValueW.Addr(), uintptr(key), uintptr(index), uintptr(unsafe.Pointer(valueName)), uintptr(unsafe.Pointer(valueNameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valueType)), uintptr(unsafe.Pointer(pData)), uintptr(unsafe.Pointer(cbData))) if r0 != 0 { ret = syscall.Errno(r0) } diff --git a/util/winutil/authenticode/zsyscall_windows.go b/util/winutil/authenticode/zsyscall_windows.go index 643721e06aad5..f1fba2828713c 100644 --- a/util/winutil/authenticode/zsyscall_windows.go +++ b/util/winutil/authenticode/zsyscall_windows.go @@ -56,7 +56,7 @@ var ( ) func cryptMsgClose(cryptMsg windows.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procCryptMsgClose.Addr(), 1, uintptr(cryptMsg), 0, 0) + r1, _, e1 := syscall.SyscallN(procCryptMsgClose.Addr(), uintptr(cryptMsg)) if int32(r1) == 0 { err = errnoErr(e1) } @@ -64,7 +64,7 @@ func cryptMsgClose(cryptMsg windows.Handle) (err error) { } func cryptMsgGetParam(cryptMsg windows.Handle, paramType uint32, index uint32, data unsafe.Pointer, dataLen *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procCryptMsgGetParam.Addr(), 5, uintptr(cryptMsg), uintptr(paramType), uintptr(index), uintptr(data), uintptr(unsafe.Pointer(dataLen)), 0) + r1, _, e1 := syscall.SyscallN(procCryptMsgGetParam.Addr(), uintptr(cryptMsg), uintptr(paramType), uintptr(index), uintptr(data), uintptr(unsafe.Pointer(dataLen))) if int32(r1) == 0 { err = errnoErr(e1) } @@ -72,7 +72,7 @@ func cryptMsgGetParam(cryptMsg windows.Handle, paramType uint32, index uint32, d } func cryptVerifyMessageSignature(pVerifyPara *_CRYPT_VERIFY_MESSAGE_PARA, signerIndex uint32, pbSignedBlob *byte, cbSignedBlob uint32, pbDecoded *byte, pdbDecoded *uint32, ppSignerCert **windows.CertContext) (err error) { - r1, _, e1 := syscall.Syscall9(procCryptVerifyMessageSignature.Addr(), 7, uintptr(unsafe.Pointer(pVerifyPara)), uintptr(signerIndex), uintptr(unsafe.Pointer(pbSignedBlob)), uintptr(cbSignedBlob), uintptr(unsafe.Pointer(pbDecoded)), uintptr(unsafe.Pointer(pdbDecoded)), uintptr(unsafe.Pointer(ppSignerCert)), 0, 0) + r1, _, e1 := syscall.SyscallN(procCryptVerifyMessageSignature.Addr(), uintptr(unsafe.Pointer(pVerifyPara)), uintptr(signerIndex), uintptr(unsafe.Pointer(pbSignedBlob)), uintptr(cbSignedBlob), uintptr(unsafe.Pointer(pbDecoded)), uintptr(unsafe.Pointer(pdbDecoded)), uintptr(unsafe.Pointer(ppSignerCert))) if int32(r1) == 0 { err = errnoErr(e1) } @@ -80,13 +80,13 @@ func cryptVerifyMessageSignature(pVerifyPara *_CRYPT_VERIFY_MESSAGE_PARA, signer } func msiGetFileSignatureInformation(signedObjectPath *uint16, flags uint32, certCtx **windows.CertContext, pbHashData *byte, cbHashData *uint32) (ret wingoes.HRESULT) { - r0, _, _ := syscall.Syscall6(procMsiGetFileSignatureInformationW.Addr(), 5, uintptr(unsafe.Pointer(signedObjectPath)), uintptr(flags), uintptr(unsafe.Pointer(certCtx)), uintptr(unsafe.Pointer(pbHashData)), uintptr(unsafe.Pointer(cbHashData)), 0) + r0, _, _ := syscall.SyscallN(procMsiGetFileSignatureInformationW.Addr(), uintptr(unsafe.Pointer(signedObjectPath)), uintptr(flags), uintptr(unsafe.Pointer(certCtx)), uintptr(unsafe.Pointer(pbHashData)), uintptr(unsafe.Pointer(cbHashData))) ret = wingoes.HRESULT(r0) return } func cryptCATAdminAcquireContext2(hCatAdmin *_HCATADMIN, pgSubsystem *windows.GUID, hashAlgorithm *uint16, strongHashPolicy *windows.CertStrongSignPara, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procCryptCATAdminAcquireContext2.Addr(), 5, uintptr(unsafe.Pointer(hCatAdmin)), uintptr(unsafe.Pointer(pgSubsystem)), uintptr(unsafe.Pointer(hashAlgorithm)), uintptr(unsafe.Pointer(strongHashPolicy)), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procCryptCATAdminAcquireContext2.Addr(), uintptr(unsafe.Pointer(hCatAdmin)), uintptr(unsafe.Pointer(pgSubsystem)), uintptr(unsafe.Pointer(hashAlgorithm)), uintptr(unsafe.Pointer(strongHashPolicy)), uintptr(flags)) if int32(r1) == 0 { err = errnoErr(e1) } @@ -94,7 +94,7 @@ func cryptCATAdminAcquireContext2(hCatAdmin *_HCATADMIN, pgSubsystem *windows.GU } func cryptCATAdminCalcHashFromFileHandle2(hCatAdmin _HCATADMIN, file windows.Handle, pcbHash *uint32, pbHash *byte, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procCryptCATAdminCalcHashFromFileHandle2.Addr(), 5, uintptr(hCatAdmin), uintptr(file), uintptr(unsafe.Pointer(pcbHash)), uintptr(unsafe.Pointer(pbHash)), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procCryptCATAdminCalcHashFromFileHandle2.Addr(), uintptr(hCatAdmin), uintptr(file), uintptr(unsafe.Pointer(pcbHash)), uintptr(unsafe.Pointer(pbHash)), uintptr(flags)) if int32(r1) == 0 { err = errnoErr(e1) } @@ -102,7 +102,7 @@ func cryptCATAdminCalcHashFromFileHandle2(hCatAdmin _HCATADMIN, file windows.Han } func cryptCATAdminEnumCatalogFromHash(hCatAdmin _HCATADMIN, pbHash *byte, cbHash uint32, flags uint32, prevCatInfo *_HCATINFO) (ret _HCATINFO, err error) { - r0, _, e1 := syscall.Syscall6(procCryptCATAdminEnumCatalogFromHash.Addr(), 5, uintptr(hCatAdmin), uintptr(unsafe.Pointer(pbHash)), uintptr(cbHash), uintptr(flags), uintptr(unsafe.Pointer(prevCatInfo)), 0) + r0, _, e1 := syscall.SyscallN(procCryptCATAdminEnumCatalogFromHash.Addr(), uintptr(hCatAdmin), uintptr(unsafe.Pointer(pbHash)), uintptr(cbHash), uintptr(flags), uintptr(unsafe.Pointer(prevCatInfo))) ret = _HCATINFO(r0) if ret == 0 { err = errnoErr(e1) @@ -111,7 +111,7 @@ func cryptCATAdminEnumCatalogFromHash(hCatAdmin _HCATADMIN, pbHash *byte, cbHash } func cryptCATAdminReleaseCatalogContext(hCatAdmin _HCATADMIN, hCatInfo _HCATINFO, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procCryptCATAdminReleaseCatalogContext.Addr(), 3, uintptr(hCatAdmin), uintptr(hCatInfo), uintptr(flags)) + r1, _, e1 := syscall.SyscallN(procCryptCATAdminReleaseCatalogContext.Addr(), uintptr(hCatAdmin), uintptr(hCatInfo), uintptr(flags)) if int32(r1) == 0 { err = errnoErr(e1) } @@ -119,7 +119,7 @@ func cryptCATAdminReleaseCatalogContext(hCatAdmin _HCATADMIN, hCatInfo _HCATINFO } func cryptCATAdminReleaseContext(hCatAdmin _HCATADMIN, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procCryptCATAdminReleaseContext.Addr(), 2, uintptr(hCatAdmin), uintptr(flags), 0) + r1, _, e1 := syscall.SyscallN(procCryptCATAdminReleaseContext.Addr(), uintptr(hCatAdmin), uintptr(flags)) if int32(r1) == 0 { err = errnoErr(e1) } @@ -127,7 +127,7 @@ func cryptCATAdminReleaseContext(hCatAdmin _HCATADMIN, flags uint32) (err error) } func cryptCATAdminCatalogInfoFromContext(hCatInfo _HCATINFO, catInfo *_CATALOG_INFO, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procCryptCATCatalogInfoFromContext.Addr(), 3, uintptr(hCatInfo), uintptr(unsafe.Pointer(catInfo)), uintptr(flags)) + r1, _, e1 := syscall.SyscallN(procCryptCATCatalogInfoFromContext.Addr(), uintptr(hCatInfo), uintptr(unsafe.Pointer(catInfo)), uintptr(flags)) if int32(r1) == 0 { err = errnoErr(e1) } diff --git a/util/winutil/zsyscall_windows.go b/util/winutil/zsyscall_windows.go index 8c899232f3431..fc9b8274f7c3e 100644 --- a/util/winutil/zsyscall_windows.go +++ b/util/winutil/zsyscall_windows.go @@ -44,7 +44,7 @@ var ( ) func queryServiceConfig2(hService windows.Handle, infoLevel uint32, buf *byte, bufLen uint32, bytesNeeded *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procQueryServiceConfig2W.Addr(), 5, uintptr(hService), uintptr(infoLevel), uintptr(unsafe.Pointer(buf)), uintptr(bufLen), uintptr(unsafe.Pointer(bytesNeeded)), 0) + r1, _, e1 := syscall.SyscallN(procQueryServiceConfig2W.Addr(), uintptr(hService), uintptr(infoLevel), uintptr(unsafe.Pointer(buf)), uintptr(bufLen), uintptr(unsafe.Pointer(bytesNeeded))) if r1 == 0 { err = errnoErr(e1) } diff --git a/wgengine/magicsock/discopingpurpose_string.go b/wgengine/magicsock/discopingpurpose_string.go index 6329172dccc2e..803dfce5980b9 100644 --- a/wgengine/magicsock/discopingpurpose_string.go +++ b/wgengine/magicsock/discopingpurpose_string.go @@ -21,8 +21,9 @@ const _discoPingPurpose_name = "DiscoveryHeartbeatCLI" var _discoPingPurpose_index = [...]uint8{0, 9, 18, 21} func (i discoPingPurpose) String() string { - if i < 0 || i >= discoPingPurpose(len(_discoPingPurpose_index)-1) { + idx := int(i) - 0 + if i < 0 || idx >= len(_discoPingPurpose_index)-1 { return "discoPingPurpose(" + strconv.FormatInt(int64(i), 10) + ")" } - return _discoPingPurpose_name[_discoPingPurpose_index[i]:_discoPingPurpose_index[i+1]] + return _discoPingPurpose_name[_discoPingPurpose_index[idx]:_discoPingPurpose_index[idx+1]] } diff --git a/wgengine/magicsock/magicsock_darwin.go b/wgengine/magicsock/magicsock_darwin.go index f3b32959f5ba8..dbc3a45a1c1c8 100644 --- a/wgengine/magicsock/magicsock_darwin.go +++ b/wgengine/magicsock/magicsock_darwin.go @@ -1,3 +1,6 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + package magicsock import ( diff --git a/wgengine/magicsock/magicsock_windows.go b/wgengine/magicsock/magicsock_windows.go index 0206fbc47c896..cef7ee48e67b1 100644 --- a/wgengine/magicsock/magicsock_windows.go +++ b/wgengine/magicsock/magicsock_windows.go @@ -1,3 +1,6 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + package magicsock import (