diff --git a/integration/acl_test.go b/integration/acl_test.go index af847aba..7d1186f5 100644 --- a/integration/acl_test.go +++ b/integration/acl_test.go @@ -54,15 +54,16 @@ func aclScenario( clientsPerUser int, ) *Scenario { t.Helper() - scenario, err := NewScenario(dockertestMaxWait()) - require.NoError(t, err) spec := ScenarioSpec{ NodesPerUser: clientsPerUser, Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, + scenario, err := NewScenario(spec) + require.NoError(t, err) + + err = scenario.CreateHeadscaleEnv( []tsic.Option{ // Alpine containers dont have ip6tables set up, which causes // tailscaled to stop configuring the wgengine, causing it @@ -256,12 +257,11 @@ func TestACLHostsInNetMapTable(t *testing.T) { for name, testCase := range tests { t.Run(name, func(t *testing.T) { - scenario, err := NewScenario(dockertestMaxWait()) + caseSpec := testCase.users + scenario, err := NewScenario(caseSpec) require.NoError(t, err) - spec := testCase.users - - err = scenario.CreateHeadscaleEnv(spec, + err = scenario.CreateHeadscaleEnv( []tsic.Option{}, hsic.WithACLPolicy(&testCase.policy), ) @@ -1009,16 +1009,16 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - require.NoError(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 1, Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, + scenario, err := NewScenario(spec) + require.NoError(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv( []tsic.Option{ // Alpine containers dont have ip6tables set up, which causes // tailscaled to stop configuring the wgengine, causing it diff --git a/integration/auth_key_test.go b/integration/auth_key_test.go index ddfd266f..9d219fca 100644 --- a/integration/auth_key_test.go +++ b/integration/auth_key_test.go @@ -19,15 +19,15 @@ func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) { for _, https := range []bool{true, false} { t.Run(fmt.Sprintf("with-https-%t", https), func(t *testing.T) { - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + opts := []hsic.Option{hsic.WithTestName("pingallbyip")} if https { opts = append(opts, []hsic.Option{ @@ -35,7 +35,7 @@ func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) { }...) } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, opts...) + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, opts...) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() @@ -152,16 +152,16 @@ func TestAuthKeyLogoutAndReloginNewUser(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("keyrelognewuser"), hsic.WithTLS(), ) @@ -235,15 +235,15 @@ func TestAuthKeyLogoutAndReloginSameUserExpiredKey(t *testing.T) { for _, https := range []bool{true, false} { t.Run(fmt.Sprintf("with-https-%t", https), func(t *testing.T) { - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + opts := []hsic.Option{hsic.WithTestName("pingallbyip")} if https { opts = append(opts, []hsic.Option{ @@ -251,7 +251,7 @@ func TestAuthKeyLogoutAndReloginSameUserExpiredKey(t *testing.T) { }...) } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, opts...) + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, opts...) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() diff --git a/integration/auth_oidc_test.go b/integration/auth_oidc_test.go index ca326861..e506471c 100644 --- a/integration/auth_oidc_test.go +++ b/integration/auth_oidc_test.go @@ -51,7 +51,15 @@ func TestOIDCAuthenticationPingAll(t *testing.T) { IntegrationSkip(t) t.Parallel() - baseScenario, err := NewScenario(dockertestMaxWait()) + // Logins to MockOIDC is served by a queue with a strict order, + // if we use more than one node per user, the order of the logins + // will not be deterministic and the test will fail. + spec := ScenarioSpec{ + NodesPerUser: 1, + Users: []string{"user1", "user2"}, + } + + baseScenario, err := NewScenario(spec) assertNoErr(t, err) scenario := AuthOIDCScenario{ @@ -59,14 +67,6 @@ func TestOIDCAuthenticationPingAll(t *testing.T) { } defer scenario.ShutdownAssertNoPanics(t) - // Logins to MockOIDC is served by a queue with a strict order, - // if we use more than one node per user, the order of the logins - // will not be deterministic and the test will fail. - spec := map[string]int{ - "user1": 1, - "user2": 1, - } - mockusers := []mockoidc.MockUser{ oidcMockUser("user1", true), oidcMockUser("user2", false), @@ -84,7 +84,6 @@ func TestOIDCAuthenticationPingAll(t *testing.T) { } err = scenario.CreateHeadscaleEnv( - spec, hsic.WithTestName("oidcauthping"), hsic.WithConfigEnv(oidcMap), hsic.WithTLS(), @@ -159,7 +158,12 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) { shortAccessTTL := 5 * time.Minute - baseScenario, err := NewScenario(dockertestMaxWait()) + spec := ScenarioSpec{ + NodesPerUser: 1, + Users: []string{"user1", "user2"}, + } + + baseScenario, err := NewScenario(spec) assertNoErr(t, err) baseScenario.pool.MaxWait = 5 * time.Minute @@ -169,11 +173,6 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) { } defer scenario.ShutdownAssertNoPanics(t) - spec := map[string]int{ - "user1": 1, - "user2": 1, - } - oidcConfig, err := scenario.runMockOIDC(shortAccessTTL, []mockoidc.MockUser{ oidcMockUser("user1", true), oidcMockUser("user2", false), @@ -189,7 +188,6 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) { } err = scenario.CreateHeadscaleEnv( - spec, hsic.WithTestName("oidcexpirenodes"), hsic.WithConfigEnv(oidcMap), ) @@ -335,7 +333,14 @@ func TestOIDC024UserCreation(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - baseScenario, err := NewScenario(dockertestMaxWait()) + spec := ScenarioSpec{ + NodesPerUser: 1, + } + for _, user := range tt.cliUsers { + spec.Users = append(spec.Users, user) + } + + baseScenario, err := NewScenario(spec) assertNoErr(t, err) scenario := AuthOIDCScenario{ @@ -343,11 +348,6 @@ func TestOIDC024UserCreation(t *testing.T) { } defer scenario.ShutdownAssertNoPanics(t) - spec := map[string]int{} - for _, user := range tt.cliUsers { - spec[user] = 1 - } - var mockusers []mockoidc.MockUser for _, user := range tt.oidcUsers { mockusers = append(mockusers, oidcMockUser(user, tt.emailVerified)) @@ -369,7 +369,6 @@ func TestOIDC024UserCreation(t *testing.T) { } err = scenario.CreateHeadscaleEnv( - spec, hsic.WithTestName("oidcmigration"), hsic.WithConfigEnv(oidcMap), hsic.WithTLS(), @@ -405,7 +404,13 @@ func TestOIDCAuthenticationWithPKCE(t *testing.T) { IntegrationSkip(t) t.Parallel() - baseScenario, err := NewScenario(dockertestMaxWait()) + // Single user with one node for testing PKCE flow + spec := ScenarioSpec{ + NodesPerUser: 1, + Users: []string{"user1"}, + } + + baseScenario, err := NewScenario(spec) assertNoErr(t, err) scenario := AuthOIDCScenario{ @@ -413,11 +418,6 @@ func TestOIDCAuthenticationWithPKCE(t *testing.T) { } defer scenario.ShutdownAssertNoPanics(t) - // Single user with one node for testing PKCE flow - spec := map[string]int{ - "user1": 1, - } - mockusers := []mockoidc.MockUser{ oidcMockUser("user1", true), } @@ -435,7 +435,6 @@ func TestOIDCAuthenticationWithPKCE(t *testing.T) { } err = scenario.CreateHeadscaleEnv( - spec, hsic.WithTestName("oidcauthpkce"), hsic.WithConfigEnv(oidcMap), hsic.WithTLS(), @@ -465,7 +464,8 @@ func TestOIDCReloginSameNodeNewUser(t *testing.T) { IntegrationSkip(t) t.Parallel() - baseScenario, err := NewScenario(dockertestMaxWait()) + // Create no nodes and no users + baseScenario, err := NewScenario(ScenarioSpec{}) assertNoErr(t, err) scenario := AuthOIDCScenario{ @@ -473,9 +473,6 @@ func TestOIDCReloginSameNodeNewUser(t *testing.T) { } defer scenario.ShutdownAssertNoPanics(t) - // Create no nodes and no users - spec := map[string]int{} - // First login creates the first OIDC user // Second login logs in the same node, which creates a new node // Third login logs in the same node back into the original user @@ -497,7 +494,6 @@ func TestOIDCReloginSameNodeNewUser(t *testing.T) { } err = scenario.CreateHeadscaleEnv( - spec, hsic.WithTestName("oidcauthrelog"), hsic.WithConfigEnv(oidcMap), hsic.WithTLS(), @@ -680,7 +676,6 @@ func TestOIDCReloginSameNodeNewUser(t *testing.T) { } func (s *AuthOIDCScenario) CreateHeadscaleEnv( - users map[string]int, opts ...hsic.Option, ) error { headscale, err := s.Headscale(opts...) @@ -693,21 +688,21 @@ func (s *AuthOIDCScenario) CreateHeadscaleEnv( return err } - for userName, clientCount := range users { - if clientCount != 1 { + for _, userName := range s.spec.Users { + if s.spec.NodesPerUser != 1 { // OIDC scenario only supports one client per user. // This is because the MockOIDC server can only serve login // requests based on a queue it has been given on startup. // We currently only populates it with one login request per user. return fmt.Errorf("client count must be 1 for OIDC scenario.") } - log.Printf("creating user %s with %d clients", userName, clientCount) + log.Printf("creating user %s with %d clients", userName, s.spec.NodesPerUser) err = s.CreateUser(userName) if err != nil { return err } - err = s.CreateTailscaleNodesInUser(userName, "all", clientCount) + err = s.CreateTailscaleNodesInUser(userName, "all", s.spec.NodesPerUser) if err != nil { return err } diff --git a/integration/auth_web_flow_test.go b/integration/auth_web_flow_test.go index acc96cec..a27b105c 100644 --- a/integration/auth_web_flow_test.go +++ b/integration/auth_web_flow_test.go @@ -23,9 +23,13 @@ type AuthWebFlowScenario struct { func TestAuthWebFlowAuthenticationPingAll(t *testing.T) { IntegrationSkip(t) - t.Parallel() - baseScenario, err := NewScenario(dockertestMaxWait()) + spec := ScenarioSpec{ + NodesPerUser: len(MustTestVersions), + Users: []string{"user1", "user2"}, + } + + baseScenario, err := NewScenario(spec) if err != nil { t.Fatalf("failed to create scenario: %s", err) } @@ -35,13 +39,7 @@ func TestAuthWebFlowAuthenticationPingAll(t *testing.T) { } defer scenario.ShutdownAssertNoPanics(t) - spec := map[string]int{ - "user1": len(MustTestVersions), - "user2": len(MustTestVersions), - } - err = scenario.CreateHeadscaleEnv( - spec, hsic.WithTestName("webauthping"), hsic.WithEmbeddedDERPServerOnly(), hsic.WithTLS(), @@ -71,7 +69,12 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { IntegrationSkip(t) t.Parallel() - baseScenario, err := NewScenario(dockertestMaxWait()) + spec := ScenarioSpec{ + NodesPerUser: len(MustTestVersions), + Users: []string{"user1", "user2"}, + } + + baseScenario, err := NewScenario(spec) assertNoErr(t, err) scenario := AuthWebFlowScenario{ @@ -79,12 +82,7 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { } defer scenario.ShutdownAssertNoPanics(t) - spec := map[string]int{ - "user1": len(MustTestVersions), - "user2": len(MustTestVersions), - } - - err = scenario.CreateHeadscaleEnv(spec, + err = scenario.CreateHeadscaleEnv( hsic.WithTestName("weblogout"), hsic.WithTLS(), ) @@ -137,7 +135,7 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { t.Logf("all clients logged out") - for userName := range spec { + for _, userName := range spec.Users { err = scenario.runTailscaleUp(userName, headscale.GetEndpoint()) if err != nil { t.Fatalf("failed to run tailscale up (%q): %s", headscale.GetEndpoint(), err) @@ -196,7 +194,6 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { } func (s *AuthWebFlowScenario) CreateHeadscaleEnv( - users map[string]int, opts ...hsic.Option, ) error { headscale, err := s.Headscale(opts...) @@ -209,14 +206,14 @@ func (s *AuthWebFlowScenario) CreateHeadscaleEnv( return err } - for userName, clientCount := range users { - log.Printf("creating user %s with %d clients", userName, clientCount) + for _, userName := range s.spec.Users { + log.Printf("creating user %s with %d clients", userName, s.spec.NodesPerUser) err = s.CreateUser(userName) if err != nil { return err } - err = s.CreateTailscaleNodesInUser(userName, "all", clientCount) + err = s.CreateTailscaleNodesInUser(userName, "all", s.spec.NodesPerUser) if err != nil { return err } diff --git a/integration/cli_test.go b/integration/cli_test.go index 0f1c6fe9..c1b738fc 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -48,15 +48,15 @@ func TestUserCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -246,15 +246,15 @@ func TestPreAuthKeyCommand(t *testing.T) { user := "preauthkeyspace" count := 3 - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{user}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipak")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clipak")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -387,16 +387,15 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) { t.Parallel() user := "pre-auth-key-without-exp-user" - - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{user}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipaknaexp")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clipaknaexp")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -450,16 +449,15 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) { t.Parallel() user := "pre-auth-key-reus-ephm-user" - - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{user}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipakresueeph")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clipakresueeph")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -529,16 +527,15 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) { user1 := "user1" user2 := "user2" - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{user1, user2}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + err = scenario.CreateHeadscaleEnv( - spec, []tsic.Option{}, hsic.WithTestName("clipak"), hsic.WithEmbeddedDERPServerOnly(), @@ -618,15 +615,15 @@ func TestApiKeyCommand(t *testing.T) { count := 5 - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -785,15 +782,15 @@ func TestNodeTagCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -974,16 +971,16 @@ func TestNodeAdvertiseTagCommand(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 1, Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv( []tsic.Option{tsic.WithTags([]string{"tag:test"})}, hsic.WithTestName("cliadvtags"), hsic.WithACLPolicy(tt.policy), @@ -1027,15 +1024,15 @@ func TestNodeCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"node-user", "other-user"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -1266,15 +1263,15 @@ func TestNodeExpireCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"node-expire-user"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -1392,15 +1389,15 @@ func TestNodeRenameCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"node-rename-command"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -1557,15 +1554,15 @@ func TestNodeMoveCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"old-user", "new-user"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clins")) assertNoErr(t, err) headscale, err := scenario.Headscale() @@ -1717,16 +1714,15 @@ func TestPolicyCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ Users: []string{"user1"}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + err = scenario.CreateHeadscaleEnv( - spec, []tsic.Option{}, hsic.WithTestName("clins"), hsic.WithConfigEnv(map[string]string{ @@ -1804,17 +1800,16 @@ func TestPolicyBrokenConfigCommand(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 1, Users: []string{"user1"}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + err = scenario.CreateHeadscaleEnv( - spec, []tsic.Option{}, hsic.WithTestName("clins"), hsic.WithConfigEnv(map[string]string{ diff --git a/integration/derp_verify_endpoint_test.go b/integration/derp_verify_endpoint_test.go index f1a2934d..20ed4872 100644 --- a/integration/derp_verify_endpoint_test.go +++ b/integration/derp_verify_endpoint_test.go @@ -31,15 +31,15 @@ func TestDERPVerifyEndpoint(t *testing.T) { certHeadscale, keyHeadscale, err := integrationutil.CreateCertificate(hostname) assertNoErr(t, err) - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1"}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + derper, err := scenario.CreateDERPServer("head", dsic.WithCACert(certHeadscale), dsic.WithVerifyClientURL(fmt.Sprintf("https://%s/verify", net.JoinHostPort(hostname, strconv.Itoa(headscalePort)))), @@ -66,7 +66,7 @@ func TestDERPVerifyEndpoint(t *testing.T) { }, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{tsic.WithCACert(derper.GetCert())}, + err = scenario.CreateHeadscaleEnv([]tsic.Option{tsic.WithCACert(derper.GetCert())}, hsic.WithHostname(hostname), hsic.WithPort(headscalePort), hsic.WithCustomTLS(certHeadscale, keyHeadscale), diff --git a/integration/dns_test.go b/integration/dns_test.go index de543992..9bd171f9 100644 --- a/integration/dns_test.go +++ b/integration/dns_test.go @@ -17,16 +17,16 @@ func TestResolveMagicDNS(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("magicdns")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("magicdns")) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() @@ -87,15 +87,15 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 1, Users: []string{"user1", "user2"}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + const erPath = "/tmp/extra_records.json" extraRecords := []tailcfg.DNSRecord{ @@ -107,7 +107,7 @@ func TestResolveMagicDNSExtraRecordsPath(t *testing.T) { } b, _ := json.Marshal(extraRecords) - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{ + err = scenario.CreateHeadscaleEnv([]tsic.Option{ tsic.WithDockerEntrypoint([]string{ "/bin/sh", "-c", @@ -364,16 +364,16 @@ func TestValidateResolvConf(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 3, Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("resolvconf"), hsic.WithConfigEnv(tt.conf)) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("resolvconf"), hsic.WithConfigEnv(tt.conf)) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() diff --git a/integration/general_test.go b/integration/general_test.go index 9b4ec153..ff22fd8f 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -28,16 +28,17 @@ func TestPingAllByIP(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, + MaxWait: dockertestMaxWait(), } - err = scenario.CreateHeadscaleEnv(spec, + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv( []tsic.Option{}, hsic.WithTestName("pingallbyip"), hsic.WithEmbeddedDERPServerOnly(), @@ -69,16 +70,16 @@ func TestPingAllByIPPublicDERP(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv( []tsic.Option{}, hsic.WithTestName("pingallbyippubderp"), ) @@ -119,25 +120,25 @@ func testEphemeralWithOptions(t *testing.T, opts ...hsic.Option) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) + spec := ScenarioSpec{ + NodesPerUser: len(MustTestVersions), + Users: []string{"user1", "user2"}, + } + + scenario, err := NewScenario(spec) assertNoErr(t, err) defer scenario.ShutdownAssertNoPanics(t) - spec := map[string]int{ - "user1": len(MustTestVersions), - "user2": len(MustTestVersions), - } - headscale, err := scenario.Headscale(opts...) assertNoErrHeadscaleEnv(t, err) - for userName, clientCount := range spec { + for _, userName := range spec.Users { err = scenario.CreateUser(userName) if err != nil { t.Fatalf("failed to create user %s: %s", userName, err) } - err = scenario.CreateTailscaleNodesInUser(userName, "all", clientCount, []tsic.Option{}...) + err = scenario.CreateTailscaleNodesInUser(userName, "all", spec.NodesPerUser, []tsic.Option{}...) if err != nil { t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) } @@ -192,15 +193,15 @@ func TestEphemeral2006DeletedTooQuickly(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) + spec := ScenarioSpec{ + NodesPerUser: len(MustTestVersions), + Users: []string{"user1", "user2"}, + } + + scenario, err := NewScenario(spec) assertNoErr(t, err) defer scenario.ShutdownAssertNoPanics(t) - spec := map[string]int{ - "user1": len(MustTestVersions), - "user2": len(MustTestVersions), - } - headscale, err := scenario.Headscale( hsic.WithTestName("ephemeral2006"), hsic.WithConfigEnv(map[string]string{ @@ -209,13 +210,13 @@ func TestEphemeral2006DeletedTooQuickly(t *testing.T) { ) assertNoErrHeadscaleEnv(t, err) - for userName, clientCount := range spec { + for _, userName := range spec.Users { err = scenario.CreateUser(userName) if err != nil { t.Fatalf("failed to create user %s: %s", userName, err) } - err = scenario.CreateTailscaleNodesInUser(userName, "all", clientCount, []tsic.Option{}...) + err = scenario.CreateTailscaleNodesInUser(userName, "all", spec.NodesPerUser, []tsic.Option{}...) if err != nil { t.Fatalf("failed to create tailscale nodes in user %s: %s", userName, err) } @@ -285,7 +286,7 @@ func TestEphemeral2006DeletedTooQuickly(t *testing.T) { // registered. time.Sleep(3 * time.Minute) - for userName := range spec { + for _, userName := range spec.Users { nodes, err := headscale.ListNodes(userName) if err != nil { log.Error(). @@ -306,16 +307,16 @@ func TestPingAllByHostname(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyname")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("pingallbyname")) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() @@ -355,16 +356,16 @@ func TestTaildrop(t *testing.T) { return err } - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("taildrop"), hsic.WithEmbeddedDERPServerOnly(), hsic.WithTLS(), @@ -527,16 +528,16 @@ func TestUpdateHostnameFromClient(t *testing.T) { "3": "user3-host", } - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErrf(t, "failed to create scenario: %s", err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 3, Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("updatehostname")) + scenario, err := NewScenario(spec) + assertNoErrf(t, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("updatehostname")) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() @@ -648,16 +649,16 @@ func TestExpireNode(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("expirenode")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("expirenode")) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() @@ -775,16 +776,16 @@ func TestNodeOnlineStatus(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("online")) + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("online")) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() @@ -891,16 +892,16 @@ func TestPingAllByIPManyUpDown(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv( []tsic.Option{}, hsic.WithTestName("pingallbyipmany"), hsic.WithEmbeddedDERPServerOnly(), @@ -971,16 +972,16 @@ func Test2118DeletingOnlineNodePanics(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 1, Users: []string{"user1", "user2"}, } - err = scenario.CreateHeadscaleEnv(spec, + scenario, err := NewScenario(spec) + assertNoErr(t, err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv( []tsic.Option{}, hsic.WithTestName("deletenocrash"), hsic.WithEmbeddedDERPServerOnly(), diff --git a/integration/route_test.go b/integration/route_test.go index ffa08177..bd582235 100644 --- a/integration/route_test.go +++ b/integration/route_test.go @@ -29,16 +29,16 @@ func TestEnablingRoutes(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - require.NoErrorf(t, err, "failed to create scenario: %s", err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 3, Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clienableroute")) + scenario, err := NewScenario(spec) + require.NoErrorf(t, err, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clienableroute")) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() @@ -202,16 +202,16 @@ func TestHASubnetRouterFailover(t *testing.T) { IntegrationSkip(t) t.Parallel() - scenario, err := NewScenario(dockertestMaxWait()) - require.NoErrorf(t, err, "failed to create scenario: %s", err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 4, Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, + scenario, err := NewScenario(spec) + require.NoErrorf(t, err, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clienableroute"), hsic.WithEmbeddedDERPServerOnly(), hsic.WithTLS(), @@ -526,18 +526,16 @@ func TestEnableDisableAutoApprovedRoute(t *testing.T) { expectedRoutes := "172.0.0.0/24" - user := "user2" - - scenario, err := NewScenario(dockertestMaxWait()) - require.NoErrorf(t, err, "failed to create scenario: %s", err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 1, Users: []string{"user1"}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{tsic.WithTags([]string{"tag:approve"})}, hsic.WithTestName("clienableroute"), hsic.WithACLPolicy( + scenario, err := NewScenario(spec) + require.NoErrorf(t, err, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{tsic.WithTags([]string{"tag:approve"})}, hsic.WithTestName("clienableroute"), hsic.WithACLPolicy( &policyv1.ACLPolicy{ ACLs: []policyv1.ACL{ { @@ -547,7 +545,7 @@ func TestEnableDisableAutoApprovedRoute(t *testing.T) { }, }, TagOwners: map[string][]string{ - "tag:approve": {user}, + "tag:approve": {"user1"}, }, AutoApprovers: policyv1.AutoApprovers{ Routes: map[string][]string{ @@ -626,16 +624,16 @@ func TestAutoApprovedSubRoute2068(t *testing.T) { user := "user1" - scenario, err := NewScenario(dockertestMaxWait()) - require.NoErrorf(t, err, "failed to create scenario: %s", err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 1, Users: []string{user}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{tsic.WithTags([]string{"tag:approve"})}, + scenario, err := NewScenario(spec) + require.NoErrorf(t, err, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{tsic.WithTags([]string{"tag:approve"})}, hsic.WithTestName("clienableroute"), hsic.WithEmbeddedDERPServerOnly(), hsic.WithTLS(), @@ -698,16 +696,16 @@ func TestSubnetRouteACL(t *testing.T) { user := "user4" - scenario, err := NewScenario(dockertestMaxWait()) - require.NoErrorf(t, err, "failed to create scenario: %s", err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 2, Users: []string{user}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clienableroute"), hsic.WithACLPolicy( + scenario, err := NewScenario(spec) + require.NoErrorf(t, err, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{}, hsic.WithTestName("clienableroute"), hsic.WithACLPolicy( &policyv1.ACLPolicy{ Groups: policyv1.Groups{ "group:admins": {user}, @@ -921,16 +919,16 @@ func TestEnablingExitRoutes(t *testing.T) { user := "user2" - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErrf(t, "failed to create scenario: %s", err) - defer scenario.ShutdownAssertNoPanics(t) - spec := ScenarioSpec{ NodesPerUser: 2, Users: []string{user}, } - err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{ + scenario, err := NewScenario(spec) + assertNoErrf(t, "failed to create scenario: %s", err) + defer scenario.ShutdownAssertNoPanics(t) + + err = scenario.CreateHeadscaleEnv([]tsic.Option{ tsic.WithExtraLoginArgs([]string{"--advertise-exit-node"}), }, hsic.WithTestName("clienableroute")) assertNoErrHeadscaleEnv(t, err) diff --git a/integration/scenario.go b/integration/scenario.go index 018bdf96..4d3f7f46 100644 --- a/integration/scenario.go +++ b/integration/scenario.go @@ -91,6 +91,30 @@ type Scenario struct { networks map[string]*dockertest.Network mu sync.Mutex + + spec ScenarioSpec + userToNetwork map[string]*dockertest.Network +} + +// ScenarioSpec describes the users, nodes, and network topology to +// set up for a given scenario. +type ScenarioSpec struct { + // Users is a list of usernames that will be created. + // Each created user will get nodes equivalent to NodesPerUser + Users []string + + // NodesPerUser is how many nodes should be attached to each user. + NodesPerUser int + + // Networks, if set, is the deparate Docker networks that should be + // created and a list of the users that should be placed in those networks. + // If not set, a single network will be created and all users+nodes will be + // added there. + // Please note that Docker networks are not necessarily routable and + // connections between them might fall back to DERP. + Networks map[string][]string + + MaxWait time.Duration } var TestHashPrefix = "hs-" + util.MustGenerateRandomStringDNSSafe(scenarioHashLength) @@ -98,20 +122,52 @@ var TestDefaultNetwork = TestHashPrefix + "-default" // NewScenario creates a test Scenario which can be used to bootstraps a ControlServer with // a set of Users and TailscaleClients. -func NewScenario(maxWait time.Duration) (*Scenario, error) { +func NewScenario(spec ScenarioSpec) (*Scenario, error) { pool, err := dockertest.NewPool("") if err != nil { return nil, fmt.Errorf("could not connect to docker: %w", err) } - pool.MaxWait = maxWait + if spec.MaxWait == 0 { + pool.MaxWait = dockertestMaxWait() + } else { + pool.MaxWait = spec.MaxWait + } - return &Scenario{ + s := &Scenario{ controlServers: xsync.NewMapOf[string, ControlServer](), users: make(map[string]*User), pool: pool, - }, nil + spec: spec, + } + + var userToNetwork map[string]*dockertest.Network + if spec.Networks != nil || len(spec.Networks) != 0 { + for name, users := range s.spec.Networks { + networkName := TestHashPrefix + "-" + name + network, err := s.AddNetwork(networkName) + if err != nil { + return nil, err + } + + for _, user := range users { + if n2, ok := userToNetwork[user]; ok { + return nil, fmt.Errorf("users can only have nodes placed in one network: %s into %s but already in %s", user, network.Network.Name, n2.Network.Name) + } + mak.Set(&userToNetwork, user, network) + } + } + } else { + _, err := s.AddNetwork(TestDefaultNetwork) + if err != nil { + return nil, err + } + } + + s.userToNetwork = userToNetwork + + return s, nil } func (s *Scenario) AddNetwork(name string) (*dockertest.Network, error) { @@ -494,76 +550,33 @@ func (s *Scenario) WaitForTailscaleSyncWithPeerCount(peerCount int) error { return nil } -// ScenarioSpec describes the users, nodes, and network topology to -// set up for a given scenario. -type ScenarioSpec struct { - // Users is a list of usernames that will be created. - // Each created user will get nodes equivalent to NodesPerUser - Users []string - - // NodesPerUser is how many nodes should be attached to each user. - NodesPerUser int - - // Networks, if set, is the deparate Docker networks that should be - // created and a list of the users that should be placed in those networks. - // If not set, a single network will be created and all users+nodes will be - // added there. - // Please note that Docker networks are not necessarily routable and - // connections between them might fall back to DERP. - Networks map[string][]string -} - -// CreateHeadscaleEnv is a convenient method returning a complete Headcale -// test environment with nodes of all versions, joined to the server with X -// users. +// CreateHeadscaleEnv starts the headscale environment and the clients +// according to the ScenarioSpec passed to the Scenario. func (s *Scenario) CreateHeadscaleEnv( - spec ScenarioSpec, tsOpts []tsic.Option, opts ...hsic.Option, ) error { - var userToNetwork map[string]*dockertest.Network - if spec.Networks != nil || len(spec.Networks) != 0 { - for name, users := range spec.Networks { - networkName := TestHashPrefix + "-" + name - network, err := s.AddNetwork(networkName) - if err != nil { - return err - } - - for _, user := range users { - if n2, ok := userToNetwork[user]; ok { - return fmt.Errorf("users can only have nodes placed in one network: %s into %s but already in %s", user, network.Network.Name, n2.Network.Name) - } - mak.Set(&userToNetwork, user, network) - } - } - } else { - _, err := s.AddNetwork(TestDefaultNetwork) - if err != nil { - return err - } - } headscale, err := s.Headscale(opts...) if err != nil { return err } - sort.Strings(spec.Users) - for _, user := range spec.Users { + sort.Strings(s.spec.Users) + for _, user := range s.spec.Users { err = s.CreateUser(user) if err != nil { return err } var opts []tsic.Option - if userToNetwork != nil { - opts = append(tsOpts, tsic.WithNetwork(userToNetwork[user])) + if s.userToNetwork != nil { + opts = append(tsOpts, tsic.WithNetwork(s.userToNetwork[user])) } else { opts = append(tsOpts, tsic.WithNetwork(s.networks[TestDefaultNetwork])) } - err = s.CreateTailscaleNodesInUser(user, "all", spec.NodesPerUser, opts...) + err = s.CreateTailscaleNodesInUser(user, "all", s.spec.NodesPerUser, opts...) if err != nil { return err } diff --git a/integration/scenario_test.go b/integration/scenario_test.go index 27da5ae1..1dfa4bf2 100644 --- a/integration/scenario_test.go +++ b/integration/scenario_test.go @@ -33,7 +33,7 @@ func TestHeadscale(t *testing.T) { user := "test-space" - scenario, err := NewScenario(dockertestMaxWait()) + scenario, err := NewScenario(ScenarioSpec{}) assertNoErr(t, err) defer scenario.ShutdownAssertNoPanics(t) @@ -82,7 +82,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) { count := 1 - scenario, err := NewScenario(dockertestMaxWait()) + scenario, err := NewScenario(ScenarioSpec{}) assertNoErr(t, err) defer scenario.ShutdownAssertNoPanics(t) diff --git a/integration/ssh_test.go b/integration/ssh_test.go index 70aa96ab..d9983f65 100644 --- a/integration/ssh_test.go +++ b/integration/ssh_test.go @@ -50,15 +50,15 @@ var retry = func(times int, sleepInterval time.Duration, func sshScenario(t *testing.T, policy *policyv1.ACLPolicy, clientsPerUser int) *Scenario { t.Helper() - scenario, err := NewScenario(dockertestMaxWait()) - assertNoErr(t, err) spec := ScenarioSpec{ NodesPerUser: clientsPerUser, Users: []string{"user1", "user2"}, } + scenario, err := NewScenario(spec) + assertNoErr(t, err) - err = scenario.CreateHeadscaleEnv(spec, + err = scenario.CreateHeadscaleEnv( []tsic.Option{ tsic.WithSSH(),