restructure command/api to use stable IDs (#2261)
This commit is contained in:
parent
08bd4b9bc5
commit
64fd1f9483
29 changed files with 1902 additions and 3613 deletions
|
@ -130,22 +130,22 @@ func TestOIDCAuthenticationPingAll(t *testing.T) {
|
|||
|
||||
want := []v1.User{
|
||||
{
|
||||
Id: "1",
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Id: 2,
|
||||
Name: "user1",
|
||||
Email: "user1@headscale.net",
|
||||
Provider: "oidc",
|
||||
ProviderId: oidcConfig.Issuer + "/user1",
|
||||
},
|
||||
{
|
||||
Id: "3",
|
||||
Id: 3,
|
||||
Name: "user2",
|
||||
},
|
||||
{
|
||||
Id: "4",
|
||||
Id: 4,
|
||||
Name: "user2",
|
||||
Email: "", // Unverified
|
||||
Provider: "oidc",
|
||||
|
@ -260,22 +260,22 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||
want: func(iss string) []v1.User {
|
||||
return []v1.User{
|
||||
{
|
||||
Id: "1",
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Id: 2,
|
||||
Name: "user1",
|
||||
Email: "user1@headscale.net",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user1",
|
||||
},
|
||||
{
|
||||
Id: "3",
|
||||
Id: 3,
|
||||
Name: "user2",
|
||||
},
|
||||
{
|
||||
Id: "4",
|
||||
Id: 4,
|
||||
Name: "user2",
|
||||
Email: "user2@headscale.net",
|
||||
Provider: "oidc",
|
||||
|
@ -295,21 +295,21 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||
want: func(iss string) []v1.User {
|
||||
return []v1.User{
|
||||
{
|
||||
Id: "1",
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Id: 2,
|
||||
Name: "user1",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user1",
|
||||
},
|
||||
{
|
||||
Id: "3",
|
||||
Id: 3,
|
||||
Name: "user2",
|
||||
},
|
||||
{
|
||||
Id: "4",
|
||||
Id: 4,
|
||||
Name: "user2",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user2",
|
||||
|
@ -329,14 +329,14 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||
want: func(iss string) []v1.User {
|
||||
return []v1.User{
|
||||
{
|
||||
Id: "1",
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
Email: "user1@headscale.net",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user1",
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Id: 2,
|
||||
Name: "user2",
|
||||
Email: "user2@headscale.net",
|
||||
Provider: "oidc",
|
||||
|
@ -357,21 +357,21 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||
want: func(iss string) []v1.User {
|
||||
return []v1.User{
|
||||
{
|
||||
Id: "1",
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Id: 2,
|
||||
Name: "user1",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user1",
|
||||
},
|
||||
{
|
||||
Id: "3",
|
||||
Id: 3,
|
||||
Name: "user2",
|
||||
},
|
||||
{
|
||||
Id: "4",
|
||||
Id: 4,
|
||||
Name: "user2",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user2",
|
||||
|
@ -393,14 +393,14 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||
// Hmm I think we will have to overwrite the initial name here
|
||||
// createuser with "user1.headscale.net", but oidc with "user1"
|
||||
{
|
||||
Id: "1",
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
Email: "user1@headscale.net",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user1",
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Id: 2,
|
||||
Name: "user2",
|
||||
Email: "user2@headscale.net",
|
||||
Provider: "oidc",
|
||||
|
@ -421,21 +421,21 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||
want: func(iss string) []v1.User {
|
||||
return []v1.User{
|
||||
{
|
||||
Id: "1",
|
||||
Id: 1,
|
||||
Name: "user1.headscale.net",
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Id: 2,
|
||||
Name: "user1",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user1",
|
||||
},
|
||||
{
|
||||
Id: "3",
|
||||
Id: 3,
|
||||
Name: "user2.headscale.net",
|
||||
},
|
||||
{
|
||||
Id: "4",
|
||||
Id: 4,
|
||||
Name: "user2",
|
||||
Provider: "oidc",
|
||||
ProviderId: iss + "/user2",
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
tcmp "github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/policy"
|
||||
"github.com/juanfont/headscale/integration/hsic"
|
||||
"github.com/juanfont/headscale/integration/tsic"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
|
@ -30,6 +33,16 @@ func executeAndUnmarshal[T any](headscale ControlServer, command []string, resul
|
|||
return nil
|
||||
}
|
||||
|
||||
// Interface ensuring that we can sort structs from gRPC that
|
||||
// have an ID field.
|
||||
type GRPCSortable interface {
|
||||
GetId() uint64
|
||||
}
|
||||
|
||||
func sortWithID[T GRPCSortable](a, b T) int {
|
||||
return cmp.Compare(a.GetId(), b.GetId())
|
||||
}
|
||||
|
||||
func TestUserCommand(t *testing.T) {
|
||||
IntegrationSkip(t)
|
||||
t.Parallel()
|
||||
|
@ -49,7 +62,7 @@ func TestUserCommand(t *testing.T) {
|
|||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
|
||||
var listUsers []v1.User
|
||||
var listUsers []*v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
[]string{
|
||||
"headscale",
|
||||
|
@ -62,8 +75,8 @@ func TestUserCommand(t *testing.T) {
|
|||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
slices.SortFunc(listUsers, sortWithID)
|
||||
result := []string{listUsers[0].GetName(), listUsers[1].GetName()}
|
||||
sort.Strings(result)
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
|
@ -76,15 +89,14 @@ func TestUserCommand(t *testing.T) {
|
|||
"headscale",
|
||||
"users",
|
||||
"rename",
|
||||
"--output",
|
||||
"json",
|
||||
"user2",
|
||||
"newname",
|
||||
"--output=json",
|
||||
fmt.Sprintf("--identifier=%d", listUsers[1].GetId()),
|
||||
"--new-name=newname",
|
||||
},
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
var listAfterRenameUsers []v1.User
|
||||
var listAfterRenameUsers []*v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
[]string{
|
||||
"headscale",
|
||||
|
@ -97,14 +109,131 @@ func TestUserCommand(t *testing.T) {
|
|||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
slices.SortFunc(listUsers, sortWithID)
|
||||
result = []string{listAfterRenameUsers[0].GetName(), listAfterRenameUsers[1].GetName()}
|
||||
sort.Strings(result)
|
||||
|
||||
assert.Equal(
|
||||
t,
|
||||
[]string{"newname", "user1"},
|
||||
[]string{"user1", "newname"},
|
||||
result,
|
||||
)
|
||||
|
||||
var listByUsername []*v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
[]string{
|
||||
"headscale",
|
||||
"users",
|
||||
"list",
|
||||
"--output",
|
||||
"json",
|
||||
"--name=user1",
|
||||
},
|
||||
&listByUsername,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
slices.SortFunc(listByUsername, sortWithID)
|
||||
want := []*v1.User{
|
||||
{
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
},
|
||||
}
|
||||
|
||||
if diff := tcmp.Diff(want, listByUsername, cmpopts.IgnoreUnexported(v1.User{}), cmpopts.IgnoreFields(v1.User{}, "CreatedAt")); diff != "" {
|
||||
t.Errorf("unexpected users (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
var listByID []*v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
[]string{
|
||||
"headscale",
|
||||
"users",
|
||||
"list",
|
||||
"--output",
|
||||
"json",
|
||||
"--identifier=1",
|
||||
},
|
||||
&listByID,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
slices.SortFunc(listByID, sortWithID)
|
||||
want = []*v1.User{
|
||||
{
|
||||
Id: 1,
|
||||
Name: "user1",
|
||||
},
|
||||
}
|
||||
|
||||
if diff := tcmp.Diff(want, listByID, cmpopts.IgnoreUnexported(v1.User{}), cmpopts.IgnoreFields(v1.User{}, "CreatedAt")); diff != "" {
|
||||
t.Errorf("unexpected users (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
deleteResult, err := headscale.Execute(
|
||||
[]string{
|
||||
"headscale",
|
||||
"users",
|
||||
"destroy",
|
||||
"--force",
|
||||
// Delete "user1"
|
||||
"--identifier=1",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
assert.Contains(t, deleteResult, "User destroyed")
|
||||
|
||||
var listAfterIDDelete []*v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
[]string{
|
||||
"headscale",
|
||||
"users",
|
||||
"list",
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
&listAfterIDDelete,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
slices.SortFunc(listAfterIDDelete, sortWithID)
|
||||
want = []*v1.User{
|
||||
{
|
||||
Id: 2,
|
||||
Name: "newname",
|
||||
},
|
||||
}
|
||||
|
||||
if diff := tcmp.Diff(want, listAfterIDDelete, cmpopts.IgnoreUnexported(v1.User{}), cmpopts.IgnoreFields(v1.User{}, "CreatedAt")); diff != "" {
|
||||
t.Errorf("unexpected users (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
deleteResult, err = headscale.Execute(
|
||||
[]string{
|
||||
"headscale",
|
||||
"users",
|
||||
"destroy",
|
||||
"--force",
|
||||
"--name=newname",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
assert.Contains(t, deleteResult, "User destroyed")
|
||||
|
||||
var listAfterNameDelete []v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
[]string{
|
||||
"headscale",
|
||||
"users",
|
||||
"list",
|
||||
"--output",
|
||||
"json",
|
||||
},
|
||||
&listAfterNameDelete,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
|
||||
require.Len(t, listAfterNameDelete, 0)
|
||||
}
|
||||
|
||||
func TestPreAuthKeyCommand(t *testing.T) {
|
||||
|
@ -1716,4 +1845,3 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
|
|||
)
|
||||
assert.ErrorContains(t, err, "acl policy not found")
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
const (
|
||||
hsicHashLength = 6
|
||||
dockerContextPath = "../."
|
||||
caCertRoot = "/usr/local/share/ca-certificates"
|
||||
caCertRoot = "/usr/local/share/ca-certificates"
|
||||
aclPolicyPath = "/etc/headscale/acl.hujson"
|
||||
tlsCertPath = "/etc/headscale/tls.cert"
|
||||
tlsKeyPath = "/etc/headscale/tls.key"
|
||||
|
@ -617,6 +617,7 @@ func (t *HeadscaleInContainer) Execute(
|
|||
[]string{},
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("command: %v", command)
|
||||
log.Printf("command stderr: %s\n", stderr)
|
||||
|
||||
if stdout != "" {
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
xmaps "golang.org/x/exp/maps"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"tailscale.com/envknob"
|
||||
)
|
||||
|
@ -512,23 +513,26 @@ func (s *Scenario) CreateHeadscaleEnv(
|
|||
return err
|
||||
}
|
||||
|
||||
for userName, clientCount := range users {
|
||||
err = s.CreateUser(userName)
|
||||
usernames := xmaps.Keys(users)
|
||||
sort.Strings(usernames)
|
||||
for _, username := range usernames {
|
||||
clientCount := users[username]
|
||||
err = s.CreateUser(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.CreateTailscaleNodesInUser(userName, "all", clientCount, tsOpts...)
|
||||
err = s.CreateTailscaleNodesInUser(username, "all", clientCount, tsOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := s.CreatePreAuthKey(userName, true, false)
|
||||
key, err := s.CreatePreAuthKey(username, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey())
|
||||
err = s.RunTailscaleUp(username, headscale.GetEndpoint(), key.GetKey())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue