diff --git a/.github/workflows/test-integration-cli.yml b/.github/workflows/test-integration-cli.yml deleted file mode 100644 index 72cf31aa..00000000 --- a/.github/workflows/test-integration-cli.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Integration Test CLI - -on: [pull_request] - -jobs: - integration-test-cli: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - 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@v34 - 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' - run: nix develop --command -- make test_integration_cli diff --git a/.github/workflows/test-integration-v2-TestACLAllowStarDst.yaml b/.github/workflows/test-integration-v2-TestACLAllowStarDst.yaml index f63a6d2a..1a0487b0 100644 --- a/.github/workflows/test-integration-v2-TestACLAllowStarDst.yaml +++ b/.github/workflows/test-integration-v2-TestACLAllowStarDst.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestACLAllowUser80Dst.yaml b/.github/workflows/test-integration-v2-TestACLAllowUser80Dst.yaml index 3b3dd59e..06e48b83 100644 --- a/.github/workflows/test-integration-v2-TestACLAllowUser80Dst.yaml +++ b/.github/workflows/test-integration-v2-TestACLAllowUser80Dst.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestACLAllowUserDst.yaml b/.github/workflows/test-integration-v2-TestACLAllowUserDst.yaml index 54b425cf..e78049b2 100644 --- a/.github/workflows/test-integration-v2-TestACLAllowUserDst.yaml +++ b/.github/workflows/test-integration-v2-TestACLAllowUserDst.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestACLDenyAllPort80.yaml b/.github/workflows/test-integration-v2-TestACLDenyAllPort80.yaml index ed01bf71..79df71d5 100644 --- a/.github/workflows/test-integration-v2-TestACLDenyAllPort80.yaml +++ b/.github/workflows/test-integration-v2-TestACLDenyAllPort80.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestACLDevice1CanAccessDevice2.yaml b/.github/workflows/test-integration-v2-TestACLDevice1CanAccessDevice2.yaml index 4efbf1a5..8a36a2d2 100644 --- a/.github/workflows/test-integration-v2-TestACLDevice1CanAccessDevice2.yaml +++ b/.github/workflows/test-integration-v2-TestACLDevice1CanAccessDevice2.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestACLHostsInNetMapTable.yaml b/.github/workflows/test-integration-v2-TestACLHostsInNetMapTable.yaml index 7445695c..c20cdf68 100644 --- a/.github/workflows/test-integration-v2-TestACLHostsInNetMapTable.yaml +++ b/.github/workflows/test-integration-v2-TestACLHostsInNetMapTable.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestACLNamedHostsCanReach.yaml b/.github/workflows/test-integration-v2-TestACLNamedHostsCanReach.yaml index f0f663ca..d1e4c9d1 100644 --- a/.github/workflows/test-integration-v2-TestACLNamedHostsCanReach.yaml +++ b/.github/workflows/test-integration-v2-TestACLNamedHostsCanReach.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestACLNamedHostsCanReachBySubnet.yaml b/.github/workflows/test-integration-v2-TestACLNamedHostsCanReachBySubnet.yaml index daeb6eee..344ae518 100644 --- a/.github/workflows/test-integration-v2-TestACLNamedHostsCanReachBySubnet.yaml +++ b/.github/workflows/test-integration-v2-TestACLNamedHostsCanReachBySubnet.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestAuthKeyLogoutAndRelogin.yaml b/.github/workflows/test-integration-v2-TestAuthKeyLogoutAndRelogin.yaml index c603d4fe..675f7dd0 100644 --- a/.github/workflows/test-integration-v2-TestAuthKeyLogoutAndRelogin.yaml +++ b/.github/workflows/test-integration-v2-TestAuthKeyLogoutAndRelogin.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestAuthWebFlowAuthenticationPingAll.yaml b/.github/workflows/test-integration-v2-TestAuthWebFlowAuthenticationPingAll.yaml index 19d99017..984eb650 100644 --- a/.github/workflows/test-integration-v2-TestAuthWebFlowAuthenticationPingAll.yaml +++ b/.github/workflows/test-integration-v2-TestAuthWebFlowAuthenticationPingAll.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestAuthWebFlowLogoutAndRelogin.yaml b/.github/workflows/test-integration-v2-TestAuthWebFlowLogoutAndRelogin.yaml index a9eeb403..47177d49 100644 --- a/.github/workflows/test-integration-v2-TestAuthWebFlowLogoutAndRelogin.yaml +++ b/.github/workflows/test-integration-v2-TestAuthWebFlowLogoutAndRelogin.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestCreateTailscale.yaml b/.github/workflows/test-integration-v2-TestCreateTailscale.yaml index e342de61..47a5157d 100644 --- a/.github/workflows/test-integration-v2-TestCreateTailscale.yaml +++ b/.github/workflows/test-integration-v2-TestCreateTailscale.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestDERPServerScenario.yaml b/.github/workflows/test-integration-v2-TestDERPServerScenario.yaml index b3246c27..0dfaeb75 100644 --- a/.github/workflows/test-integration-v2-TestDERPServerScenario.yaml +++ b/.github/workflows/test-integration-v2-TestDERPServerScenario.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestEnablingRoutes.yaml b/.github/workflows/test-integration-v2-TestEnablingRoutes.yaml index f6607736..aeb86428 100644 --- a/.github/workflows/test-integration-v2-TestEnablingRoutes.yaml +++ b/.github/workflows/test-integration-v2-TestEnablingRoutes.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestEphemeral.yaml b/.github/workflows/test-integration-v2-TestEphemeral.yaml index f3af06ac..e81e0937 100644 --- a/.github/workflows/test-integration-v2-TestEphemeral.yaml +++ b/.github/workflows/test-integration-v2-TestEphemeral.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestExpireNode.yaml b/.github/workflows/test-integration-v2-TestExpireNode.yaml index 9daac7e1..80af2608 100644 --- a/.github/workflows/test-integration-v2-TestExpireNode.yaml +++ b/.github/workflows/test-integration-v2-TestExpireNode.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestHeadscale.yaml b/.github/workflows/test-integration-v2-TestHeadscale.yaml index d93a54c9..52b562df 100644 --- a/.github/workflows/test-integration-v2-TestHeadscale.yaml +++ b/.github/workflows/test-integration-v2-TestHeadscale.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestOIDCAuthenticationPingAll.yaml b/.github/workflows/test-integration-v2-TestOIDCAuthenticationPingAll.yaml index 003319e6..e9a4e1ca 100644 --- a/.github/workflows/test-integration-v2-TestOIDCAuthenticationPingAll.yaml +++ b/.github/workflows/test-integration-v2-TestOIDCAuthenticationPingAll.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestOIDCExpireNodesBasedOnTokenExpiry.yaml b/.github/workflows/test-integration-v2-TestOIDCExpireNodesBasedOnTokenExpiry.yaml index cd8d9c34..f9068888 100644 --- a/.github/workflows/test-integration-v2-TestOIDCExpireNodesBasedOnTokenExpiry.yaml +++ b/.github/workflows/test-integration-v2-TestOIDCExpireNodesBasedOnTokenExpiry.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestPingAllByHostname.yaml b/.github/workflows/test-integration-v2-TestPingAllByHostname.yaml index 12633c80..60e9fbaf 100644 --- a/.github/workflows/test-integration-v2-TestPingAllByHostname.yaml +++ b/.github/workflows/test-integration-v2-TestPingAllByHostname.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestPingAllByIP.yaml b/.github/workflows/test-integration-v2-TestPingAllByIP.yaml index 834fce94..05413e86 100644 --- a/.github/workflows/test-integration-v2-TestPingAllByIP.yaml +++ b/.github/workflows/test-integration-v2-TestPingAllByIP.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestPreAuthKeyCommand.yaml b/.github/workflows/test-integration-v2-TestPreAuthKeyCommand.yaml index 6bc5ded6..f828b51d 100644 --- a/.github/workflows/test-integration-v2-TestPreAuthKeyCommand.yaml +++ b/.github/workflows/test-integration-v2-TestPreAuthKeyCommand.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestPreAuthKeyCommandReusableEphemeral.yaml b/.github/workflows/test-integration-v2-TestPreAuthKeyCommandReusableEphemeral.yaml index 003128b6..2114bd89 100644 --- a/.github/workflows/test-integration-v2-TestPreAuthKeyCommandReusableEphemeral.yaml +++ b/.github/workflows/test-integration-v2-TestPreAuthKeyCommandReusableEphemeral.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestPreAuthKeyCommandWithoutExpiry.yaml b/.github/workflows/test-integration-v2-TestPreAuthKeyCommandWithoutExpiry.yaml index 619b2647..53cb3957 100644 --- a/.github/workflows/test-integration-v2-TestPreAuthKeyCommandWithoutExpiry.yaml +++ b/.github/workflows/test-integration-v2-TestPreAuthKeyCommandWithoutExpiry.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestResolveMagicDNS.yaml b/.github/workflows/test-integration-v2-TestResolveMagicDNS.yaml index ad6aba62..ac2b0b92 100644 --- a/.github/workflows/test-integration-v2-TestResolveMagicDNS.yaml +++ b/.github/workflows/test-integration-v2-TestResolveMagicDNS.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestSSHIsBlockedInACL.yaml b/.github/workflows/test-integration-v2-TestSSHIsBlockedInACL.yaml index 5664151b..5ee979b4 100644 --- a/.github/workflows/test-integration-v2-TestSSHIsBlockedInACL.yaml +++ b/.github/workflows/test-integration-v2-TestSSHIsBlockedInACL.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestSSHMultipleUsersAllToAll.yaml b/.github/workflows/test-integration-v2-TestSSHMultipleUsersAllToAll.yaml index 286ad486..bcc88863 100644 --- a/.github/workflows/test-integration-v2-TestSSHMultipleUsersAllToAll.yaml +++ b/.github/workflows/test-integration-v2-TestSSHMultipleUsersAllToAll.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestSSHNoSSHConfigured.yaml b/.github/workflows/test-integration-v2-TestSSHNoSSHConfigured.yaml index e4fa04a6..bf641a0e 100644 --- a/.github/workflows/test-integration-v2-TestSSHNoSSHConfigured.yaml +++ b/.github/workflows/test-integration-v2-TestSSHNoSSHConfigured.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestSSHOneUserAllToAll.yaml b/.github/workflows/test-integration-v2-TestSSHOneUserAllToAll.yaml index 3ef3e991..d61378de 100644 --- a/.github/workflows/test-integration-v2-TestSSHOneUserAllToAll.yaml +++ b/.github/workflows/test-integration-v2-TestSSHOneUserAllToAll.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestSSUserOnlyIsolation.yaml b/.github/workflows/test-integration-v2-TestSSUserOnlyIsolation.yaml index 52fd09a4..a40a3af0 100644 --- a/.github/workflows/test-integration-v2-TestSSUserOnlyIsolation.yaml +++ b/.github/workflows/test-integration-v2-TestSSUserOnlyIsolation.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestTaildrop.yaml b/.github/workflows/test-integration-v2-TestTaildrop.yaml index 97601239..1d6c2430 100644 --- a/.github/workflows/test-integration-v2-TestTaildrop.yaml +++ b/.github/workflows/test-integration-v2-TestTaildrop.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestTailscaleNodesJoiningHeadcale.yaml b/.github/workflows/test-integration-v2-TestTailscaleNodesJoiningHeadcale.yaml index ca70dd72..941c2311 100644 --- a/.github/workflows/test-integration-v2-TestTailscaleNodesJoiningHeadcale.yaml +++ b/.github/workflows/test-integration-v2-TestTailscaleNodesJoiningHeadcale.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/.github/workflows/test-integration-v2-TestUserCommand.yaml b/.github/workflows/test-integration-v2-TestUserCommand.yaml index 5b37b9e7..449512bf 100644 --- a/.github/workflows/test-integration-v2-TestUserCommand.yaml +++ b/.github/workflows/test-integration-v2-TestUserCommand.yaml @@ -43,7 +43,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/Makefile b/Makefile index 62bd6b11..4fdf418e 100644 --- a/Makefile +++ b/Makefile @@ -24,21 +24,9 @@ build: dev: lint test build test: - @go test $(TAGS) -short -coverprofile=coverage.out ./... + gotestsum -- $(TAGS) -short -coverprofile=coverage.out ./... -test_integration: test_integration_cli test_integration_v2_general - -test_integration_cli: - 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 run gotest.tools/gotestsum@latest -- $(TAGS) -failfast -timeout 30m -count=1 -run IntegrationCLI ./... - -test_integration_v2_general: +test_integration: docker run \ -t --rm \ -v ~/.cache/hs-integration-go:/go \ diff --git a/cmd/gh-action-integration-generator/main.go b/cmd/gh-action-integration-generator/main.go index cc0e7c92..0b363b0c 100644 --- a/cmd/gh-action-integration-generator/main.go +++ b/cmd/gh-action-integration-generator/main.go @@ -64,7 +64,7 @@ jobs: --volume /var/run/docker.sock:/var/run/docker.sock \ --volume $PWD/control_logs:/tmp/control \ golang:1 \ - go test ./... \ + go run gotest.tools/gotestsum@latest -- ./... \ -tags ts2019 \ -failfast \ -timeout 120m \ diff --git a/integration/cli_test.go b/integration/cli_test.go index ff90fd9a..039b065a 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -532,3 +532,989 @@ func TestEnablingRoutes(t *testing.T) { } } } + +func TestApiKeyCommand(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + count := 5 + + scenario, err := NewScenario() + assert.NoError(t, err) + + spec := map[string]int{ + "user1": 0, + "user2": 0, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + assert.NoError(t, err) + + headscale, err := scenario.Headscale() + assert.NoError(t, err) + + keys := make([]string, count) + + for idx := 0; idx < count; idx++ { + apiResult, err := headscale.Execute( + []string{ + "headscale", + "apikeys", + "create", + "--expiration", + "24h", + "--output", + "json", + }, + ) + assert.Nil(t, err) + assert.NotEmpty(t, apiResult) + + keys[idx] = apiResult + } + + assert.Len(t, keys, 5) + + var listedAPIKeys []v1.ApiKey + err = executeAndUnmarshal(headscale, + []string{ + "headscale", + "apikeys", + "list", + "--output", + "json", + }, + &listedAPIKeys, + ) + assert.Nil(t, err) + + assert.Len(t, listedAPIKeys, 5) + + assert.Equal(t, uint64(1), listedAPIKeys[0].Id) + assert.Equal(t, uint64(2), listedAPIKeys[1].Id) + assert.Equal(t, uint64(3), listedAPIKeys[2].Id) + assert.Equal(t, uint64(4), listedAPIKeys[3].Id) + assert.Equal(t, uint64(5), listedAPIKeys[4].Id) + + assert.NotEmpty(t, listedAPIKeys[0].Prefix) + assert.NotEmpty(t, listedAPIKeys[1].Prefix) + assert.NotEmpty(t, listedAPIKeys[2].Prefix) + assert.NotEmpty(t, listedAPIKeys[3].Prefix) + assert.NotEmpty(t, listedAPIKeys[4].Prefix) + + assert.True(t, listedAPIKeys[0].Expiration.AsTime().After(time.Now())) + assert.True(t, listedAPIKeys[1].Expiration.AsTime().After(time.Now())) + assert.True(t, listedAPIKeys[2].Expiration.AsTime().After(time.Now())) + assert.True(t, listedAPIKeys[3].Expiration.AsTime().After(time.Now())) + assert.True(t, listedAPIKeys[4].Expiration.AsTime().After(time.Now())) + + assert.True( + t, + listedAPIKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + t, + listedAPIKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + t, + listedAPIKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + t, + listedAPIKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + assert.True( + t, + listedAPIKeys[4].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), + ) + + expiredPrefixes := make(map[string]bool) + + // Expire three keys + for idx := 0; idx < 3; idx++ { + _, err := headscale.Execute( + []string{ + "headscale", + "apikeys", + "expire", + "--prefix", + listedAPIKeys[idx].Prefix, + }, + ) + assert.Nil(t, err) + + expiredPrefixes[listedAPIKeys[idx].Prefix] = true + } + + var listedAfterExpireAPIKeys []v1.ApiKey + err = executeAndUnmarshal(headscale, + []string{ + "headscale", + "apikeys", + "list", + "--output", + "json", + }, + &listedAfterExpireAPIKeys, + ) + assert.Nil(t, err) + + for index := range listedAfterExpireAPIKeys { + if _, ok := expiredPrefixes[listedAfterExpireAPIKeys[index].Prefix]; ok { + // Expired + assert.True( + t, + listedAfterExpireAPIKeys[index].Expiration.AsTime().Before(time.Now()), + ) + } else { + // Not expired + assert.False( + t, + listedAfterExpireAPIKeys[index].Expiration.AsTime().Before(time.Now()), + ) + } + } + + err = scenario.Shutdown() + assert.NoError(t, err) +} + +func TestNodeTagCommand(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + scenario, err := NewScenario() + assert.NoError(t, err) + + spec := map[string]int{ + "user1": 0, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + assert.NoError(t, err) + + headscale, err := scenario.Headscale() + assert.NoError(t, err) + + machineKeys := []string{ + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + } + machines := make([]*v1.Machine, len(machineKeys)) + assert.Nil(t, err) + + for index, machineKey := range machineKeys { + _, err := headscale.Execute( + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("machine-%d", index+1), + "--user", + "user1", + "--key", + machineKey, + "--output", + "json", + }, + ) + assert.Nil(t, err) + + var machine v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "--user", + "user1", + "register", + "--key", + machineKey, + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + machines[index] = &machine + } + assert.Len(t, machines, len(machineKeys)) + + var machine v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "tag", + "-i", "1", + "-t", "tag:test", + "--output", "json", + }, + &machine, + ) + assert.Nil(t, err) + + assert.Equal(t, []string{"tag:test"}, machine.ForcedTags) + + // try to set a wrong tag and retrieve the error + type errOutput struct { + Error string `json:"error"` + } + var errorOutput errOutput + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "tag", + "-i", "2", + "-t", "wrong-tag", + "--output", "json", + }, + &errorOutput, + ) + assert.Nil(t, err) + assert.Contains(t, errorOutput.Error, "tag must start with the string 'tag:'") + + // Test list all nodes after added seconds + resultMachines := make([]*v1.Machine, len(machineKeys)) + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", "json", + }, + &resultMachines, + ) + assert.Nil(t, err) + found := false + for _, machine := range resultMachines { + if machine.ForcedTags != nil { + for _, tag := range machine.ForcedTags { + if tag == "tag:test" { + found = true + } + } + } + } + assert.Equal( + t, + true, + found, + "should find a machine with the tag 'tag:test' in the list of machines", + ) + + err = scenario.Shutdown() + assert.NoError(t, err) +} + +func TestNodeCommand(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + scenario, err := NewScenario() + assert.NoError(t, err) + + spec := map[string]int{ + "machine-user": 0, + "other-user": 0, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + assert.NoError(t, err) + + headscale, err := scenario.Headscale() + assert.NoError(t, err) + + // Randomly generated machine keys + machineKeys := []string{ + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + } + machines := make([]*v1.Machine, len(machineKeys)) + assert.Nil(t, err) + + for index, machineKey := range machineKeys { + _, err := headscale.Execute( + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("machine-%d", index+1), + "--user", + "machine-user", + "--key", + machineKey, + "--output", + "json", + }, + ) + assert.Nil(t, err) + + var machine v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "--user", + "machine-user", + "register", + "--key", + machineKey, + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + machines[index] = &machine + } + + assert.Len(t, machines, len(machineKeys)) + + // Test list all nodes after added seconds + var listAll []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAll, + ) + assert.Nil(t, err) + + assert.Len(t, listAll, 5) + + assert.Equal(t, uint64(1), listAll[0].Id) + assert.Equal(t, uint64(2), listAll[1].Id) + assert.Equal(t, uint64(3), listAll[2].Id) + assert.Equal(t, uint64(4), listAll[3].Id) + assert.Equal(t, uint64(5), listAll[4].Id) + + assert.Equal(t, "machine-1", listAll[0].Name) + assert.Equal(t, "machine-2", listAll[1].Name) + assert.Equal(t, "machine-3", listAll[2].Name) + assert.Equal(t, "machine-4", listAll[3].Name) + assert.Equal(t, "machine-5", listAll[4].Name) + + otherUserMachineKeys := []string{ + "nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", + "nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", + } + otherUserMachines := make([]*v1.Machine, len(otherUserMachineKeys)) + assert.Nil(t, err) + + for index, machineKey := range otherUserMachineKeys { + _, err := headscale.Execute( + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("otherUser-machine-%d", index+1), + "--user", + "other-user", + "--key", + machineKey, + "--output", + "json", + }, + ) + assert.Nil(t, err) + + var machine v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "--user", + "other-user", + "register", + "--key", + machineKey, + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + otherUserMachines[index] = &machine + } + + assert.Len(t, otherUserMachines, len(otherUserMachineKeys)) + + // Test list all nodes after added otherUser + var listAllWithotherUser []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAllWithotherUser, + ) + assert.Nil(t, err) + + // All nodes, machines + otherUser + assert.Len(t, listAllWithotherUser, 7) + + assert.Equal(t, uint64(6), listAllWithotherUser[5].Id) + assert.Equal(t, uint64(7), listAllWithotherUser[6].Id) + + assert.Equal(t, "otherUser-machine-1", listAllWithotherUser[5].Name) + assert.Equal(t, "otherUser-machine-2", listAllWithotherUser[6].Name) + + // Test list all nodes after added otherUser + var listOnlyotherUserMachineUser []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--user", + "other-user", + "--output", + "json", + }, + &listOnlyotherUserMachineUser, + ) + assert.Nil(t, err) + + assert.Len(t, listOnlyotherUserMachineUser, 2) + + assert.Equal(t, uint64(6), listOnlyotherUserMachineUser[0].Id) + assert.Equal(t, uint64(7), listOnlyotherUserMachineUser[1].Id) + + assert.Equal( + t, + "otherUser-machine-1", + listOnlyotherUserMachineUser[0].Name, + ) + assert.Equal( + t, + "otherUser-machine-2", + listOnlyotherUserMachineUser[1].Name, + ) + + // Delete a machines + _, err = headscale.Execute( + []string{ + "headscale", + "nodes", + "delete", + "--identifier", + // Delete the last added machine + "4", + "--output", + "json", + "--force", + }, + ) + assert.Nil(t, err) + + // Test: list main user after machine is deleted + var listOnlyMachineUserAfterDelete []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--user", + "machine-user", + "--output", + "json", + }, + &listOnlyMachineUserAfterDelete, + ) + assert.Nil(t, err) + + assert.Len(t, listOnlyMachineUserAfterDelete, 4) + + err = scenario.Shutdown() + assert.NoError(t, err) +} + +func TestNodeExpireCommand(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + scenario, err := NewScenario() + assert.NoError(t, err) + + spec := map[string]int{ + "machine-expire-user": 0, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + assert.NoError(t, err) + + headscale, err := scenario.Headscale() + assert.NoError(t, err) + + // Randomly generated machine keys + machineKeys := []string{ + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + } + machines := make([]*v1.Machine, len(machineKeys)) + + for index, machineKey := range machineKeys { + _, err := headscale.Execute( + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("machine-%d", index+1), + "--user", + "machine-expire-user", + "--key", + machineKey, + "--output", + "json", + }, + ) + assert.Nil(t, err) + + var machine v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "--user", + "machine-expire-user", + "register", + "--key", + machineKey, + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + machines[index] = &machine + } + + assert.Len(t, machines, len(machineKeys)) + + var listAll []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAll, + ) + assert.Nil(t, err) + + assert.Len(t, listAll, 5) + + assert.True(t, listAll[0].Expiry.AsTime().IsZero()) + assert.True(t, listAll[1].Expiry.AsTime().IsZero()) + assert.True(t, listAll[2].Expiry.AsTime().IsZero()) + assert.True(t, listAll[3].Expiry.AsTime().IsZero()) + assert.True(t, listAll[4].Expiry.AsTime().IsZero()) + + for idx := 0; idx < 3; idx++ { + _, err := headscale.Execute( + []string{ + "headscale", + "nodes", + "expire", + "--identifier", + fmt.Sprintf("%d", listAll[idx].Id), + }, + ) + assert.Nil(t, err) + } + + var listAllAfterExpiry []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAllAfterExpiry, + ) + assert.Nil(t, err) + + assert.Len(t, listAllAfterExpiry, 5) + + assert.True(t, listAllAfterExpiry[0].Expiry.AsTime().Before(time.Now())) + assert.True(t, listAllAfterExpiry[1].Expiry.AsTime().Before(time.Now())) + assert.True(t, listAllAfterExpiry[2].Expiry.AsTime().Before(time.Now())) + assert.True(t, listAllAfterExpiry[3].Expiry.AsTime().IsZero()) + assert.True(t, listAllAfterExpiry[4].Expiry.AsTime().IsZero()) + + err = scenario.Shutdown() + assert.NoError(t, err) +} + +func TestNodeRenameCommand(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + scenario, err := NewScenario() + assert.NoError(t, err) + + spec := map[string]int{ + "machine-rename-command": 0, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + assert.NoError(t, err) + + headscale, err := scenario.Headscale() + assert.NoError(t, err) + + // Randomly generated machine keys + machineKeys := []string{ + "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", + "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", + "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", + "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", + "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", + } + machines := make([]*v1.Machine, len(machineKeys)) + assert.Nil(t, err) + + for index, machineKey := range machineKeys { + _, err := headscale.Execute( + []string{ + "headscale", + "debug", + "create-node", + "--name", + fmt.Sprintf("machine-%d", index+1), + "--user", + "machine-rename-command", + "--key", + machineKey, + "--output", + "json", + }, + ) + assert.Nil(t, err) + + var machine v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "--user", + "machine-rename-command", + "register", + "--key", + machineKey, + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + machines[index] = &machine + } + + assert.Len(t, machines, len(machineKeys)) + + var listAll []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAll, + ) + assert.Nil(t, err) + + assert.Len(t, listAll, 5) + + assert.Contains(t, listAll[0].GetGivenName(), "machine-1") + assert.Contains(t, listAll[1].GetGivenName(), "machine-2") + assert.Contains(t, listAll[2].GetGivenName(), "machine-3") + assert.Contains(t, listAll[3].GetGivenName(), "machine-4") + assert.Contains(t, listAll[4].GetGivenName(), "machine-5") + + for idx := 0; idx < 3; idx++ { + _, err := headscale.Execute( + []string{ + "headscale", + "nodes", + "rename", + "--identifier", + fmt.Sprintf("%d", listAll[idx].Id), + fmt.Sprintf("newmachine-%d", idx+1), + }, + ) + assert.Nil(t, err) + } + + var listAllAfterRename []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAllAfterRename, + ) + assert.Nil(t, err) + + assert.Len(t, listAllAfterRename, 5) + + assert.Equal(t, "newmachine-1", listAllAfterRename[0].GetGivenName()) + assert.Equal(t, "newmachine-2", listAllAfterRename[1].GetGivenName()) + assert.Equal(t, "newmachine-3", listAllAfterRename[2].GetGivenName()) + assert.Contains(t, listAllAfterRename[3].GetGivenName(), "machine-4") + assert.Contains(t, listAllAfterRename[4].GetGivenName(), "machine-5") + + // Test failure for too long names + result, err := headscale.Execute( + []string{ + "headscale", + "nodes", + "rename", + "--identifier", + fmt.Sprintf("%d", listAll[4].Id), + "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine12345678901234567890", + }, + ) + assert.Nil(t, err) + assert.Contains(t, result, "not be over 63 chars") + + var listAllAfterRenameAttempt []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &listAllAfterRenameAttempt, + ) + assert.Nil(t, err) + + assert.Len(t, listAllAfterRenameAttempt, 5) + + assert.Equal(t, "newmachine-1", listAllAfterRenameAttempt[0].GetGivenName()) + assert.Equal(t, "newmachine-2", listAllAfterRenameAttempt[1].GetGivenName()) + assert.Equal(t, "newmachine-3", listAllAfterRenameAttempt[2].GetGivenName()) + assert.Contains(t, listAllAfterRenameAttempt[3].GetGivenName(), "machine-4") + assert.Contains(t, listAllAfterRenameAttempt[4].GetGivenName(), "machine-5") + + err = scenario.Shutdown() + assert.NoError(t, err) +} + +func TestNodeMoveCommand(t *testing.T) { + IntegrationSkip(t) + t.Parallel() + + scenario, err := NewScenario() + assert.NoError(t, err) + + spec := map[string]int{ + "old-user": 0, + "new-user": 0, + } + + err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + assert.NoError(t, err) + + headscale, err := scenario.Headscale() + assert.NoError(t, err) + + // Randomly generated machine key + machineKey := "nodekey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa" + + _, err = headscale.Execute( + []string{ + "headscale", + "debug", + "create-node", + "--name", + "nomad-machine", + "--user", + "old-user", + "--key", + machineKey, + "--output", + "json", + }, + ) + assert.Nil(t, err) + + var machine v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "--user", + "old-user", + "register", + "--key", + machineKey, + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + assert.Equal(t, uint64(1), machine.Id) + assert.Equal(t, "nomad-machine", machine.Name) + assert.Equal(t, machine.User.Name, "old-user") + + machineID := fmt.Sprintf("%d", machine.Id) + + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "move", + "--identifier", + machineID, + "--user", + "new-user", + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + assert.Equal(t, machine.User.Name, "new-user") + + var allNodes []v1.Machine + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "list", + "--output", + "json", + }, + &allNodes, + ) + assert.Nil(t, err) + + assert.Len(t, allNodes, 1) + + assert.Equal(t, allNodes[0].Id, machine.Id) + assert.Equal(t, allNodes[0].User, machine.User) + assert.Equal(t, allNodes[0].User.Name, "new-user") + + moveToNonExistingNSResult, err := headscale.Execute( + []string{ + "headscale", + "nodes", + "move", + "--identifier", + machineID, + "--user", + "non-existing-user", + "--output", + "json", + }, + ) + assert.Nil(t, err) + + assert.Contains( + t, + moveToNonExistingNSResult, + "User not found", + ) + assert.Equal(t, machine.User.Name, "new-user") + + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "move", + "--identifier", + machineID, + "--user", + "old-user", + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + assert.Equal(t, machine.User.Name, "old-user") + + err = executeAndUnmarshal( + headscale, + []string{ + "headscale", + "nodes", + "move", + "--identifier", + machineID, + "--user", + "old-user", + "--output", + "json", + }, + &machine, + ) + assert.Nil(t, err) + + assert.Equal(t, machine.User.Name, "old-user") + + err = scenario.Shutdown() + assert.NoError(t, err) +} diff --git a/integration_cli_test.go b/integration_cli_test.go deleted file mode 100644 index beadd2c0..00000000 --- a/integration_cli_test.go +++ /dev/null @@ -1,1635 +0,0 @@ -// nolint -package headscale - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "os" - "testing" - "time" - - v1 "github.com/juanfont/headscale/gen/go/headscale/v1" - "github.com/ory/dockertest/v3" - "github.com/ory/dockertest/v3/docker" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -type IntegrationCLITestSuite struct { - suite.Suite - stats *suite.SuiteInformation - - pool dockertest.Pool - network dockertest.Network - headscale dockertest.Resource -} - -func TestIntegrationCLITestSuite(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration tests due to short flag") - } - - s := new(IntegrationCLITestSuite) - - suite.Run(t, s) -} - -func (s *IntegrationCLITestSuite) SetupTest() { - var err error - - if ppool, err := dockertest.NewPool(""); err == nil { - s.pool = *ppool - } else { - 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 - - headscaleBuildOptions := &dockertest.BuildOptions{ - Dockerfile: "Dockerfile", - ContextDir: ".", - } - - currentPath, err := os.Getwd() - if err != nil { - s.FailNow(fmt.Sprintf("Could not determine current path: %s", err), "") - } - - headscaleOptions := &dockertest.RunOptions{ - Name: "headscale-cli", - Mounts: []string{ - fmt.Sprintf("%s/integration_legacy/etc:/etc/headscale", currentPath), - }, - 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) - if err != nil { - s.FailNow( - fmt.Sprintf( - "Could not remove existing container before building test: %s", - err, - ), - "", - ) - } - - fmt.Println("Creating headscale container for CLI tests") - if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { - s.headscale = *pheadscale - } else { - s.FailNow(fmt.Sprintf("Could not start headscale container: %s", err), "") - } - fmt.Println("Created headscale container for CLI tests") - - fmt.Println("Waiting for headscale to be ready for CLI tests") - hostEndpoint := fmt.Sprintf("%s:%s", - s.headscale.GetIPInNetwork(&s.network), - s.headscale.GetPort("8080/tcp")) - - if err := s.pool.Retry(func() error { - url := fmt.Sprintf("http://%s/health", hostEndpoint) - resp, err := http.Get(url) - if err != nil { - fmt.Printf("headscale for CLI test 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) - } - fmt.Println("headscale container is ready for CLI tests") -} - -func (s *IntegrationCLITestSuite) TearDownTest() { - if err := s.pool.Purge(&s.headscale); err != nil { - log.Printf("Could not purge resource: %s\n", err) - } - - if err := s.network.Close(); err != nil { - log.Printf("Could not close network: %s\n", err) - } -} - -func (s *IntegrationCLITestSuite) HandleStats( - suiteName string, - stats *suite.SuiteInformation, -) { - s.stats = stats -} - -func (s *IntegrationCLITestSuite) createUser(name string) (*v1.User, error) { - result, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "users", - "create", - name, - "--output", - "json", - }, - []string{}, - ) - if err != nil { - return nil, err - } - - var user v1.User - err = json.Unmarshal([]byte(result), &user) - if err != nil { - return nil, err - } - - return &user, nil -} - -func (s *IntegrationCLITestSuite) TestUserCommand() { - names := []string{"user1", "otherspace", "tasty"} - users := make([]*v1.User, len(names)) - - for index, userName := range names { - user, err := s.createUser(userName) - assert.Nil(s.T(), err) - - users[index] = user - } - - assert.Len(s.T(), users, len(names)) - - assert.Equal(s.T(), names[0], users[0].Name) - assert.Equal(s.T(), names[1], users[1].Name) - assert.Equal(s.T(), names[2], users[2].Name) - - // Test list users - listResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "users", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedUsers []v1.User - err = json.Unmarshal([]byte(listResult), &listedUsers) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), names[0], listedUsers[0].Name) - assert.Equal(s.T(), names[1], listedUsers[1].Name) - assert.Equal(s.T(), names[2], listedUsers[2].Name) - - // Test rename user - renameResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "users", - "rename", - "--output", - "json", - "tasty", - "newname", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var renamedUser v1.User - err = json.Unmarshal([]byte(renameResult), &renamedUser) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), renamedUser.Name, "newname") - - // Test list after rename users - listAfterRenameResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "users", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedAfterRenameUsers []v1.User - err = json.Unmarshal([]byte(listAfterRenameResult), &listedAfterRenameUsers) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), names[0], listedAfterRenameUsers[0].Name) - assert.Equal(s.T(), names[1], listedAfterRenameUsers[1].Name) - assert.Equal(s.T(), "newname", listedAfterRenameUsers[2].Name) -} - -func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { - count := 5 - - user, err := s.createUser("pre-auth-key-user") - - keys := make([]*v1.PreAuthKey, count) - assert.Nil(s.T(), err) - - for i := 0; i < count; i++ { - preAuthResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "create", - "--reusable", - "--expiration", - "24h", - "--output", - "json", - "--tags", - "tag:test1,tag:test2", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var preAuthKey v1.PreAuthKey - err = json.Unmarshal([]byte(preAuthResult), &preAuthKey) - assert.Nil(s.T(), err) - - keys[i] = &preAuthKey - } - - assert.Len(s.T(), keys, 5) - - // Test list of keys - listResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedPreAuthKeys []v1.PreAuthKey - err = json.Unmarshal([]byte(listResult), &listedPreAuthKeys) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), "1", listedPreAuthKeys[0].Id) - assert.Equal(s.T(), "2", listedPreAuthKeys[1].Id) - assert.Equal(s.T(), "3", listedPreAuthKeys[2].Id) - assert.Equal(s.T(), "4", listedPreAuthKeys[3].Id) - assert.Equal(s.T(), "5", listedPreAuthKeys[4].Id) - - assert.NotEmpty(s.T(), listedPreAuthKeys[0].Key) - assert.NotEmpty(s.T(), listedPreAuthKeys[1].Key) - assert.NotEmpty(s.T(), listedPreAuthKeys[2].Key) - assert.NotEmpty(s.T(), listedPreAuthKeys[3].Key) - assert.NotEmpty(s.T(), listedPreAuthKeys[4].Key) - - assert.True(s.T(), listedPreAuthKeys[0].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedPreAuthKeys[1].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedPreAuthKeys[2].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedPreAuthKeys[3].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedPreAuthKeys[4].Expiration.AsTime().After(time.Now())) - - assert.True( - s.T(), - listedPreAuthKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedPreAuthKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedPreAuthKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedPreAuthKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedPreAuthKeys[4].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - - // Test that tags are present - for i := 0; i < count; i++ { - assert.Equal(s.T(), listedPreAuthKeys[i].AclTags, []string{"tag:test1", "tag:test2"}) - } - - // Expire three keys - for i := 0; i < 3; i++ { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "expire", - listedPreAuthKeys[i].Key, - }, - []string{}, - ) - assert.Nil(s.T(), err) - } - - // Test list pre auth keys after expire - listAfterExpireResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedAfterExpirePreAuthKeys []v1.PreAuthKey - err = json.Unmarshal([]byte(listAfterExpireResult), &listedAfterExpirePreAuthKeys) - assert.Nil(s.T(), err) - - assert.True( - s.T(), - listedAfterExpirePreAuthKeys[0].Expiration.AsTime().Before(time.Now()), - ) - assert.True( - s.T(), - listedAfterExpirePreAuthKeys[1].Expiration.AsTime().Before(time.Now()), - ) - assert.True( - s.T(), - listedAfterExpirePreAuthKeys[2].Expiration.AsTime().Before(time.Now()), - ) - assert.True( - s.T(), - listedAfterExpirePreAuthKeys[3].Expiration.AsTime().After(time.Now()), - ) - assert.True( - s.T(), - listedAfterExpirePreAuthKeys[4].Expiration.AsTime().After(time.Now()), - ) -} - -func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandWithoutExpiry() { - user, err := s.createUser("pre-auth-key-without-exp-user") - assert.Nil(s.T(), err) - - preAuthResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "create", - "--reusable", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var preAuthKey v1.PreAuthKey - err = json.Unmarshal([]byte(preAuthResult), &preAuthKey) - assert.Nil(s.T(), err) - - // Test list of keys - listResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedPreAuthKeys []v1.PreAuthKey - err = json.Unmarshal([]byte(listResult), &listedPreAuthKeys) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listedPreAuthKeys, 1) - - assert.True(s.T(), listedPreAuthKeys[0].Expiration.AsTime().After(time.Now())) - assert.True( - s.T(), - listedPreAuthKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Minute*70)), - ) -} - -func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { - user, err := s.createUser("pre-auth-key-reus-ephm-user") - assert.Nil(s.T(), err) - - preAuthReusableResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "create", - "--reusable=true", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var preAuthReusableKey v1.PreAuthKey - err = json.Unmarshal([]byte(preAuthReusableResult), &preAuthReusableKey) - assert.Nil(s.T(), err) - - assert.True(s.T(), preAuthReusableKey.GetReusable()) - assert.False(s.T(), preAuthReusableKey.GetEphemeral()) - - preAuthEphemeralResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "create", - "--ephemeral=true", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var preAuthEphemeralKey v1.PreAuthKey - err = json.Unmarshal([]byte(preAuthEphemeralResult), &preAuthEphemeralKey) - assert.Nil(s.T(), err) - - assert.True(s.T(), preAuthEphemeralKey.GetEphemeral()) - assert.False(s.T(), preAuthEphemeralKey.GetReusable()) - - // TODO(kradalby): Evaluate if we need a case to test for reusable and ephemeral - // preAuthReusableAndEphemeralResult, err := ExecuteCommand( - // &s.headscale, - // []string{ - // "headscale", - // "preauthkeys", - // "--user", - // user.Name, - // "create", - // "--ephemeral", - // "--reusable", - // "--output", - // "json", - // }, - // []string{}, - // ) - // assert.NotNil(s.T(), err) - - // Test list of keys - listResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "preauthkeys", - "--user", - user.Name, - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedPreAuthKeys []v1.PreAuthKey - err = json.Unmarshal([]byte(listResult), &listedPreAuthKeys) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listedPreAuthKeys, 2) -} - -func (s *IntegrationCLITestSuite) TestNodeTagCommand() { - user, err := s.createUser("machine-user") - assert.Nil(s.T(), err) - - machineKeys := []string{ - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", - } - machines := make([]*v1.Machine, len(machineKeys)) - assert.Nil(s.T(), err) - - for index, machineKey := range machineKeys { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "debug", - "create-node", - "--name", - fmt.Sprintf("machine-%d", index+1), - "--user", - user.Name, - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - machineResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "--user", - user.Name, - "register", - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machine v1.Machine - err = json.Unmarshal([]byte(machineResult), &machine) - assert.Nil(s.T(), err) - - machines[index] = &machine - } - assert.Len(s.T(), machines, len(machineKeys)) - - addTagResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "tag", - "-i", "1", - "-t", "tag:test", - "--output", "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machine v1.Machine - err = json.Unmarshal([]byte(addTagResult), &machine) - assert.Nil(s.T(), err) - assert.Equal(s.T(), []string{"tag:test"}, machine.ForcedTags) - - // try to set a wrong tag and retrieve the error - wrongTagResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "tag", - "-i", "2", - "-t", "wrong-tag", - "--output", "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - type errOutput struct { - Error string `json:"error"` - } - var errorOutput errOutput - err = json.Unmarshal([]byte(wrongTagResult), &errorOutput) - assert.Nil(s.T(), err) - assert.Contains(s.T(), errorOutput.Error, "tag must start with the string 'tag:'") - - // Test list all nodes after added seconds - listAllResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", "json", - }, - []string{}, - ) - resultMachines := make([]*v1.Machine, len(machineKeys)) - assert.Nil(s.T(), err) - json.Unmarshal([]byte(listAllResult), &resultMachines) - found := false - for _, machine := range resultMachines { - if machine.ForcedTags != nil { - for _, tag := range machine.ForcedTags { - if tag == "tag:test" { - found = true - } - } - } - } - assert.Equal( - s.T(), - true, - found, - "should find a machine with the tag 'tag:test' in the list of machines", - ) -} - -func (s *IntegrationCLITestSuite) TestNodeCommand() { - user, err := s.createUser("machine-user") - assert.Nil(s.T(), err) - - secondUser, err := s.createUser("other-user") - assert.Nil(s.T(), err) - - // Randomly generated machine keys - machineKeys := []string{ - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", - "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", - "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", - } - machines := make([]*v1.Machine, len(machineKeys)) - assert.Nil(s.T(), err) - - for index, machineKey := range machineKeys { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "debug", - "create-node", - "--name", - fmt.Sprintf("machine-%d", index+1), - "--user", - user.Name, - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - machineResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "--user", - user.Name, - "register", - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machine v1.Machine - err = json.Unmarshal([]byte(machineResult), &machine) - assert.Nil(s.T(), err) - - machines[index] = &machine - } - - assert.Len(s.T(), machines, len(machineKeys)) - - // Test list all nodes after added seconds - listAllResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listAll []v1.Machine - err = json.Unmarshal([]byte(listAllResult), &listAll) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listAll, 5) - - assert.Equal(s.T(), uint64(1), listAll[0].Id) - assert.Equal(s.T(), uint64(2), listAll[1].Id) - assert.Equal(s.T(), uint64(3), listAll[2].Id) - assert.Equal(s.T(), uint64(4), listAll[3].Id) - assert.Equal(s.T(), uint64(5), listAll[4].Id) - - assert.Equal(s.T(), "machine-1", listAll[0].Name) - assert.Equal(s.T(), "machine-2", listAll[1].Name) - assert.Equal(s.T(), "machine-3", listAll[2].Name) - assert.Equal(s.T(), "machine-4", listAll[3].Name) - assert.Equal(s.T(), "machine-5", listAll[4].Name) - - otherUserMachineKeys := []string{ - "nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", - "nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", - } - otherUserMachines := make([]*v1.Machine, len(otherUserMachineKeys)) - assert.Nil(s.T(), err) - - for index, machineKey := range otherUserMachineKeys { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "debug", - "create-node", - "--name", - fmt.Sprintf("otherUser-machine-%d", index+1), - "--user", - secondUser.Name, - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - machineResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "--user", - secondUser.Name, - "register", - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machine v1.Machine - err = json.Unmarshal([]byte(machineResult), &machine) - assert.Nil(s.T(), err) - - otherUserMachines[index] = &machine - } - - assert.Len(s.T(), otherUserMachines, len(otherUserMachineKeys)) - - // Test list all nodes after added otherUser - listAllWithotherUserResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listAllWithotherUser []v1.Machine - err = json.Unmarshal( - []byte(listAllWithotherUserResult), - &listAllWithotherUser, - ) - assert.Nil(s.T(), err) - - // All nodes, machines + otherUser - assert.Len(s.T(), listAllWithotherUser, 7) - - assert.Equal(s.T(), uint64(6), listAllWithotherUser[5].Id) - assert.Equal(s.T(), uint64(7), listAllWithotherUser[6].Id) - - assert.Equal(s.T(), "otherUser-machine-1", listAllWithotherUser[5].Name) - assert.Equal(s.T(), "otherUser-machine-2", listAllWithotherUser[6].Name) - - // Test list all nodes after added otherUser - listOnlyotherUserMachineUserResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--user", - secondUser.Name, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listOnlyotherUserMachineUser []v1.Machine - err = json.Unmarshal( - []byte(listOnlyotherUserMachineUserResult), - &listOnlyotherUserMachineUser, - ) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listOnlyotherUserMachineUser, 2) - - assert.Equal(s.T(), uint64(6), listOnlyotherUserMachineUser[0].Id) - assert.Equal(s.T(), uint64(7), listOnlyotherUserMachineUser[1].Id) - - assert.Equal( - s.T(), - "otherUser-machine-1", - listOnlyotherUserMachineUser[0].Name, - ) - assert.Equal( - s.T(), - "otherUser-machine-2", - listOnlyotherUserMachineUser[1].Name, - ) - - // Delete a machines - _, _, err = ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "delete", - "--identifier", - // Delete the last added machine - "4", - "--output", - "json", - "--force", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - // Test: list main user after machine is deleted - listOnlyMachineUserAfterDeleteResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--user", - user.Name, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listOnlyMachineUserAfterDelete []v1.Machine - err = json.Unmarshal( - []byte(listOnlyMachineUserAfterDeleteResult), - &listOnlyMachineUserAfterDelete, - ) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listOnlyMachineUserAfterDelete, 4) -} - -func (s *IntegrationCLITestSuite) TestNodeExpireCommand() { - user, err := s.createUser("machine-expire-user") - assert.Nil(s.T(), err) - - // Randomly generated machine keys - machineKeys := []string{ - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", - "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", - "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", - } - machines := make([]*v1.Machine, len(machineKeys)) - assert.Nil(s.T(), err) - - for index, machineKey := range machineKeys { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "debug", - "create-node", - "--name", - fmt.Sprintf("machine-%d", index+1), - "--user", - user.Name, - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - machineResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "--user", - user.Name, - "register", - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machine v1.Machine - err = json.Unmarshal([]byte(machineResult), &machine) - assert.Nil(s.T(), err) - - machines[index] = &machine - } - - assert.Len(s.T(), machines, len(machineKeys)) - - listAllResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listAll []v1.Machine - err = json.Unmarshal([]byte(listAllResult), &listAll) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listAll, 5) - - assert.True(s.T(), listAll[0].Expiry.AsTime().IsZero()) - assert.True(s.T(), listAll[1].Expiry.AsTime().IsZero()) - assert.True(s.T(), listAll[2].Expiry.AsTime().IsZero()) - assert.True(s.T(), listAll[3].Expiry.AsTime().IsZero()) - assert.True(s.T(), listAll[4].Expiry.AsTime().IsZero()) - - for i := 0; i < 3; i++ { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "expire", - "--identifier", - fmt.Sprintf("%d", listAll[i].Id), - }, - []string{}, - ) - assert.Nil(s.T(), err) - } - - listAllAfterExpiryResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listAllAfterExpiry []v1.Machine - err = json.Unmarshal([]byte(listAllAfterExpiryResult), &listAllAfterExpiry) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listAllAfterExpiry, 5) - - assert.True(s.T(), listAllAfterExpiry[0].Expiry.AsTime().Before(time.Now())) - assert.True(s.T(), listAllAfterExpiry[1].Expiry.AsTime().Before(time.Now())) - assert.True(s.T(), listAllAfterExpiry[2].Expiry.AsTime().Before(time.Now())) - assert.True(s.T(), listAllAfterExpiry[3].Expiry.AsTime().IsZero()) - assert.True(s.T(), listAllAfterExpiry[4].Expiry.AsTime().IsZero()) -} - -func (s *IntegrationCLITestSuite) TestNodeRenameCommand() { - user, err := s.createUser("machine-rename-command") - assert.Nil(s.T(), err) - - // Randomly generated machine keys - machineKeys := []string{ - "nodekey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", - "nodekey:8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1", - "nodekey:f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507", - "nodekey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c", - "nodekey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe", - } - machines := make([]*v1.Machine, len(machineKeys)) - assert.Nil(s.T(), err) - - for index, machineKey := range machineKeys { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "debug", - "create-node", - "--name", - fmt.Sprintf("machine-%d", index+1), - "--user", - user.Name, - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - machineResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "--user", - user.Name, - "register", - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machine v1.Machine - err = json.Unmarshal([]byte(machineResult), &machine) - assert.Nil(s.T(), err) - - machines[index] = &machine - } - - assert.Len(s.T(), machines, len(machineKeys)) - - listAllResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listAll []v1.Machine - err = json.Unmarshal([]byte(listAllResult), &listAll) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listAll, 5) - - assert.Contains(s.T(), listAll[0].GetGivenName(), "machine-1") - assert.Contains(s.T(), listAll[1].GetGivenName(), "machine-2") - assert.Contains(s.T(), listAll[2].GetGivenName(), "machine-3") - assert.Contains(s.T(), listAll[3].GetGivenName(), "machine-4") - assert.Contains(s.T(), listAll[4].GetGivenName(), "machine-5") - - for i := 0; i < 3; i++ { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "rename", - "--identifier", - fmt.Sprintf("%d", listAll[i].Id), - fmt.Sprintf("newmachine-%d", i+1), - }, - []string{}, - ) - assert.Nil(s.T(), err) - } - - listAllAfterRenameResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listAllAfterRename []v1.Machine - err = json.Unmarshal([]byte(listAllAfterRenameResult), &listAllAfterRename) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listAllAfterRename, 5) - - assert.Equal(s.T(), "newmachine-1", listAllAfterRename[0].GetGivenName()) - assert.Equal(s.T(), "newmachine-2", listAllAfterRename[1].GetGivenName()) - assert.Equal(s.T(), "newmachine-3", listAllAfterRename[2].GetGivenName()) - assert.Contains(s.T(), listAllAfterRename[3].GetGivenName(), "machine-4") - assert.Contains(s.T(), listAllAfterRename[4].GetGivenName(), "machine-5") - - // Test failure for too long names - result, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "rename", - "--identifier", - fmt.Sprintf("%d", listAll[4].Id), - "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine12345678901234567890", - }, - []string{}, - ) - assert.Nil(s.T(), err) - assert.Contains(s.T(), result, "not be over 63 chars") - - listAllAfterRenameAttemptResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listAllAfterRenameAttempt []v1.Machine - err = json.Unmarshal( - []byte(listAllAfterRenameAttemptResult), - &listAllAfterRenameAttempt, - ) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listAllAfterRenameAttempt, 5) - - assert.Equal(s.T(), "newmachine-1", listAllAfterRenameAttempt[0].GetGivenName()) - assert.Equal(s.T(), "newmachine-2", listAllAfterRenameAttempt[1].GetGivenName()) - assert.Equal(s.T(), "newmachine-3", listAllAfterRenameAttempt[2].GetGivenName()) - assert.Contains(s.T(), listAllAfterRenameAttempt[3].GetGivenName(), "machine-4") - assert.Contains(s.T(), listAllAfterRenameAttempt[4].GetGivenName(), "machine-5") -} - -func (s *IntegrationCLITestSuite) TestApiKeyCommand() { - count := 5 - - keys := make([]string, count) - - for i := 0; i < count; i++ { - apiResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "apikeys", - "create", - "--expiration", - "24h", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - assert.NotEmpty(s.T(), apiResult) - - // var apiKey v1.ApiKey - // err = json.Unmarshal([]byte(apiResult), &apiKey) - // assert.Nil(s.T(), err) - - keys[i] = apiResult - } - - assert.Len(s.T(), keys, 5) - - // Test list of keys - listResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "apikeys", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedApiKeys []v1.ApiKey - err = json.Unmarshal([]byte(listResult), &listedApiKeys) - assert.Nil(s.T(), err) - - assert.Len(s.T(), listedApiKeys, 5) - - assert.Equal(s.T(), uint64(1), listedApiKeys[0].Id) - assert.Equal(s.T(), uint64(2), listedApiKeys[1].Id) - assert.Equal(s.T(), uint64(3), listedApiKeys[2].Id) - assert.Equal(s.T(), uint64(4), listedApiKeys[3].Id) - assert.Equal(s.T(), uint64(5), listedApiKeys[4].Id) - - assert.NotEmpty(s.T(), listedApiKeys[0].Prefix) - assert.NotEmpty(s.T(), listedApiKeys[1].Prefix) - assert.NotEmpty(s.T(), listedApiKeys[2].Prefix) - assert.NotEmpty(s.T(), listedApiKeys[3].Prefix) - assert.NotEmpty(s.T(), listedApiKeys[4].Prefix) - - assert.True(s.T(), listedApiKeys[0].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedApiKeys[1].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedApiKeys[2].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedApiKeys[3].Expiration.AsTime().After(time.Now())) - assert.True(s.T(), listedApiKeys[4].Expiration.AsTime().After(time.Now())) - - assert.True( - s.T(), - listedApiKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedApiKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedApiKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedApiKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - assert.True( - s.T(), - listedApiKeys[4].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), - ) - - expiredPrefixes := make(map[string]bool) - - // Expire three keys - for i := 0; i < 3; i++ { - _, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "apikeys", - "expire", - "--prefix", - listedApiKeys[i].Prefix, - }, - []string{}, - ) - assert.Nil(s.T(), err) - - expiredPrefixes[listedApiKeys[i].Prefix] = true - } - - // Test list pre auth keys after expire - listAfterExpireResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "apikeys", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var listedAfterExpireApiKeys []v1.ApiKey - err = json.Unmarshal([]byte(listAfterExpireResult), &listedAfterExpireApiKeys) - assert.Nil(s.T(), err) - - for index := range listedAfterExpireApiKeys { - if _, ok := expiredPrefixes[listedAfterExpireApiKeys[index].Prefix]; ok { - // Expired - assert.True( - s.T(), - listedAfterExpireApiKeys[index].Expiration.AsTime().Before(time.Now()), - ) - } else { - // Not expired - assert.False( - s.T(), - listedAfterExpireApiKeys[index].Expiration.AsTime().Before(time.Now()), - ) - } - } -} - -func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { - oldUser, err := s.createUser("old-user") - assert.Nil(s.T(), err) - newUser, err := s.createUser("new-user") - assert.Nil(s.T(), err) - - // Randomly generated machine key - machineKey := "nodekey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa" - - _, _, err = ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "debug", - "create-node", - "--name", - "nomad-machine", - "--user", - oldUser.Name, - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - machineResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "--user", - oldUser.Name, - "register", - "--key", - machineKey, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var machine v1.Machine - err = json.Unmarshal([]byte(machineResult), &machine) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), uint64(1), machine.Id) - assert.Equal(s.T(), "nomad-machine", machine.Name) - assert.Equal(s.T(), machine.User.Name, oldUser.Name) - - machineId := fmt.Sprintf("%d", machine.Id) - - moveToNewNSResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "move", - "--identifier", - machineId, - "--user", - newUser.Name, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - err = json.Unmarshal([]byte(moveToNewNSResult), &machine) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), machine.User, newUser) - - listAllNodesResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - var allNodes []v1.Machine - err = json.Unmarshal([]byte(listAllNodesResult), &allNodes) - assert.Nil(s.T(), err) - - assert.Len(s.T(), allNodes, 1) - - assert.Equal(s.T(), allNodes[0].Id, machine.Id) - assert.Equal(s.T(), allNodes[0].User, machine.User) - assert.Equal(s.T(), allNodes[0].User, newUser) - - moveToNonExistingNSResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "move", - "--identifier", - machineId, - "--user", - "non-existing-user", - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - assert.Contains( - s.T(), - string(moveToNonExistingNSResult), - "User not found", - ) - assert.Equal(s.T(), machine.User, newUser) - - moveToOldNSResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "move", - "--identifier", - machineId, - "--user", - oldUser.Name, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - err = json.Unmarshal([]byte(moveToOldNSResult), &machine) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), machine.User, oldUser) - - moveToSameNSResult, _, err := ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "nodes", - "move", - "--identifier", - machineId, - "--user", - oldUser.Name, - "--output", - "json", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - err = json.Unmarshal([]byte(moveToSameNSResult), &machine) - assert.Nil(s.T(), err) - - assert.Equal(s.T(), machine.User, oldUser) -} - -func (s *IntegrationCLITestSuite) TestLoadConfigFromCommand() { - // TODO: make sure defaultConfig is not same as altConfig - defaultConfig, err := os.ReadFile("integration_legacy/etc/config.dump.gold.yaml") - assert.Nil(s.T(), err) - altConfig, err := os.ReadFile("integration_legacy/etc/alt-config.dump.gold.yaml") - assert.Nil(s.T(), err) - altEnvConfig, err := os.ReadFile("integration_legacy/etc/alt-env-config.dump.gold.yaml") - assert.Nil(s.T(), err) - - _, _, err = ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "dumpConfig", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - defaultDumpConfig, err := os.ReadFile("integration_legacy/etc/config.dump.yaml") - assert.Nil(s.T(), err) - - assert.YAMLEq(s.T(), string(defaultConfig), string(defaultDumpConfig)) - - _, _, err = ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "-c", - "/etc/headscale/alt-config.yaml", - "dumpConfig", - }, - []string{}, - ) - assert.Nil(s.T(), err) - - altDumpConfig, err := os.ReadFile("integration_legacy/etc/config.dump.yaml") - assert.Nil(s.T(), err) - - assert.YAMLEq(s.T(), string(altConfig), string(altDumpConfig)) - - _, _, err = ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "dumpConfig", - }, - []string{ - "HEADSCALE_CONFIG=/etc/headscale/alt-env-config.yaml", - }, - ) - assert.Nil(s.T(), err) - - altEnvDumpConfig, err := os.ReadFile("integration_legacy/etc/config.dump.yaml") - assert.Nil(s.T(), err) - - assert.YAMLEq(s.T(), string(altEnvConfig), string(altEnvDumpConfig)) - - _, _, err = ExecuteCommand( - &s.headscale, - []string{ - "headscale", - "-c", - "/etc/headscale/alt-config.yaml", - "dumpConfig", - }, - []string{ - "HEADSCALE_CONFIG=/etc/headscale/alt-env-config.yaml", - }, - ) - assert.Nil(s.T(), err) - - altDumpConfig, err = os.ReadFile("integration_legacy/etc/config.dump.yaml") - assert.Nil(s.T(), err) - - assert.YAMLEq(s.T(), string(altConfig), string(altDumpConfig)) -} diff --git a/integration_common_test.go b/integration_common_test.go deleted file mode 100644 index edc0280a..00000000 --- a/integration_common_test.go +++ /dev/null @@ -1,271 +0,0 @@ -// nolint -package headscale - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "net/netip" - "os" - "strconv" - "time" - - v1 "github.com/juanfont/headscale/gen/go/headscale/v1" - "github.com/ory/dockertest/v3" - "github.com/ory/dockertest/v3/docker" -) - -const ( - headscaleNetwork = "headscale-test" - headscaleHostname = "headscale" - DOCKER_EXECUTE_TIMEOUT = 10 * time.Second -) - -var ( - errEnvVarEmpty = errors.New("getenv: environment variable empty") - - IpPrefix4 = netip.MustParsePrefix("100.64.0.0/10") - IpPrefix6 = netip.MustParsePrefix("fd7a:115c:a1e0::/48") - - tailscaleVersions = []string{ - "head", - "unstable", - "1.38.4", - "1.36.2", - "1.34.2", - "1.32.3", - "1.30.2", - "1.28.0", - "1.26.2", - "1.24.2", - "1.22.2", - "1.20.4", - "1.18.2", - "1.16.2", - "1.14.3", - "1.12.3", - } -) - -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: DOCKER_EXECUTE_TIMEOUT, - } - - 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 { - fmt.Println("Command: ", cmd) - fmt.Println("stdout: ", stdout.String()) - fmt.Println("stderr: ", stderr.String()) - - return stdout.String(), stderr.String(), fmt.Errorf( - "command failed with: %s", - stderr.String(), - ) - } - - return stdout.String(), stderr.String(), nil - case <-time.After(execConfig.timeout): - - return stdout.String(), stderr.String(), fmt.Errorf( - "command timed out after %s", - execConfig.timeout, - ) - } -} - -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", - }) -} - -func getDockerBuildOptions(version string) *dockertest.BuildOptions { - var tailscaleBuildOptions *dockertest.BuildOptions - switch version { - case "head": - tailscaleBuildOptions = &dockertest.BuildOptions{ - Dockerfile: "Dockerfile.tailscale-HEAD", - ContextDir: ".", - BuildArgs: []docker.BuildArg{}, - } - case "unstable": - tailscaleBuildOptions = &dockertest.BuildOptions{ - Dockerfile: "Dockerfile.tailscale", - ContextDir: ".", - 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: ".", - BuildArgs: []docker.BuildArg{ - { - Name: "TAILSCALE_VERSION", - Value: version, - }, - { - Name: "TAILSCALE_CHANNEL", - Value: "stable", - }, - }, - } - } - return tailscaleBuildOptions -} - -func getDNSNames( - headscale *dockertest.Resource, -) ([]string, error) { - listAllResult, _, err := ExecuteCommand( - headscale, - []string{ - "headscale", - "nodes", - "list", - "--output", - "json", - }, - []string{}, - ) - if err != nil { - return nil, err - } - - var listAll []v1.Machine - err = json.Unmarshal([]byte(listAllResult), &listAll) - if err != nil { - return nil, err - } - - hostnames := make([]string, len(listAll)) - - for index := range listAll { - hostnames[index] = listAll[index].GetGivenName() - } - - return hostnames, nil -} - -func GetEnvStr(key string) (string, error) { - v := os.Getenv(key) - if v == "" { - return v, errEnvVarEmpty - } - - return v, nil -} - -func GetEnvBool(key string) (bool, error) { - s, err := GetEnvStr(key) - if err != nil { - return false, err - } - v, err := strconv.ParseBool(s) - if err != nil { - return false, err - } - - return v, nil -} - -func GetFirstOrCreateNetwork(pool *dockertest.Pool, name string) (dockertest.Network, error) { - networks, err := pool.NetworksByName(name) - if err != nil || len(networks) == 0 { - if _, err := pool.CreateNetwork(name); err == nil { - // Create does not give us an updated version of the resource, so we need to - // get it again. - networks, err := pool.NetworksByName(name) - if err != nil { - return dockertest.Network{}, err - } - - return networks[0], nil - } - } - - return networks[0], nil -} diff --git a/integration_legacy/.gitignore b/integration_legacy/.gitignore deleted file mode 100644 index 4e9cb7a1..00000000 --- a/integration_legacy/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -derp.yaml -*.sqlite -*.sqlite3 diff --git a/integration_legacy/etc/alt-config.dump.gold.yaml b/integration_legacy/etc/alt-config.dump.gold.yaml deleted file mode 100644 index c0665b30..00000000 --- a/integration_legacy/etc/alt-config.dump.gold.yaml +++ /dev/null @@ -1,56 +0,0 @@ -acl_policy_path: "" -cli: - insecure: false - timeout: 5s -db_path: /tmp/integration_test_db.sqlite3 -db_ssl: false -db_type: sqlite3 -derp: - auto_update_enabled: false - server: - enabled: false - stun: - enabled: true - update_frequency: 1m - urls: - - https://controlplane.tailscale.com/derpmap/default -dns_config: - override_local_dns: true - base_domain: headscale.net - domains: [] - magic_dns: true - nameservers: - - 127.0.0.11 - - 1.1.1.1 -ephemeral_node_inactivity_timeout: 30m -node_update_check_interval: 10s -grpc_allow_insecure: false -grpc_listen_addr: :50443 -ip_prefixes: - - fd7a:115c:a1e0::/48 - - 100.64.0.0/10 -listen_addr: 0.0.0.0:18080 -log: - level: disabled - format: text -logtail: - enabled: false -metrics_listen_addr: 127.0.0.1:19090 -oidc: - expiry: 180d - only_start_if_oidc_is_available: true - scope: - - openid - - profile - - email - strip_email_domain: true - use_expiry_from_token: false -private_key_path: private.key -noise: - private_key_path: noise_private.key -server_url: http://headscale:18080 -tls_letsencrypt_cache_dir: /var/www/.cache -tls_letsencrypt_challenge_type: HTTP-01 -unix_socket: /var/run/headscale.sock -unix_socket_permission: "0o770" -randomize_client_port: false diff --git a/integration_legacy/etc/alt-config.yaml b/integration_legacy/etc/alt-config.yaml deleted file mode 100644 index 55567861..00000000 --- a/integration_legacy/etc/alt-config.yaml +++ /dev/null @@ -1,31 +0,0 @@ -log: - level: trace -acl_policy_path: "" -db_type: sqlite3 -ephemeral_node_inactivity_timeout: 30m -node_update_check_interval: 10s -ip_prefixes: - - fd7a:115c:a1e0::/48 - - 100.64.0.0/10 -dns_config: - override_local_dns: true - base_domain: headscale.net - magic_dns: true - domains: [] - nameservers: - - 127.0.0.11 - - 1.1.1.1 -db_path: /tmp/integration_test_db.sqlite3 -db_ssl: false -private_key_path: private.key -noise: - private_key_path: noise_private.key -listen_addr: 0.0.0.0:18080 -metrics_listen_addr: 127.0.0.1:19090 -server_url: http://headscale:18080 - -derp: - urls: - - https://controlplane.tailscale.com/derpmap/default - auto_update_enabled: false - update_frequency: 1m diff --git a/integration_legacy/etc/alt-env-config.dump.gold.yaml b/integration_legacy/etc/alt-env-config.dump.gold.yaml deleted file mode 100644 index 0be5d9ed..00000000 --- a/integration_legacy/etc/alt-env-config.dump.gold.yaml +++ /dev/null @@ -1,55 +0,0 @@ -acl_policy_path: "" -cli: - insecure: false - timeout: 5s -db_path: /tmp/integration_test_db.sqlite3 -db_ssl: false -db_type: sqlite3 -derp: - auto_update_enabled: false - server: - enabled: false - stun: - enabled: true - update_frequency: 1m - urls: - - https://controlplane.tailscale.com/derpmap/default -dns_config: - override_local_dns: true - base_domain: headscale.net - domains: [] - magic_dns: true - nameservers: - - 1.1.1.1 -ephemeral_node_inactivity_timeout: 30m -node_update_check_interval: 30s -grpc_allow_insecure: false -grpc_listen_addr: :50443 -ip_prefixes: - - fd7a:115c:a1e0::/48 - - 100.64.0.0/10 -listen_addr: 0.0.0.0:18080 -log: - level: disabled - format: text -logtail: - enabled: false -metrics_listen_addr: 127.0.0.1:19090 -oidc: - expiry: 180d - only_start_if_oidc_is_available: true - scope: - - openid - - profile - - email - strip_email_domain: true - use_expiry_from_token: false -private_key_path: private.key -noise: - private_key_path: noise_private.key -server_url: http://headscale:18080 -tls_letsencrypt_cache_dir: /var/www/.cache -tls_letsencrypt_challenge_type: HTTP-01 -unix_socket: /var/run/headscale.sock -unix_socket_permission: "0o770" -randomize_client_port: false diff --git a/integration_legacy/etc/alt-env-config.yaml b/integration_legacy/etc/alt-env-config.yaml deleted file mode 100644 index 2410ca64..00000000 --- a/integration_legacy/etc/alt-env-config.yaml +++ /dev/null @@ -1,30 +0,0 @@ -log: - level: trace -acl_policy_path: "" -db_type: sqlite3 -ephemeral_node_inactivity_timeout: 30m -node_update_check_interval: 30s -ip_prefixes: - - fd7a:115c:a1e0::/48 - - 100.64.0.0/10 -dns_config: - override_local_dns: true - base_domain: headscale.net - magic_dns: true - domains: [] - nameservers: - - 1.1.1.1 -db_path: /tmp/integration_test_db.sqlite3 -db_ssl: false -private_key_path: private.key -noise: - private_key_path: noise_private.key -listen_addr: 0.0.0.0:18080 -metrics_listen_addr: 127.0.0.1:19090 -server_url: http://headscale:18080 - -derp: - urls: - - https://controlplane.tailscale.com/derpmap/default - auto_update_enabled: false - update_frequency: 1m diff --git a/integration_legacy/etc/config.dump.gold.yaml b/integration_legacy/etc/config.dump.gold.yaml deleted file mode 100644 index e6a822a5..00000000 --- a/integration_legacy/etc/config.dump.gold.yaml +++ /dev/null @@ -1,56 +0,0 @@ -acl_policy_path: "" -cli: - insecure: false - timeout: 5s -db_path: /tmp/integration_test_db.sqlite3 -db_ssl: false -db_type: sqlite3 -derp: - auto_update_enabled: false - server: - enabled: false - stun: - enabled: true - update_frequency: 1m - urls: - - https://controlplane.tailscale.com/derpmap/default -dns_config: - override_local_dns: true - base_domain: headscale.net - domains: [] - magic_dns: true - nameservers: - - 127.0.0.11 - - 1.1.1.1 -ephemeral_node_inactivity_timeout: 30m -node_update_check_interval: 10s -grpc_allow_insecure: false -grpc_listen_addr: :50443 -ip_prefixes: - - fd7a:115c:a1e0::/48 - - 100.64.0.0/10 -listen_addr: 0.0.0.0:8080 -log: - format: text - level: disabled -logtail: - enabled: false -metrics_listen_addr: 127.0.0.1:9090 -oidc: - expiry: 180d - only_start_if_oidc_is_available: true - scope: - - openid - - profile - - email - strip_email_domain: true - use_expiry_from_token: false -private_key_path: private.key -noise: - private_key_path: noise_private.key -server_url: http://headscale:8080 -tls_letsencrypt_cache_dir: /var/www/.cache -tls_letsencrypt_challenge_type: HTTP-01 -unix_socket: /var/run/headscale.sock -unix_socket_permission: "0o770" -randomize_client_port: false diff --git a/integration_legacy/etc/config.dump.yaml b/integration_legacy/etc/config.dump.yaml deleted file mode 100644 index e19ab23a..00000000 --- a/integration_legacy/etc/config.dump.yaml +++ /dev/null @@ -1,54 +0,0 @@ -acl_policy_path: "" -cli: - insecure: false - timeout: 5s -db_path: /tmp/integration_test_db.sqlite3 -db_type: sqlite3 -derp: - auto_update_enabled: false - server: - enabled: false - stun: - enabled: true - update_frequency: 1m - urls: - - https://controlplane.tailscale.com/derpmap/default -dns_config: - base_domain: headscale.net - domains: [] - magic_dns: true - nameservers: - - 127.0.0.11 - - 1.1.1.1 - override_local_dns: true -ephemeral_node_inactivity_timeout: 30m -grpc_allow_insecure: false -grpc_listen_addr: :50443 -ip_prefixes: - - fd7a:115c:a1e0::/48 - - 100.64.0.0/10 -listen_addr: 0.0.0.0:18080 -log: - format: text - level: disabled -logtail: - enabled: false -metrics_listen_addr: 127.0.0.1:19090 -node_update_check_interval: 10s -noise: - private_key_path: noise_private.key -oidc: - only_start_if_oidc_is_available: true - scope: - - openid - - profile - - email - strip_email_domain: true -private_key_path: private.key -randomize_client_port: false -server_url: http://headscale:18080 -tls_client_auth_mode: relaxed -tls_letsencrypt_cache_dir: /var/www/.cache -tls_letsencrypt_challenge_type: HTTP-01 -unix_socket: /var/run/headscale.sock -unix_socket_permission: "0o770"