mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Merge branch 'main' into integration-remove-v1-genera
This commit is contained in:
		
						commit
						611f7c374c
					
				
							
								
								
									
										14
									
								
								app.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								app.go
									
									
									
									
									
								
							| @ -24,7 +24,7 @@ import ( | |||||||
| 	"github.com/patrickmn/go-cache" | 	"github.com/patrickmn/go-cache" | ||||||
| 	zerolog "github.com/philip-bui/grpc-zerolog" | 	zerolog "github.com/philip-bui/grpc-zerolog" | ||||||
| 	"github.com/prometheus/client_golang/prometheus/promhttp" | 	"github.com/prometheus/client_golang/prometheus/promhttp" | ||||||
| 	"github.com/puzpuzpuz/xsync" | 	"github.com/puzpuzpuz/xsync/v2" | ||||||
| 	zl "github.com/rs/zerolog" | 	zl "github.com/rs/zerolog" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"golang.org/x/crypto/acme" | 	"golang.org/x/crypto/acme" | ||||||
| @ -94,7 +94,7 @@ type Headscale struct { | |||||||
| 	aclPolicy *ACLPolicy | 	aclPolicy *ACLPolicy | ||||||
| 	aclRules  []tailcfg.FilterRule | 	aclRules  []tailcfg.FilterRule | ||||||
| 
 | 
 | ||||||
| 	lastStateChange *xsync.MapOf[time.Time] | 	lastStateChange *xsync.MapOf[string, time.Time] | ||||||
| 
 | 
 | ||||||
| 	oidcProvider *oidc.Provider | 	oidcProvider *oidc.Provider | ||||||
| 	oauth2Config *oauth2.Config | 	oauth2Config *oauth2.Config | ||||||
| @ -884,7 +884,7 @@ func (h *Headscale) setLastStateChangeToNow() { | |||||||
| 
 | 
 | ||||||
| 	now := time.Now().UTC() | 	now := time.Now().UTC() | ||||||
| 
 | 
 | ||||||
| 	namespaces, err := h.ListNamespacesStr() | 	namespaces, err := h.ListNamespaces() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(). | 		log.Error(). | ||||||
| 			Caller(). | 			Caller(). | ||||||
| @ -893,22 +893,22 @@ func (h *Headscale) setLastStateChangeToNow() { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range namespaces { | 	for _, namespace := range namespaces { | ||||||
| 		lastStateUpdate.WithLabelValues(namespace, "headscale").Set(float64(now.Unix())) | 		lastStateUpdate.WithLabelValues(namespace.Name, "headscale").Set(float64(now.Unix())) | ||||||
| 		if h.lastStateChange == nil { | 		if h.lastStateChange == nil { | ||||||
| 			h.lastStateChange = xsync.NewMapOf[time.Time]() | 			h.lastStateChange = xsync.NewMapOf[time.Time]() | ||||||
| 		} | 		} | ||||||
| 		h.lastStateChange.Store(namespace, now) | 		h.lastStateChange.Store(namespace.Name, now) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *Headscale) getLastStateChange(namespaces ...string) time.Time { | func (h *Headscale) getLastStateChange(namespaces ...Namespace) time.Time { | ||||||
| 	times := []time.Time{} | 	times := []time.Time{} | ||||||
| 
 | 
 | ||||||
| 	// getLastStateChange takes a list of namespaces as a "filter", if no namespaces
 | 	// getLastStateChange takes a list of namespaces as a "filter", if no namespaces
 | ||||||
| 	// are past, then use the entier list of namespaces and look for the last update
 | 	// are past, then use the entier list of namespaces and look for the last update
 | ||||||
| 	if len(namespaces) > 0 { | 	if len(namespaces) > 0 { | ||||||
| 		for _, namespace := range namespaces { | 		for _, namespace := range namespaces { | ||||||
| 			if lastChange, ok := h.lastStateChange.Load(namespace); ok { | 			if lastChange, ok := h.lastStateChange.Load(namespace.Name); ok { | ||||||
| 				times = append(times, lastChange) | 				times = append(times, lastChange) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ | |||||||
| 
 | 
 | ||||||
|           # When updating go.mod or go.sum, a new sha will need to be calculated, |           # When updating go.mod or go.sum, a new sha will need to be calculated, | ||||||
|           # update this if you have a mismatch after doing a change to thos files. |           # update this if you have a mismatch after doing a change to thos files. | ||||||
|           vendorSha256 = "sha256-DosFCSiQ5FURbIrt4NcPGkExc84t2MGMqe9XLxNHdIM="; |           vendorSha256 = "sha256-nbPCCqGqBFtfbrCeT2WgtUZ+6DerV/bpYpkXtoRaCHE="; | ||||||
| 
 | 
 | ||||||
|           ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"]; |           ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"]; | ||||||
|         }; |         }; | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -22,7 +22,6 @@ require ( | |||||||
| 	github.com/prometheus/client_golang v1.13.0 | 	github.com/prometheus/client_golang v1.13.0 | ||||||
| 	github.com/prometheus/common v0.37.0 | 	github.com/prometheus/common v0.37.0 | ||||||
| 	github.com/pterm/pterm v0.12.45 | 	github.com/pterm/pterm v0.12.45 | ||||||
| 	github.com/puzpuzpuz/xsync v1.4.3 |  | ||||||
| 	github.com/rs/zerolog v1.28.0 | 	github.com/rs/zerolog v1.28.0 | ||||||
| 	github.com/spf13/cobra v1.5.0 | 	github.com/spf13/cobra v1.5.0 | ||||||
| 	github.com/spf13/viper v1.12.0 | 	github.com/spf13/viper v1.12.0 | ||||||
| @ -117,6 +116,7 @@ require ( | |||||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||||
| 	github.com/prometheus/client_model v0.2.0 // indirect | 	github.com/prometheus/client_model v0.2.0 // indirect | ||||||
| 	github.com/prometheus/procfs v0.8.0 // indirect | 	github.com/prometheus/procfs v0.8.0 // indirect | ||||||
|  | 	github.com/puzpuzpuz/xsync/v2 v2.0.2 // indirect | ||||||
| 	github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect | 	github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect | ||||||
| 	github.com/rivo/uniseg v0.2.0 // indirect | 	github.com/rivo/uniseg v0.2.0 // indirect | ||||||
| 	github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect | 	github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -846,8 +846,8 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b | |||||||
| github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= | github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= | ||||||
| github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE= | github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE= | ||||||
| github.com/pterm/pterm v0.12.45/go.mod h1:hJgLlBafm45w/Hr0dKXxY//POD7CgowhePaG1sdPNBg= | github.com/pterm/pterm v0.12.45/go.mod h1:hJgLlBafm45w/Hr0dKXxY//POD7CgowhePaG1sdPNBg= | ||||||
| github.com/puzpuzpuz/xsync v1.4.3 h1:nS/Iqc4EnpJ8jm/MzJ+e3MUaP2Ys2mqXeEfoxoU0HaM= | github.com/puzpuzpuz/xsync/v2 v2.0.2 h1:IpXQ8gGkrnZlLGpJLDmq56sYjNhF88n934Yq5BV5fKw= | ||||||
| github.com/puzpuzpuz/xsync v1.4.3/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg= | github.com/puzpuzpuz/xsync/v2 v2.0.2/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= | ||||||
| github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= | github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= | ||||||
| github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= | github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= | ||||||
| github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ= | github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ= | ||||||
|  | |||||||
							
								
								
									
										377
									
								
								integration/cli_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								integration/cli_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,377 @@ | |||||||
|  | package integration | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"sort" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1" | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func executeAndUnmarshal[T any](headscale ControlServer, command []string, result T) error { | ||||||
|  | 	str, err := headscale.Execute(command) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = json.Unmarshal([]byte(str), result) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNamespaceCommand(t *testing.T) { | ||||||
|  | 	IntegrationSkip(t) | ||||||
|  | 	t.Parallel() | ||||||
|  | 
 | ||||||
|  | 	scenario, err := NewScenario() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	spec := map[string]int{ | ||||||
|  | 		"namespace1": 0, | ||||||
|  | 		"namespace2": 0, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = scenario.CreateHeadscaleEnv(spec) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	var listNamespaces []v1.Namespace | ||||||
|  | 	err = executeAndUnmarshal(scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"namespaces", | ||||||
|  | 			"list", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&listNamespaces, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	result := []string{listNamespaces[0].Name, listNamespaces[1].Name} | ||||||
|  | 	sort.Strings(result) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal( | ||||||
|  | 		t, | ||||||
|  | 		[]string{"namespace1", "namespace2"}, | ||||||
|  | 		result, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	_, err = scenario.Headscale().Execute( | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"namespaces", | ||||||
|  | 			"rename", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 			"namespace2", | ||||||
|  | 			"newname", | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	var listAfterRenameNamespaces []v1.Namespace | ||||||
|  | 	err = executeAndUnmarshal(scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"namespaces", | ||||||
|  | 			"list", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&listAfterRenameNamespaces, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	result = []string{listAfterRenameNamespaces[0].Name, listAfterRenameNamespaces[1].Name} | ||||||
|  | 	sort.Strings(result) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal( | ||||||
|  | 		t, | ||||||
|  | 		[]string{"namespace1", "newname"}, | ||||||
|  | 		result, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	err = scenario.Shutdown() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestPreAuthKeyCommand(t *testing.T) { | ||||||
|  | 	IntegrationSkip(t) | ||||||
|  | 	t.Parallel() | ||||||
|  | 
 | ||||||
|  | 	namespace := "preauthkeyspace" | ||||||
|  | 	count := 3 | ||||||
|  | 
 | ||||||
|  | 	scenario, err := NewScenario() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	spec := map[string]int{ | ||||||
|  | 		namespace: 0, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = scenario.CreateHeadscaleEnv(spec) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	keys := make([]*v1.PreAuthKey, count) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	for index := 0; index < count; index++ { | ||||||
|  | 		var preAuthKey v1.PreAuthKey | ||||||
|  | 		err := executeAndUnmarshal( | ||||||
|  | 			scenario.Headscale(), | ||||||
|  | 			[]string{ | ||||||
|  | 				"headscale", | ||||||
|  | 				"preauthkeys", | ||||||
|  | 				"--namespace", | ||||||
|  | 				namespace, | ||||||
|  | 				"create", | ||||||
|  | 				"--reusable", | ||||||
|  | 				"--expiration", | ||||||
|  | 				"24h", | ||||||
|  | 				"--output", | ||||||
|  | 				"json", | ||||||
|  | 				"--tags", | ||||||
|  | 				"tag:test1,tag:test2", | ||||||
|  | 			}, | ||||||
|  | 			&preAuthKey, | ||||||
|  | 		) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 		keys[index] = &preAuthKey | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	assert.Len(t, keys, 3) | ||||||
|  | 
 | ||||||
|  | 	var listedPreAuthKeys []v1.PreAuthKey | ||||||
|  | 	err = executeAndUnmarshal( | ||||||
|  | 		scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"list", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&listedPreAuthKeys, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	// There is one key created by "scenario.CreateHeadscaleEnv"
 | ||||||
|  | 	assert.Len(t, listedPreAuthKeys, 4) | ||||||
|  | 
 | ||||||
|  | 	assert.Equal( | ||||||
|  | 		t, | ||||||
|  | 		[]string{keys[0].Id, keys[1].Id, keys[2].Id}, | ||||||
|  | 		[]string{listedPreAuthKeys[1].Id, listedPreAuthKeys[2].Id, listedPreAuthKeys[3].Id}, | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	assert.NotEmpty(t, listedPreAuthKeys[1].Key) | ||||||
|  | 	assert.NotEmpty(t, listedPreAuthKeys[2].Key) | ||||||
|  | 	assert.NotEmpty(t, listedPreAuthKeys[3].Key) | ||||||
|  | 
 | ||||||
|  | 	assert.True(t, listedPreAuthKeys[1].Expiration.AsTime().After(time.Now())) | ||||||
|  | 	assert.True(t, listedPreAuthKeys[2].Expiration.AsTime().After(time.Now())) | ||||||
|  | 	assert.True(t, listedPreAuthKeys[3].Expiration.AsTime().After(time.Now())) | ||||||
|  | 
 | ||||||
|  | 	assert.True( | ||||||
|  | 		t, | ||||||
|  | 		listedPreAuthKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||||
|  | 	) | ||||||
|  | 	assert.True( | ||||||
|  | 		t, | ||||||
|  | 		listedPreAuthKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||||
|  | 	) | ||||||
|  | 	assert.True( | ||||||
|  | 		t, | ||||||
|  | 		listedPreAuthKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	for index := range listedPreAuthKeys { | ||||||
|  | 		if index == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		assert.Equal(t, listedPreAuthKeys[index].AclTags, []string{"tag:test1", "tag:test2"}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Test key expiry
 | ||||||
|  | 	_, err = scenario.Headscale().Execute( | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"expire", | ||||||
|  | 			listedPreAuthKeys[1].Key, | ||||||
|  | 		}, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	var listedPreAuthKeysAfterExpire []v1.PreAuthKey | ||||||
|  | 	err = executeAndUnmarshal( | ||||||
|  | 		scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"list", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&listedPreAuthKeysAfterExpire, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	assert.True(t, listedPreAuthKeysAfterExpire[1].Expiration.AsTime().Before(time.Now())) | ||||||
|  | 	assert.True(t, listedPreAuthKeysAfterExpire[2].Expiration.AsTime().After(time.Now())) | ||||||
|  | 	assert.True(t, listedPreAuthKeysAfterExpire[3].Expiration.AsTime().After(time.Now())) | ||||||
|  | 
 | ||||||
|  | 	err = scenario.Shutdown() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) { | ||||||
|  | 	IntegrationSkip(t) | ||||||
|  | 	t.Parallel() | ||||||
|  | 
 | ||||||
|  | 	namespace := "pre-auth-key-without-exp-namespace" | ||||||
|  | 
 | ||||||
|  | 	scenario, err := NewScenario() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	spec := map[string]int{ | ||||||
|  | 		namespace: 0, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = scenario.CreateHeadscaleEnv(spec) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	var preAuthKey v1.PreAuthKey | ||||||
|  | 	err = executeAndUnmarshal( | ||||||
|  | 		scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"create", | ||||||
|  | 			"--reusable", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&preAuthKey, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	var listedPreAuthKeys []v1.PreAuthKey | ||||||
|  | 	err = executeAndUnmarshal( | ||||||
|  | 		scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"list", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&listedPreAuthKeys, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	// There is one key created by "scenario.CreateHeadscaleEnv"
 | ||||||
|  | 	assert.Len(t, listedPreAuthKeys, 2) | ||||||
|  | 
 | ||||||
|  | 	assert.True(t, listedPreAuthKeys[1].Expiration.AsTime().After(time.Now())) | ||||||
|  | 	assert.True( | ||||||
|  | 		t, | ||||||
|  | 		listedPreAuthKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Minute*70)), | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	err = scenario.Shutdown() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) { | ||||||
|  | 	IntegrationSkip(t) | ||||||
|  | 	t.Parallel() | ||||||
|  | 
 | ||||||
|  | 	namespace := "pre-auth-key-reus-ephm-namespace" | ||||||
|  | 
 | ||||||
|  | 	scenario, err := NewScenario() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	spec := map[string]int{ | ||||||
|  | 		namespace: 0, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = scenario.CreateHeadscaleEnv(spec) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	var preAuthReusableKey v1.PreAuthKey | ||||||
|  | 	err = executeAndUnmarshal( | ||||||
|  | 		scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"create", | ||||||
|  | 			"--reusable=true", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&preAuthReusableKey, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	var preAuthEphemeralKey v1.PreAuthKey | ||||||
|  | 	err = executeAndUnmarshal( | ||||||
|  | 		scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"create", | ||||||
|  | 			"--ephemeral=true", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&preAuthEphemeralKey, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	assert.True(t, preAuthEphemeralKey.GetEphemeral()) | ||||||
|  | 	assert.False(t, preAuthEphemeralKey.GetReusable()) | ||||||
|  | 
 | ||||||
|  | 	var listedPreAuthKeys []v1.PreAuthKey | ||||||
|  | 	err = executeAndUnmarshal( | ||||||
|  | 		scenario.Headscale(), | ||||||
|  | 		[]string{ | ||||||
|  | 			"headscale", | ||||||
|  | 			"preauthkeys", | ||||||
|  | 			"--namespace", | ||||||
|  | 			namespace, | ||||||
|  | 			"list", | ||||||
|  | 			"--output", | ||||||
|  | 			"json", | ||||||
|  | 		}, | ||||||
|  | 		&listedPreAuthKeys, | ||||||
|  | 	) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	// There is one key created by "scenario.CreateHeadscaleEnv"
 | ||||||
|  | 	assert.Len(t, listedPreAuthKeys, 3) | ||||||
|  | 
 | ||||||
|  | 	err = scenario.Shutdown() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
| @ -6,6 +6,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| type ControlServer interface { | type ControlServer interface { | ||||||
| 	Shutdown() error | 	Shutdown() error | ||||||
|  | 	Execute(command []string) (string, error) | ||||||
| 	GetHealthEndpoint() string | 	GetHealthEndpoint() string | ||||||
| 	GetEndpoint() string | 	GetEndpoint() string | ||||||
| 	WaitForReady() error | 	WaitForReady() error | ||||||
|  | |||||||
| @ -103,6 +103,29 @@ func (t *HeadscaleInContainer) Shutdown() error { | |||||||
| 	return t.pool.Purge(t.container) | 	return t.pool.Purge(t.container) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (t *HeadscaleInContainer) Execute( | ||||||
|  | 	command []string, | ||||||
|  | ) (string, error) { | ||||||
|  | 	log.Println("command", command) | ||||||
|  | 	log.Printf("running command for %s\n", t.hostname) | ||||||
|  | 	stdout, stderr, err := dockertestutil.ExecuteCommand( | ||||||
|  | 		t.container, | ||||||
|  | 		command, | ||||||
|  | 		[]string{}, | ||||||
|  | 	) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("command stderr: %s\n", stderr) | ||||||
|  | 
 | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if stdout != "" { | ||||||
|  | 		log.Printf("command stdout: %s\n", stdout) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return stdout, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (t *HeadscaleInContainer) GetIP() string { | func (t *HeadscaleInContainer) GetIP() string { | ||||||
| 	return t.container.GetIPInNetwork(t.network) | 	return t.container.GetIPInNetwork(t.network) | ||||||
| } | } | ||||||
|  | |||||||
| @ -148,21 +148,6 @@ func (h *Headscale) ListNamespaces() ([]Namespace, error) { | |||||||
| 	return namespaces, nil | 	return namespaces, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *Headscale) ListNamespacesStr() ([]string, error) { |  | ||||||
| 	namespaces, err := h.ListNamespaces() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return []string{}, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	namespaceStrs := make([]string, len(namespaces)) |  | ||||||
| 
 |  | ||||||
| 	for index, namespace := range namespaces { |  | ||||||
| 		namespaceStrs[index] = namespace.Name |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return namespaceStrs, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ListMachinesInNamespace gets all the nodes in a given namespace.
 | // ListMachinesInNamespace gets all the nodes in a given namespace.
 | ||||||
| func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { | func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { | ||||||
| 	err := CheckForFQDNRules(name) | 	err := CheckForFQDNRules(name) | ||||||
|  | |||||||
| @ -449,7 +449,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 					Bool("noise", isNoise). | 					Bool("noise", isNoise). | ||||||
| 					Str("machine", machine.Hostname). | 					Str("machine", machine.Hostname). | ||||||
| 					Time("last_successful_update", lastUpdate). | 					Time("last_successful_update", lastUpdate). | ||||||
| 					Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)). | 					Time("last_state_change", h.getLastStateChange(machine.Namespace)). | ||||||
| 					Msgf("There has been updates since the last successful update to %s", machine.Hostname) | 					Msgf("There has been updates since the last successful update to %s", machine.Hostname) | ||||||
| 				data, err := h.getMapResponseData(mapRequest, machine, false) | 				data, err := h.getMapResponseData(mapRequest, machine, false) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| @ -549,7 +549,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 					Bool("noise", isNoise). | 					Bool("noise", isNoise). | ||||||
| 					Str("machine", machine.Hostname). | 					Str("machine", machine.Hostname). | ||||||
| 					Time("last_successful_update", lastUpdate). | 					Time("last_successful_update", lastUpdate). | ||||||
| 					Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)). | 					Time("last_state_change", h.getLastStateChange(machine.Namespace)). | ||||||
| 					Msgf("%s is up to date", machine.Hostname) | 					Msgf("%s is up to date", machine.Hostname) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user