Skip to content

Commit e0d1931

Browse files
committed
android: add emulator integration test
Updates tailscale/tailscale#13038 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
1 parent 8fb1ef9 commit e0d1931

13 files changed

Lines changed: 1023 additions & 2 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Integration Test
2+
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
5+
cancel-in-progress: true
6+
7+
on:
8+
workflow_dispatch:
9+
pull_request:
10+
types: [labeled, synchronize, reopened]
11+
12+
jobs:
13+
integration-test:
14+
if: |
15+
github.event_name == 'workflow_dispatch' ||
16+
(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'run-integration-test'))
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 90
19+
20+
steps:
21+
- name: Check out code
22+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
23+
24+
- name: Enable KVM
25+
run: |
26+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
27+
sudo udevadm control --reload-rules
28+
sudo udevadm trigger --name-match=kvm
29+
ls -l /dev/kvm
30+
31+
- name: Run integration test
32+
run: make android-integration-test

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ tailscale.jks
3131
# Persistent $HOME/.android for `make docker-*` (keeps debug.keystore so
3232
# the debug signer is stable across container runs).
3333
.android-docker
34+
.android-integration-docker
35+
.cache-docker
36+
.gradle-docker
3437

3538
# Java profiling output
3639
*.hprof

Makefile

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
#
1212
# The convention here is tailscale-android-build-amd64-<date>
1313
DOCKER_IMAGE := tailscale-android-build-amd64-041425-1
14+
15+
# The integration test image contains the Android emulator, system image, SDK,
16+
# build-tools, NDK, adb, and helper tools needed to run the emulator-backed Go
17+
# integration tests. Bump this tag when docker/Dockerfile.android-integration
18+
# or the required tool versions change, using:
19+
# tailscale-android-integration-amd64-YYYYMMDD-N
20+
ANDROID_INTEGRATION_DOCKER_IMAGE := tailscale-android-integration-amd64-20260526-3
1421
export TS_USE_TOOLCHAIN=1
1522

1623
# If set, additional comma-separated build tags passed to the libtailscale Go
@@ -365,6 +372,14 @@ docker-build-image: ## Builds the docker image for the android build environment
365372
docker build -f docker/DockerFile.amd64-build -t $(DOCKER_IMAGE) .; \
366373
fi
367374

375+
.PHONY: docker-build-android-integration-image
376+
docker-build-android-integration-image: ## Build the Docker image used to run Android integration tests
377+
@echo "Checking if docker image $(ANDROID_INTEGRATION_DOCKER_IMAGE) already exists..."
378+
@if ! docker images $(ANDROID_INTEGRATION_DOCKER_IMAGE) -q | grep -q . ; then \
379+
echo "Image does not exist. Building..."; \
380+
docker build -f docker/Dockerfile.android-integration -t $(ANDROID_INTEGRATION_DOCKER_IMAGE) .; \
381+
fi
382+
368383
# DOCKER_ANDROID_DIR is bind-mounted as /root/.android inside the container
369384
# so the Gradle-generated debug keystore (and anything else under ~/.android)
370385
# persists across docker runs. Without this, every docker-based debug build
@@ -373,12 +388,19 @@ docker-build-image: ## Builds the docker image for the android build environment
373388
# JVM's user.home resolves to /root for the container's root user, regardless
374389
# of the Dockerfile's HOME=/build env.
375390
DOCKER_ANDROID_DIR := $(CURDIR)/.android-docker
391+
DOCKER_ANDROID_INTEGRATION_DIR := $(CURDIR)/.android-integration-docker
392+
DOCKER_GRADLE_DIR := $(CURDIR)/.gradle-docker
393+
DOCKER_GO_CACHE_DIR := $(CURDIR)/.cache-docker
376394

377395
.PHONY: docker-android-dir
378396
docker-android-dir:
379-
@mkdir -p $(DOCKER_ANDROID_DIR)
397+
@mkdir -p $(DOCKER_ANDROID_DIR) $(DOCKER_GRADLE_DIR) $(DOCKER_GO_CACHE_DIR)
398+
399+
.PHONY: docker-android-integration-dir
400+
docker-android-integration-dir:
401+
@mkdir -p $(DOCKER_ANDROID_INTEGRATION_DIR) $(DOCKER_GO_CACHE_DIR)
380402

381-
DOCKER_RUN_VOLS := -v $(CURDIR):/build/tailscale-android -v $(DOCKER_ANDROID_DIR):/root/.android
403+
DOCKER_RUN_VOLS := -v $(CURDIR):/build/tailscale-android -v $(DOCKER_ANDROID_DIR):/root/.android -v $(DOCKER_GRADLE_DIR):/build/.gradle -v $(DOCKER_GO_CACHE_DIR):/build/.cache --env GOPATH=/build/.cache/go --env GOMODCACHE=/build/.cache/go/pkg/mod
382404

383405
.PHONY: docker-run-build
384406
docker-run-build: clean jarsign-env docker-build-image docker-android-dir ## Runs the docker image for the android build environment and builds release
@@ -388,6 +410,21 @@ docker-run-build: clean jarsign-env docker-build-image docker-android-dir ## Run
388410
docker-tailscale-debug: docker-build-image docker-android-dir ## Build tailscale-debug.apk inside the docker env (stable signer across runs)
389411
@docker run --rm $(DOCKER_RUN_VOLS) $(DOCKER_IMAGE) make tailscale-debug
390412

413+
.PHONY: android-integration-test
414+
android-integration-test: docker-tailscale-debug android-integration-test-run ## Build APK and run adb-backed Android integration tests in Docker
415+
416+
.PHONY: android-integration-test-run
417+
android-integration-test-run: docker-build-android-integration-image docker-android-integration-dir ## Run adb-backed Android integration tests in Docker using existing APK
418+
@docker run --rm --device /dev/kvm \
419+
-v $(CURDIR):/workspace \
420+
-v $(DOCKER_ANDROID_INTEGRATION_DIR):/root/.android \
421+
-v $(DOCKER_GO_CACHE_DIR):/root/.cache \
422+
--env GOPATH=/root/.cache/go \
423+
--env GOMODCACHE=/root/.cache/go/pkg/mod \
424+
-w /workspace \
425+
$(ANDROID_INTEGRATION_DOCKER_IMAGE) \
426+
/usr/local/bin/run-android-integration-test /workspace/$(DEBUG_APK)
427+
391428
.PHONY: docker-remove-build-image
392429
docker-remove-build-image: ## Removes the current docker build image
393430
docker rmi --force $(DOCKER_IMAGE)

android/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ dependencies {
114114
implementation "androidx.browser:browser:1.8.0"
115115
implementation "androidx.security:security-crypto:1.1.0-alpha06"
116116
implementation "androidx.work:work-runtime:2.9.1"
117+
implementation "androidx.work:work-runtime-ktx:2.9.1"
117118

118119
// Kotlin dependencies.
119120
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"

android/src/main/java/com/tailscale/ipn/IPNReceiver.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class IPNReceiver extends BroadcastReceiver {
2222

2323
public static final String INTENT_CONNECT_VPN = "com.tailscale.ipn.CONNECT_VPN";
2424
public static final String INTENT_DISCONNECT_VPN = "com.tailscale.ipn.DISCONNECT_VPN";
25+
public static final String INTENT_INTEGRATION_LOGIN = "com.tailscale.ipn.integration.LOGIN";
2526
private static final String INTENT_USE_EXIT_NODE = "com.tailscale.ipn.USE_EXIT_NODE";
2627

2728
// Unique work names prevent connect/disconnect flapping from enqueuing a long backlog.
@@ -72,6 +73,27 @@ public void onReceive(Context context, Intent intent) {
7273
.build();
7374

7475
workManager.enqueueUniqueWork(WORK_USE_EXIT_NODE, ExistingWorkPolicy.REPLACE, req);
76+
} else if (BuildConfig.DEBUG && Objects.equals(action, INTENT_INTEGRATION_LOGIN)) {
77+
Data input =
78+
new Data.Builder()
79+
.putString(
80+
IntegrationLoginWorker.EXTRA_CONTROL_URL,
81+
intent.getStringExtra(
82+
IntegrationLoginWorker.EXTRA_CONTROL_URL))
83+
.putString(
84+
IntegrationLoginWorker.EXTRA_AUTH_KEY,
85+
intent.getStringExtra(IntegrationLoginWorker.EXTRA_AUTH_KEY))
86+
.build();
87+
88+
OneTimeWorkRequest req =
89+
new OneTimeWorkRequest.Builder(IntegrationLoginWorker.class)
90+
.setInputData(input)
91+
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
92+
.addTag(IntegrationLoginWorker.WORK_NAME)
93+
.build();
94+
95+
workManager.enqueueUniqueWork(
96+
IntegrationLoginWorker.WORK_NAME, ExistingWorkPolicy.REPLACE, req);
7597
}
7698
}
7799
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) Tailscale Inc & AUTHORS
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
4+
package com.tailscale.ipn
5+
6+
import android.content.Context
7+
import androidx.work.CoroutineWorker
8+
import androidx.work.WorkerParameters
9+
import com.tailscale.ipn.ui.localapi.Client
10+
import com.tailscale.ipn.ui.model.Ipn
11+
import com.tailscale.ipn.util.TSLog
12+
import kotlinx.coroutines.CompletableDeferred
13+
14+
/**
15+
* Test-only worker used by emulator integration tests to log a debug APK into a local
16+
* testcontrol server with an auth key.
17+
*
18+
* IPNReceiver only enqueues this worker when BuildConfig.DEBUG is true. Release builds do not
19+
* expose the broadcast entry point, and minified final APKs strip this unreachable debug-only path.
20+
*/
21+
class IntegrationLoginWorker(context: Context, params: WorkerParameters) :
22+
CoroutineWorker(context, params) {
23+
override suspend fun doWork(): Result {
24+
if (!BuildConfig.DEBUG) {
25+
return Result.failure()
26+
}
27+
28+
val controlURL = inputData.getString(EXTRA_CONTROL_URL)
29+
val authKey = inputData.getString(EXTRA_AUTH_KEY)
30+
if (controlURL.isNullOrBlank() || authKey.isNullOrBlank()) {
31+
TSLog.e(TAG, "missing $EXTRA_CONTROL_URL or $EXTRA_AUTH_KEY")
32+
return Result.failure()
33+
}
34+
35+
return try {
36+
login(controlURL, authKey)
37+
Result.success()
38+
} catch (e: Exception) {
39+
TSLog.e(TAG, "integration login failed: $e")
40+
Result.failure()
41+
}
42+
}
43+
44+
private suspend fun login(controlURL: String, authKey: String) {
45+
val app = App.get()
46+
app.startForegroundForLogin()
47+
48+
val client = Client(app.applicationScope)
49+
val maskedPrefs =
50+
Ipn.MaskedPrefs().apply {
51+
ControlURL = controlURL
52+
LoggedOut = false
53+
}
54+
55+
val prefs = await<Ipn.Prefs> { client.editPrefs(maskedPrefs, it) }.getOrThrow()
56+
prefs.WantRunning = true
57+
58+
val opts = Ipn.Options(UpdatePrefs = prefs, AuthKey = authKey)
59+
await<Unit> { client.start(opts, it) }.getOrThrow()
60+
await<Unit> { client.startLoginInteractive(it) }.getOrThrow()
61+
app.startVPN()
62+
}
63+
64+
private suspend fun <T> await(call: ((kotlin.Result<T>) -> Unit) -> Unit): kotlin.Result<T> {
65+
val result = CompletableDeferred<kotlin.Result<T>>()
66+
call { result.complete(it) }
67+
return result.await()
68+
}
69+
70+
companion object {
71+
const val TAG = "IntegrationLoginWorker"
72+
const val WORK_NAME = "ipn-integration-login"
73+
const val EXTRA_CONTROL_URL = "control_url"
74+
const val EXTRA_AUTH_KEY = "auth_key"
75+
}
76+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright (c) Tailscale Inc & AUTHORS
2+
# SPDX-License-Identifier: BSD-3-Clause
3+
4+
FROM --platform=linux/amd64 eclipse-temurin:21
5+
6+
ENV ANDROID_HOME=/opt/android-sdk
7+
ENV ANDROID_SDK_ROOT=/opt/android-sdk
8+
ENV ANDROID_CMDLINE_TOOLS_VERSION=9477386
9+
ENV PATH=$PATH:/opt/android-sdk/cmdline-tools/latest/bin:/opt/android-sdk/platform-tools:/opt/android-sdk/emulator
10+
11+
RUN apt-get update && \
12+
apt-get -y install --no-install-recommends \
13+
ca-certificates \
14+
curl \
15+
gcc \
16+
git \
17+
libc6-dev \
18+
libdbus-1-3 \
19+
libfontconfig1 \
20+
libgl1 \
21+
libglib2.0-0 \
22+
libnss3 \
23+
libpulse0 \
24+
libstdc++6 \
25+
libvulkan1 \
26+
libx11-6 \
27+
libxcb-cursor0 \
28+
libxcb1 \
29+
libxext6 \
30+
libxi6 \
31+
libxrender1 \
32+
libxtst6 \
33+
make \
34+
tar \
35+
unzip \
36+
zip && \
37+
apt-get -y clean && \
38+
rm -rf /var/lib/apt/lists/*
39+
40+
RUN mkdir -p /opt/android-sdk/cmdline-tools /tmp/android-sdk && \
41+
curl -fsSL -o /tmp/android-sdk/commandlinetools.zip \
42+
"https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" && \
43+
unzip -q /tmp/android-sdk/commandlinetools.zip -d /tmp/android-sdk && \
44+
mv /tmp/android-sdk/cmdline-tools /opt/android-sdk/cmdline-tools/latest && \
45+
rm -rf /tmp/android-sdk
46+
47+
RUN yes | sdkmanager --licenses >/dev/null && \
48+
sdkmanager \
49+
"build-tools;34.0.0" \
50+
"emulator" \
51+
"ndk;27.2.12479018" \
52+
"platform-tools" \
53+
"platforms;android-34" \
54+
"system-images;android-33;google_apis;x86_64"
55+
56+
COPY scripts/run-android-integration-test-docker.sh /usr/local/bin/run-android-integration-test
57+
RUN chmod 0755 /usr/local/bin/run-android-integration-test
58+
59+
WORKDIR /workspace
60+
CMD ["/usr/local/bin/run-android-integration-test"]

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,19 @@ require (
2626
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect
2727
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
2828
github.com/aws/smithy-go v1.24.0 // indirect
29+
github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02 // indirect
2930
github.com/coder/websocket v1.8.12 // indirect
3031
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
3132
github.com/creachadair/msync v0.7.1 // indirect
3233
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
34+
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect
3335
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
3436
github.com/djherbis/times v1.6.0 // indirect
3537
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
3638
github.com/gaissmai/bart v0.26.1 // indirect
3739
github.com/go-json-experiment/json v0.0.0-20260214004413-d219187c3433 // indirect
3840
github.com/go-ole/go-ole v1.3.0 // indirect
41+
github.com/go4org/hashtriemap v0.0.0-20251130024219-545ba229f689 // indirect
3942
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
4043
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
4144
github.com/google/btree v1.1.3 // indirect
@@ -76,6 +79,7 @@ require (
7679
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
7780
golang.org/x/mod v0.35.0 // indirect
7881
golang.org/x/net v0.53.0 // indirect
82+
golang.org/x/oauth2 v0.36.0 // indirect
7983
golang.org/x/sync v0.20.0 // indirect
8084
golang.org/x/sys v0.43.0 // indirect
8185
golang.org/x/term v0.42.0 // indirect

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ github.com/creachadair/taskgroup v0.13.2/go.mod h1:i3V1Zx7H8RjwljUEeUWYT30Lmb9po
5757
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
5858
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
5959
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
60+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6061
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
6162
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6263
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
@@ -137,6 +138,8 @@ github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
137138
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
138139
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
139140
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
141+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
142+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
140143
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
141144
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
142145
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
@@ -148,6 +151,10 @@ github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Q
148151
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
149152
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
150153
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
154+
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
155+
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
156+
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
157+
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
151158
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
152159
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
153160
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
@@ -210,6 +217,8 @@ golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
210217
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
211218
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
212219
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
220+
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
221+
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
213222
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
214223
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
215224
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
@@ -233,6 +242,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
233242
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
234243
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
235244
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
245+
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
246+
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
236247
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
237248
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
238249
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

0 commit comments

Comments
 (0)