mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Merge branch 'main' into patch-1
This commit is contained in:
		
						commit
						1eea9c943c
					
				
							
								
								
									
										40
									
								
								.github/workflows/test-integration-cli.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/test-integration-cli.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
@ -3,7 +3,7 @@ name: CI
 | 
				
			|||||||
on: [pull_request]
 | 
					on: [pull_request]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  integration-test:
 | 
					  integration-test-derp:
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
@ -30,15 +30,6 @@ jobs:
 | 
				
			|||||||
      - uses: cachix/install-nix-action@v16
 | 
					      - uses: cachix/install-nix-action@v16
 | 
				
			||||||
        if: steps.changed-files.outputs.any_changed == 'true'
 | 
					        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
 | 
					      - name: Run Embedded DERP server integration tests
 | 
				
			||||||
        if: steps.changed-files.outputs.any_changed == 'true'
 | 
					        if: steps.changed-files.outputs.any_changed == 'true'
 | 
				
			||||||
        uses: nick-fields/retry@v2
 | 
					        uses: nick-fields/retry@v2
 | 
				
			||||||
@ -47,21 +38,3 @@ jobs:
 | 
				
			|||||||
          max_attempts: 5
 | 
					          max_attempts: 5
 | 
				
			||||||
          retry_on: error
 | 
					          retry_on: error
 | 
				
			||||||
          command: nix develop --command -- make test_integration_derp
 | 
					          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
 | 
					 | 
				
			||||||
							
								
								
									
										40
									
								
								.github/workflows/test-integration-general.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/test-integration-general.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
							
								
								
									
										40
									
								
								.github/workflows/test-integration-oidc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/test-integration-oidc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
							
								
								
									
										40
									
								
								.github/workflows/test-integration-v2-general.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								.github/workflows/test-integration-v2-general.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
@ -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)
 | 
					- 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)
 | 
					- 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)
 | 
					- 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 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 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)
 | 
					- Add config flag to allow Headscale to start if OIDC provider is down [#829](https://github.com/juanfont/headscale/pull/829)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										48
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								Makefile
									
									
									
									
									
								
							@ -22,21 +22,59 @@ build:
 | 
				
			|||||||
dev: lint test build
 | 
					dev: lint test build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test:
 | 
					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: test_integration_cli test_integration_derp test_integration_oidc test_integration_general
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test_integration_cli:
 | 
					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:
 | 
					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:
 | 
					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:
 | 
					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:
 | 
					coverprofile_func:
 | 
				
			||||||
	go tool cover -func=coverage.out
 | 
						go tool cover -func=coverage.out
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										32
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								api.go
									
									
									
									
									
								
							@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/mux"
 | 
						"github.com/gorilla/mux"
 | 
				
			||||||
	"github.com/rs/zerolog/log"
 | 
						"github.com/rs/zerolog/log"
 | 
				
			||||||
 | 
						"tailscale.com/types/key"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@ -93,7 +94,34 @@ func (h *Headscale) RegisterWebAPI(
 | 
				
			|||||||
) {
 | 
					) {
 | 
				
			||||||
	vars := mux.Vars(req)
 | 
						vars := mux.Vars(req)
 | 
				
			||||||
	nodeKeyStr, ok := vars["nkey"]
 | 
						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.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
				
			||||||
		writer.WriteHeader(http.StatusBadRequest)
 | 
							writer.WriteHeader(http.StatusBadRequest)
 | 
				
			||||||
		_, err := writer.Write([]byte("Wrong params"))
 | 
							_, 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.Header().Set("Content-Type", "text/html; charset=utf-8")
 | 
				
			||||||
	writer.WriteHeader(http.StatusOK)
 | 
						writer.WriteHeader(http.StatusOK)
 | 
				
			||||||
	_, err := writer.Write(content.Bytes())
 | 
						_, err = writer.Write(content.Bytes())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error().
 | 
							log.Error().
 | 
				
			||||||
			Caller().
 | 
								Caller().
 | 
				
			||||||
 | 
				
			|||||||
@ -46,6 +46,10 @@ func mockOIDC() error {
 | 
				
			|||||||
	if clientSecret == "" {
 | 
						if clientSecret == "" {
 | 
				
			||||||
		return errMockOidcClientSecretNotDefined
 | 
							return errMockOidcClientSecretNotDefined
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						addrStr := os.Getenv("MOCKOIDC_ADDR")
 | 
				
			||||||
 | 
						if addrStr == "" {
 | 
				
			||||||
 | 
							return errMockOidcPortNotDefined
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	portStr := os.Getenv("MOCKOIDC_PORT")
 | 
						portStr := os.Getenv("MOCKOIDC_PORT")
 | 
				
			||||||
	if portStr == "" {
 | 
						if portStr == "" {
 | 
				
			||||||
		return errMockOidcPortNotDefined
 | 
							return errMockOidcPortNotDefined
 | 
				
			||||||
@ -61,7 +65,7 @@ func mockOIDC() error {
 | 
				
			|||||||
		return err
 | 
							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 {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@ import (
 | 
				
			|||||||
var cfgFile string = ""
 | 
					var cfgFile string = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					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
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
				
			|||||||
@ -55,7 +55,7 @@ metrics_listen_addr: 0.0.0.0:9090
 | 
				
			|||||||
private_key_path: /etc/headscale/private.key
 | 
					private_key_path: /etc/headscale/private.key
 | 
				
			||||||
# The default /var/lib/headscale path is not writable in the container
 | 
					# The default /var/lib/headscale path is not writable in the container
 | 
				
			||||||
noise:
 | 
					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
 | 
					# The default /var/lib/headscale path is not writable  in the container
 | 
				
			||||||
db_path: /etc/headscale/db.sqlite
 | 
					db_path: /etc/headscale/db.sqlite
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								flake.nix
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								flake.nix
									
									
									
									
									
								
							@ -26,6 +26,9 @@
 | 
				
			|||||||
          version = headscaleVersion;
 | 
					          version = headscaleVersion;
 | 
				
			||||||
          src = pkgs.lib.cleanSource self;
 | 
					          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,
 | 
					          # 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.
 | 
					          # update this if you have a mismatch after doing a change to thos files.
 | 
				
			||||||
          vendorSha256 = "sha256-DosFCSiQ5FURbIrt4NcPGkExc84t2MGMqe9XLxNHdIM=";
 | 
					          vendorSha256 = "sha256-DosFCSiQ5FURbIrt4NcPGkExc84t2MGMqe9XLxNHdIM=";
 | 
				
			||||||
@ -128,7 +131,13 @@
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
    in rec {
 | 
					    in rec {
 | 
				
			||||||
      # `nix develop`
 | 
					      # `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`
 | 
					      # `nix build`
 | 
				
			||||||
      packages = with pkgs; {
 | 
					      packages = with pkgs; {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@ -5,6 +5,7 @@ go 1.19
 | 
				
			|||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/AlecAivazis/survey/v2 v2.3.5
 | 
						github.com/AlecAivazis/survey/v2 v2.3.5
 | 
				
			||||||
	github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029
 | 
						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/coreos/go-oidc/v3 v3.3.0
 | 
				
			||||||
	github.com/deckarep/golang-set/v2 v2.1.0
 | 
						github.com/deckarep/golang-set/v2 v2.1.0
 | 
				
			||||||
	github.com/efekarakus/termcolor v1.0.1
 | 
						github.com/efekarakus/termcolor v1.0.1
 | 
				
			||||||
@ -54,7 +55,6 @@ require (
 | 
				
			|||||||
	github.com/akutz/memconn v0.1.0 // indirect
 | 
						github.com/akutz/memconn v0.1.0 // indirect
 | 
				
			||||||
	github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
 | 
						github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
 | 
				
			||||||
	github.com/beorn7/perks v1.0.1 // 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/cespare/xxhash/v2 v2.1.2 // indirect
 | 
				
			||||||
	github.com/containerd/console v1.0.3 // indirect
 | 
						github.com/containerd/console v1.0.3 // indirect
 | 
				
			||||||
	github.com/containerd/continuity v0.3.0 // indirect
 | 
						github.com/containerd/continuity v0.3.0 // indirect
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										126
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								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=
 | 
					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 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
 | 
				
			||||||
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 | 
					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=
 | 
					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 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
 | 
				
			||||||
github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
 | 
					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/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 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-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.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
				
			||||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
					github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
				
			||||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
 | 
					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 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
 | 
				
			||||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
 | 
					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/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 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.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
 | 
					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.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
 | 
				
			||||||
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
 | 
					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.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/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-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
				
			||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
					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/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/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-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 h1:Y1LV3mP+QT3MEycATZpAiwfyN+uxZLqVbAHJUuOJEe4=
 | 
				
			||||||
github.com/coreos/go-oidc/v3 v3.3.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
 | 
					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=
 | 
					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/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 h1:aLQ8XowgKpR3/IysPj8qZQJBVQ+Qws61icFuZl6iKYs=
 | 
				
			||||||
github.com/docker/cli v20.10.16+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
					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 h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ=
 | 
				
			||||||
github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
					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 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 | 
				
			||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 | 
					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 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
 | 
				
			||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
					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 v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
				
			||||||
github.com/dustin/go-humanize v1.0.0/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=
 | 
					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/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 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 | 
				
			||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 | 
					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 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk=
 | 
				
			||||||
github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY=
 | 
					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 h1:D5uxD2f6UJ82cHnVtO2TZ9pqsLyto3fpDKHIk2OsR8A=
 | 
				
			||||||
github.com/glebarez/sqlite v1.4.6/go.mod h1:WYEtEFjhADPaPJqL/PGlbQQGINBA3eUAfDNbKFJf/zA=
 | 
					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=
 | 
					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.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 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
 | 
				
			||||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
 | 
					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/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-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
 | 
				
			||||||
github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw=
 | 
					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/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.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.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 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 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/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=
 | 
					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.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
				
			||||||
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
 | 
					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.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/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.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.1/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.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
				
			||||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 | 
					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.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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 | 
				
			||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
					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 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.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
 | 
				
			||||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
 | 
					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.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 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
 | 
				
			||||||
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
 | 
					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 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
 | 
				
			||||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
 | 
					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=
 | 
					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/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 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
 | 
				
			||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
 | 
					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 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.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=
 | 
					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.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 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
 | 
				
			||||||
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
 | 
					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 h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
 | 
				
			||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
 | 
					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=
 | 
					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.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 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
 | 
				
			||||||
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
 | 
					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-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-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.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.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 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
 | 
				
			||||||
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
 | 
					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-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 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.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 | 
				
			||||||
github.com/jackc/puddle v1.2.1/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/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/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=
 | 
					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/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 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
 | 
				
			||||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
					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/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.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 | 
				
			||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 | 
					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/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 h1:Yws7RV6kZr2O7PPdT+RkbSmmOponA8i/1DuGHe8BRsM=
 | 
				
			||||||
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b/go.mod h1:TzDCVOZKUa79z6iXbbXqhtAflVgUKaFkZ21M5tK5tzY=
 | 
					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.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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
				
			||||||
github.com/json-iterator/go v1.1.9/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.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 | 
				
			||||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
 | 
					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.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.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.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
					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.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 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
					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.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.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
				
			||||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 | 
					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-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.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.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/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 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 | 
				
			||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
					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/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 h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 | 
				
			||||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
					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.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.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 | 
				
			||||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 | 
					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/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 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
 | 
				
			||||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
 | 
					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-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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
				
			||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
					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/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 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw=
 | 
				
			||||||
github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
 | 
					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/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/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
 | 
				
			||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 | 
					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 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 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.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/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/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
 | 
				
			||||||
github.com/philip-bui/grpc-zerolog v1.0.1 h1:EMacvLRUd2O1K0eWod27ZP5CY1iTNkhBDLSN+Q4JEvA=
 | 
					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.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
 | 
				
			||||||
github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE=
 | 
					github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE=
 | 
				
			||||||
github.com/pterm/pterm v0.12.45/go.mod h1:hJgLlBafm45w/Hr0dKXxY//POD7CgowhePaG1sdPNBg=
 | 
					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 h1:nS/Iqc4EnpJ8jm/MzJ+e3MUaP2Ys2mqXeEfoxoU0HaM=
 | 
				
			||||||
github.com/puzpuzpuz/xsync v1.4.3/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
 | 
					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=
 | 
					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/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 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 | 
				
			||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
					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 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/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
					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.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 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.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/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.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/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.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 | 
				
			||||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
 | 
					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 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
 | 
				
			||||||
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
 | 
					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=
 | 
					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/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/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.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/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.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 | 
				
			||||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
 | 
					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.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
				
			||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 | 
					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.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/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/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
				
			||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 | 
					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.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
 | 
				
			||||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
 | 
					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.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.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.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
				
			||||||
github.com/spf13/cast v1.4.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.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 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
 | 
				
			||||||
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
 | 
					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/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/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=
 | 
					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/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 h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
 | 
				
			||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
 | 
					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 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
 | 
				
			||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 | 
					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=
 | 
					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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
				
			||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
 | 
					go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
 | 
				
			||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 | 
					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 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4=
 | 
				
			||||||
go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
 | 
					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 h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
 | 
				
			||||||
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
 | 
					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-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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-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-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-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 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
					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=
 | 
					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-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-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-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-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-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
				
			||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/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-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-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-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-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 h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
 | 
				
			||||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 | 
					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-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-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-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 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 | 
					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=
 | 
					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-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-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-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 h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					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=
 | 
					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-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-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-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-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-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-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-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-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-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 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/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-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 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-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.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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 | 
					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.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-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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
				
			||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/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-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 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
 | 
				
			||||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
					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-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-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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 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 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.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.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.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 | 
				
			||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 | 
					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-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-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-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 h1:vXJMM8Shg7TGaYxZsQ++A/FOSlbDmDtWhS/o+3w/hj4=
 | 
				
			||||||
google.golang.org/genproto v0.0.0-20220902135211-223410557253/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
 | 
					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=
 | 
					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.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
				
			||||||
google.golang.org/grpc v1.46.2/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.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 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
 | 
				
			||||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 | 
					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=
 | 
					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.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
				
			||||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
 | 
					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.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/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 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
 | 
				
			||||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
					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=
 | 
					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.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-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-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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					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 h1:lWGiVt5CijhQAg0PWB7Od1RNcBw/jS4d2cAScBcSDXg=
 | 
				
			||||||
gorm.io/driver/postgres v1.3.9/go.mod h1:qw/FeqjxmYqW5dBcYNBsnhQULIApQdk7YuuDPktVi1U=
 | 
					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.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
 | 
				
			||||||
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
 | 
					gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
 | 
				
			||||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
 | 
					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.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.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
 | 
				
			||||||
modernc.org/ccgo/v3 v3.16.6/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/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
 | 
				
			||||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
 | 
					modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
 | 
				
			||||||
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
 | 
					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.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
 | 
				
			||||||
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
 | 
					modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
 | 
				
			||||||
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
 | 
					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.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 | 
				
			||||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
 | 
					modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
 | 
				
			||||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 | 
					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 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
 | 
				
			||||||
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
 | 
					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/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
 | 
				
			||||||
modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
 | 
					modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
 | 
				
			||||||
modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k=
 | 
					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/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
 | 
				
			||||||
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
 | 
					modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
 | 
				
			||||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
 | 
					modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								integration/control.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								integration/control.go
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								integration/dockertestutil/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								integration/dockertestutil/config.go
									
									
									
									
									
										Normal file
									
								
							@ -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",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										94
									
								
								integration/dockertestutil/execute.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								integration/dockertestutil/execute.go
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										62
									
								
								integration/dockertestutil/network.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								integration/dockertestutil/network.go
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										78
									
								
								integration/general_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								integration/general_test.go
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										223
									
								
								integration/hsic/hsic.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								integration/hsic/hsic.go
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										347
									
								
								integration/scenario.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										347
									
								
								integration/scenario.go
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										182
									
								
								integration/scenario_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								integration/scenario_test.go
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										295
									
								
								integration/tsic/tsic.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								integration/tsic/tsic.go
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
//go:build integration_cli
 | 
					//nolint
 | 
				
			||||||
 | 
					 | 
				
			||||||
package headscale
 | 
					package headscale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@ -13,6 +12,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
						v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
				
			||||||
	"github.com/ory/dockertest/v3"
 | 
						"github.com/ory/dockertest/v3"
 | 
				
			||||||
 | 
						"github.com/ory/dockertest/v3/docker"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"github.com/stretchr/testify/suite"
 | 
						"github.com/stretchr/testify/suite"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -27,7 +27,11 @@ type IntegrationCLITestSuite struct {
 | 
				
			|||||||
	env       []string
 | 
						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)
 | 
						s := new(IntegrationCLITestSuite)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	suite.Run(t, s)
 | 
						suite.Run(t, s)
 | 
				
			||||||
@ -42,11 +46,11 @@ func (s *IntegrationCLITestSuite) SetupTest() {
 | 
				
			|||||||
		s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
 | 
							s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil {
 | 
						network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork)
 | 
				
			||||||
		s.network = *pnetwork
 | 
						if err != nil {
 | 
				
			||||||
	} else {
 | 
							s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "")
 | 
				
			||||||
		s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						s.network = network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	headscaleBuildOptions := &dockertest.BuildOptions{
 | 
						headscaleBuildOptions := &dockertest.BuildOptions{
 | 
				
			||||||
		Dockerfile: "Dockerfile",
 | 
							Dockerfile: "Dockerfile",
 | 
				
			||||||
@ -63,8 +67,12 @@ func (s *IntegrationCLITestSuite) SetupTest() {
 | 
				
			|||||||
		Mounts: []string{
 | 
							Mounts: []string{
 | 
				
			||||||
			fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath),
 | 
								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)
 | 
						err = s.pool.RemoveContainerByName(headscaleHostname)
 | 
				
			||||||
@ -87,7 +95,9 @@ func (s *IntegrationCLITestSuite) SetupTest() {
 | 
				
			|||||||
	fmt.Println("Created headscale container for CLI tests")
 | 
						fmt.Println("Created headscale container for CLI tests")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Println("Waiting for headscale to be ready 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 {
 | 
						if err := s.pool.Retry(func() error {
 | 
				
			||||||
		url := fmt.Sprintf("http://%s/health", hostEndpoint)
 | 
							url := fmt.Sprintf("http://%s/health", hostEndpoint)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
//go:build integration
 | 
					//nolint
 | 
				
			||||||
 | 
					 | 
				
			||||||
package headscale
 | 
					package headscale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@ -19,7 +18,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	headscaleHostname      = "headscale-derp"
 | 
						headscaleNetwork       = "headscale-test"
 | 
				
			||||||
 | 
						headscaleHostname      = "headscale"
 | 
				
			||||||
	DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
 | 
						DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -30,9 +30,10 @@ var (
 | 
				
			|||||||
	IpPrefix6 = netip.MustParsePrefix("fd7a:115c:a1e0::/48")
 | 
						IpPrefix6 = netip.MustParsePrefix("fd7a:115c:a1e0::/48")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tailscaleVersions = []string{
 | 
						tailscaleVersions = []string{
 | 
				
			||||||
		// "head",
 | 
							"head",
 | 
				
			||||||
		// "unstable",
 | 
							"unstable",
 | 
				
			||||||
		"1.30.0",
 | 
							"1.32.0",
 | 
				
			||||||
 | 
							"1.30.2",
 | 
				
			||||||
		"1.28.0",
 | 
							"1.28.0",
 | 
				
			||||||
		"1.26.2",
 | 
							"1.26.2",
 | 
				
			||||||
		"1.24.2",
 | 
							"1.24.2",
 | 
				
			||||||
@ -115,13 +116,19 @@ func ExecuteCommand(
 | 
				
			|||||||
			fmt.Println("stdout: ", stdout.String())
 | 
								fmt.Println("stdout: ", stdout.String())
 | 
				
			||||||
			fmt.Println("stderr: ", stderr.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
 | 
							return stdout.String(), stderr.String(), nil
 | 
				
			||||||
	case <-time.After(execConfig.timeout):
 | 
						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
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
//go:build integration_derp
 | 
					//nolint
 | 
				
			||||||
 | 
					 | 
				
			||||||
package headscale
 | 
					package headscale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@ -17,34 +16,39 @@ import (
 | 
				
			|||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ccding/go-stun/stun"
 | 
				
			||||||
	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
						v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 | 
				
			||||||
	"github.com/ory/dockertest/v3"
 | 
						"github.com/ory/dockertest/v3"
 | 
				
			||||||
	"github.com/ory/dockertest/v3/docker"
 | 
						"github.com/ory/dockertest/v3/docker"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"github.com/stretchr/testify/suite"
 | 
						"github.com/stretchr/testify/suite"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/ccding/go-stun/stun"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	namespaceName   = "derpnamespace"
 | 
						headscaleDerpHostname = "headscale-derp"
 | 
				
			||||||
	totalContainers = 3
 | 
						namespaceName         = "derpnamespace"
 | 
				
			||||||
 | 
						totalContainers       = 3
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IntegrationDERPTestSuite struct {
 | 
					type IntegrationDERPTestSuite struct {
 | 
				
			||||||
	suite.Suite
 | 
						suite.Suite
 | 
				
			||||||
	stats *suite.SuiteInformation
 | 
						stats *suite.SuiteInformation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pool      dockertest.Pool
 | 
						pool              dockertest.Pool
 | 
				
			||||||
	networks  map[int]dockertest.Network // so we keep the containers isolated
 | 
						network           dockertest.Network
 | 
				
			||||||
	headscale dockertest.Resource
 | 
						containerNetworks map[int]dockertest.Network // so we keep the containers isolated
 | 
				
			||||||
	saveLogs  bool
 | 
						headscale         dockertest.Resource
 | 
				
			||||||
 | 
						saveLogs          bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tailscales    map[string]dockertest.Resource
 | 
						tailscales    map[string]dockertest.Resource
 | 
				
			||||||
	joinWaitGroup sync.WaitGroup
 | 
						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")
 | 
						saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		saveLogs = false
 | 
							saveLogs = false
 | 
				
			||||||
@ -53,7 +57,7 @@ func TestDERPIntegrationTestSuite(t *testing.T) {
 | 
				
			|||||||
	s := new(IntegrationDERPTestSuite)
 | 
						s := new(IntegrationDERPTestSuite)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.tailscales = make(map[string]dockertest.Resource)
 | 
						s.tailscales = make(map[string]dockertest.Resource)
 | 
				
			||||||
	s.networks = make(map[int]dockertest.Network)
 | 
						s.containerNetworks = make(map[int]dockertest.Network)
 | 
				
			||||||
	s.saveLogs = saveLogs
 | 
						s.saveLogs = saveLogs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	suite.Run(t, s)
 | 
						suite.Run(t, s)
 | 
				
			||||||
@ -78,7 +82,7 @@ func TestDERPIntegrationTestSuite(t *testing.T) {
 | 
				
			|||||||
			log.Printf("Could not purge resource: %s\n", err)
 | 
								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 {
 | 
								if err := network.Close(); err != nil {
 | 
				
			||||||
				log.Printf("Could not close network: %s\n", err)
 | 
									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), "")
 | 
							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++ {
 | 
						for i := 0; i < totalContainers; i++ {
 | 
				
			||||||
		if pnetwork, err := s.pool.CreateNetwork(fmt.Sprintf("headscale-derp-%d", i)); err == nil {
 | 
							if pnetwork, err := s.pool.CreateNetwork(fmt.Sprintf("headscale-derp-%d", i)); err == nil {
 | 
				
			||||||
			s.networks[i] = *pnetwork
 | 
								s.containerNetworks[i] = *pnetwork
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
 | 
								s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -112,7 +122,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	headscaleOptions := &dockertest.RunOptions{
 | 
						headscaleOptions := &dockertest.RunOptions{
 | 
				
			||||||
		Name: headscaleHostname,
 | 
							Name: headscaleDerpHostname,
 | 
				
			||||||
		Mounts: []string{
 | 
							Mounts: []string{
 | 
				
			||||||
			fmt.Sprintf(
 | 
								fmt.Sprintf(
 | 
				
			||||||
				"%s/integration_test/etc_embedded_derp:/etc/headscale",
 | 
									"%s/integration_test/etc_embedded_derp:/etc/headscale",
 | 
				
			||||||
@ -120,6 +130,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
 | 
				
			|||||||
			),
 | 
								),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Cmd:          []string{"headscale", "serve"},
 | 
							Cmd:          []string{"headscale", "serve"},
 | 
				
			||||||
 | 
							Networks:     []*dockertest.Network{&s.network},
 | 
				
			||||||
		ExposedPorts: []string{"8443/tcp", "3478/udp"},
 | 
							ExposedPorts: []string{"8443/tcp", "3478/udp"},
 | 
				
			||||||
		PortBindings: map[docker.Port][]docker.PortBinding{
 | 
							PortBindings: map[docker.Port][]docker.PortBinding{
 | 
				
			||||||
			"8443/tcp": {{HostPort: "8443"}},
 | 
								"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 {
 | 
						if err != nil {
 | 
				
			||||||
		s.FailNow(
 | 
							s.FailNow(
 | 
				
			||||||
			fmt.Sprintf(
 | 
								fmt.Sprintf(
 | 
				
			||||||
@ -153,13 +164,15 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
 | 
				
			|||||||
		hostname, container := s.tailscaleContainer(
 | 
							hostname, container := s.tailscaleContainer(
 | 
				
			||||||
			fmt.Sprint(i),
 | 
								fmt.Sprint(i),
 | 
				
			||||||
			version,
 | 
								version,
 | 
				
			||||||
			s.networks[i],
 | 
								s.containerNetworks[i],
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		s.tailscales[hostname] = *container
 | 
							s.tailscales[hostname] = *container
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Println("Waiting for headscale to be ready for embedded DERP tests")
 | 
						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 {
 | 
						if err := s.pool.Retry(func() error {
 | 
				
			||||||
		url := fmt.Sprintf("https://%s/health", hostEndpoint)
 | 
							url := fmt.Sprintf("https://%s/health", hostEndpoint)
 | 
				
			||||||
@ -320,7 +333,7 @@ func (s *IntegrationDERPTestSuite) TearDownSuite() {
 | 
				
			|||||||
			log.Printf("Could not purge resource: %s\n", err)
 | 
								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 {
 | 
								if err := network.Close(); err != nil {
 | 
				
			||||||
				log.Printf("Could not close network: %s\n", err)
 | 
									log.Printf("Could not close network: %s\n", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -428,7 +441,9 @@ func (s *IntegrationDERPTestSuite) TestPingAllPeersByHostname() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (s *IntegrationDERPTestSuite) TestDERPSTUN() {
 | 
					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 := stun.NewClient()
 | 
				
			||||||
	client.SetVerbose(true)
 | 
						client.SetVerbose(true)
 | 
				
			||||||
	client.SetVVerbose(true)
 | 
						client.SetVVerbose(true)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
//go:build integration_general
 | 
					//nolint
 | 
				
			||||||
 | 
					 | 
				
			||||||
package headscale
 | 
					package headscale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@ -41,7 +40,11 @@ type IntegrationTestSuite struct {
 | 
				
			|||||||
	joinWaitGroup sync.WaitGroup
 | 
						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")
 | 
						saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		saveLogs = false
 | 
							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(
 | 
						pts, err := s.pool.BuildAndRunWithBuildOptions(
 | 
				
			||||||
		tailscaleBuildOptions,
 | 
							tailscaleBuildOptions,
 | 
				
			||||||
		tailscaleOptions,
 | 
							tailscaleOptions,
 | 
				
			||||||
@ -219,11 +233,11 @@ func (s *IntegrationTestSuite) SetupSuite() {
 | 
				
			|||||||
		s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
 | 
							s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil {
 | 
						network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork)
 | 
				
			||||||
		s.network = *pnetwork
 | 
						if err != nil {
 | 
				
			||||||
	} else {
 | 
							s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "")
 | 
				
			||||||
		s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						s.network = network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	headscaleBuildOptions := &dockertest.BuildOptions{
 | 
						headscaleBuildOptions := &dockertest.BuildOptions{
 | 
				
			||||||
		Dockerfile: "Dockerfile",
 | 
							Dockerfile: "Dockerfile",
 | 
				
			||||||
@ -236,10 +250,14 @@ func (s *IntegrationTestSuite) SetupSuite() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	headscaleOptions := &dockertest.RunOptions{
 | 
						headscaleOptions := &dockertest.RunOptions{
 | 
				
			||||||
		Name: "headscale",
 | 
							Name: headscaleHostname,
 | 
				
			||||||
		Mounts: []string{
 | 
							Mounts: []string{
 | 
				
			||||||
			fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath),
 | 
								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},
 | 
							Networks: []*dockertest.Network{&s.network},
 | 
				
			||||||
		Cmd:      []string{"headscale", "serve"},
 | 
							Cmd:      []string{"headscale", "serve"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -278,7 +296,9 @@ func (s *IntegrationTestSuite) SetupSuite() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Println("Waiting for headscale to be ready for core integration tests")
 | 
						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 {
 | 
						if err := s.pool.Retry(func() error {
 | 
				
			||||||
		url := fmt.Sprintf("http://%s/health", hostEndpoint)
 | 
							url := fmt.Sprintf("http://%s/health", hostEndpoint)
 | 
				
			||||||
@ -487,7 +507,7 @@ func getIPsfromIPNstate(status ipnstate.Status) []netip.Addr {
 | 
				
			|||||||
	return ips
 | 
						return ips
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Adopt test for cross communication between namespaces
 | 
					// TODO: Adopt test for cross communication between namespaces.
 | 
				
			||||||
func (s *IntegrationTestSuite) TestPingAllPeersByAddress() {
 | 
					func (s *IntegrationTestSuite) TestPingAllPeersByAddress() {
 | 
				
			||||||
	for _, scales := range s.namespaces {
 | 
						for _, scales := range s.namespaces {
 | 
				
			||||||
		ips, err := getIPs(scales.tailscales)
 | 
							ips, err := getIPs(scales.tailscales)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
//go:build integration_oidc
 | 
					//nolint
 | 
				
			||||||
 | 
					 | 
				
			||||||
package headscale
 | 
					package headscale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@ -25,7 +24,8 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	oidcHeadscaleHostname = "headscale"
 | 
						oidcHeadscaleHostname = "headscale-oidc"
 | 
				
			||||||
 | 
						oidcMockHostname      = "headscale-mock-oidc"
 | 
				
			||||||
	oidcNamespaceName     = "oidcnamespace"
 | 
						oidcNamespaceName     = "oidcnamespace"
 | 
				
			||||||
	totalOidcContainers   = 3
 | 
						totalOidcContainers   = 3
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -44,7 +44,11 @@ type IntegrationOIDCTestSuite struct {
 | 
				
			|||||||
	joinWaitGroup sync.WaitGroup
 | 
						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")
 | 
						saveLogs, err := GetEnvBool("HEADSCALE_INTEGRATION_SAVE_LOG")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		saveLogs = false
 | 
							saveLogs = false
 | 
				
			||||||
@ -95,33 +99,25 @@ func (s *IntegrationOIDCTestSuite) SetupSuite() {
 | 
				
			|||||||
		s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
 | 
							s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil {
 | 
						network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork)
 | 
				
			||||||
		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")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						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])
 | 
						log.Printf("Network config: %v", s.network.Network.IPAM.Config[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s.Suite.T().Log("Setting up mock OIDC")
 | 
						s.Suite.T().Log("Setting up mock OIDC")
 | 
				
			||||||
	mockOidcOptions := &dockertest.RunOptions{
 | 
						mockOidcOptions := &dockertest.RunOptions{
 | 
				
			||||||
		Name:         "mockoidc",
 | 
							Name:         oidcMockHostname,
 | 
				
			||||||
		Hostname:     "mockoidc",
 | 
					 | 
				
			||||||
		Cmd:          []string{"headscale", "mockoidc"},
 | 
							Cmd:          []string{"headscale", "mockoidc"},
 | 
				
			||||||
		ExposedPorts: []string{"10000/tcp"},
 | 
							ExposedPorts: []string{"10000/tcp"},
 | 
				
			||||||
		Networks:     []*dockertest.Network{&s.network},
 | 
					 | 
				
			||||||
		PortBindings: map[docker.Port][]docker.PortBinding{
 | 
							PortBindings: map[docker.Port][]docker.PortBinding{
 | 
				
			||||||
			"10000/tcp": {{HostPort: "10000"}},
 | 
								"10000/tcp": {{HostPort: "10000"}},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							Networks: []*dockertest.Network{&s.network},
 | 
				
			||||||
		Env: []string{
 | 
							Env: []string{
 | 
				
			||||||
 | 
								fmt.Sprintf("MOCKOIDC_ADDR=%s", oidcMockHostname),
 | 
				
			||||||
			"MOCKOIDC_PORT=10000",
 | 
								"MOCKOIDC_PORT=10000",
 | 
				
			||||||
			"MOCKOIDC_CLIENT_ID=superclient",
 | 
								"MOCKOIDC_CLIENT_ID=superclient",
 | 
				
			||||||
			"MOCKOIDC_CLIENT_SECRET=supersecret",
 | 
								"MOCKOIDC_CLIENT_SECRET=supersecret",
 | 
				
			||||||
@ -133,6 +129,17 @@ func (s *IntegrationOIDCTestSuite) SetupSuite() {
 | 
				
			|||||||
		ContextDir: ".",
 | 
							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(
 | 
						if pmockoidc, err := s.pool.BuildAndRunWithBuildOptions(
 | 
				
			||||||
		headscaleBuildOptions,
 | 
							headscaleBuildOptions,
 | 
				
			||||||
		mockOidcOptions,
 | 
							mockOidcOptions,
 | 
				
			||||||
@ -142,6 +149,35 @@ func (s *IntegrationOIDCTestSuite) SetupSuite() {
 | 
				
			|||||||
		s.FailNow(fmt.Sprintf("Could not start mockOIDC container: %s", err), "")
 | 
							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(`
 | 
						oidcCfg := fmt.Sprintf(`
 | 
				
			||||||
oidc:
 | 
					oidc:
 | 
				
			||||||
  issuer: http://%s:10000/oidc
 | 
					  issuer: http://%s:10000/oidc
 | 
				
			||||||
@ -164,7 +200,7 @@ oidc:
 | 
				
			|||||||
	log.Println(config)
 | 
						log.Println(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	configPath := path.Join(currentPath, "integration_test/etc_oidc/config.yaml")
 | 
						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 {
 | 
						if err != nil {
 | 
				
			||||||
		s.FailNow(fmt.Sprintf("Could not write config: %s", err), "")
 | 
							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")
 | 
						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 {
 | 
						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 := http.DefaultTransport.(*http.Transport).Clone()
 | 
				
			||||||
		insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
 | 
							insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
 | 
				
			||||||
		client := &http.Client{Transport: insecureTransport}
 | 
							client := &http.Client{Transport: insecureTransport}
 | 
				
			||||||
@ -294,6 +334,8 @@ func (s *IntegrationOIDCTestSuite) AuthenticateOIDC(
 | 
				
			|||||||
	resp, err := client.Get(loginURL.String())
 | 
						resp, err := client.Get(loginURL.String())
 | 
				
			||||||
	assert.Nil(s.T(), err)
 | 
						assert.Nil(s.T(), err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Printf("auth body, err: %#v, %s", resp, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := io.ReadAll(resp.Body)
 | 
						body, err := io.ReadAll(resp.Body)
 | 
				
			||||||
	assert.Nil(s.T(), err)
 | 
						assert.Nil(s.T(), err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -308,7 +350,6 @@ func (s *IntegrationOIDCTestSuite) joinOIDC(
 | 
				
			|||||||
	endpoint, hostname string,
 | 
						endpoint, hostname string,
 | 
				
			||||||
	tailscale dockertest.Resource,
 | 
						tailscale dockertest.Resource,
 | 
				
			||||||
) (*url.URL, error) {
 | 
					) (*url.URL, error) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	command := []string{
 | 
						command := []string{
 | 
				
			||||||
		"tailscale",
 | 
							"tailscale",
 | 
				
			||||||
		"up",
 | 
							"up",
 | 
				
			||||||
@ -374,7 +415,13 @@ func (s *IntegrationOIDCTestSuite) tailscaleContainer(
 | 
				
			|||||||
		DockerAllowNetworkAdministration,
 | 
							DockerAllowNetworkAdministration,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if err != nil {
 | 
						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)
 | 
						log.Printf("Created %s container\n", hostname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -497,7 +544,12 @@ func (s *IntegrationOIDCTestSuite) TestPingAllPeersByAddress() {
 | 
				
			|||||||
							[]string{},
 | 
												[]string{},
 | 
				
			||||||
						)
 | 
											)
 | 
				
			||||||
						assert.Nil(t, err)
 | 
											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")
 | 
											assert.Contains(t, stdout, "pong")
 | 
				
			||||||
					})
 | 
										})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ private_key_path: private.key
 | 
				
			|||||||
noise:
 | 
					noise:
 | 
				
			||||||
  private_key_path: noise_private.key
 | 
					  private_key_path: noise_private.key
 | 
				
			||||||
listen_addr: 0.0.0.0:8443
 | 
					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_cert_path: "/etc/headscale/tls/server.crt"
 | 
				
			||||||
tls_key_path: "/etc/headscale/tls/server.key"
 | 
					tls_key_path: "/etc/headscale/tls/server.key"
 | 
				
			||||||
tls_client_auth_mode: disabled
 | 
					tls_client_auth_mode: disabled
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								utils.go
									
									
									
									
									
								
							@ -17,6 +17,7 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -64,6 +65,8 @@ const (
 | 
				
			|||||||
	ZstdCompression = "zstd"
 | 
						ZstdCompression = "zstd"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var NodePublicKeyRegex = regexp.MustCompile("nodekey:[a-fA-F0-9]+")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func MachinePublicKeyStripPrefix(machineKey key.MachinePublic) string {
 | 
					func MachinePublicKeyStripPrefix(machineKey key.MachinePublic) string {
 | 
				
			||||||
	return strings.TrimPrefix(machineKey.String(), machinePublicHexPrefix)
 | 
						return strings.TrimPrefix(machineKey.String(), machinePublicHexPrefix)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -325,7 +328,9 @@ func GenerateRandomStringDNSSafe(size int) (string, error) {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		str = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", ""))
 | 
							str = strings.ToLower(
 | 
				
			||||||
 | 
								strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", ""),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return str[:size], nil
 | 
						return str[:size], nil
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user