diff --git a/.github/workflows/test-integration-cli.yml b/.github/workflows/test-integration-cli.yml new file mode 100644 index 00000000..de288fd9 --- /dev/null +++ b/.github/workflows/test-integration-cli.yml @@ -0,0 +1,40 @@ +name: CI + +on: [pull_request] + +jobs: + integration-test-cli: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 10 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + *.nix + go.* + **/*.go + integration_test/ + config-example.yaml + + - uses: cachix/install-nix-action@v16 + if: steps.changed-files.outputs.any_changed == 'true' + + - name: Run CLI integration tests + if: steps.changed-files.outputs.any_changed == 'true' + uses: nick-fields/retry@v2 + with: + timeout_minutes: 240 + max_attempts: 5 + retry_on: error + command: nix develop --command -- make test_integration_cli diff --git a/.github/workflows/test-integration.yml b/.github/workflows/test-integration-derp.yml similarity index 50% rename from .github/workflows/test-integration.yml rename to .github/workflows/test-integration-derp.yml index f2adfa19..88b7cf47 100644 --- a/.github/workflows/test-integration.yml +++ b/.github/workflows/test-integration-derp.yml @@ -3,7 +3,7 @@ name: CI on: [pull_request] jobs: - integration-test: + integration-test-derp: runs-on: ubuntu-latest steps: @@ -30,15 +30,6 @@ jobs: - uses: cachix/install-nix-action@v16 if: steps.changed-files.outputs.any_changed == 'true' - - name: Run CLI integration tests - if: steps.changed-files.outputs.any_changed == 'true' - uses: nick-fields/retry@v2 - with: - timeout_minutes: 240 - max_attempts: 5 - retry_on: error - command: nix develop --command -- make test_integration_cli - - name: Run Embedded DERP server integration tests if: steps.changed-files.outputs.any_changed == 'true' uses: nick-fields/retry@v2 @@ -47,21 +38,3 @@ jobs: max_attempts: 5 retry_on: error command: nix develop --command -- make test_integration_derp - - - name: Run OIDC integration tests - if: steps.changed-files.outputs.any_changed == 'true' - uses: nick-fields/retry@v2 - with: - timeout_minutes: 240 - max_attempts: 5 - retry_on: error - command: nix develop --command -- make test_integration_oidc - - - name: Run general integration tests - if: steps.changed-files.outputs.any_changed == 'true' - uses: nick-fields/retry@v2 - with: - timeout_minutes: 240 - max_attempts: 5 - retry_on: error - command: nix develop --command -- make test_integration_general diff --git a/.github/workflows/test-integration-general.yml b/.github/workflows/test-integration-general.yml new file mode 100644 index 00000000..01b31d96 --- /dev/null +++ b/.github/workflows/test-integration-general.yml @@ -0,0 +1,40 @@ +name: CI + +on: [pull_request] + +jobs: + integration-test-general: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 10 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + *.nix + go.* + **/*.go + integration_test/ + config-example.yaml + + - uses: cachix/install-nix-action@v16 + if: steps.changed-files.outputs.any_changed == 'true' + + - name: Run general integration tests + if: steps.changed-files.outputs.any_changed == 'true' + uses: nick-fields/retry@v2 + with: + timeout_minutes: 240 + max_attempts: 5 + retry_on: error + command: nix develop --command -- make test_integration_general diff --git a/.github/workflows/test-integration-oidc.yml b/.github/workflows/test-integration-oidc.yml new file mode 100644 index 00000000..e8afc1bf --- /dev/null +++ b/.github/workflows/test-integration-oidc.yml @@ -0,0 +1,40 @@ +name: CI + +on: [pull_request] + +jobs: + integration-test-oidc: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 10 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + *.nix + go.* + **/*.go + integration_test/ + config-example.yaml + + - uses: cachix/install-nix-action@v16 + if: steps.changed-files.outputs.any_changed == 'true' + + - name: Run OIDC integration tests + if: steps.changed-files.outputs.any_changed == 'true' + uses: nick-fields/retry@v2 + with: + timeout_minutes: 240 + max_attempts: 5 + retry_on: error + command: nix develop --command -- make test_integration_oidc diff --git a/.github/workflows/test-integration-v2-general.yml b/.github/workflows/test-integration-v2-general.yml new file mode 100644 index 00000000..fdd98d07 --- /dev/null +++ b/.github/workflows/test-integration-v2-general.yml @@ -0,0 +1,40 @@ +name: CI + +on: [pull_request] + +jobs: + integration-test-v2-general: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 10 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v14.1 + with: + files: | + *.nix + go.* + **/*.go + integration_test/ + config-example.yaml + + - uses: cachix/install-nix-action@v16 + if: steps.changed-files.outputs.any_changed == 'true' + + - name: Run general integration tests + if: steps.changed-files.outputs.any_changed == 'true' + uses: nick-fields/retry@v2 + with: + timeout_minutes: 240 + max_attempts: 5 + retry_on: error + command: nix develop --command -- make test_integration_v2_general diff --git a/CHANGELOG.md b/CHANGELOG.md index d5080aaf..fe706c45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Give a warning when running Headscale with reverse proxy improperly configured for WebSockets [#788](https://github.com/juanfont/headscale/pull/788) - Fix subnet routers with Primary Routes [#811](https://github.com/juanfont/headscale/pull/811) - Added support for JSON logs [#653](https://github.com/juanfont/headscale/issues/653) +- Sanitise the node key passed to registration url [#823](https://github.com/juanfont/headscale/pull/823) - Add support for generating pre-auth keys with tags [#767](https://github.com/juanfont/headscale/pull/767) - Add support for evaluating `autoApprovers` ACL entries when a machine is registered [#763](https://github.com/juanfont/headscale/pull/763) - Add config flag to allow Headscale to start if OIDC provider is down [#829](https://github.com/juanfont/headscale/pull/829) diff --git a/Makefile b/Makefile index 84cb63cf..431e1076 100644 --- a/Makefile +++ b/Makefile @@ -22,21 +22,59 @@ build: dev: lint test build test: - @go test -coverprofile=coverage.out ./... + @go test -short -coverprofile=coverage.out ./... test_integration: test_integration_cli test_integration_derp test_integration_oidc test_integration_general test_integration_cli: - go test -failfast -tags integration_cli,integration -timeout 30m -count=1 ./... + docker network rm $$(docker network ls --filter name=headscale --quiet) || true + docker network create headscale-test || true + docker run -t --rm \ + --network headscale-test \ + -v ~/.cache/hs-integration-go:/go \ + -v $$PWD:$$PWD -w $$PWD \ + -v /var/run/docker.sock:/var/run/docker.sock golang:1 \ + go test -failfast -timeout 30m -count=1 -run IntegrationCLI ./... test_integration_derp: - go test -failfast -tags integration_derp,integration -timeout 30m -count=1 ./... + docker network rm $$(docker network ls --filter name=headscale --quiet) || true + docker network create headscale-test || true + docker run -t --rm \ + --network headscale-test \ + -v ~/.cache/hs-integration-go:/go \ + -v $$PWD:$$PWD -w $$PWD \ + -v /var/run/docker.sock:/var/run/docker.sock golang:1 \ + go test -failfast -timeout 30m -count=1 -run IntegrationDERP ./... test_integration_general: - go test -failfast -tags integration_general,integration -timeout 30m -count=1 ./... + docker network rm $$(docker network ls --filter name=headscale --quiet) || true + docker network create headscale-test || true + docker run -t --rm \ + --network headscale-test \ + -v ~/.cache/hs-integration-go:/go \ + -v $$PWD:$$PWD -w $$PWD \ + -v /var/run/docker.sock:/var/run/docker.sock golang:1 \ + go test -failfast -timeout 30m -count=1 -run IntegrationGeneral ./... test_integration_oidc: - go test -failfast -tags integration_oidc,integration -timeout 30m -count=1 ./... + docker network rm $$(docker network ls --filter name=headscale --quiet) || true + docker network create headscale-test || true + docker run -t --rm \ + --network headscale-test \ + -v ~/.cache/hs-integration-go:/go \ + -v $$PWD:$$PWD -w $$PWD \ + -v /var/run/docker.sock:/var/run/docker.sock golang:1 \ + go test -failfast -timeout 30m -count=1 -run IntegrationOIDC ./... + +test_integration_v2_general: + docker run \ + -t --rm \ + -v ~/.cache/hs-integration-go:/go \ + --name headscale-test-suite \ + -v $$PWD:$$PWD -w $$PWD/integration \ + -v /var/run/docker.sock:/var/run/docker.sock \ + golang:1 \ + go test ./... -timeout 15m -v coverprofile_func: go tool cover -func=coverage.out diff --git a/api.go b/api.go index f5de5038..4c3a6ca8 100644 --- a/api.go +++ b/api.go @@ -9,6 +9,7 @@ import ( "github.com/gorilla/mux" "github.com/rs/zerolog/log" + "tailscale.com/types/key" ) const ( @@ -93,7 +94,34 @@ func (h *Headscale) RegisterWebAPI( ) { vars := mux.Vars(req) nodeKeyStr, ok := vars["nkey"] - if !ok || nodeKeyStr == "" { + + if !NodePublicKeyRegex.Match([]byte(nodeKeyStr)) { + log.Warn().Str("node_key", nodeKeyStr).Msg("Invalid node key passed to registration url") + + writer.Header().Set("Content-Type", "text/plain; charset=utf-8") + writer.WriteHeader(http.StatusUnauthorized) + _, err := writer.Write([]byte("Unauthorized")) + if err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Failed to write response") + } + + return + } + + // We need to make sure we dont open for XSS style injections, if the parameter that + // is passed as a key is not parsable/validated as a NodePublic key, then fail to render + // the template and log an error. + var nodeKey key.NodePublic + err := nodeKey.UnmarshalText( + []byte(NodePublicKeyEnsurePrefix(nodeKeyStr)), + ) + + if !ok || nodeKeyStr == "" || err != nil { + log.Warn().Err(err).Msg("Failed to parse incoming nodekey") + writer.Header().Set("Content-Type", "text/plain; charset=utf-8") writer.WriteHeader(http.StatusBadRequest) _, err := writer.Write([]byte("Wrong params")) @@ -130,7 +158,7 @@ func (h *Headscale) RegisterWebAPI( writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(http.StatusOK) - _, err := writer.Write(content.Bytes()) + _, err = writer.Write(content.Bytes()) if err != nil { log.Error(). Caller(). diff --git a/cmd/headscale/cli/mockoidc.go b/cmd/headscale/cli/mockoidc.go index 4313bbf6..165a5115 100644 --- a/cmd/headscale/cli/mockoidc.go +++ b/cmd/headscale/cli/mockoidc.go @@ -46,6 +46,10 @@ func mockOIDC() error { if clientSecret == "" { return errMockOidcClientSecretNotDefined } + addrStr := os.Getenv("MOCKOIDC_ADDR") + if addrStr == "" { + return errMockOidcPortNotDefined + } portStr := os.Getenv("MOCKOIDC_PORT") if portStr == "" { return errMockOidcPortNotDefined @@ -61,7 +65,7 @@ func mockOIDC() error { return err } - listener, err := net.Listen("tcp", fmt.Sprintf("mockoidc:%d", port)) + listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addrStr, port)) if err != nil { return err } diff --git a/cmd/headscale/cli/root.go b/cmd/headscale/cli/root.go index bd526203..52a3f1b8 100644 --- a/cmd/headscale/cli/root.go +++ b/cmd/headscale/cli/root.go @@ -15,7 +15,7 @@ import ( var cfgFile string = "" func init() { - if len(os.Args) > 1 && os.Args[1] == "version" || os.Args[1] == "mockoidc" { + if len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "mockoidc") { return } diff --git a/docs/reverse-proxy.md b/docs/reverse-proxy.md index 74bbff71..1c7e5804 100644 --- a/docs/reverse-proxy.md +++ b/docs/reverse-proxy.md @@ -59,3 +59,42 @@ server { } } ``` + +## istio/envoy + +If you using [Istio](https://istio.io/) ingressgateway or [Envoy](https://www.envoyproxy.io/) as reverse proxy, there are some tips for you. If not set, you may see some debug log in proxy as below: + +```log +Sending local reply with details upgrade_failed +``` + +### Envoy + +You need add a new upgrade_type named `tailscale-control-protocol`. [see detail](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto#extensions-filters-network-http-connection-manager-v3-httpconnectionmanager-upgradeconfig) + +### Istio + +Same as envoy, we can use `EnvoyFilter` to add upgrade_type. + +```yaml +apiVersion: networking.istio.io/v1alpha3 +kind: EnvoyFilter +metadata: + name: headscale-behind-istio-ingress + namespace: istio-system +spec: + configPatches: + - applyTo: NETWORK_FILTER + match: + listener: + filterChain: + filter: + name: envoy.filters.network.http_connection_manager + patch: + operation: MERGE + value: + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + upgrade_configs: + - upgrade_type: tailscale-control-protocol +``` diff --git a/docs/running-headscale-container.md b/docs/running-headscale-container.md index f3626e9f..afbc1c56 100644 --- a/docs/running-headscale-container.md +++ b/docs/running-headscale-container.md @@ -55,7 +55,7 @@ metrics_listen_addr: 0.0.0.0:9090 private_key_path: /etc/headscale/private.key # The default /var/lib/headscale path is not writable in the container noise: - private_key_path: /var/lib/headscale/noise_private.key + private_key_path: /etc/headscale/noise_private.key # The default /var/lib/headscale path is not writable in the container db_path: /etc/headscale/db.sqlite ``` diff --git a/flake.nix b/flake.nix index 050fd747..6775e9cb 100644 --- a/flake.nix +++ b/flake.nix @@ -26,6 +26,9 @@ version = headscaleVersion; src = pkgs.lib.cleanSource self; + # Only run unit tests when testing a build + checkFlags = ["-short"]; + # When updating go.mod or go.sum, a new sha will need to be calculated, # update this if you have a mismatch after doing a change to thos files. vendorSha256 = "sha256-DosFCSiQ5FURbIrt4NcPGkExc84t2MGMqe9XLxNHdIM="; @@ -128,7 +131,13 @@ }; in rec { # `nix develop` - devShell = pkgs.mkShell {buildInputs = devDeps;}; + devShell = pkgs.mkShell { + buildInputs = devDeps; + + shellHook = '' + export GOFLAGS=-tags="integration,integration_general,integration_oidc,integration_cli,integration_derp" + ''; + }; # `nix build` packages = with pkgs; { diff --git a/go.mod b/go.mod index 24463c34..53f1fd91 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/AlecAivazis/survey/v2 v2.3.5 github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 + github.com/cenkalti/backoff/v4 v4.1.3 github.com/coreos/go-oidc/v3 v3.3.0 github.com/deckarep/golang-set/v2 v2.1.0 github.com/efekarakus/termcolor v1.0.1 @@ -54,7 +55,6 @@ require ( github.com/akutz/memconn v0.1.0 // indirect github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/containerd/console v1.0.3 // indirect github.com/containerd/continuity v0.3.0 // indirect diff --git a/go.sum b/go.sum index 0567a055..180e7c37 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,6 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EU dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/mkcert v1.4.3 h1:axpnmtrZMM8u5Hf4N3UXxboGemMOV+Tn+e+pkHM6E3o= github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ= github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= @@ -80,8 +78,6 @@ github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCq github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= @@ -154,7 +150,6 @@ github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029/go.mod h1:Rpr5 github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -168,8 +163,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao= github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk= -github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= -github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -189,8 +182,6 @@ github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvA github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc/v3 v3.2.0 h1:2eR2MGR7thBXSQ2YbODlF0fcmgtliLCfr9iX6RW11fc= -github.com/coreos/go-oidc/v3 v3.2.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-oidc/v3 v3.3.0 h1:Y1LV3mP+QT3MEycATZpAiwfyN+uxZLqVbAHJUuOJEe4= github.com/coreos/go-oidc/v3 v3.3.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -224,18 +215,12 @@ github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCF github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/cli v20.10.16+incompatible h1:aLQ8XowgKpR3/IysPj8qZQJBVQ+Qws61icFuZl6iKYs= github.com/docker/cli v20.10.16+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M= -github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ= github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/efekarakus/termcolor v1.0.1 h1:YAKFO3bnLrqZGTWyNLcYoSIAQFKVOmbqmDnwsU/znzg= @@ -273,10 +258,10 @@ github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASx github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk= github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY= -github.com/glebarez/go-sqlite v1.18.1 h1:w0xtxKWktqYsUsXg//SQK+l1IcpKb3rGOQHmMptvL2U= -github.com/glebarez/go-sqlite v1.18.1/go.mod h1:ydXIGq2M4OzF4YyNhH129SPp7jWoVvgkEgb6pldmS0s= github.com/glebarez/sqlite v1.4.6 h1:D5uxD2f6UJ82cHnVtO2TZ9pqsLyto3fpDKHIk2OsR8A= github.com/glebarez/sqlite v1.4.6/go.mod h1:WYEtEFjhADPaPJqL/PGlbQQGINBA3eUAfDNbKFJf/zA= github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM= @@ -453,8 +438,6 @@ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= -github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= -github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= @@ -484,10 +467,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.2 h1:BqHID5W5qnMkug0Z8UmL8tN0gAy4jQ+B4WFt8cCgluU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.2/go.mod h1:ZbS3MZTZq/apAfAEHGoB5HbsQQstoqP92SjAqtQ9zeg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -511,8 +491,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -539,13 +517,8 @@ github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -558,8 +531,6 @@ github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8 github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8= github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono= -github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= -github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -568,7 +539,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -578,8 +548,6 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= -github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= @@ -588,21 +556,16 @@ github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrU github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= -github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y= github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ= -github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= -github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= @@ -610,8 +573,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= -github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -625,8 +586,6 @@ github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOv github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b h1:Yws7RV6kZr2O7PPdT+RkbSmmOponA8i/1DuGHe8BRsM= github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b/go.mod h1:TzDCVOZKUa79z6iXbbXqhtAflVgUKaFkZ21M5tK5tzY= -github.com/jsimonetti/rtnetlink v1.2.2 h1:Ok9vYMcpxfHyF/iRqNTYJPDLxVaVItvPamAhtzttDBY= -github.com/jsimonetti/rtnetlink v1.2.2/go.mod h1:T3BJ2qI9ZJFkUYWLrzECdcXhCvaGRfnMFmoYF0X8w2A= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -713,8 +672,6 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -725,8 +682,6 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -735,7 +690,6 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -749,8 +703,6 @@ github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfp github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= @@ -777,8 +729,6 @@ github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= -github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -822,8 +772,6 @@ github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+cz github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q= github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg= -github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -845,8 +793,6 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= github.com/philip-bui/grpc-zerolog v1.0.1 h1:EMacvLRUd2O1K0eWod27ZP5CY1iTNkhBDLSN+Q4JEvA= @@ -900,8 +846,6 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE= github.com/pterm/pterm v0.12.45/go.mod h1:hJgLlBafm45w/Hr0dKXxY//POD7CgowhePaG1sdPNBg= -github.com/puzpuzpuz/xsync v1.4.2 h1:3SmMNFuNaSHy1xsGGR8edy3Rzv0Ix3dOirPWtAnXvKk= -github.com/puzpuzpuz/xsync v1.4.2/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg= github.com/puzpuzpuz/xsync v1.4.3 h1:nS/Iqc4EnpJ8jm/MzJ+e3MUaP2Ys2mqXeEfoxoU0HaM= github.com/puzpuzpuz/xsync v1.4.3/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= @@ -916,8 +860,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6O github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= -github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -925,16 +867,11 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w= github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= -github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -949,7 +886,6 @@ github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dms github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= @@ -969,8 +905,6 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -982,8 +916,6 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1026,8 +958,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tailscale/hujson v0.0.0-20220630195928-54599719472f h1:n4r/sJ92cBSBHK8n9lR1XLFr0OiTVeGfN5TR+9LaN7E= @@ -1068,8 +998,6 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= @@ -1119,16 +1047,10 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4= go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= -go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= -go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= -go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf h1:IdwJUzqoIo5lkr2EOyKoe5qipUaEjbOKKY5+fzPBZ3A= -go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf/go.mod h1:+QXzaoURFd0rGDIjDNpyIkv+F9R7EmeKorvlKRnhqgA= go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw= go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= -go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1152,8 +1074,6 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1226,7 +1146,6 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1257,7 +1176,6 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -1282,8 +1200,6 @@ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw= -golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1299,8 +1215,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1406,20 +1320,14 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1428,8 +1336,6 @@ golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1440,8 +1346,6 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y= -golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1449,8 +1353,6 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1571,8 +1473,6 @@ golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNq golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8= golang.zx2c4.com/wireguard/windows v0.4.10 h1:HmjzJnb+G4NCdX+sfjsQlsxGPuYaThxRbZUZFLyR0/s= golang.zx2c4.com/wireguard/windows v0.4.10/go.mod h1:v7w/8FC48tTBm1IzScDVPEEb0/GjLta+T0ybpP9UWRg= -golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= -golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1709,8 +1609,6 @@ google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220808204814-fd01256a5276 h1:7PEE9xCtufpGJzrqweakEEnTh7YFELmnKm/ee+5jmfQ= -google.golang.org/genproto v0.0.0-20220808204814-fd01256a5276/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220902135211-223410557253 h1:vXJMM8Shg7TGaYxZsQ++A/FOSlbDmDtWhS/o+3w/hj4= google.golang.org/genproto v0.0.0-20220902135211-223410557253/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1748,8 +1646,6 @@ google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11 google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -1786,10 +1682,7 @@ gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1807,14 +1700,10 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.3.8 h1:8bEphSAB69t3odsCR4NDzt581iZEWQuRM27Cg6KgfPY= -gorm.io/driver/postgres v1.3.8/go.mod h1:qB98Aj6AhRO/oyu/jmZsi/YM9g6UzVCjMxO/6frFvcA= gorm.io/driver/postgres v1.3.9 h1:lWGiVt5CijhQAg0PWB7Od1RNcBw/jS4d2cAScBcSDXg= gorm.io/driver/postgres v1.3.9/go.mod h1:qw/FeqjxmYqW5dBcYNBsnhQULIApQdk7YuuDPktVi1U= -gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= @@ -1836,7 +1725,6 @@ modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpN modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= @@ -1845,24 +1733,14 @@ modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o= modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.3 h1:MbsE18Y/xJMTxGGjq2Il6aZZf1YFUu6mnRZgQm/VUWQ= -modernc.org/libc v1.17.3/go.mod h1:9y68dPDczc/zwZupQ2yLX/HTAC4YY/u3grUCeD5fC9I= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= -modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/integration/control.go b/integration/control.go new file mode 100644 index 00000000..bcda4a56 --- /dev/null +++ b/integration/control.go @@ -0,0 +1,13 @@ +package integration + +import v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + +type ControlServer interface { + Shutdown() error + GetHealthEndpoint() string + GetEndpoint() string + WaitForReady() error + CreateNamespace(namespace string) error + CreateAuthKey(namespace string) (*v1.PreAuthKey, error) + ListNodes(namespace string) ([]*v1.Machine, error) +} diff --git a/integration/dockertestutil/config.go b/integration/dockertestutil/config.go new file mode 100644 index 00000000..4dc3ee33 --- /dev/null +++ b/integration/dockertestutil/config.go @@ -0,0 +1,40 @@ +package dockertestutil + +import ( + "os" + + "github.com/ory/dockertest/v3/docker" +) + +func IsRunningInContainer() bool { + if _, err := os.Stat("/.dockerenv"); err != nil { + return false + } + + return true +} + +func DockerRestartPolicy(config *docker.HostConfig) { + // set AutoRemove to true so that stopped container goes away by itself on error *immediately*. + // when set to false, containers remain until the end of the integration test. + config.AutoRemove = false + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } +} + +func DockerAllowLocalIPv6(config *docker.HostConfig) { + if config.Sysctls == nil { + config.Sysctls = make(map[string]string, 1) + } + config.Sysctls["net.ipv6.conf.all.disable_ipv6"] = "0" +} + +func DockerAllowNetworkAdministration(config *docker.HostConfig) { + config.CapAdd = append(config.CapAdd, "NET_ADMIN") + config.Mounts = append(config.Mounts, docker.HostMount{ + Type: "bind", + Source: "/dev/net/tun", + Target: "/dev/net/tun", + }) +} diff --git a/integration/dockertestutil/execute.go b/integration/dockertestutil/execute.go new file mode 100644 index 00000000..71afa698 --- /dev/null +++ b/integration/dockertestutil/execute.go @@ -0,0 +1,94 @@ +package dockertestutil + +import ( + "bytes" + "errors" + "fmt" + "time" + + "github.com/ory/dockertest/v3" +) + +const dockerExecuteTimeout = time.Second * 10 + +var ( + ErrDockertestCommandFailed = errors.New("dockertest command failed") + ErrDockertestCommandTimeout = errors.New("dockertest command timed out") +) + +type ExecuteCommandConfig struct { + timeout time.Duration +} + +type ExecuteCommandOption func(*ExecuteCommandConfig) error + +func ExecuteCommandTimeout(timeout time.Duration) ExecuteCommandOption { + return ExecuteCommandOption(func(conf *ExecuteCommandConfig) error { + conf.timeout = timeout + + return nil + }) +} + +func ExecuteCommand( + resource *dockertest.Resource, + cmd []string, + env []string, + options ...ExecuteCommandOption, +) (string, string, error) { + var stdout bytes.Buffer + var stderr bytes.Buffer + + execConfig := ExecuteCommandConfig{ + timeout: dockerExecuteTimeout, + } + + for _, opt := range options { + if err := opt(&execConfig); err != nil { + return "", "", fmt.Errorf("execute-command/options: %w", err) + } + } + + type result struct { + exitCode int + err error + } + + resultChan := make(chan result, 1) + + // Run your long running function in it's own goroutine and pass back it's + // response into our channel. + go func() { + exitCode, err := resource.Exec( + cmd, + dockertest.ExecOptions{ + Env: append(env, "HEADSCALE_LOG_LEVEL=disabled"), + StdOut: &stdout, + StdErr: &stderr, + }, + ) + resultChan <- result{exitCode, err} + }() + + // Listen on our channel AND a timeout channel - which ever happens first. + select { + case res := <-resultChan: + if res.err != nil { + return stdout.String(), stderr.String(), res.err + } + + if res.exitCode != 0 { + // Uncomment for debugging + // log.Println("Command: ", cmd) + // log.Println("stdout: ", stdout.String()) + // log.Println("stderr: ", stderr.String()) + + return stdout.String(), stderr.String(), ErrDockertestCommandFailed + } + + return stdout.String(), stderr.String(), nil + case <-time.After(execConfig.timeout): + + return stdout.String(), stderr.String(), ErrDockertestCommandTimeout + } +} diff --git a/integration/dockertestutil/network.go b/integration/dockertestutil/network.go new file mode 100644 index 00000000..15c99082 --- /dev/null +++ b/integration/dockertestutil/network.go @@ -0,0 +1,62 @@ +package dockertestutil + +import ( + "errors" + + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" +) + +var ErrContainerNotFound = errors.New("container not found") + +func GetFirstOrCreateNetwork(pool *dockertest.Pool, name string) (*dockertest.Network, error) { + networks, err := pool.NetworksByName(name) + if err != nil || len(networks) == 0 { + if _, err := pool.CreateNetwork(name); err == nil { + // Create does not give us an updated version of the resource, so we need to + // get it again. + networks, err := pool.NetworksByName(name) + if err != nil { + return nil, err + } + + return &networks[0], nil + } + } + + return &networks[0], nil +} + +func AddContainerToNetwork( + pool *dockertest.Pool, + network *dockertest.Network, + testContainer string, +) error { + containers, err := pool.Client.ListContainers(docker.ListContainersOptions{ + All: true, + Filters: map[string][]string{ + "name": {testContainer}, + }, + }) + if err != nil { + return err + } + + err = pool.Client.ConnectNetwork(network.Network.ID, docker.NetworkConnectionOptions{ + Container: containers[0].ID, + }) + if err != nil { + return err + } + + // TODO(kradalby): This doesnt work reliably, but calling the exact same functions + // seem to work fine... + // if container, ok := pool.ContainerByName("/" + testContainer); ok { + // err := container.ConnectToNetwork(network) + // if err != nil { + // return err + // } + // } + + return nil +} diff --git a/integration/general_test.go b/integration/general_test.go new file mode 100644 index 00000000..2e7689ac --- /dev/null +++ b/integration/general_test.go @@ -0,0 +1,78 @@ +package integration + +import ( + "net/netip" + "testing" + + "github.com/juanfont/headscale/integration/tsic" +) + +func TestPingAll(t *testing.T) { + IntegrationSkip(t) + + scenario, err := NewScenario() + if err != nil { + t.Errorf("failed to create scenario: %s", err) + } + + spec := map[string]int{ + "namespace1": len(TailscaleVersions), + "namespace2": len(TailscaleVersions), + } + + err = scenario.CreateHeadscaleEnv(spec) + if err != nil { + t.Errorf("failed to create headscale environment: %s", err) + } + + var allIps []netip.Addr + var allClients []*tsic.TailscaleInContainer + + for namespace, count := range spec { + ips, err := scenario.GetIPs(namespace) + if err != nil { + t.Errorf("failed to get tailscale ips: %s", err) + } + + if len(ips) != count*2 { + t.Errorf( + "got the wrong amount of tailscale ips, %d != %d", + len(ips), + count*2, + ) + } + + clients, err := scenario.GetClients(namespace) + if err != nil { + t.Errorf("failed to get tailscale clients: %s", err) + } + + allIps = append(allIps, ips...) + allClients = append(allClients, clients...) + } + + err = scenario.WaitForTailscaleSync() + if err != nil { + t.Errorf("failed wait for tailscale clients to be in sync: %s", err) + } + + success := 0 + + for _, client := range allClients { + for _, ip := range allIps { + err := client.Ping(ip) + if err != nil { + t.Errorf("failed to ping %s from %s: %s", ip, client.Hostname, err) + } else { + success++ + } + } + } + + t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps)) + + err = scenario.Shutdown() + if err != nil { + t.Errorf("failed to tear down scenario: %s", err) + } +} diff --git a/integration/hsic/hsic.go b/integration/hsic/hsic.go new file mode 100644 index 00000000..1790a0a2 --- /dev/null +++ b/integration/hsic/hsic.go @@ -0,0 +1,223 @@ +package hsic + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "net/http" + "os" + "path" + + "github.com/juanfont/headscale" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/juanfont/headscale/integration/dockertestutil" + "github.com/ory/dockertest/v3" +) + +const ( + hsicHashLength = 6 + dockerContextPath = "../." +) + +var errHeadscaleStatusCodeNotOk = errors.New("headscale status code not ok") + +type HeadscaleInContainer struct { + hostname string + port int + + pool *dockertest.Pool + container *dockertest.Resource + network *dockertest.Network +} + +func New( + pool *dockertest.Pool, + port int, + network *dockertest.Network, +) (*HeadscaleInContainer, error) { + hash, err := headscale.GenerateRandomStringDNSSafe(hsicHashLength) + if err != nil { + return nil, err + } + + headscaleBuildOptions := &dockertest.BuildOptions{ + Dockerfile: "Dockerfile", + ContextDir: dockerContextPath, + } + + hostname := fmt.Sprintf("hs-%s", hash) + portProto := fmt.Sprintf("%d/tcp", port) + + currentPath, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("could not determine current path: %w", err) + } + + integrationConfigPath := path.Join(currentPath, "..", "integration_test", "etc") + + runOptions := &dockertest.RunOptions{ + Name: hostname, + // TODO(kradalby): Do something clever here, can we ditch the config repo? + // Always generate the config from code? + Mounts: []string{ + fmt.Sprintf("%s:/etc/headscale", integrationConfigPath), + }, + ExposedPorts: []string{portProto}, + // TODO(kradalby): WHY do we need to bind these now that we run fully in docker? + Networks: []*dockertest.Network{network}, + Cmd: []string{"headscale", "serve"}, + } + + // dockertest isnt very good at handling containers that has already + // been created, this is an attempt to make sure this container isnt + // present. + err = pool.RemoveContainerByName(hostname) + if err != nil { + return nil, err + } + + container, err := pool.BuildAndRunWithBuildOptions( + headscaleBuildOptions, + runOptions, + dockertestutil.DockerRestartPolicy, + dockertestutil.DockerAllowLocalIPv6, + dockertestutil.DockerAllowNetworkAdministration, + ) + if err != nil { + return nil, fmt.Errorf("could not start headscale container: %w", err) + } + log.Printf("Created %s container\n", hostname) + + return &HeadscaleInContainer{ + hostname: hostname, + port: port, + + pool: pool, + container: container, + network: network, + }, nil +} + +func (t *HeadscaleInContainer) Shutdown() error { + return t.pool.Purge(t.container) +} + +func (t *HeadscaleInContainer) GetIP() string { + return t.container.GetIPInNetwork(t.network) +} + +func (t *HeadscaleInContainer) GetPort() string { + portProto := fmt.Sprintf("%d/tcp", t.port) + + return t.container.GetPort(portProto) +} + +func (t *HeadscaleInContainer) GetHealthEndpoint() string { + hostEndpoint := fmt.Sprintf("%s:%d", + t.GetIP(), + t.port) + + return fmt.Sprintf("http://%s/health", hostEndpoint) +} + +func (t *HeadscaleInContainer) GetEndpoint() string { + hostEndpoint := fmt.Sprintf("%s:%d", + t.GetIP(), + t.port) + + return fmt.Sprintf("http://%s", hostEndpoint) +} + +func (t *HeadscaleInContainer) WaitForReady() error { + url := t.GetHealthEndpoint() + + log.Printf("waiting for headscale to be ready at %s", url) + + return t.pool.Retry(func() error { + resp, err := http.Get(url) //nolint + if err != nil { + return fmt.Errorf("headscale is not ready: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return errHeadscaleStatusCodeNotOk + } + + return nil + }) +} + +func (t *HeadscaleInContainer) CreateNamespace( + namespace string, +) error { + command := []string{"headscale", "namespaces", "create", namespace} + + _, _, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + return err + } + + return nil +} + +func (t *HeadscaleInContainer) CreateAuthKey( + namespace string, +) (*v1.PreAuthKey, error) { + command := []string{ + "headscale", + "--namespace", + namespace, + "preauthkeys", + "create", + "--reusable", + "--expiration", + "24h", + "--output", + "json", + } + + result, _, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + return nil, fmt.Errorf("failed to execute create auth key command: %w", err) + } + + var preAuthKey v1.PreAuthKey + err = json.Unmarshal([]byte(result), &preAuthKey) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal auth key: %w", err) + } + + return &preAuthKey, nil +} + +func (t *HeadscaleInContainer) ListNodes( + namespace string, +) ([]*v1.Machine, error) { + command := []string{"headscale", "--namespace", namespace, "nodes", "list", "--output", "json"} + + result, _, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + return nil, fmt.Errorf("failed to execute list node command: %w", err) + } + + var nodes []*v1.Machine + err = json.Unmarshal([]byte(result), &nodes) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal nodes: %w", err) + } + + return nodes, nil +} diff --git a/integration/scenario.go b/integration/scenario.go new file mode 100644 index 00000000..ebe3aff2 --- /dev/null +++ b/integration/scenario.go @@ -0,0 +1,347 @@ +package integration + +import ( + "errors" + "fmt" + "log" + "net/netip" + "os" + "sync" + "time" + + "github.com/juanfont/headscale" + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" + "github.com/juanfont/headscale/integration/dockertestutil" + "github.com/juanfont/headscale/integration/hsic" + "github.com/juanfont/headscale/integration/tsic" + "github.com/ory/dockertest/v3" +) + +const ( + scenarioHashLength = 6 + maxWait = 60 * time.Second + headscalePort = 8080 +) + +var ( + errNoHeadscaleAvailable = errors.New("no headscale available") + errNoNamespaceAvailable = errors.New("no namespace available") + TailscaleVersions = []string{ + "head", + "unstable", + "1.32.0", + "1.30.2", + "1.28.0", + "1.26.2", + "1.24.2", + "1.22.2", + "1.20.4", + "1.18.2", + "1.16.2", + + // These versions seem to fail when fetching from apt. + // "1.14.6", + // "1.12.4", + // "1.10.2", + // "1.8.7", + } +) + +type Namespace struct { + Clients map[string]*tsic.TailscaleInContainer + + createWaitGroup sync.WaitGroup + joinWaitGroup sync.WaitGroup + syncWaitGroup sync.WaitGroup +} + +// TODO(kradalby): make control server configurable, test test correctness with +// Tailscale SaaS. +type Scenario struct { + // TODO(kradalby): support multiple headcales for later, currently only + // use one. + controlServers map[string]ControlServer + + namespaces map[string]*Namespace + + pool *dockertest.Pool + network *dockertest.Network +} + +func NewScenario() (*Scenario, error) { + hash, err := headscale.GenerateRandomStringDNSSafe(scenarioHashLength) + if err != nil { + return nil, err + } + + pool, err := dockertest.NewPool("") + if err != nil { + return nil, fmt.Errorf("could not connect to docker: %w", err) + } + + pool.MaxWait = maxWait + + networkName := fmt.Sprintf("hs-%s", hash) + if overrideNetworkName := os.Getenv("HEADSCALE_TEST_NETWORK_NAME"); overrideNetworkName != "" { + networkName = overrideNetworkName + } + + network, err := dockertestutil.GetFirstOrCreateNetwork(pool, networkName) + if err != nil { + return nil, fmt.Errorf("failed to create or get network: %w", err) + } + + // We run the test suite in a docker container that calls a couple of endpoints for + // readiness checks, this ensures that we can run the tests with individual networks + // and have the client reach the different containers + err = dockertestutil.AddContainerToNetwork(pool, network, "headscale-test-suite") + if err != nil { + return nil, fmt.Errorf("failed to add test suite container to network: %w", err) + } + + return &Scenario{ + controlServers: make(map[string]ControlServer), + namespaces: make(map[string]*Namespace), + + pool: pool, + network: network, + }, nil +} + +func (s *Scenario) Shutdown() error { + for _, control := range s.controlServers { + err := control.Shutdown() + if err != nil { + return fmt.Errorf("failed to tear down control: %w", err) + } + } + + for namespaceName, namespace := range s.namespaces { + for _, client := range namespace.Clients { + log.Printf("removing client %s in namespace %s", client.Hostname, namespaceName) + err := client.Shutdown() + if err != nil { + return fmt.Errorf("failed to tear down client: %w", err) + } + } + } + + if err := s.pool.RemoveNetwork(s.network); err != nil { + return fmt.Errorf("failed to remove network: %w", err) + } + + // TODO(kradalby): This seem redundant to the previous call + // if err := s.network.Close(); err != nil { + // return fmt.Errorf("failed to tear down network: %w", err) + // } + + return nil +} + +/// Headscale related stuff +// Note: These functions assume that there is a _single_ headscale instance for now + +// TODO(kradalby): make port and headscale configurable, multiple instances support? +func (s *Scenario) StartHeadscale() error { + headscale, err := hsic.New(s.pool, headscalePort, s.network) + if err != nil { + return fmt.Errorf("failed to create headscale container: %w", err) + } + + s.controlServers["headscale"] = headscale + + return nil +} + +func (s *Scenario) Headscale() *hsic.HeadscaleInContainer { + //nolint + return s.controlServers["headscale"].(*hsic.HeadscaleInContainer) +} + +func (s *Scenario) CreatePreAuthKey(namespace string) (*v1.PreAuthKey, error) { + if headscale, ok := s.controlServers["headscale"]; ok { + key, err := headscale.CreateAuthKey(namespace) + if err != nil { + return nil, fmt.Errorf("failed to create namespace: %w", err) + } + + return key, nil + } + + return nil, fmt.Errorf("failed to create namespace: %w", errNoHeadscaleAvailable) +} + +func (s *Scenario) CreateNamespace(namespace string) error { + if headscale, ok := s.controlServers["headscale"]; ok { + err := headscale.CreateNamespace(namespace) + if err != nil { + return fmt.Errorf("failed to create namespace: %w", err) + } + + s.namespaces[namespace] = &Namespace{ + Clients: make(map[string]*tsic.TailscaleInContainer), + } + + return nil + } + + return fmt.Errorf("failed to create namespace: %w", errNoHeadscaleAvailable) +} + +/// Client related stuff + +func (s *Scenario) CreateTailscaleNodesInNamespace( + namespaceStr string, + requestedVersion string, + count int, +) error { + if namespace, ok := s.namespaces[namespaceStr]; ok { + for i := 0; i < count; i++ { + version := requestedVersion + if requestedVersion == "all" { + version = TailscaleVersions[i%len(TailscaleVersions)] + } + + namespace.createWaitGroup.Add(1) + + go func() { + defer namespace.createWaitGroup.Done() + + // TODO(kradalby): error handle this + tsClient, err := tsic.New(s.pool, version, s.network) + if err != nil { + // return fmt.Errorf("failed to add tailscale node: %w", err) + log.Printf("failed to add tailscale node: %s", err) + } + + namespace.Clients[tsClient.Hostname] = tsClient + }() + } + namespace.createWaitGroup.Wait() + + return nil + } + + return fmt.Errorf("failed to add tailscale node: %w", errNoNamespaceAvailable) +} + +func (s *Scenario) RunTailscaleUp( + namespaceStr, loginServer, authKey string, +) error { + if namespace, ok := s.namespaces[namespaceStr]; ok { + for _, client := range namespace.Clients { + namespace.joinWaitGroup.Add(1) + + go func(c *tsic.TailscaleInContainer) { + defer namespace.joinWaitGroup.Done() + + // TODO(kradalby): error handle this + _ = c.Up(loginServer, authKey) + }(client) + } + namespace.joinWaitGroup.Wait() + + return nil + } + + return fmt.Errorf("failed to up tailscale node: %w", errNoNamespaceAvailable) +} + +func (s *Scenario) CountTailscale() int { + count := 0 + + for _, namespace := range s.namespaces { + count += len(namespace.Clients) + } + + return count +} + +func (s *Scenario) WaitForTailscaleSync() error { + tsCount := s.CountTailscale() + + for _, namespace := range s.namespaces { + for _, client := range namespace.Clients { + namespace.syncWaitGroup.Add(1) + + go func(c *tsic.TailscaleInContainer) { + defer namespace.syncWaitGroup.Done() + + // TODO(kradalby): error handle this + _ = c.WaitForPeers(tsCount) + }(client) + } + namespace.syncWaitGroup.Wait() + } + + return nil +} + +// CreateHeadscaleEnv is a conventient method returning a set up Headcale +// test environment with nodes of all versions, joined to the server with X +// namespaces. +func (s *Scenario) CreateHeadscaleEnv(namespaces map[string]int) error { + err := s.StartHeadscale() + if err != nil { + return err + } + + err = s.Headscale().WaitForReady() + if err != nil { + return err + } + + for namespaceName, clientCount := range namespaces { + err = s.CreateNamespace(namespaceName) + if err != nil { + return err + } + + err = s.CreateTailscaleNodesInNamespace(namespaceName, "all", clientCount) + if err != nil { + return err + } + + key, err := s.CreatePreAuthKey(namespaceName) + if err != nil { + return err + } + + err = s.RunTailscaleUp(namespaceName, s.Headscale().GetEndpoint(), key.GetKey()) + if err != nil { + return err + } + } + + return nil +} + +func (s *Scenario) GetIPs(namespace string) ([]netip.Addr, error) { + var ips []netip.Addr + if ns, ok := s.namespaces[namespace]; ok { + for _, client := range ns.Clients { + clientIps, err := client.IPs() + if err != nil { + return ips, fmt.Errorf("failed to get ips: %w", err) + } + ips = append(ips, clientIps...) + } + + return ips, nil + } + + return ips, fmt.Errorf("failed to get ips: %w", errNoNamespaceAvailable) +} + +func (s *Scenario) GetClients(namespace string) ([]*tsic.TailscaleInContainer, error) { + var clients []*tsic.TailscaleInContainer + if ns, ok := s.namespaces[namespace]; ok { + for _, client := range ns.Clients { + clients = append(clients, client) + } + + return clients, nil + } + + return clients, fmt.Errorf("failed to get clients: %w", errNoNamespaceAvailable) +} diff --git a/integration/scenario_test.go b/integration/scenario_test.go new file mode 100644 index 00000000..c1a51f30 --- /dev/null +++ b/integration/scenario_test.go @@ -0,0 +1,182 @@ +package integration + +import ( + "testing" + + "github.com/juanfont/headscale/integration/dockertestutil" + "github.com/juanfont/headscale/integration/tsic" +) + +// This file is intendet to "test the test framework", by proxy it will also test +// some Headcsale/Tailscale stuff, but mostly in very simple ways. + +func IntegrationSkip(t *testing.T) { + t.Helper() + + if !dockertestutil.IsRunningInContainer() { + t.Skip("not running in docker, skipping") + } + + if testing.Short() { + t.Skip("skipping integration tests due to short flag") + } +} + +func TestHeadscale(t *testing.T) { + IntegrationSkip(t) + + var err error + + namespace := "test-space" + + scenario, err := NewScenario() + if err != nil { + t.Errorf("failed to create scenario: %s", err) + } + + t.Run("start-headscale", func(t *testing.T) { + err = scenario.StartHeadscale() + if err != nil { + t.Errorf("failed to create start headcale: %s", err) + } + + err = scenario.Headscale().WaitForReady() + if err != nil { + t.Errorf("headscale failed to become ready: %s", err) + } + }) + + t.Run("create-namespace", func(t *testing.T) { + err := scenario.CreateNamespace(namespace) + if err != nil { + t.Errorf("failed to create namespace: %s", err) + } + + if _, ok := scenario.namespaces[namespace]; !ok { + t.Errorf("namespace is not in scenario") + } + }) + + t.Run("create-auth-key", func(t *testing.T) { + _, err := scenario.CreatePreAuthKey(namespace) + if err != nil { + t.Errorf("failed to create preauthkey: %s", err) + } + }) + + err = scenario.Shutdown() + if err != nil { + t.Errorf("failed to tear down scenario: %s", err) + } +} + +func TestCreateTailscale(t *testing.T) { + IntegrationSkip(t) + + namespace := "only-create-containers" + + scenario, err := NewScenario() + if err != nil { + t.Errorf("failed to create scenario: %s", err) + } + + scenario.namespaces[namespace] = &Namespace{ + Clients: make(map[string]*tsic.TailscaleInContainer), + } + + t.Run("create-tailscale", func(t *testing.T) { + err := scenario.CreateTailscaleNodesInNamespace(namespace, "all", 3) + if err != nil { + t.Errorf("failed to add tailscale nodes: %s", err) + } + + if clients := len(scenario.namespaces[namespace].Clients); clients != 3 { + t.Errorf("wrong number of tailscale clients: %d != %d", clients, 3) + } + + // TODO(kradalby): Test "all" version logic + }) + + err = scenario.Shutdown() + if err != nil { + t.Errorf("failed to tear down scenario: %s", err) + } +} + +func TestTailscaleNodesJoiningHeadcale(t *testing.T) { + IntegrationSkip(t) + + var err error + + namespace := "join-node-test" + + count := 1 + + scenario, err := NewScenario() + if err != nil { + t.Errorf("failed to create scenario: %s", err) + } + + t.Run("start-headscale", func(t *testing.T) { + err = scenario.StartHeadscale() + if err != nil { + t.Errorf("failed to create start headcale: %s", err) + } + + headscale := scenario.Headscale() + err = headscale.WaitForReady() + if err != nil { + t.Errorf("headscale failed to become ready: %s", err) + } + }) + + t.Run("create-namespace", func(t *testing.T) { + err := scenario.CreateNamespace(namespace) + if err != nil { + t.Errorf("failed to create namespace: %s", err) + } + + if _, ok := scenario.namespaces[namespace]; !ok { + t.Errorf("namespace is not in scenario") + } + }) + + t.Run("create-tailscale", func(t *testing.T) { + err := scenario.CreateTailscaleNodesInNamespace(namespace, "1.30.2", count) + if err != nil { + t.Errorf("failed to add tailscale nodes: %s", err) + } + + if clients := len(scenario.namespaces[namespace].Clients); clients != count { + t.Errorf("wrong number of tailscale clients: %d != %d", clients, count) + } + }) + + t.Run("join-headscale", func(t *testing.T) { + key, err := scenario.CreatePreAuthKey(namespace) + if err != nil { + t.Errorf("failed to create preauthkey: %s", err) + } + + err = scenario.RunTailscaleUp(namespace, scenario.Headscale().GetEndpoint(), key.GetKey()) + if err != nil { + t.Errorf("failed to login: %s", err) + } + }) + + t.Run("get-ips", func(t *testing.T) { + ips, err := scenario.GetIPs(namespace) + if err != nil { + t.Errorf("failed to get tailscale ips: %s", err) + } + + if len(ips) != count*2 { + t.Errorf("got the wrong amount of tailscale ips, %d != %d", len(ips), count*2) + } + }) + + err = scenario.Shutdown() + if err != nil { + t.Errorf("failed to tear down scenario: %s", err) + } +} diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go new file mode 100644 index 00000000..f3188135 --- /dev/null +++ b/integration/tsic/tsic.go @@ -0,0 +1,295 @@ +package tsic + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "net/netip" + "strings" + + "github.com/cenkalti/backoff/v4" + "github.com/juanfont/headscale" + "github.com/juanfont/headscale/integration/dockertestutil" + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "tailscale.com/ipn/ipnstate" +) + +const ( + tsicHashLength = 6 + dockerContextPath = "../." +) + +var ( + errTailscalePingFailed = errors.New("ping failed") + errTailscaleNotLoggedIn = errors.New("tailscale not logged in") + errTailscaleWrongPeerCount = errors.New("wrong peer count") +) + +type TailscaleInContainer struct { + version string + Hostname string + + pool *dockertest.Pool + container *dockertest.Resource + network *dockertest.Network +} + +func New( + pool *dockertest.Pool, + version string, + network *dockertest.Network, +) (*TailscaleInContainer, error) { + hash, err := headscale.GenerateRandomStringDNSSafe(tsicHashLength) + if err != nil { + return nil, err + } + + hostname := fmt.Sprintf("ts-%s-%s", version, hash) + + // TODO(kradalby): figure out why we need to "refresh" the network here. + // network, err = dockertestutil.GetFirstOrCreateNetwork(pool, network.Network.Name) + // if err != nil { + // return nil, err + // } + + tailscaleOptions := &dockertest.RunOptions{ + Name: hostname, + Networks: []*dockertest.Network{network}, + Cmd: []string{ + "tailscaled", "--tun=tsdev", + }, + } + + // dockertest isnt very good at handling containers that has already + // been created, this is an attempt to make sure this container isnt + // present. + err = pool.RemoveContainerByName(hostname) + if err != nil { + return nil, err + } + + container, err := pool.BuildAndRunWithBuildOptions( + createTailscaleBuildOptions(version), + tailscaleOptions, + dockertestutil.DockerRestartPolicy, + dockertestutil.DockerAllowLocalIPv6, + dockertestutil.DockerAllowNetworkAdministration, + ) + if err != nil { + return nil, fmt.Errorf("could not start tailscale container: %w", err) + } + log.Printf("Created %s container\n", hostname) + + return &TailscaleInContainer{ + version: version, + Hostname: hostname, + + pool: pool, + container: container, + network: network, + }, nil +} + +func (t *TailscaleInContainer) Shutdown() error { + return t.pool.Purge(t.container) +} + +func (t *TailscaleInContainer) Version() string { + return t.version +} + +func (t *TailscaleInContainer) Up( + loginServer, authKey string, +) error { + command := []string{ + "tailscale", + "up", + "-login-server", + loginServer, + "--authkey", + authKey, + "--hostname", + t.Hostname, + } + + log.Println("Join command:", command) + log.Printf("Running join command for %s\n", t.Hostname) + stdout, stderr, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + log.Printf("tailscale join stderr: %s\n", stderr) + + return err + } + + if stdout != "" { + log.Printf("tailscale join stdout: %s\n", stdout) + } + + log.Printf("%s joined\n", t.Hostname) + + return nil +} + +// TODO(kradalby): Make cached/lazy. +func (t *TailscaleInContainer) IPs() ([]netip.Addr, error) { + ips := make([]netip.Addr, 0) + + command := []string{ + "tailscale", + "ip", + } + + result, stderr, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + log.Printf("failed commands stderr: %s\n", stderr) + + if strings.Contains(stderr, "NeedsLogin") { + return []netip.Addr{}, errTailscaleNotLoggedIn + } + + return []netip.Addr{}, err + } + + for _, address := range strings.Split(result, "\n") { + address = strings.TrimSuffix(address, "\n") + if len(address) < 1 { + continue + } + ip, err := netip.ParseAddr(address) + if err != nil { + return nil, err + } + ips = append(ips, ip) + } + + return ips, nil +} + +func (t *TailscaleInContainer) Status() (*ipnstate.Status, error) { + command := []string{ + "tailscale", + "status", + "--json", + } + + result, _, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + return nil, fmt.Errorf("failed to execute tailscale status command: %w", err) + } + + var status ipnstate.Status + err = json.Unmarshal([]byte(result), &status) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal tailscale status: %w", err) + } + + return &status, err +} + +func (t *TailscaleInContainer) WaitForPeers(expected int) error { + return t.pool.Retry(func() error { + status, err := t.Status() + if err != nil { + return fmt.Errorf("failed to fetch tailscale status: %w", err) + } + + if peers := status.Peers(); len(peers) != expected { + return errTailscaleWrongPeerCount + } + + return nil + }) +} + +// TODO(kradalby): Make multiping, go routine magic. +func (t *TailscaleInContainer) Ping(ip netip.Addr) error { + return t.pool.Retry(func() error { + command := []string{ + "tailscale", "ping", + "--timeout=1s", + "--c=10", + "--until-direct=true", + ip.String(), + } + + result, _, err := dockertestutil.ExecuteCommand( + t.container, + command, + []string{}, + ) + if err != nil { + log.Printf( + "failed to run ping command from %s to %s, err: %s", + t.Hostname, + ip.String(), + err, + ) + + return err + } + + if !strings.Contains(result, "pong") && !strings.Contains(result, "is local") { + return backoff.Permanent(errTailscalePingFailed) + } + + return nil + }) +} + +func createTailscaleBuildOptions(version string) *dockertest.BuildOptions { + var tailscaleBuildOptions *dockertest.BuildOptions + switch version { + case "head": + tailscaleBuildOptions = &dockertest.BuildOptions{ + Dockerfile: "Dockerfile.tailscale-HEAD", + ContextDir: dockerContextPath, + BuildArgs: []docker.BuildArg{}, + } + case "unstable": + tailscaleBuildOptions = &dockertest.BuildOptions{ + Dockerfile: "Dockerfile.tailscale", + ContextDir: dockerContextPath, + BuildArgs: []docker.BuildArg{ + { + Name: "TAILSCALE_VERSION", + Value: "*", // Installs the latest version https://askubuntu.com/a/824926 + }, + { + Name: "TAILSCALE_CHANNEL", + Value: "unstable", + }, + }, + } + default: + tailscaleBuildOptions = &dockertest.BuildOptions{ + Dockerfile: "Dockerfile.tailscale", + ContextDir: dockerContextPath, + BuildArgs: []docker.BuildArg{ + { + Name: "TAILSCALE_VERSION", + Value: version, + }, + { + Name: "TAILSCALE_CHANNEL", + Value: "stable", + }, + }, + } + } + + return tailscaleBuildOptions +} diff --git a/integration_cli_test.go b/integration_cli_test.go index bf302d2d..ee6a9e1f 100644 --- a/integration_cli_test.go +++ b/integration_cli_test.go @@ -1,5 +1,4 @@ -//go:build integration_cli - +//nolint package headscale import ( @@ -13,6 +12,7 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) @@ -27,7 +27,11 @@ type IntegrationCLITestSuite struct { env []string } -func TestCLIIntegrationTestSuite(t *testing.T) { +func TestIntegrationCLITestSuite(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration tests due to short flag") + } + s := new(IntegrationCLITestSuite) suite.Run(t, s) @@ -42,11 +46,11 @@ func (s *IntegrationCLITestSuite) SetupTest() { s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "") } - if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil { - s.network = *pnetwork - } else { - s.FailNow(fmt.Sprintf("Could not create network: %s", err), "") + network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork) + if err != nil { + s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "") } + s.network = network headscaleBuildOptions := &dockertest.BuildOptions{ Dockerfile: "Dockerfile", @@ -63,8 +67,12 @@ func (s *IntegrationCLITestSuite) SetupTest() { Mounts: []string{ fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath), }, - Networks: []*dockertest.Network{&s.network}, - Cmd: []string{"headscale", "serve"}, + Cmd: []string{"headscale", "serve"}, + Networks: []*dockertest.Network{&s.network}, + ExposedPorts: []string{"8080/tcp"}, + PortBindings: map[docker.Port][]docker.PortBinding{ + "8080/tcp": {{HostPort: "8080"}}, + }, } err = s.pool.RemoveContainerByName(headscaleHostname) @@ -87,7 +95,9 @@ func (s *IntegrationCLITestSuite) SetupTest() { fmt.Println("Created headscale container for CLI tests") fmt.Println("Waiting for headscale to be ready for CLI tests") - hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp")) + hostEndpoint := fmt.Sprintf("%s:%s", + s.headscale.GetIPInNetwork(&s.network), + s.headscale.GetPort("8080/tcp")) if err := s.pool.Retry(func() error { url := fmt.Sprintf("http://%s/health", hostEndpoint) diff --git a/integration_common_test.go b/integration_common_test.go index 9cce12fa..a1658863 100644 --- a/integration_common_test.go +++ b/integration_common_test.go @@ -1,5 +1,4 @@ -//go:build integration - +//nolint package headscale import ( @@ -19,7 +18,8 @@ import ( ) const ( - headscaleHostname = "headscale-derp" + headscaleNetwork = "headscale-test" + headscaleHostname = "headscale" DOCKER_EXECUTE_TIMEOUT = 10 * time.Second ) @@ -30,9 +30,10 @@ var ( IpPrefix6 = netip.MustParsePrefix("fd7a:115c:a1e0::/48") tailscaleVersions = []string{ - // "head", - // "unstable", - "1.30.0", + "head", + "unstable", + "1.32.0", + "1.30.2", "1.28.0", "1.26.2", "1.24.2", @@ -115,13 +116,19 @@ func ExecuteCommand( fmt.Println("stdout: ", stdout.String()) fmt.Println("stderr: ", stderr.String()) - return stdout.String(), stderr.String(), fmt.Errorf("command failed with: %s", stderr.String()) + return stdout.String(), stderr.String(), fmt.Errorf( + "command failed with: %s", + stderr.String(), + ) } return stdout.String(), stderr.String(), nil case <-time.After(execConfig.timeout): - return stdout.String(), stderr.String(), fmt.Errorf("command timed out after %s", execConfig.timeout) + return stdout.String(), stderr.String(), fmt.Errorf( + "command timed out after %s", + execConfig.timeout, + ) } } @@ -316,3 +323,21 @@ func GetEnvBool(key string) (bool, error) { return v, nil } + +func GetFirstOrCreateNetwork(pool *dockertest.Pool, name string) (dockertest.Network, error) { + networks, err := pool.NetworksByName(name) + if err != nil || len(networks) == 0 { + if _, err := pool.CreateNetwork(name); err == nil { + // Create does not give us an updated version of the resource, so we need to + // get it again. + networks, err := pool.NetworksByName(name) + if err != nil { + return dockertest.Network{}, err + } + + return networks[0], nil + } + } + + return networks[0], nil +} diff --git a/integration_embedded_derp_test.go b/integration_embedded_derp_test.go index a31006e1..5bd6deed 100644 --- a/integration_embedded_derp_test.go +++ b/integration_embedded_derp_test.go @@ -1,5 +1,4 @@ -//go:build integration_derp - +//nolint package headscale import ( @@ -17,34 +16,39 @@ import ( "testing" "time" + "github.com/ccding/go-stun/stun" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" - - "github.com/ccding/go-stun/stun" ) const ( - namespaceName = "derpnamespace" - totalContainers = 3 + headscaleDerpHostname = "headscale-derp" + namespaceName = "derpnamespace" + totalContainers = 3 ) type IntegrationDERPTestSuite struct { suite.Suite stats *suite.SuiteInformation - pool dockertest.Pool - networks map[int]dockertest.Network // so we keep the containers isolated - headscale dockertest.Resource - saveLogs bool + pool dockertest.Pool + network dockertest.Network + containerNetworks map[int]dockertest.Network // so we keep the containers isolated + headscale dockertest.Resource + saveLogs bool tailscales map[string]dockertest.Resource joinWaitGroup sync.WaitGroup } -func TestDERPIntegrationTestSuite(t *testing.T) { +func TestIntegrationDERPTestSuite(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration tests due to short flag") + } + saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG") if err != nil { saveLogs = false @@ -53,7 +57,7 @@ func TestDERPIntegrationTestSuite(t *testing.T) { s := new(IntegrationDERPTestSuite) s.tailscales = make(map[string]dockertest.Resource) - s.networks = make(map[int]dockertest.Network) + s.containerNetworks = make(map[int]dockertest.Network) s.saveLogs = saveLogs suite.Run(t, s) @@ -78,7 +82,7 @@ func TestDERPIntegrationTestSuite(t *testing.T) { log.Printf("Could not purge resource: %s\n", err) } - for _, network := range s.networks { + for _, network := range s.containerNetworks { if err := network.Close(); err != nil { log.Printf("Could not close network: %s\n", err) } @@ -93,9 +97,15 @@ func (s *IntegrationDERPTestSuite) SetupSuite() { s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "") } + network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork) + if err != nil { + s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "") + } + s.network = network + for i := 0; i < totalContainers; i++ { if pnetwork, err := s.pool.CreateNetwork(fmt.Sprintf("headscale-derp-%d", i)); err == nil { - s.networks[i] = *pnetwork + s.containerNetworks[i] = *pnetwork } else { s.FailNow(fmt.Sprintf("Could not create network: %s", err), "") } @@ -112,7 +122,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() { } headscaleOptions := &dockertest.RunOptions{ - Name: headscaleHostname, + Name: headscaleDerpHostname, Mounts: []string{ fmt.Sprintf( "%s/integration_test/etc_embedded_derp:/etc/headscale", @@ -120,6 +130,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() { ), }, Cmd: []string{"headscale", "serve"}, + Networks: []*dockertest.Network{&s.network}, ExposedPorts: []string{"8443/tcp", "3478/udp"}, PortBindings: map[docker.Port][]docker.PortBinding{ "8443/tcp": {{HostPort: "8443"}}, @@ -127,7 +138,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() { }, } - err = s.pool.RemoveContainerByName(headscaleHostname) + err = s.pool.RemoveContainerByName(headscaleDerpHostname) if err != nil { s.FailNow( fmt.Sprintf( @@ -153,13 +164,15 @@ func (s *IntegrationDERPTestSuite) SetupSuite() { hostname, container := s.tailscaleContainer( fmt.Sprint(i), version, - s.networks[i], + s.containerNetworks[i], ) s.tailscales[hostname] = *container } log.Println("Waiting for headscale to be ready for embedded DERP tests") - hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8443/tcp")) + hostEndpoint := fmt.Sprintf("%s:%s", + s.headscale.GetIPInNetwork(&s.network), + s.headscale.GetPort("8443/tcp")) if err := s.pool.Retry(func() error { url := fmt.Sprintf("https://%s/health", hostEndpoint) @@ -320,7 +333,7 @@ func (s *IntegrationDERPTestSuite) TearDownSuite() { log.Printf("Could not purge resource: %s\n", err) } - for _, network := range s.networks { + for _, network := range s.containerNetworks { if err := network.Close(); err != nil { log.Printf("Could not close network: %s\n", err) } @@ -428,7 +441,9 @@ func (s *IntegrationDERPTestSuite) TestPingAllPeersByHostname() { } func (s *IntegrationDERPTestSuite) TestDERPSTUN() { - headscaleSTUNAddr := fmt.Sprintf("localhost:%s", s.headscale.GetPort("3478/udp")) + headscaleSTUNAddr := fmt.Sprintf("%s:%s", + s.headscale.GetIPInNetwork(&s.network), + s.headscale.GetPort("3478/udp")) client := stun.NewClient() client.SetVerbose(true) client.SetVVerbose(true) diff --git a/integration_general_test.go b/integration_general_test.go index 5abdccbb..52bfddd4 100644 --- a/integration_general_test.go +++ b/integration_general_test.go @@ -1,5 +1,4 @@ -//go:build integration_general - +//nolint package headscale import ( @@ -41,7 +40,11 @@ type IntegrationTestSuite struct { joinWaitGroup sync.WaitGroup } -func TestIntegrationTestSuite(t *testing.T) { +func TestIntegrationGeneralTestSuite(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration tests due to short flag") + } + saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG") if err != nil { saveLogs = false @@ -191,6 +194,17 @@ func (s *IntegrationTestSuite) tailscaleContainer( }, } + err := s.pool.RemoveContainerByName(hostname) + if err != nil { + s.FailNow( + fmt.Sprintf( + "Could not remove existing container before building test: %s", + err, + ), + "", + ) + } + pts, err := s.pool.BuildAndRunWithBuildOptions( tailscaleBuildOptions, tailscaleOptions, @@ -219,11 +233,11 @@ func (s *IntegrationTestSuite) SetupSuite() { s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "") } - if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil { - s.network = *pnetwork - } else { - s.FailNow(fmt.Sprintf("Could not create network: %s", err), "") + network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork) + if err != nil { + s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "") } + s.network = network headscaleBuildOptions := &dockertest.BuildOptions{ Dockerfile: "Dockerfile", @@ -236,10 +250,14 @@ func (s *IntegrationTestSuite) SetupSuite() { } headscaleOptions := &dockertest.RunOptions{ - Name: "headscale", + Name: headscaleHostname, Mounts: []string{ fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath), }, + ExposedPorts: []string{"8080/tcp"}, + PortBindings: map[docker.Port][]docker.PortBinding{ + "8080/tcp": {{HostPort: "8080"}}, + }, Networks: []*dockertest.Network{&s.network}, Cmd: []string{"headscale", "serve"}, } @@ -278,7 +296,9 @@ func (s *IntegrationTestSuite) SetupSuite() { } log.Println("Waiting for headscale to be ready for core integration tests") - hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp")) + hostEndpoint := fmt.Sprintf("%s:%s", + s.headscale.GetIPInNetwork(&s.network), + s.headscale.GetPort("8080/tcp")) if err := s.pool.Retry(func() error { url := fmt.Sprintf("http://%s/health", hostEndpoint) @@ -487,7 +507,7 @@ func getIPsfromIPNstate(status ipnstate.Status) []netip.Addr { return ips } -// TODO: Adopt test for cross communication between namespaces +// TODO: Adopt test for cross communication between namespaces. func (s *IntegrationTestSuite) TestPingAllPeersByAddress() { for _, scales := range s.namespaces { ips, err := getIPs(scales.tailscales) diff --git a/integration_oidc_test.go b/integration_oidc_test.go index 70f793b2..8ca46db7 100644 --- a/integration_oidc_test.go +++ b/integration_oidc_test.go @@ -1,5 +1,4 @@ -//go:build integration_oidc - +//nolint package headscale import ( @@ -25,7 +24,8 @@ import ( ) const ( - oidcHeadscaleHostname = "headscale" + oidcHeadscaleHostname = "headscale-oidc" + oidcMockHostname = "headscale-mock-oidc" oidcNamespaceName = "oidcnamespace" totalOidcContainers = 3 ) @@ -44,7 +44,11 @@ type IntegrationOIDCTestSuite struct { joinWaitGroup sync.WaitGroup } -func TestOIDCIntegrationTestSuite(t *testing.T) { +func TestIntegrationOIDCTestSuite(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration tests due to short flag") + } + saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG") if err != nil { saveLogs = false @@ -95,33 +99,25 @@ func (s *IntegrationOIDCTestSuite) SetupSuite() { s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "") } - if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil { - s.network = *pnetwork - } else { - s.FailNow(fmt.Sprintf("Could not create network: %s", err), "") - } - - // Create does not give us an updated version of the resource, so we need to - // get it again. - networks, err := s.pool.NetworksByName("headscale-test") + network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork) if err != nil { - s.FailNow(fmt.Sprintf("Could not get network: %s", err), "") + s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "") } - s.network = networks[0] + s.network = network log.Printf("Network config: %v", s.network.Network.IPAM.Config[0]) s.Suite.T().Log("Setting up mock OIDC") mockOidcOptions := &dockertest.RunOptions{ - Name: "mockoidc", - Hostname: "mockoidc", + Name: oidcMockHostname, Cmd: []string{"headscale", "mockoidc"}, ExposedPorts: []string{"10000/tcp"}, - Networks: []*dockertest.Network{&s.network}, PortBindings: map[docker.Port][]docker.PortBinding{ "10000/tcp": {{HostPort: "10000"}}, }, + Networks: []*dockertest.Network{&s.network}, Env: []string{ + fmt.Sprintf("MOCKOIDC_ADDR=%s", oidcMockHostname), "MOCKOIDC_PORT=10000", "MOCKOIDC_CLIENT_ID=superclient", "MOCKOIDC_CLIENT_SECRET=supersecret", @@ -133,6 +129,17 @@ func (s *IntegrationOIDCTestSuite) SetupSuite() { ContextDir: ".", } + err = s.pool.RemoveContainerByName(oidcMockHostname) + if err != nil { + s.FailNow( + fmt.Sprintf( + "Could not remove existing container before building test: %s", + err, + ), + "", + ) + } + if pmockoidc, err := s.pool.BuildAndRunWithBuildOptions( headscaleBuildOptions, mockOidcOptions, @@ -142,6 +149,35 @@ func (s *IntegrationOIDCTestSuite) SetupSuite() { s.FailNow(fmt.Sprintf("Could not start mockOIDC container: %s", err), "") } + s.Suite.T().Logf("Waiting for headscale mock oidc to be ready for tests") + hostEndpoint := fmt.Sprintf( + "%s:%s", + s.mockOidc.GetIPInNetwork(&s.network), + s.mockOidc.GetPort("10000/tcp"), + ) + + if err := s.pool.Retry(func() error { + url := fmt.Sprintf("http://%s/oidc/.well-known/openid-configuration", hostEndpoint) + resp, err := http.Get(url) + if err != nil { + log.Printf("headscale mock OIDC tests is not ready: %s\n", err) + return err + } + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("status code not OK") + } + + return nil + }); err != nil { + // TODO(kradalby): If we cannot access headscale, or any other fatal error during + // test setup, we need to abort and tear down. However, testify does not seem to + // support that at the moment: + // https://github.com/stretchr/testify/issues/849 + return // fmt.Errorf("Could not connect to headscale: %s", err) + } + s.Suite.T().Log("headscale-mock-oidc container is ready for embedded OIDC tests") + oidcCfg := fmt.Sprintf(` oidc: issuer: http://%s:10000/oidc @@ -164,7 +200,7 @@ oidc: log.Println(config) configPath := path.Join(currentPath, "integration_test/etc_oidc/config.yaml") - err = os.WriteFile(configPath, []byte(config), 0644) + err = os.WriteFile(configPath, []byte(config), 0o644) if err != nil { s.FailNow(fmt.Sprintf("Could not write config: %s", err), "") } @@ -216,10 +252,14 @@ oidc: } s.Suite.T().Logf("Waiting for headscale to be ready for embedded OIDC tests") - hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8443/tcp")) + hostMockEndpoint := fmt.Sprintf( + "%s:%s", + s.headscale.GetIPInNetwork(&s.network), + s.headscale.GetPort("8443/tcp"), + ) if err := s.pool.Retry(func() error { - url := fmt.Sprintf("https://%s/health", hostEndpoint) + url := fmt.Sprintf("https://%s/health", hostMockEndpoint) insecureTransport := http.DefaultTransport.(*http.Transport).Clone() insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} client := &http.Client{Transport: insecureTransport} @@ -294,6 +334,8 @@ func (s *IntegrationOIDCTestSuite) AuthenticateOIDC( resp, err := client.Get(loginURL.String()) assert.Nil(s.T(), err) + log.Printf("auth body, err: %#v, %s", resp, err) + body, err := io.ReadAll(resp.Body) assert.Nil(s.T(), err) @@ -308,7 +350,6 @@ func (s *IntegrationOIDCTestSuite) joinOIDC( endpoint, hostname string, tailscale dockertest.Resource, ) (*url.URL, error) { - command := []string{ "tailscale", "up", @@ -374,7 +415,13 @@ func (s *IntegrationOIDCTestSuite) tailscaleContainer( DockerAllowNetworkAdministration, ) if err != nil { - log.Fatalf("Could not start tailscale container version %s: %s", version, err) + s.FailNow( + fmt.Sprintf( + "Could not start tailscale container version %s: %s", + version, + err, + ), + ) } log.Printf("Created %s container\n", hostname) @@ -497,7 +544,12 @@ func (s *IntegrationOIDCTestSuite) TestPingAllPeersByAddress() { []string{}, ) assert.Nil(t, err) - log.Printf("result for %s: stdout: %s, stderr: %s\n", hostname, stdout, stderr) + log.Printf( + "result for %s: stdout: %s, stderr: %s\n", + hostname, + stdout, + stderr, + ) assert.Contains(t, stdout, "pong") }) } diff --git a/integration_test/etc_oidc/base_config.yaml b/integration_test/etc_oidc/base_config.yaml index 10fa7751..7db58a2a 100644 --- a/integration_test/etc_oidc/base_config.yaml +++ b/integration_test/etc_oidc/base_config.yaml @@ -11,7 +11,7 @@ private_key_path: private.key noise: private_key_path: noise_private.key listen_addr: 0.0.0.0:8443 -server_url: https://localhost:8443 +server_url: https://headscale-oidc:8443 tls_cert_path: "/etc/headscale/tls/server.crt" tls_key_path: "/etc/headscale/tls/server.key" tls_client_auth_mode: disabled diff --git a/utils.go b/utils.go index 666683cf..fc1fafa2 100644 --- a/utils.go +++ b/utils.go @@ -17,6 +17,7 @@ import ( "os" "path/filepath" "reflect" + "regexp" "strconv" "strings" @@ -64,6 +65,8 @@ const ( ZstdCompression = "zstd" ) +var NodePublicKeyRegex = regexp.MustCompile("nodekey:[a-fA-F0-9]+") + func MachinePublicKeyStripPrefix(machineKey key.MachinePublic) string { return strings.TrimPrefix(machineKey.String(), machinePublicHexPrefix) } @@ -325,7 +328,9 @@ func GenerateRandomStringDNSSafe(size int) (string, error) { if err != nil { return "", err } - str = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", "")) + str = strings.ToLower( + strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", ""), + ) } return str[:size], nil